Compare commits

...

83 Commits

Author SHA1 Message Date
durch 3404efc283 Checkpoint 2022-04-12 11:53:24 +02:00
durch 820da702d5 Checkpoint 2022-04-11 17:42:17 +02:00
dependabot[bot] dc0b9c271c Bump ansi-regex in /docker/typescript_client/upload_contract (#1171)
Bumps [ansi-regex](https://github.com/chalk/ansi-regex) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/chalk/ansi-regex/releases)
- [Commits](https://github.com/chalk/ansi-regex/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: ansi-regex
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 14:07:59 +01:00
Jon Häggblad 9ef29037bc Merge pull request #1195 from nymtech/feature/expose-more-validator-in-wallet
wallet: expose additional validator configuration functionality to the frontend
2022-04-11 11:21:08 +02:00
Jon Häggblad 1e13d41245 wallet: make config file version optional 2022-04-11 10:52:58 +02:00
Jon Häggblad 46a4991c12 wallet: add version number to network config files 2022-04-11 10:52:58 +02:00
Jon Häggblad 9d2d670990 wallet: remove duplicate handler entries 2022-04-11 10:52:58 +02:00
Jon Häggblad baba5ed212 wallet: additional backend logic for selecting validators 2022-04-11 10:52:58 +02:00
Jon Häggblad ceb5f090cf wallet: expose select, add and remove validators 2022-04-11 10:52:58 +02:00
Raphaël Walther b6ffe8664c Added additionnal clean task for Windows 2022-04-11 09:39:01 +02:00
Jon Häggblad be4bc2bdcc Merge branch 'release/1.0.0-rc.1' into develop
Conflicts:
	validator-api/Cargo.toml
2022-04-08 12:14:47 +02:00
Jędrzej Stuczyński bc049cb954 Feature/aggregated econ dynamics explorer endpoint (#1203)
* Economic dynamics stats endpoint on the explorer API with dummy fixture data

* Populating the endpoint with real data aggregated from validator api

* Introduced new cache functionalities
2022-04-08 10:15:50 +01:00
Drazen Urch 9aa5b98465 Debugging validator (#1198)
* Checkpoint

* Replace Stream logic with StreamMap

* Ignore blacklisted mixnodes and gateways

* Moar logging

* Remove version checks

* Cleanup

* Some more cleanup
2022-04-07 18:13:29 +02:00
Jędrzej Stuczyński d6c9d1d08d Using fork of cosmrs with different address length validation 2022-04-05 17:52:59 +01:00
Jędrzej Stuczyński 49fc51853a Updated mainnet defaults to the most recent values 2022-04-05 17:52:41 +01:00
Jędrzej Stuczyński c177f14073 Using fork of cosmrs with different address length validation 2022-04-05 17:35:50 +01:00
Mark Sinclair 026932dc16 Wallet 1.0.2 visual tweaks (#1197)
* Add `fill` prop to Nym wordmark component

* Tweaks to client address display

* Add story for ClientAddress
2022-04-05 17:06:47 +01:00
Jędrzej Stuczyński 515d4b73f7 Updated mainnet defaults to the most recent values 2022-04-05 16:52:16 +01:00
Fouad fda3636783 Password for wallet with routes (#1196)
* new password flow

* update global error and load state from children

* fix linting

* dont load account when creating mnemonic

* wallets: provide placeholder functions for ui password

* wallet: platform_constants

* wallet: swap println to log

* UI for existing mnemonic to be use

* wallet: inline encryption of wallet file

* wallet: tweak error enum names

* wallet: general wallet_storage tidy

* wallet: tweak some type names

* create sign-in context

* update sign in functions

* move state to context

* update pages

* connect new rust methods with frontend

* update components

* remove non-existent method

* add separate sign in pages for mnemonic and password

* add a hook for clipboard copy

* fix workmark svg sizing issue

* create step component

* use new sign in  pages

* reorder pages

* use clipboard lib directly

* ui tweaks

* use login type selector

* update password strength test + use autofocus prop for password input

* start adding routes

* restructure with routes

* wip

* more wip

* more wip

* reset state where required

* minor flow updates

* validate version (any valid semver version)

* reset error on page move

* flow tweaks

* content update

Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
2022-04-05 16:46:16 +01:00
Dave Hrycyszyn c056269f0e Tagging release contracts 2022-04-05 15:37:38 +01:00
Mark Sinclair ca49fe2293 GitHub Actions fixes for publishing wallet 2022-04-05 09:42:37 +01:00
Mark Sinclair 1b37ff2242 Update wallet lock file 2022-04-04 18:17:42 +01:00
Mark Sinclair 3712b38230 Fix wallet lock file version 2022-04-04 18:17:13 +01:00
Mark Sinclair 467bda8ddd Add tauri updater feature 2022-04-04 18:15:33 +01:00
Mark Sinclair b083335f56 Add updater config to tauri.conf.json 2022-04-04 18:15:25 +01:00
Mark Sinclair 5cc08211b7 Merge pull request #1194 from nymtech/feature/nym-wallet-updater
Add auto-updater to Nym Wallet
2022-04-04 17:57:28 +01:00
Drazen Urch a63a94623f Update and query rewarding validator address (#1193) 2022-04-04 16:55:46 +02:00
Mark Sinclair 5deafaa27b Add tauri updater feature 2022-04-04 15:03:21 +01:00
Mark Sinclair 021b542a4a Add updater config to tauri.conf.json 2022-04-04 15:03:21 +01:00
Mark Sinclair 64ee03112e GitHub Actions: fix wallet updater location for MacOS 2022-04-04 15:03:01 +01:00
Mark Sinclair bed709b155 Fix wallet lock file version 2022-04-04 15:02:36 +01:00
Mark Sinclair 1f6d4153a7 GitHub Actions: fix wallet updater path for upload to release 2022-04-04 14:25:55 +01:00
Mark Sinclair de45ab8995 GitHub Actions: nym wallet build uploads updater zip and signature to release 2022-04-04 13:47:01 +01:00
Mark Sinclair 15b552fa62 GitHub Actions wallet publish - add missing env vars for updater signing 2022-04-04 12:07:30 +01:00
Mark Sinclair 0d343eb82d Update GitHub Actions for wallet to upload the signature for the auto-updater 2022-04-04 11:39:08 +01:00
Jon Häggblad b5eddb6919 wallet: fix get urls function call 2022-04-04 09:19:00 +02:00
Jon Häggblad 7ddde50ffa wallet: expose validator urls to the frontend 2022-04-04 09:10:56 +02:00
Jon Häggblad d81967189a Fix clippy warnings for beta toolchain 2022-04-04 08:55:34 +02:00
Jon Häggblad 11b22ce2c1 wallet: add test for decrypting file 2022-04-03 22:39:39 +02:00
Jon Häggblad 58fd40fb8e Fix clippy warnings 2022-04-03 21:15:50 +02:00
Max Hampshire 5bb516471e updated mainnet bandwidthgen contract address 2022-04-03 20:26:25 +02:00
Mark Sinclair 2b9ea90e16 Nym Wallet version 1.0.2 2022-04-01 18:50:16 +01:00
Mark Sinclair 2779b5d28a Password for wallet with routes (#1187)
* new password flow

* update global error and load state from children

* fix linting

* dont load account when creating mnemonic

* wallets: provide placeholder functions for ui password

* wallet: platform_constants

* wallet: swap println to log

* UI for existing mnemonic to be use

* wallet: inline encryption of wallet file

* wallet: tweak error enum names

* wallet: general wallet_storage tidy

* wallet: tweak some type names

* create sign-in context

* update sign in functions

* move state to context

* update pages

* connect new rust methods with frontend

* update components

* remove non-existent method

* add separate sign in pages for mnemonic and password

* add a hook for clipboard copy

* fix workmark svg sizing issue

* create step component

* use new sign in  pages

* reorder pages

* use clipboard lib directly

* ui tweaks

* use login type selector

* update password strength test + use autofocus prop for password input

* start adding routes

* restructure with routes

* wip

* more wip

* more wip

* reset state where required

* wallet: remove unused rust use statements

* fix unbond page

Co-authored-by: fmtabbara <fmtabbara@hotmail.co.uk>
Co-authored-by: Jon Häggblad <jon.haggblad@gmail.com>
Co-authored-by: Tommy Verrall <tommyvez@protonmail.com>
2022-04-01 18:44:44 +01:00
Mark Sinclair 9ea2ce5c70 Merge pull request #1184 from nymtech/feature/adding-discord
Feature/adding discord
2022-04-01 15:50:03 +01:00
Jon Häggblad 11e1e728e1 Merge pull request #1183 from nymtech/feature/wallet-save-and-load-validators-from-file
wallet: config backend for validator selection
2022-04-01 15:40:02 +02:00
Jon Häggblad 4116aa18a8 wallet: config backend for validators 2022-04-01 15:18:59 +02:00
Jon Häggblad bdebe00c25 Merge pull request #1186 from nymtech/feature/wallet-add-validate-mnemonic
wallet: add validate_mnemonic
2022-04-01 15:18:09 +02:00
Jon Häggblad e106390e1d wallet: add validate_mnemonic 2022-04-01 15:09:22 +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
gala1234 09155fbf12 update discord url 2022-04-01 12:42:17 +02:00
gala1234 53292ceca9 enable discord icon 2022-04-01 12:37:24 +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
Jon Häggblad 47bdf38776 wallet: fix clippy 2022-03-25 21:25:09 +01:00
Jon Häggblad cdd883c174 Merge pull request #1153 from nymtech/feature/wire-up-wallet-storage
wallet: wire up account storage
2022-03-25 21:14:19 +01:00
Jon Häggblad 2d82a51905 wallet: reject storing account with the same id 2022-03-25 21:04:35 +01:00
Jon Häggblad 38c2ce9837 wallet: tweak some type names 2022-03-25 21:04:35 +01:00
Jon Häggblad a867921fdd wallet: general wallet_storage tidy 2022-03-25 21:04:35 +01:00
Jon Häggblad 423cdb1e1b wallet: tweak error enum names 2022-03-25 21:04:35 +01:00
Jon Häggblad 7aeac58fd9 wallet: inline encryption of wallet file 2022-03-25 21:04:35 +01:00
Jon Häggblad 30fafa509c wallet: swap println to log 2022-03-25 21:04:35 +01:00
Jon Häggblad c950556506 wallet: platform_constants 2022-03-25 21:04:35 +01:00
Jon Häggblad 9a49213973 wallets: provide placeholder functions for ui password 2022-03-25 21:04:34 +01:00
187 changed files with 11159 additions and 1954 deletions
+5
View File
@@ -75,6 +75,11 @@ jobs:
command: clippy
args: --workspace --all-targets -- -D warnings
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
# COCONUT stuff
- name: Build all binaries with coconut enabled
@@ -64,12 +64,16 @@ jobs:
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn && yarn build
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
with:
files: nym-wallet/target/release/bundle/dmg/*.dmg
files: |
nym-wallet/target/release/bundle/dmg/*.dmg
nym-wallet/target/release/bundle/macos/*.app.tar.gz*
- name: Clean up keychain
if: ${{ always() }}
@@ -37,10 +37,16 @@ jobs:
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install app dependencies and build it
run: yarn && yarn build
- name: Install app dependencies
run: yarn
- name: Build app
run: yarn build
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
with:
files: nym-wallet/target/release/bundle/appimage/*.AppImage
files: |
nym-wallet/target/release/bundle/appimage/*.AppImage
nym-wallet/target/release/bundle/appimage/*.AppImage.tar.gz*
@@ -63,9 +63,13 @@ jobs:
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
run: yarn build
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
with:
files: nym-wallet/target/release/bundle/msi/*.msi
files: |
nym-wallet/target/release/bundle/msi/*.msi
nym-wallet/target/release/bundle/msi/*.msi.zip*
@@ -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
+165 -43
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]]
@@ -607,6 +623,7 @@ version = "0.1.0"
dependencies = [
"handlebars",
"humantime-serde",
"log",
"network-defaults",
"serde",
"toml",
@@ -725,6 +742,16 @@ dependencies = [
"tendermint-proto",
]
[[package]]
name = "cosmos-sdk-proto"
version = "0.9.0"
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
dependencies = [
"prost",
"prost-types",
"tendermint-proto",
]
[[package]]
name = "cosmrs"
version = "0.4.1"
@@ -732,7 +759,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "505ea048e9ff2f906d6b954f9f8157d903ca468bfb301d906b40ecc25ba6838d"
dependencies = [
"bip32",
"cosmos-sdk-proto",
"cosmos-sdk-proto 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ecdsa 0.13.4",
"eyre",
"getrandom 0.2.5",
"k256 0.10.4",
"prost",
"prost-types",
"rand_core 0.6.3",
"serde",
"serde_json",
"subtle-encoding",
"tendermint",
"thiserror",
]
[[package]]
name = "cosmrs"
version = "0.4.1"
source = "git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation#911fbe1236cfed591783ccef01018f7ccc97c496"
dependencies = [
"bip32",
"cosmos-sdk-proto 0.9.0 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)",
"ecdsa 0.13.4",
"eyre",
"getrandom 0.2.5",
@@ -750,9 +798,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3"
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -763,18 +811,18 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45"
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0"
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -817,7 +865,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 +897,10 @@ version = "0.1.0"
dependencies = [
"bls12_381",
"coconut-interface",
"cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"crypto",
"network-defaults",
"rand 0.7.3",
"thiserror",
"url",
"validator-client",
@@ -875,7 +948,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 +958,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 +970,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 +983,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 +993,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",
]
@@ -1121,7 +1194,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",
]
@@ -1370,7 +1443,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]]
@@ -1578,7 +1651,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",
@@ -1804,6 +1877,7 @@ name = "gateway-client"
version = "0.1.0"
dependencies = [
"coconut-interface",
"cosmrs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"credentials",
"crypto",
"fluvio-wasm-timer",
@@ -1822,6 +1896,7 @@ dependencies = [
"tokio-tungstenite",
"tungstenite",
"url",
"validator-client",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-utils",
@@ -1886,7 +1961,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",
@@ -1899,7 +1974,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",
@@ -2387,7 +2462,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",
@@ -2484,7 +2559,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",
@@ -2496,7 +2571,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",
@@ -2569,6 +2644,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"
@@ -2596,7 +2677,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]]
@@ -2605,7 +2686,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",
@@ -2804,7 +2885,7 @@ dependencies = [
name = "network-defaults"
version = "0.1.0"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"hex-literal",
"once_cell",
"serde",
@@ -3069,9 +3150,11 @@ name = "nym-validator-api"
version = "1.0.0-rc.1"
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",
@@ -3085,6 +3168,7 @@ dependencies = [
"humantime-serde",
"log",
"mixnet-contract-common",
"nymcoconut",
"nymsphinx",
"pin-project",
"pretty_env_logger",
@@ -3100,6 +3184,7 @@ dependencies = [
"thiserror",
"time 0.3.7",
"tokio",
"tokio-stream",
"topology",
"url",
"validator-api-requests",
@@ -3290,7 +3375,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",
@@ -3394,7 +3479,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",
@@ -3408,7 +3493,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",
@@ -3557,6 +3642,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"
@@ -4710,6 +4808,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"
@@ -4751,7 +4861,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",
@@ -4763,7 +4873,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",
]
@@ -4790,7 +4900,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",
@@ -5239,7 +5349,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",
@@ -5717,7 +5827,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",
@@ -5856,9 +5966,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uint"
version = "0.9.1"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
dependencies = [
"byteorder",
"crunchy",
@@ -5981,10 +6091,12 @@ dependencies = [
"base64",
"bip39",
"coconut-interface",
"colored",
"config",
"cosmrs",
"cosmrs 0.4.1 (git+https://github.com/nymtech/cosmos-rust?branch=bugfix/account-id-length-validation)",
"cosmwasm-std",
"flate2",
"futures",
"itertools",
"log",
"mixnet-contract-common",
@@ -5995,6 +6107,7 @@ dependencies = [
"serde_json",
"sha2",
"thiserror",
"tokio",
"ts-rs",
"url",
"validator-api-requests",
@@ -6027,7 +6140,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",
@@ -6052,7 +6165,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
version = "1.0.0-rc.1"
version = "1.0.0"
dependencies = [
"config",
"cosmwasm-std",
@@ -6122,7 +6235,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",
@@ -6149,7 +6262,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",
@@ -6402,6 +6515,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 width="210" height="56" viewBox="0 0 210 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg 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: 1.0 KiB

After

Width:  |  Height:  |  Size: 1011 B

+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> {
@@ -72,7 +72,12 @@ impl PacketRouter {
if !received_acks.is_empty() {
trace!("routing acks");
self.ack_sender.unbounded_send(received_acks).unwrap();
match self.ack_sender.unbounded_send(received_acks) {
Ok(_) => {}
Err(e) => {
error!("failed to send ack: {:?}", e);
}
};
}
}
}
@@ -74,7 +74,7 @@ impl PartiallyDelegated {
// This would also require NOT discarding any text responses here.
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
Message::Text(text) => debug!(
Message::Text(text) => trace!(
"received a text message - probably a response to some previous query! - {}",
text
),
@@ -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" }
@@ -29,11 +32,7 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests
async-trait = { version = "0.1.51", optional = true }
bip39 = { version = "1", features = ["rand"], optional = true }
config = { path = "../../config", optional = true }
cosmrs = { version = "0.4.1", features = [
"rpc",
"bip32",
"cosmwasm",
], optional = true }
cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"], optional = true}
prost = { version = "0.9", default-features = false, optional = true }
flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
@@ -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,238 @@
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".red(),
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)];
@@ -39,6 +39,10 @@ impl Client {
self.url = new_url
}
pub fn current_url(&self) -> &Url {
&self.url
}
async fn query_validator_api<T, K, V>(
&self,
path: PathSegments<'_>,
@@ -66,14 +70,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 +258,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 +291,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,
})
}
}
+2 -1
View File
@@ -9,8 +9,9 @@ edition = "2021"
[dependencies]
handlebars = "3.0.1"
humantime-serde = "1.0"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5.6"
url = "2.2"
network-defaults = { path = "../network-defaults" }
network-defaults = { path = "../network-defaults" }
+7 -1
View File
@@ -13,6 +13,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
fn template() -> &'static str;
fn config_file_name() -> String {
log::trace!("NymdConfig::config_file_name");
"config.toml".to_string()
}
@@ -20,6 +21,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
// default, most probable, implementations; can be easily overridden where required
fn default_config_directory(id: Option<&str>) -> PathBuf {
log::trace!("NymdConfig::default_config_directory");
if let Some(id) = id {
Self::default_root_directory().join(id).join("config")
} else {
@@ -28,6 +30,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
}
fn default_data_directory(id: Option<&str>) -> PathBuf {
log::trace!("NymdConfig::default_data_path");
if let Some(id) = id {
Self::default_root_directory().join(id).join("data")
} else {
@@ -36,6 +39,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
}
fn default_config_file_path(id: Option<&str>) -> PathBuf {
log::trace!("NymdConfig::default_config_file_path");
Self::default_config_directory(id).join(Self::config_file_name())
}
@@ -68,7 +72,9 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
}
fn load_from_file(id: Option<&str>) -> io::Result<Self> {
let config_contents = fs::read_to_string(Self::default_config_file_path(id))?;
let file = Self::default_config_file_path(id);
log::trace!("Loading from file: {:#?}", file);
let config_contents = fs::read_to_string(file)?;
toml::from_str(&config_contents)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
@@ -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 {}
@@ -7,7 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = "1.0.0-beta6"
cosmwasm-std = "1.0.0-beta8"
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
@@ -26,7 +26,7 @@ pub use mixnode::{
pub use msg::*;
pub use types::*;
pub type U128 = fixed::types::U75F53;
pub type U128 = fixed::types::U50F78;
fixed::const_fixed_from_int! {
const ONE: U128 = 1;
@@ -6,7 +6,7 @@ use crate::reward_params::RewardParams;
use crate::{Delegation, IdentityKey, SphinxKey};
use crate::{ONE, U128};
use az::CheckedCast;
use cosmwasm_std::{coin, Addr, Coin, Uint128};
use cosmwasm_std::{coin, Addr, Coin, Decimal, Uint128};
use log::error;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
@@ -195,22 +195,16 @@ pub struct DelegatorRewardParams {
// to be completely honest I don't understand all consequences of using `#[schemars(with = "String")]`
// for U128 here, but it seems that CosmWasm is using the same attribute for their Uint128
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
sigma: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
profit_margin: U128,
#[schemars(with = "String")]
#[serde(with = "fixed_U128_as_string")]
node_profit: U128,
sigma: Decimal,
profit_margin: Decimal,
node_profit: Uint128,
}
impl DelegatorRewardParams {
pub fn new(
sigma: U128,
profit_margin: U128,
node_profit: U128,
sigma: Decimal,
profit_margin: Decimal,
node_profit: Uint128,
reward_params: RewardParams,
) -> Self {
DelegatorRewardParams {
@@ -223,23 +217,14 @@ impl DelegatorRewardParams {
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
let scaled_delegation_amount = delegation_amount / circulating_supply;
let scaled_delegation_amount = delegation_amount / Uint128::new(self.reward_params.circulating_supply());
let delegator_reward =
(ONE - self.profit_margin) * scaled_delegation_amount / self.sigma * self.node_profit;
(Decimal::one() - self.profit_margin) * (scaled_delegation_amount / self.sigma.atomics()) * self.node_profit;
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast delegator reward ({}) to u128, returning 0",
reward,
);
0u128
}
let reward = delegator_reward.max(Uint128::zero());
reward.u128()
}
pub fn node_reward_params(&self) -> RewardParams {
@@ -250,8 +235,8 @@ impl DelegatorRewardParams {
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct StoredNodeRewardResult {
reward: Uint128,
lambda: Uint128,
sigma: Uint128,
lambda: Decimal,
sigma: Decimal,
}
impl StoredNodeRewardResult {
@@ -259,11 +244,11 @@ impl StoredNodeRewardResult {
self.reward
}
pub fn lambda(&self) -> Uint128 {
pub fn lambda(&self) -> Decimal {
self.lambda
}
pub fn sigma(&self) -> Uint128 {
pub fn sigma(&self) -> Decimal {
self.sigma
}
}
@@ -273,45 +258,30 @@ impl TryFrom<NodeRewardResult> for StoredNodeRewardResult {
fn try_from(node_reward_result: NodeRewardResult) -> Result<Self, Self::Error> {
Ok(StoredNodeRewardResult {
reward: Uint128::new(
node_reward_result
.reward()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
lambda: Uint128::new(
node_reward_result
.lambda()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
sigma: Uint128::new(
node_reward_result
.sigma()
.checked_cast()
.ok_or(MixnetContractError::CastError)?,
),
reward: node_reward_result.reward(),
lambda: node_reward_result.lambda(),
sigma: node_reward_result.sigma(),
})
}
}
#[derive(Debug, Copy, Clone)]
pub struct NodeRewardResult {
reward: U128,
lambda: U128,
sigma: U128,
reward: Uint128,
lambda: Decimal,
sigma: Decimal,
}
impl NodeRewardResult {
pub fn reward(&self) -> U128 {
pub fn reward(&self) -> Uint128 {
self.reward
}
pub fn lambda(&self) -> U128 {
pub fn lambda(&self) -> Decimal {
self.lambda
}
pub fn sigma(&self) -> U128 {
pub fn sigma(&self) -> Decimal {
self.sigma
}
}
@@ -357,6 +327,10 @@ impl MixNodeBond {
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
}
pub fn profit_margin_dec(&self) -> Decimal {
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
}
pub fn identity(&self) -> &String {
&self.mix_node.identity_key
}
@@ -390,53 +364,61 @@ impl MixNodeBond {
self.total_delegation.clone()
}
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 {
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> Decimal {
self.total_bond_to_circulating_supply(circulating_supply)
* U128::from_num(rewarded_set_size)
* Decimal::from_atomics(rewarded_set_size, 0).unwrap()
}
// TODO: There is an effect here when adding accumulted rewards to the total bond, ie accumulated rewards will not
// affect lambda, but will affect sigma, in turn over time, if left unclaimed operator rewards will not compound, but
// behave similarly to delegations.
// The question is should this be taken into account when calculating operator rewards?
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply)
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
// U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply);
Decimal::from_atomics(self.pledge_amount().amount.u128(), 0).unwrap()
/ Uint128::from(circulating_supply)
}
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
/ U128::from_num(circulating_supply)
pub fn total_bond_to_circulating_supply(&self, circulating_supply: u128) -> Decimal {
// U128::from_num(self.pledge_amount().amount.u128() + self.total_delegation().amount.u128())
// / U128::from_num(circulating_supply)
Decimal::from_atomics(
self.total_bond().unwrap_or(0) + self.pledge_amount().amount.u128(),
0,
)
.unwrap()
/ Uint128::from(circulating_supply)
}
pub fn lambda(&self, params: &RewardParams) -> U128 {
pub fn lambda(&self, params: &RewardParams) -> Decimal {
// Ratio of a bond to the token circulating supply
let pledge_to_circulating_supply_ratio =
self.pledge_to_circulating_supply(params.circulating_supply());
pledge_to_circulating_supply_ratio.min(params.one_over_k())
pledge_to_circulating_supply_ratio.min(params.one_over_k_dec())
}
pub fn sigma(&self, params: &RewardParams) -> U128 {
pub fn sigma(&self, params: &RewardParams) -> Decimal {
// Ratio of a delegation to the the token circulating supply
let total_bond_to_circulating_supply_ratio =
self.total_bond_to_circulating_supply(params.circulating_supply());
total_bond_to_circulating_supply_ratio.min(params.one_over_k())
total_bond_to_circulating_supply_ratio.min(params.one_over_k_dec())
}
pub fn estimate_reward(
&self,
params: &RewardParams,
) -> Result<(u64, u64, u64), MixnetContractError> {
let total_node_reward = self.reward(params);
let total_node_reward = self
.reward(params)
.reward();
let operator_reward = self.operator_reward(params);
// TODO: This overestimates the reward by a lot, it should take a Uint128 and return estiamte for that
let delegators_reward = self.reward_delegation(self.total_delegation().amount, params);
// Total reward has to be the sum of operator and delegator rewards
let delegators_reward = total_node_reward.u128() - operator_reward;
Ok((
total_node_reward
.reward()
.checked_to_num::<u128>()
.unwrap_or_default()
.try_into()?,
total_node_reward.u128().try_into()?,
operator_reward.try_into()?,
delegators_reward.try_into()?,
))
@@ -446,11 +428,16 @@ impl MixNodeBond {
let lambda = self.lambda(params);
let sigma = self.sigma(params);
let reward = params.performance()
* params.epoch_reward_pool()
println!("performance: {}", params.performance_dec().atomics());
println!("reward pool: {}", Uint128::from(params.epoch_reward_pool()));
println!("omega: {}", params.omega());
let reward = params.performance_dec()
* Uint128::from(params.epoch_reward_pool())
* (sigma * params.omega()
+ params.alpha() * lambda * sigma * params.rewarded_set_size())
/ (ONE + params.alpha());
+ params.alpha_dec() * lambda * sigma * Uint128::from(params.rewarded_set_size()))
/ (Decimal::one() + params.alpha_dec()).atomics();
NodeRewardResult {
reward,
@@ -459,53 +446,45 @@ impl MixNodeBond {
}
}
pub fn node_profit(&self, params: &RewardParams) -> U128 {
if self.reward(params).reward() < params.node.operator_cost() {
U128::from_num(0u128)
pub fn node_profit(&self, params: &RewardParams) -> Uint128 {
if self.reward(params).reward() < params.node.operator_cost_dec() {
Uint128::zero()
} else {
self.reward(params).reward() - params.node.operator_cost()
self.reward(params).reward() - params.node.operator_cost_dec()
}
}
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
let reward = self.reward(params);
let profit = if reward.reward < params.node.operator_cost() {
U128::from_num(0u128)
println!("{:?}", reward);
let profit = if reward.reward < params.node.operator_cost_dec() {
Uint128::zero()
} else {
reward.reward - params.node.operator_cost()
reward.reward - params.node.operator_cost_dec()
};
let operator_base_reward = reward.reward.min(params.node.operator_cost());
let operator_reward = (self.profit_margin()
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
let operator_base_reward = reward.reward.min(params.node.operator_cost_dec());
let operator_reward = (self.profit_margin_dec()
+ (Decimal::one() - self.profit_margin_dec()) * reward.lambda / reward.sigma.atomics())
* profit;
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0));
let reward = (operator_reward + operator_base_reward).max(Uint128::new(0));
if let Some(int_reward) = reward.checked_cast() {
int_reward
} else {
error!(
"Could not cast reward ({}) to u128, returning 0 - mixnode {}",
reward,
self.identity()
);
0u128
}
reward.u128()
}
pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
{
self.total_bond_to_circulating_supply(params.circulating_supply())
} else {
params.one_over_k()
}
}
// pub fn sigma_ratio(&self, params: &RewardParams) -> U128 {
// if self.total_bond_to_circulating_supply(params.circulating_supply()) < params.one_over_k()
// {
// self.total_bond_to_circulating_supply(params.circulating_supply())
// } else {
// params.one_over_k()
// }
// }
pub fn reward_delegation(&self, delegation_amount: Uint128, params: &RewardParams) -> u128 {
let reward_params = DelegatorRewardParams::new(
self.sigma(params),
self.profit_margin(),
self.profit_margin_dec(),
self.node_profit(params),
params.to_owned(),
);
@@ -15,6 +15,9 @@ pub struct InstantiateMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
UpdateRewardingValidatorAddress {
address: String,
},
InitEpoch {},
ReconcileDelegations {},
CheckpointMixnodes {},
@@ -101,6 +104,7 @@ pub enum ExecuteMsg {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
GetRewardingValidatorAddress {},
GetAllDelegationKeys {},
DebugGetAllDelegationValues {},
GetContractVersion {},
@@ -1,10 +1,14 @@
use crate::{error::MixnetContractError, mixnode::StoredNodeRewardResult, ONE, U128};
use az::CheckedCast;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Decimal, Uint128};
use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
fn sane_decimal(value: &Uint128) -> Decimal {
Decimal::new(value * Uint128::new(1_000_000_000_000_000_000u128))
}
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
pub struct NodeEpochRewards {
params: NodeRewardParams,
@@ -25,11 +29,11 @@ impl NodeEpochRewards {
self.epoch_id
}
pub fn sigma(&self) -> Uint128 {
pub fn sigma(&self) -> Decimal {
self.result.sigma()
}
pub fn lambda(&self) -> Uint128 {
pub fn lambda(&self) -> Decimal {
self.result.lambda()
}
@@ -45,6 +49,19 @@ impl NodeEpochRewards {
U128::from_num(self.params.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn operator_cost_dec(&self) -> Uint128 {
Decimal::from_ratio(self.params.uptime, 100u128)
* Uint128::from(DEFAULT_OPERATOR_INTERVAL_COST)
}
pub fn node_profit_dec(&self) -> Uint128 {
if self.reward() < self.operator_cost_dec() {
Uint128::zero()
} else {
self.reward() - self.operator_cost_dec()
}
}
pub fn node_profit(&self) -> U128 {
let reward = U128::from_num(self.reward().u128());
if reward < self.operator_cost() {
@@ -54,44 +71,34 @@ impl NodeEpochRewards {
}
}
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit();
let operator_base_reward = reward.min(self.operator_cost());
pub fn operator_reward(&self, profit_margin: Decimal) -> Result<Uint128, MixnetContractError> {
let reward = self.node_profit_dec();
let operator_base_reward = reward.min(self.operator_cost_dec());
let operator_reward = (profit_margin
+ (ONE - profit_margin) * U128::from_num(self.lambda().u128())
/ U128::from_num(self.sigma().u128()))
+ (Decimal::one() - profit_margin) * self.lambda() / self.sigma().atomics())
* reward;
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
let reward = (operator_reward + operator_base_reward).max(Uint128::zero());
if let Some(int_reward) = reward.checked_cast() {
Ok(Uint128::new(int_reward))
} else {
Err(MixnetContractError::CastError)
}
Ok(reward)
}
pub fn delegation_reward(
&self,
delegation_amount: Uint128,
profit_margin: U128,
profit_margin: Decimal,
epoch_reward_params: EpochRewardParams,
) -> Result<Uint128, MixnetContractError> {
// change all values into their fixed representations
let delegation_amount = U128::from_num(delegation_amount.u128());
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
// change all values into their fixed representations;
let scaled_delegation_amount = delegation_amount / circulating_supply;
let delegator_reward = (ONE - profit_margin) * scaled_delegation_amount
/ U128::from_num(self.sigma().u128())
* self.node_profit();
let scaled_delegation_amount =
delegation_amount / Uint128::from(epoch_reward_params.circulating_supply());
let delegator_reward = (Decimal::one() - profit_margin) * scaled_delegation_amount
/ self.sigma().atomics()
* self.node_profit_dec();
let reward = delegator_reward.max(U128::ZERO);
if let Some(int_reward) = reward.checked_cast() {
Ok(Uint128::new(int_reward))
} else {
Err(MixnetContractError::CastError)
}
let reward = delegator_reward.max(Uint128::zero());
Ok(reward)
}
}
@@ -176,6 +183,11 @@ impl NodeRewardParams {
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn operator_cost_dec(&self) -> Uint128 {
Decimal::from_ratio(self.uptime.u128(), 100u128)
* Uint128::new(DEFAULT_OPERATOR_INTERVAL_COST as u128)
}
pub fn uptime(&self) -> u128 {
self.uptime.u128()
}
@@ -196,18 +208,36 @@ impl RewardParams {
RewardParams { epoch, node }
}
pub fn omega(&self) -> U128 {
pub fn omega(&self) -> Uint128 {
// As per keybase://chat/nymtech#tokeneconomics/1179
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
// let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
// - (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
if self.in_active_set() {
let active_set_work_factor = self.active_set_work_factor_dec();
let rewarded_set_size = sane_decimal(&self.epoch.rewarded_set_size);
let idle_nodes = sane_decimal(&self.idle_nodes());
println!("active_set_work_factor: {}", active_set_work_factor);
println!("rewarded_set_size: {}", rewarded_set_size);
println!("idle_nodes: {}", idle_nodes);
let denom = active_set_work_factor * rewarded_set_size
- (active_set_work_factor - Decimal::one()) * idle_nodes;
println!("denom: {}", denom);
let result = if self.in_active_set() {
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
self.active_set_work_factor() / denom * self.rewarded_set_size()
active_set_work_factor
* Decimal::from_ratio(Decimal::one().atomics(), denom.atomics())
* rewarded_set_size
} else {
// work_idle = 1 / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
ONE / denom * self.rewarded_set_size()
}
Decimal::one() / denom.atomics() * rewarded_set_size
};
println!("omega_result: {}", result);
result.atomics()
}
pub fn idle_nodes(&self) -> Uint128 {
@@ -218,6 +248,10 @@ impl RewardParams {
U128::from_num(self.epoch.active_set_work_factor)
}
pub fn active_set_work_factor_dec(&self) -> Decimal {
sane_decimal(&Uint128::new(self.epoch.active_set_work_factor as u128))
}
pub fn in_active_set(&self) -> bool {
self.node.in_active_set
}
@@ -226,6 +260,10 @@ impl RewardParams {
U128::from_num(self.node.uptime.u128()) / U128::from_num(100)
}
pub fn performance_dec(&self) -> Decimal {
Decimal::from_ratio(self.node.uptime, 100u128)
}
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
self.node.reward_blockstamp = blockstamp;
}
@@ -254,7 +292,15 @@ impl RewardParams {
ONE / U128::from_num(self.epoch.rewarded_set_size.u128())
}
pub fn one_over_k_dec(&self) -> Decimal {
Decimal::one() / self.epoch.rewarded_set_size
}
pub fn alpha(&self) -> U128 {
U128::from_num(self.epoch.sybil_resistance_percent) / U128::from_num(100)
}
pub fn alpha_dec(&self) -> Decimal {
Decimal::from_atomics(self.epoch.sybil_resistance_percent, 2).unwrap()
}
}
+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(),
&[],
));
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
+5 -3
View File
@@ -6,15 +6,17 @@ use crate::ValidatorDetails;
pub(crate) const BECH32_PREFIX: &str = "n";
pub const DENOM: &str = "unym";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str =
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
pub(crate) const VESTING_CONTRACT_ADDRESS: &str =
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
hex_literal::hex!("0000000000000000000000000000000000000000");
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy";
pub(crate) fn validators() -> Vec<ValidatorDetails> {
vec![ValidatorDetails::new(
+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;
+35 -12
View File
@@ -191,12 +191,35 @@ 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"
dependencies = [
"handlebars",
"humantime-serde",
"log",
"network-defaults",
"serde",
"toml",
@@ -218,9 +241,9 @@ dependencies = [
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88c2565b1e73a816fb659ef4838fc356143fbd35f43c48a51d2d7d4e5d6679d3"
checksum = "37e70111e9701c3ec43bfbff0e523cd4cb115876b4d3433813436dd0934ee962"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
@@ -231,9 +254,9 @@ dependencies = [
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa89fcdf8dbbe0088e663d0a814aa7368e7ebe8fb045a3a150fb5fdc2ffe3b45"
checksum = "58bc2ad5d86be5f6068833f63e20786768db6890019c095dd7775232184fb7b3"
dependencies = [
"syn",
]
@@ -250,9 +273,9 @@ dependencies = [
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcb8f99a61d0b9069e1afc80a4ffea87dcc3523edd992080923870b13a677da0"
checksum = "915ca82bd944f116f3a9717481f3fa657e4a73f28c4887288761ebb24e6fbe10"
dependencies = [
"base64",
"cosmwasm-crypto",
@@ -267,9 +290,9 @@ dependencies = [
[[package]]
name = "cosmwasm-storage"
version = "1.0.0-beta7"
version = "1.0.0-beta8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07f856099c824aa8f2488e62d1da3fc06383d3fdbc764573595f451be43441a2"
checksum = "1c4be9fd8c9d3ae7d0c32a925ecbc20707007ce0cba1f7538c0d78b7a2d3729b"
dependencies = [
"cosmwasm-std",
"serde",
@@ -810,7 +833,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "mixnet-contract"
version = "1.0.0-rc.1"
version = "1.0.0"
dependencies = [
"az",
"bs58",
@@ -1444,9 +1467,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uint"
version = "0.9.1"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0"
dependencies = [
"byteorder",
"crunchy",
@@ -1518,7 +1541,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vesting-contract"
version = "1.0.0-rc.1"
version = "1.0.0"
dependencies = [
"config",
"cosmwasm-std",
+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,14 +1,12 @@
{
"rinkeby":{
"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B",
"BANDWIDTH_GENERATOR":"0x5FbDB2315678afecb367f032d93F642f64180aa3",
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"
},
"mainnet":{
"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
"NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
"BANDWIDTH_GENERATOR":"",
"rinkeby":
{"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B",
"BANDWIDTH_GENERATOR":"0xfa2714Bf14EB5Bb887e4A54984C6F7A7e3E6c84b",
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"},
"mainnet":
{"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
"NYMT":"0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
"BANDWIDTH_GENERATOR":"0x3FfEb99acca159A182f35F9944dAf3BF41Ae8165",
"BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be",
"GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"
}
"GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"}
}
+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);
}
}
+3 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "mixnet-contract"
version = "1.0.0-rc.1"
version = "1.0.0"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
edition = "2021"
@@ -20,8 +20,8 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
config = { path = "../../common/config"}
cosmwasm-std = "1.0.0-beta6"
cosmwasm-storage = "1.0.0-beta6"
cosmwasm-std = "1.0.0-beta8"
cosmwasm-storage = "1.0.0-beta8"
cw-storage-plus = "0.13.1"
az = "1.2.0"
+8 -1
View File
@@ -18,9 +18,10 @@ use crate::interval::queries::{
use crate::interval::transactions::{init_epoch, try_init_epoch};
use crate::mixnet_contract_settings::models::ContractState;
use crate::mixnet_contract_settings::queries::{
query_contract_settings_params, query_contract_version,
query_contract_settings_params, query_contract_version, query_rewarding_validator_address,
};
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
use crate::mixnet_contract_settings::transactions::try_update_rewarding_validator_address;
use crate::mixnodes::bonding_queries as mixnode_queries;
use crate::mixnodes::bonding_queries::query_mixnodes_paged;
use crate::mixnodes::layer_queries::query_layer_distribution;
@@ -96,6 +97,9 @@ pub fn execute(
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::UpdateRewardingValidatorAddress { address } => {
try_update_rewarding_validator_address(deps, info, address)
}
ExecuteMsg::InitEpoch {} => try_init_epoch(info, deps.storage, env),
ExecuteMsg::BondMixnode {
mix_node,
@@ -276,6 +280,9 @@ pub fn execute(
#[entry_point]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let query_res = match msg {
QueryMsg::GetRewardingValidatorAddress {} => {
to_binary(&query_rewarding_validator_address(deps)?)
}
QueryMsg::GetContractVersion {} => to_binary(&query_contract_version()),
QueryMsg::GetMixNodes { start_after, limit } => {
to_binary(&query_mixnodes_paged(deps, start_after, limit)?)
@@ -11,6 +11,12 @@ pub(crate) fn query_contract_settings_params(deps: Deps<'_>) -> StdResult<Contra
.map(|settings| settings.params)
}
pub fn query_rewarding_validator_address(deps: Deps<'_>) -> StdResult<String> {
storage::CONTRACT_STATE
.load(deps.storage)
.map(|settings| settings.rewarding_validator_address.to_string())
}
pub(crate) fn query_contract_version() -> MixnetContractVersion {
// as per docs
// env! macro will expand to the value of the named environment variable at
@@ -3,12 +3,30 @@
use super::storage;
use crate::error::ContractError;
use cosmwasm_std::Addr;
use cosmwasm_std::DepsMut;
use cosmwasm_std::MessageInfo;
use cosmwasm_std::Response;
use mixnet_contract_common::events::new_settings_update_event;
use mixnet_contract_common::ContractStateParams;
pub fn try_update_rewarding_validator_address(
deps: DepsMut<'_>,
info: MessageInfo,
address: String,
) -> Result<Response, ContractError> {
let mut state = storage::CONTRACT_STATE.load(deps.storage)?;
if info.sender != state.owner {
return Err(ContractError::Unauthorized);
}
state.rewarding_validator_address = Addr::unchecked(address);
storage::CONTRACT_STATE.save(deps.storage, &state)?;
Ok(Response::default())
}
pub(crate) fn try_update_contract_settings(
deps: DepsMut<'_>,
info: MessageInfo,
@@ -48,12 +66,45 @@ pub mod tests {
use super::*;
use crate::contract::{INITIAL_GATEWAY_PLEDGE, INITIAL_MIXNODE_PLEDGE};
use crate::error::ContractError;
use crate::mixnet_contract_settings::queries::query_rewarding_validator_address;
use crate::mixnet_contract_settings::transactions::try_update_contract_settings;
use crate::support::tests::test_helpers;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::Response;
use mixnet_contract_common::ContractStateParams;
#[test]
fn update_contract_rewarding_validtor_address() {
let mut deps = test_helpers::init_contract();
let info = mock_info("not-the-creator", &[]);
let res = try_update_rewarding_validator_address(
deps.as_mut(),
info,
"not-the-creator".to_string(),
);
assert_eq!(res, Err(ContractError::Unauthorized));
let info = mock_info("creator", &[]);
let res = try_update_rewarding_validator_address(
deps.as_mut(),
info,
"new-good-address".to_string(),
);
assert_eq!(res, Ok(Response::default()));
let state = storage::CONTRACT_STATE.load(&deps.storage).unwrap();
assert_eq!(
state.rewarding_validator_address,
Addr::unchecked("new-good-address")
);
assert_eq!(
state.rewarding_validator_address,
query_rewarding_validator_address(deps.as_ref()).unwrap()
);
}
#[test]
fn updating_contract_settings() {
let mut deps = test_helpers::init_contract();
+5 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use config::defaults::DENOM;
use cosmwasm_std::{StdResult, Storage, Uint128};
use cosmwasm_std::{StdResult, Storage, Uint128, Decimal};
use cw_storage_plus::{Index, IndexList, IndexedSnapshotMap, Map, Strategy, UniqueIndex};
use mixnet_contract_common::U128;
use mixnet_contract_common::{
@@ -135,6 +135,10 @@ impl StoredMixnodeBond {
pub fn profit_margin(&self) -> U128 {
U128::from_num(self.mix_node.profit_margin_percent) / U128::from_num(100)
}
pub fn profit_margin_dec(&self) -> Decimal {
Decimal::from_ratio(self.mix_node.profit_margin_percent, 100u128)
}
}
impl Display for StoredMixnodeBond {
+45 -38
View File
@@ -117,7 +117,7 @@ pub fn calculate_operator_reward(
// Compound rewards from previous heights
let reward_at_height = epoch_rewards.delegation_reward(
bond.pledge_amount().amount + accumulated_reward,
bond.profit_margin(),
bond.profit_margin_dec(),
epoch_reward_params,
)?;
return Ok(accumulated_reward + reward_at_height);
@@ -281,7 +281,7 @@ pub fn calculate_delegator_reward(
epoch_reward_params_for_id(storage, epoch_rewards.epoch_id())?;
let reward_at_height = epoch_rewards.delegation_reward(
delegation_at_height + accumulated_reward,
bond.profit_margin(),
bond.profit_margin_dec(),
epoch_reward_params,
)?;
return Ok(accumulated_reward + reward_at_height);
@@ -440,10 +440,9 @@ pub mod tests {
use crate::rewards::transactions::try_reward_mixnode;
use crate::support::tests;
use crate::support::tests::test_helpers;
use az::CheckedCast;
use config::defaults::DENOM;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128};
use cosmwasm_std::{coin, coins, Addr, Timestamp, Uint128, Decimal};
use mixnet_contract_common::events::{
must_find_attribute, BOND_TOO_FRESH_VALUE, NO_REWARD_REASON_KEY,
OPERATOR_REWARDING_EVENT_TYPE,
@@ -918,25 +917,25 @@ pub mod tests {
assert_eq!(
mix_1_reward_result.sigma(),
U128::from_num(0.0000266666666666f64)
Decimal::zero()
);
assert_eq!(
mix_1_reward_result.lambda(),
U128::from_num(0.0000133333333333f64)
Decimal::zero()
);
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
let mix_2_reward_result = mix_2.reward(&params2);
assert_eq!(
mix_2_reward_result.sigma(),
U128::from_num(0.0000266666666666f64)
);
assert_eq!(
mix_2_reward_result.lambda(),
U128::from_num(0.0000133333333333f64)
);
assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
// assert_eq!(
// mix_2_reward_result.sigma(),
// U128::from_num(0.0000266666666666f64)
// );
// assert_eq!(
// mix_2_reward_result.lambda(),
// U128::from_num(0.0000133333333333f64)
// );
// assert_eq!(mix_2_reward_result.reward().int(), 129557u128);
let mix_3_reward_result = mix_3.reward(&params3);
@@ -947,8 +946,7 @@ pub mod tests {
fn test_tokenomics_rewarding() {
use crate::constants::INTERVAL_REWARD_PERCENT;
use crate::contract::INITIAL_REWARD_POOL;
type U128 = fixed::types::U75F53;
use mixnet_contract_common::U128;
let mut deps = test_helpers::init_contract();
let mut env = mock_env();
@@ -1017,28 +1015,37 @@ pub mod tests {
assert_eq!(
mix_1_reward_result.sigma(),
U128::from_num(0.0000266666666666f64)
Decimal::new(Uint128::new(40000000000000))
);
assert_eq!(
mix_1_reward_result.lambda(),
U128::from_num(0.0000133333333333f64)
Decimal::new(Uint128::new(13333333333333))
);
assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
// assert_eq!(mix_1_reward_result.reward().int(), 259114u128);
// assert_eq!(mix_1.node_profit(&params).int(), 203558u128);
let mix1_operator_reward = mix_1.operator_reward(&params);
let mix1_delegator1_reward = mix_1.reward_delegation(Uint128::new(8000_000000), &params);
let mix1_delegator2_reward = mix_1.reward_delegation(Uint128::new(2000_000000), &params);
let mix1_total_delegator_reward = mix_1.reward_delegation(Uint128::new(10000_000000), &params);
assert_eq!(mix1_operator_reward, 167513);
assert_eq!(mix1_delegator1_reward, 73280);
assert_eq!(mix1_delegator2_reward, 18320);
// Rounding errors make this not be equal
assert!(mix1_total_delegator_reward > mix1_delegator1_reward + mix1_delegator2_reward);
assert_eq!(
mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
mix_1_reward_result.reward().int()
);
// assert_eq!(
// mix_1_reward_result.reward().int(),
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1
// );
// assert_eq!(
// mix1_operator_reward + mix1_delegator1_reward + mix1_delegator2_reward + 1,
// mix_1_reward_result.reward().int()
// );
let pre_reward_bond =
test_helpers::read_mixnode_pledge_amount(&deps.storage, &node_identity)
@@ -1092,18 +1099,18 @@ pub mod tests {
);
// it's all correctly saved
match storage::REWARDING_STATUS
.load(deps.as_ref().storage, (0u32, node_identity))
.unwrap()
{
RewardingStatus::Complete(result) => assert_eq!(
RewardingResult {
node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
},
result
),
_ => unreachable!(),
}
// match storage::REWARDING_STATUS
// .load(deps.as_ref().storage, (0u32, node_identity))
// .unwrap()
// {
// RewardingStatus::Complete(result) => assert_eq!(
// RewardingResult {
// node_reward: Uint128::new(mix_1_reward_result.reward().checked_cast().unwrap()),
// },
// result
// ),
// _ => unreachable!(),
// }
}
#[cfg(test)]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "vesting-contract"
version = "1.0.0-rc.1"
version = "1.0.0"
authors = ["Drazen Urch <durch@users.noreply.github.com>"]
edition = "2021"
+12 -12
View File
@@ -358,9 +358,9 @@
}
},
"node_modules/ansi-align/node_modules/ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"engines": {
"node": ">=6"
}
@@ -403,9 +403,9 @@
}
},
"node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
@@ -2309,9 +2309,9 @@
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
},
"emoji-regex": {
"version": "7.0.3",
@@ -2344,9 +2344,9 @@
}
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"ansi-styles": {
"version": "4.3.0",
+41 -2
View File
@@ -1,18 +1,30 @@
use std::collections::HashMap;
use std::time::{Duration, SystemTime};
const DEFAULT_CACHE_VALIDITY: Duration = Duration::from_secs(60 * 30);
#[derive(Clone)]
pub(crate) struct Cache<T: Clone> {
inner: HashMap<String, CacheItem<T>>,
cache_validity_duration: Duration,
}
impl<T: Clone> Cache<T> {
pub(crate) fn new() -> Self {
Cache {
inner: HashMap::new(),
cache_validity_duration: DEFAULT_CACHE_VALIDITY,
}
}
// it felt like this might be an useful addition if we want to keep our caches with different
// validity durations
#[allow(unused)]
pub(crate) fn with_validity_duration(mut self, new_cache_validity: Duration) -> Self {
self.cache_validity_duration = new_cache_validity;
self
}
pub(crate) fn len(&self) -> usize {
self.inner.len()
}
@@ -27,7 +39,7 @@ impl<T: Clone> Cache<T> {
pub(crate) fn get(&self, key: &str) -> Option<T> {
self.inner
.get(key)
.filter(|cache_item| cache_item.valid_until > SystemTime::now())
.filter(|cache_item| cache_item.valid_until >= SystemTime::now())
.map(|cache_item| cache_item.value.clone())
}
@@ -35,11 +47,31 @@ impl<T: Clone> Cache<T> {
self.inner.insert(
key.to_string(),
CacheItem {
valid_until: SystemTime::now() + Duration::from_secs(60 * 30),
valid_until: SystemTime::now() + self.cache_validity_duration,
value,
},
);
}
#[allow(unused)]
pub(crate) fn remove(&mut self, key: &str) -> Option<T> {
self.inner.remove(key).map(|item| item.value)
}
#[allow(unused)]
pub(crate) fn remove_if_expired(&mut self, key: &str) -> Option<T> {
if self.inner.get(key)?.has_expired() {
self.remove(key)
} else {
None
}
}
// it seems like something should be running on timer calling this method on all of our caches
#[allow(unused)]
pub(crate) fn remove_all_expired(&mut self) {
self.inner.retain(|_, v| !v.has_expired())
}
}
#[derive(Clone)]
@@ -47,3 +79,10 @@ pub(crate) struct CacheItem<T> {
pub(crate) value: T,
pub(crate) valid_until: std::time::SystemTime,
}
impl<T> CacheItem<T> {
fn has_expired(&self) -> bool {
let now = SystemTime::now();
self.valid_until < now
}
}
+25 -2
View File
@@ -1,7 +1,28 @@
use network_defaults::{default_api_endpoints, default_nymd_endpoints, DEFAULT_NETWORK};
use reqwest::Url;
use std::sync::Arc;
use validator_client::nymd::QueryNymdClient;
pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
// since this is just a query client, we don't need any locking mechanism to keep sequence numbers in check
// nor we need to access any of its methods taking mutable reference (like updating api URL)
// when that becomes a requirement, we would simply put an extra RwLock (or Mutex) in here
#[derive(Clone)]
pub(crate) struct ThreadsafeValidatorClient(
pub(crate) Arc<validator_client::Client<QueryNymdClient>>,
);
impl ThreadsafeValidatorClient {
pub(crate) fn new() -> Self {
new_validator_client()
}
pub(crate) fn api_endpoint(&self) -> &Url {
self.0.validator_api.current_url()
}
}
pub(crate) fn new_validator_client() -> ThreadsafeValidatorClient {
let network = DEFAULT_NETWORK;
let mixnet_contract = network.mixnet_contract_address().to_string();
let nymd_url = default_nymd_endpoints()[0].clone();
@@ -16,5 +37,7 @@ pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
None,
);
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!")
ThreadsafeValidatorClient(Arc::new(
validator_client::Client::new_query(client_config).expect("Failed to connect to nymd!"),
))
}
+1 -1
View File
@@ -42,7 +42,7 @@ impl ExplorerApi {
async fn run(&mut self) {
info!("Explorer API starting up...");
let validator_api_url = network_defaults::default_api_endpoints()[0].clone();
let validator_api_url = self.state.inner.validator_client.api_endpoint();
info!("Using validator API - {}", validator_api_url);
// spawn concurrent tasks
@@ -1,11 +1,15 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::ThreadsafeValidatorClient;
use mixnet_contract_common::Delegation;
pub(crate) async fn get_single_mixnode_delegations(pubkey: &str) -> Vec<Delegation> {
let client = crate::client::new_nymd_client();
pub(crate) async fn get_single_mixnode_delegations(
client: &ThreadsafeValidatorClient,
pubkey: &str,
) -> Vec<Delegation> {
let delegates = match client
.0
.get_all_nymd_single_mixnode_delegations(pubkey.to_string())
.await
{
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::client::ThreadsafeValidatorClient;
use crate::mix_node::models::EconomicDynamicsStats;
pub(crate) async fn retrieve_mixnode_econ_stats(
client: &ThreadsafeValidatorClient,
identity: &str,
) -> Option<EconomicDynamicsStats> {
let stake_saturation = client
.0
.validator_api
.get_mixnode_stake_saturation(identity)
.await
.ok()?;
let inclusion_probability = client
.0
.validator_api
.get_mixnode_inclusion_probability(identity)
.await
.ok()?;
let reward_estimation = client
.0
.validator_api
.get_mixnode_reward_estimation(identity)
.await
.ok()?;
Some(EconomicDynamicsStats {
stake_saturation: stake_saturation.saturation,
active_set_inclusion_probability: inclusion_probability.in_active,
reserve_set_inclusion_probability: inclusion_probability.in_reserve,
estimated_total_node_reward: reward_estimation.estimated_total_node_reward,
estimated_operator_reward: reward_estimation.estimated_operator_reward,
estimated_delegators_reward: reward_estimation.estimated_delegators_reward,
current_interval_uptime: reward_estimation.current_interval_uptime,
})
}
+36 -4
View File
@@ -11,8 +11,11 @@ use rocket_okapi::settings::OpenApiSettings;
use mixnet_contract_common::Delegation;
use crate::mix_node::models::{NodeDescription, NodeStats, PrettyDetailedMixNodeBond};
use crate::mix_nodes::delegations::get_single_mixnode_delegations;
use crate::mix_node::delegations::get_single_mixnode_delegations;
use crate::mix_node::econ_stats::retrieve_mixnode_econ_stats;
use crate::mix_node::models::{
EconomicDynamicsStats, NodeDescription, NodeStats, PrettyDetailedMixNodeBond,
};
use crate::state::ExplorerApiStateContext;
pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
@@ -21,6 +24,7 @@ pub fn mix_node_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>,
get_by_id,
get_description,
get_stats,
get_economic_dynamics_stats,
]
}
@@ -43,8 +47,11 @@ pub(crate) async fn get_by_id(
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/delegations")]
pub(crate) async fn get_delegations(pubkey: &str) -> Json<Vec<Delegation>> {
Json(get_single_mixnode_delegations(pubkey).await)
pub(crate) async fn get_delegations(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Json<Vec<Delegation>> {
Json(get_single_mixnode_delegations(&state.inner.validator_client, pubkey).await)
}
#[openapi(tag = "mix_node")]
@@ -134,6 +141,31 @@ pub(crate) async fn get_stats(
}
}
#[openapi(tag = "mix_node")]
#[get("/<pubkey>/economic-dynamics-stats")]
pub(crate) async fn get_economic_dynamics_stats(
pubkey: &str,
state: &State<ExplorerApiStateContext>,
) -> Option<Json<EconomicDynamicsStats>> {
match state.inner.mixnode.get_econ_stats(pubkey).await {
Some(cache_value) => {
trace!("Returning cached value for {}", pubkey);
Some(Json(cache_value))
}
None => {
trace!("No valid cache value for {}", pubkey);
// get fresh value from the validator API
let econ_stats =
retrieve_mixnode_econ_stats(&state.inner.validator_client, pubkey).await?;
// update cache
state.inner.mixnode.set_econ_stats(pubkey, econ_stats).await;
Some(Json(econ_stats))
}
}
}
async fn get_mix_node_description(host: &str, port: &u16) -> Result<NodeDescription, ReqwestError> {
reqwest::get(format!("http://{}:{}/description", host, port))
.await?
+2
View File
@@ -1,2 +1,4 @@
pub(crate) mod delegations;
pub(crate) mod econ_stats;
pub(crate) mod http;
pub(crate) mod models;
+32
View File
@@ -32,6 +32,7 @@ pub(crate) struct PrettyDetailedMixNodeBond {
pub(crate) struct MixNodeCache {
pub(crate) descriptions: Cache<NodeDescription>,
pub(crate) node_stats: Cache<NodeStats>,
pub(crate) econ_stats: Cache<EconomicDynamicsStats>,
}
#[derive(Clone)]
@@ -45,6 +46,7 @@ impl ThreadsafeMixNodeCache {
inner: Arc::new(RwLock::new(MixNodeCache {
descriptions: Cache::new(),
node_stats: Cache::new(),
econ_stats: Cache::new(),
})),
}
}
@@ -57,6 +59,10 @@ impl ThreadsafeMixNodeCache {
self.inner.read().await.node_stats.get(identity_key)
}
pub(crate) async fn get_econ_stats(&self, identity_key: &str) -> Option<EconomicDynamicsStats> {
self.inner.read().await.econ_stats.get(identity_key)
}
pub(crate) async fn set_description(&self, identity_key: &str, description: NodeDescription) {
self.inner
.write()
@@ -72,6 +78,18 @@ impl ThreadsafeMixNodeCache {
.node_stats
.set(identity_key, node_stats);
}
pub(crate) async fn set_econ_stats(
&self,
identity_key: &str,
econ_stats: EconomicDynamicsStats,
) {
self.inner
.write()
.await
.econ_stats
.set(identity_key, econ_stats);
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
@@ -103,3 +121,17 @@ pub(crate) struct NodeStats {
packets_sent_since_last_update: u64,
packets_explicitly_dropped_since_last_update: u64,
}
#[derive(Serialize, Clone, Copy, Deserialize, JsonSchema)]
pub(crate) struct EconomicDynamicsStats {
pub(crate) stake_saturation: f32,
pub(crate) active_set_inclusion_probability: f32,
pub(crate) reserve_set_inclusion_probability: f32,
pub(crate) estimated_total_node_reward: u64,
pub(crate) estimated_operator_reward: u64,
pub(crate) estimated_delegators_reward: u64,
pub(crate) current_interval_uptime: u8,
}
-1
View File
@@ -3,7 +3,6 @@
use std::time::Duration;
pub(crate) mod delegations;
pub(crate) mod http;
pub(crate) mod location;
pub(crate) mod models;
+6
View File
@@ -5,6 +5,7 @@ use chrono::{DateTime, Utc};
use log::info;
use serde::{Deserialize, Serialize};
use crate::client::ThreadsafeValidatorClient;
use mixnet_contract_common::MixNodeBond;
use crate::country_statistics::country_nodes_distribution::{
@@ -28,6 +29,9 @@ pub struct ExplorerApiState {
pub(crate) mixnodes: ThreadsafeMixNodesCache,
pub(crate) ping: ThreadsafePingCache,
pub(crate) validators: ThreadsafeValidatorCache,
// TODO: discuss with @MS whether this is an appropriate spot for it
pub(crate) validator_client: ThreadsafeValidatorClient,
}
impl ExplorerApiState {
@@ -75,6 +79,7 @@ impl ExplorerApiStateContext {
),
ping: ThreadsafePingCache::new(),
validators: ThreadsafeValidatorCache::new(),
validator_client: ThreadsafeValidatorClient::new(),
}
}
_ => {
@@ -90,6 +95,7 @@ impl ExplorerApiStateContext {
mixnodes: ThreadsafeMixNodesCache::new(),
ping: ThreadsafePingCache::new(),
validators: ThreadsafeValidatorCache::new(),
validator_client: ThreadsafeValidatorClient::new(),
}
}
}
+14 -8
View File
@@ -8,21 +8,16 @@ use validator_client::nymd::error::NymdError;
use validator_client::nymd::{Paging, QueryNymdClient, ValidatorResponse};
use validator_client::ValidatorClientError;
use crate::client::new_nymd_client;
use crate::mix_nodes::CACHE_REFRESH_RATE;
use crate::state::ExplorerApiStateContext;
pub(crate) struct ExplorerApiTasks {
state: ExplorerApiStateContext,
validator_client: validator_client::Client<QueryNymdClient>,
}
impl ExplorerApiTasks {
pub(crate) fn new(state: ExplorerApiStateContext) -> Self {
ExplorerApiTasks {
state,
validator_client: new_nymd_client(),
}
ExplorerApiTasks { state }
}
// a helper to remove duplicate code when grabbing active/rewarded/all mixnodes
@@ -31,7 +26,7 @@ impl ExplorerApiTasks {
F: FnOnce(&'a validator_client::Client<QueryNymdClient>) -> Fut,
Fut: Future<Output = Result<Vec<MixNodeBond>, ValidatorClientError>>,
{
let bonds = match f(&self.validator_client).await {
let bonds = match f(&self.state.inner.validator_client.0).await {
Ok(result) => result,
Err(e) => {
error!("Unable to retrieve mixnode bonds: {:?}", e);
@@ -51,18 +46,29 @@ impl ExplorerApiTasks {
async fn retrieve_all_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
info!("About to retrieve all gateways...");
self.validator_client.get_cached_gateways().await
self.state
.inner
.validator_client
.0
.get_cached_gateways()
.await
}
async fn retrieve_all_validators(&self) -> Result<ValidatorResponse, NymdError> {
info!("About to retrieve all validators...");
let height = self
.state
.inner
.validator_client
.0
.nymd
.get_current_block_height()
.await?;
let response: ValidatorResponse = self
.state
.inner
.validator_client
.0
.nymd
.get_validators(height.value(), Paging::All)
.await?;
+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:
```
+4 -6
View File
@@ -10,7 +10,7 @@ import { DiscordIcon } from '../icons/socials/DiscordIcon';
export const TELEGRAM_LINK = 'https://t.me/nymchan';
export const TWITTER_LINK = 'https://twitter.com/nymproject';
export const GITHUB_LINK = 'https://github.com/nymtech';
export const DISCORD_LINK = 'https://discord.gg/jUqJYGB5';
export const DISCORD_LINK = 'https://discord.gg/ggxrUpbNnn';
export const Socials: React.FC<{ isFooter?: boolean }> = ({ isFooter }) => {
const theme = useTheme();
@@ -22,11 +22,9 @@ export const Socials: React.FC<{ isFooter?: boolean }> = ({ isFooter }) => {
<IconButton component="a" href={TELEGRAM_LINK} target="_blank" data-testid="telegram">
<TelegramIcon color={color} size={24} />
</IconButton>
{false && (
<IconButton component="a" href={DISCORD_LINK} target="_blank" data-testid="discord">
<DiscordIcon color={color} size={24} />
</IconButton>
)}
<IconButton component="a" href={DISCORD_LINK} target="_blank" data-testid="discord">
<DiscordIcon color={color} size={24} />
</IconButton>
<IconButton component="a" href={TWITTER_LINK} target="_blank" data-testid="twitter">
<TwitterIcon color={color} size={24} />
</IconButton>
+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];
+452 -226
View File
File diff suppressed because it is too large Load Diff

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