Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c237b37ea6 | |||
| d7e82b075f | |||
| f684664472 | |||
| cd5fff92ad | |||
| 4b95e71adb | |||
| cde5b66306 | |||
| 7f3166d230 | |||
| 68050d77df | |||
| 943337db0d | |||
| 8fbf84174d | |||
| d42a175289 | |||
| ac01df0817 | |||
| 7b27065608 | |||
| 0523ccdce8 | |||
| df8ae52d8c | |||
| 188a7ec91d | |||
| c91bf3f8d1 | |||
| 777fcf8cb3 | |||
| 945dda0c24 | |||
| 479d410d20 | |||
| a1ca330ce9 | |||
| 139e89643c | |||
| 8eb3f6f862 | |||
| 9032d81d52 | |||
| f2e95d2fd5 | |||
| 5730c914e8 | |||
| 7845e32742 | |||
| 4cce235e13 | |||
| 521eb98f25 | |||
| c77ccddcb3 | |||
| b8ce97e005 | |||
| 49e29af5f4 | |||
| 41319fe7ad | |||
| 4fcf0da5c0 | |||
| 0a6b2a8aaf | |||
| b09db50bba | |||
| 82e6d7335b | |||
| 21d19d2447 | |||
| 5f116f8104 | |||
| 04f633446e | |||
| be5421719c | |||
| 96f2718b94 | |||
| 632612eca0 | |||
| c250492f50 | |||
| 52fdc3e0cf | |||
| 5a91240992 | |||
| c9638097a0 | |||
| 383b197e5b | |||
| 787a55a4ba | |||
| bbdd53d2aa | |||
| 67f6394a26 | |||
| dbfeaff661 | |||
| 8318002b0a | |||
| 9fb980dc5e | |||
| ce77b17534 | |||
| e6372d3b02 | |||
| 836ef9d4c8 | |||
| dd66697884 | |||
| 7c4bb68399 | |||
| 58f3a96cde | |||
| 76a61cb3ae | |||
| 94be4c71a4 | |||
| 046dd4cbba | |||
| 485257d29b | |||
| 37de4bf2f7 | |||
| d3372bfc85 | |||
| 25e1bfa345 | |||
| dc0b9c271c | |||
| 9ef29037bc | |||
| 1e13d41245 | |||
| 46a4991c12 | |||
| 9d2d670990 | |||
| baba5ed212 | |||
| ceb5f090cf | |||
| b6ffe8664c | |||
| be4bc2bdcc | |||
| bc049cb954 | |||
| 9aa5b98465 | |||
| d6c9d1d08d | |||
| 49fc51853a | |||
| c177f14073 | |||
| 026932dc16 | |||
| 515d4b73f7 | |||
| fda3636783 | |||
| c056269f0e | |||
| ca49fe2293 | |||
| 1b37ff2242 | |||
| 3712b38230 | |||
| 467bda8ddd | |||
| b083335f56 | |||
| 5cc08211b7 | |||
| a63a94623f | |||
| 5deafaa27b | |||
| 021b542a4a | |||
| 64ee03112e | |||
| bed709b155 | |||
| 1f6d4153a7 | |||
| de45ab8995 | |||
| 15b552fa62 | |||
| 0d343eb82d | |||
| b5eddb6919 | |||
| 7ddde50ffa | |||
| d81967189a | |||
| 11b22ce2c1 | |||
| 58fd40fb8e | |||
| 5bb516471e | |||
| 2b9ea90e16 | |||
| 2779b5d28a | |||
| 9ea2ce5c70 | |||
| 11e1e728e1 | |||
| 4116aa18a8 | |||
| bdebe00c25 | |||
| e106390e1d | |||
| 7a53821af9 | |||
| efe6df12c9 | |||
| 23fb34f564 | |||
| 09155fbf12 | |||
| 53292ceca9 | |||
| 45b41d9e20 | |||
| c85b0ad07d | |||
| adf4537183 | |||
| 1cf101d50f | |||
| e91e6943c6 | |||
| 700f6a4e98 | |||
| b759e5e7f2 | |||
| deefa09066 | |||
| 3f6cb919ac | |||
| d08bf61905 | |||
| da18a60a91 | |||
| cec7496794 | |||
| dd82b24d61 | |||
| df827b6b09 | |||
| cb25cc2eb9 | |||
| abf7e1f6ad | |||
| 0f5137ea24 | |||
| 34903bfae6 | |||
| 9e8f550e6d | |||
| 8ad3565f2c | |||
| 47bdf38776 | |||
| cdd883c174 | |||
| 2d82a51905 | |||
| 38c2ce9837 | |||
| a867921fdd | |||
| 423cdb1e1b | |||
| 7aeac58fd9 | |||
| 30fafa509c | |||
| c950556506 | |||
| 9a49213973 |
@@ -0,0 +1,9 @@
|
|||||||
|
# Description
|
||||||
|
|
||||||
|
Closes: #XXXX
|
||||||
|
|
||||||
|
<!-- If appropriate, insert relevant description here -->
|
||||||
|
|
||||||
|
# Checklist:
|
||||||
|
|
||||||
|
- [ ] added a changelog entry to `CHANGELOG.md`
|
||||||
@@ -31,6 +31,8 @@ jobs:
|
|||||||
# continue-on-error: true
|
# continue-on-error: true
|
||||||
- run: yarn && yarn build
|
- run: yarn && yarn build
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
- run: yarn storybook:build
|
||||||
|
name: Build storybook
|
||||||
- name: Deploy branch to CI www
|
- name: Deploy branch to CI www
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
uses: easingthemes/ssh-deploy@main
|
uses: easingthemes/ssh-deploy@main
|
||||||
@@ -42,6 +44,17 @@ jobs:
|
|||||||
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||||
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
|
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/network-explorer-${{ env.GITHUB_REF_SLUG }}
|
||||||
EXCLUDE: "/dist/, /node_modules/"
|
EXCLUDE: "/dist/, /node_modules/"
|
||||||
|
- name: Deploy storybook to CI www
|
||||||
|
continue-on-error: true
|
||||||
|
uses: easingthemes/ssh-deploy@main
|
||||||
|
env:
|
||||||
|
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
|
||||||
|
ARGS: "-rltgoDzvO --delete"
|
||||||
|
SOURCE: "explorer/storybook-static/"
|
||||||
|
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
|
||||||
|
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
|
||||||
|
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/ne-sb-${{ env.GITHUB_REF_SLUG }}
|
||||||
|
EXCLUDE: "/dist/, /node_modules/"
|
||||||
- name: Keybase - Node Install
|
- name: Keybase - Node Install
|
||||||
run: npm install
|
run: npm install
|
||||||
working-directory: .github/workflows/support-files
|
working-directory: .github/workflows/support-files
|
||||||
@@ -51,6 +64,7 @@ jobs:
|
|||||||
NYM_PROJECT_NAME: "Network Explorer"
|
NYM_PROJECT_NAME: "Network Explorer"
|
||||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||||
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
||||||
|
NYM_CI_WWW_LOCATION_STORYBOOK: "ne-sb-${{ env.GITHUB_REF_SLUG }}"
|
||||||
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
|
||||||
GIT_BRANCH: "${GITHUB_REF##*/}"
|
GIT_BRANCH: "${GITHUB_REF##*/}"
|
||||||
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ jobs:
|
|||||||
command: clippy
|
command: clippy
|
||||||
args: --workspace --all-targets -- -D warnings
|
args: --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
|
|
||||||
# COCONUT stuff
|
# COCONUT stuff
|
||||||
- name: Build all binaries with coconut enabled
|
- name: Build all binaries with coconut enabled
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
@@ -89,12 +88,45 @@ jobs:
|
|||||||
command: test
|
command: test
|
||||||
args: --workspace --features=coconut
|
args: --workspace --features=coconut
|
||||||
|
|
||||||
|
- name: Reclaim some disk space (because Windows is being annoying)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.os == 'windows-latest' }}
|
||||||
|
with:
|
||||||
|
command: clean
|
||||||
|
|
||||||
- name: Run clippy with coconut enabled
|
- name: Run clippy with coconut enabled
|
||||||
uses: actions-rs/cargo@v1
|
uses: actions-rs/cargo@v1
|
||||||
if: ${{ matrix.rust != 'nightly' }}
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
with:
|
with:
|
||||||
command: clippy
|
command: clippy
|
||||||
args: --workspace --all-targets --features=coconut -- -D warnings
|
args: --workspace --all-targets --features=coconut -- -D warnings
|
||||||
|
|
||||||
|
# nym-wallet (the rust part)
|
||||||
|
- name: Build nym-wallet rust code
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||||
|
|
||||||
|
- name: Run nym-wallet tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||||
|
|
||||||
|
- name: Check nym-wallet formatting
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
|
||||||
|
|
||||||
|
- name: Run clippy for nym-wallet
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
if: ${{ matrix.rust != 'nightly' }}
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-targets -- -D warnings
|
||||||
|
|
||||||
notification:
|
notification:
|
||||||
needs: build
|
needs: build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ name: Publish Nym binaries
|
|||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [created]
|
types: [created]
|
||||||
|
|
||||||
|
env:
|
||||||
|
NETWORK: mainnet
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish-nym:
|
publish-nym:
|
||||||
@@ -41,3 +44,4 @@ jobs:
|
|||||||
target/release/nym-mixnode
|
target/release/nym-mixnode
|
||||||
target/release/nym-socks5-client
|
target/release/nym-socks5-client
|
||||||
target/release/nym-validator-api
|
target/release/nym-validator-api
|
||||||
|
target/release/nym-network-requester
|
||||||
|
|||||||
@@ -64,12 +64,16 @@ jobs:
|
|||||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
run: yarn && yarn build
|
run: yarn && yarn build
|
||||||
|
|
||||||
- name: Upload to release based on tag name
|
- name: Upload to release based on tag name
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
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
|
- name: Clean up keychain
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
|
|||||||
@@ -37,10 +37,16 @@ jobs:
|
|||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
- name: Install app dependencies and build it
|
- name: Install app dependencies
|
||||||
run: yarn && yarn build
|
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
|
- name: Upload to release based on tag name
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
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 }}
|
ENABLE_CODE_SIGNING: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||||
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
|
||||||
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
|
||||||
|
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||||
|
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||||
run: yarn build
|
run: yarn build
|
||||||
|
|
||||||
- name: Upload to release based on tag name
|
- name: Upload to release based on tag name
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
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
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||||
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
|
> :rocket: {{ env.NYM_PROJECT_NAME }} ➡️➡️➡️➡️➡️ **View output:** https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}/
|
||||||
|
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION_STORYBOOK }}.{{ env.NYM_CI_WWW_BASE }}
|
||||||
> ✅ **SUCCESS**
|
> ✅ **SUCCESS**
|
||||||
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
|
> `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 }}
|
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ require('dotenv').config();
|
|||||||
const Bot = require('keybase-bot');
|
const Bot = require('keybase-bot');
|
||||||
|
|
||||||
let context = {
|
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 }}
|
||||||
|
```
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
name: Nym Wallet (rust)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'explorer/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||||
|
steps:
|
||||||
|
- name: Install Dependencies (Linux)
|
||||||
|
run: sudo apt-get update && sudo apt-get -y install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||||
|
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install rust toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Build all binaries
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: build
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||||
|
|
||||||
|
- name: Run all tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: test
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace
|
||||||
|
|
||||||
|
- name: Check formatting
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --all -- --check
|
||||||
|
|
||||||
|
- uses: actions-rs/clippy-check@v1
|
||||||
|
name: Clippy checks
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features
|
||||||
|
|
||||||
|
- name: Run clippy
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: --manifest-path nym-wallet/Cargo.toml --workspace --all-features -- -D warnings
|
||||||
+322
@@ -1,5 +1,326 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- mixnet-contract: Replace all naked `-` with `saturating_sub`.
|
||||||
|
- validator-api: add Swagger to document the REST API ([#1249]).
|
||||||
|
- all: added network compilation target to `--help` (or `--version`) commands ([#1256]).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- mixnet-contract: removed `expect` in `query_delegator_reward` and queries containing invalid proxy address should now return a more human-readable error ([#1257])
|
||||||
|
- mixnet-contract: Under certain circumstances nodes could not be unbonded ([#1255](https://github.com/nymtech/nym/issues/1255)) ([#1258])
|
||||||
|
|
||||||
|
[#1258]: https://github.com/nymtech/nym/pull/1258
|
||||||
|
[#1249]: https://github.com/nymtech/nym/pull/1249
|
||||||
|
[#1256]: https://github.com/nymtech/nym/pull/1256
|
||||||
|
[#1257]: https://github.com/nymtech/nym/pull/1257
|
||||||
|
|
||||||
|
## [nym-wallet-v1.0.4](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.4) (2022-05-04)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
|
||||||
|
|
||||||
|
## [v1.0.1](https://github.com/nymtech/nym/tree/v1.0.1) (2022-05-04)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- validator-api: introduced endpoint for getting average mixnode uptime ([#1238])
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- all: the default behaviour of validator client is changed to use `broadcast_sync` and poll for transaction inclusion instead of using `broadcast_commit` to deal with timeouts ([#1246])
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- nym-network-requester: is included in the Github Actions for building release binaries
|
||||||
|
|
||||||
|
[#1238]: https://github.com/nymtech/nym/pull/1238
|
||||||
|
[#1246]: https://github.com/nymtech/nym/pull/1246
|
||||||
|
|
||||||
|
## [v1.0.0](https://github.com/nymtech/nym/tree/v1.0.0) (2022-05-03)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...v1.0.0)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Feature/show pending delegations [\#1229](https://github.com/nymtech/nym/pull/1229) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Bucket inclusion probabilities [\#1224](https://github.com/nymtech/nym/pull/1224) ([durch](https://github.com/durch))
|
||||||
|
- Create a new bundled delegation when compounding rewards [\#1221](https://github.com/nymtech/nym/pull/1221) ([durch](https://github.com/durch))
|
||||||
|
|
||||||
|
## [nym-binaries-1.0.0](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0) (2022-04-27)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.3...nym-binaries-1.0.0)
|
||||||
|
|
||||||
|
## [nym-wallet-v1.0.3](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.3) (2022-04-25)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.2...nym-wallet-v1.0.3)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- \[Issue\] Wallet 1.0.2 cannot send NYM tokens from a DelayedVestingAccount [\#1215](https://github.com/nymtech/nym/issues/1215)
|
||||||
|
- Main README not showing properly with GitHub dark mode [\#1211](https://github.com/nymtech/nym/issues/1211)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Bugfix - wallet undelegation for vesting accounts [\#1220](https://github.com/nymtech/nym/pull/1220) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Bugfix/delegation reconcile [\#1219](https://github.com/nymtech/nym/pull/1219) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Bugfix/query proxied pending delegations [\#1218](https://github.com/nymtech/nym/pull/1218) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Using custom gas multiplier in the wallet [\#1217](https://github.com/nymtech/nym/pull/1217) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Feature/vesting accounts support [\#1216](https://github.com/nymtech/nym/pull/1216) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Release/1.0.0 rc.2 [\#1214](https://github.com/nymtech/nym/pull/1214) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- chore: fix dark mode rendering [\#1212](https://github.com/nymtech/nym/pull/1212) ([pwnfoo](https://github.com/pwnfoo))
|
||||||
|
- Feature/spend coconut [\#1210](https://github.com/nymtech/nym/pull/1210) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Bugfix/unique sphinx key [\#1207](https://github.com/nymtech/nym/pull/1207) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Add cache read and write timeouts [\#1206](https://github.com/nymtech/nym/pull/1206) ([durch](https://github.com/durch))
|
||||||
|
- Additional, more informative routes [\#1204](https://github.com/nymtech/nym/pull/1204) ([durch](https://github.com/durch))
|
||||||
|
- Feature/aggregated econ dynamics explorer endpoint [\#1203](https://github.com/nymtech/nym/pull/1203) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Debugging validator [\#1198](https://github.com/nymtech/nym/pull/1198) ([durch](https://github.com/durch))
|
||||||
|
- wallet: expose additional validator configuration functionality to the frontend [\#1195](https://github.com/nymtech/nym/pull/1195) ([octol](https://github.com/octol))
|
||||||
|
- Update rewarding validator address [\#1193](https://github.com/nymtech/nym/pull/1193) ([durch](https://github.com/durch))
|
||||||
|
- Crypto part of the Groth's NIDKG [\#1182](https://github.com/nymtech/nym/pull/1182) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- fix unbond page [\#1180](https://github.com/nymtech/nym/pull/1180) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Type safe bounds [\#1179](https://github.com/nymtech/nym/pull/1179) ([durch](https://github.com/durch))
|
||||||
|
- Fix delegation paging [\#1174](https://github.com/nymtech/nym/pull/1174) ([durch](https://github.com/durch))
|
||||||
|
- Update binaries to rc version [\#1172](https://github.com/nymtech/nym/pull/1172) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Bump ansi-regex from 4.1.0 to 4.1.1 in /docker/typescript\_client/upload\_contract [\#1171](https://github.com/nymtech/nym/pull/1171) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
|
||||||
|
## [nym-binaries-1.0.0-rc.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.2) (2022-04-15)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.2...nym-binaries-1.0.0-rc.2)
|
||||||
|
|
||||||
|
## [nym-wallet-v1.0.2](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.2) (2022-04-05)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.1...nym-wallet-v1.0.2)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Wallet 1.0.2 visual tweaks [\#1197](https://github.com/nymtech/nym/pull/1197) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Password for wallet with routes [\#1196](https://github.com/nymtech/nym/pull/1196) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Add auto-updater to Nym Wallet [\#1194](https://github.com/nymtech/nym/pull/1194) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Fix clippy warnings for beta toolchain [\#1191](https://github.com/nymtech/nym/pull/1191) ([octol](https://github.com/octol))
|
||||||
|
- wallet: expose validator urls to the frontend [\#1190](https://github.com/nymtech/nym/pull/1190) ([octol](https://github.com/octol))
|
||||||
|
- wallet: add test for decrypting stored wallet file [\#1189](https://github.com/nymtech/nym/pull/1189) ([octol](https://github.com/octol))
|
||||||
|
- Fix clippy warnings [\#1188](https://github.com/nymtech/nym/pull/1188) ([octol](https://github.com/octol))
|
||||||
|
- Password for wallet with routes [\#1187](https://github.com/nymtech/nym/pull/1187) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- wallet: add validate\_mnemonic [\#1186](https://github.com/nymtech/nym/pull/1186) ([octol](https://github.com/octol))
|
||||||
|
- wallet: support removing accounts from the wallet file [\#1185](https://github.com/nymtech/nym/pull/1185) ([octol](https://github.com/octol))
|
||||||
|
- Feature/adding discord [\#1184](https://github.com/nymtech/nym/pull/1184) ([gala1234](https://github.com/gala1234))
|
||||||
|
- wallet: config backend for validator selection [\#1183](https://github.com/nymtech/nym/pull/1183) ([octol](https://github.com/octol))
|
||||||
|
- Add storybook to wallet [\#1178](https://github.com/nymtech/nym/pull/1178) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- wallet: connection test nymd and api urls independently [\#1170](https://github.com/nymtech/nym/pull/1170) ([octol](https://github.com/octol))
|
||||||
|
- wallet: wire up account storage [\#1153](https://github.com/nymtech/nym/pull/1153) ([octol](https://github.com/octol))
|
||||||
|
- Feature/signature on deposit [\#1151](https://github.com/nymtech/nym/pull/1151) ([neacsu](https://github.com/neacsu))
|
||||||
|
|
||||||
|
## [nym-wallet-v1.0.1](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.1) (2022-04-05)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-binaries-1.0.0-rc.1...nym-wallet-v1.0.1)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Check enabling bbbc simultaneously with open access. Estimate what it would take to make this the default compilation target. [\#1175](https://github.com/nymtech/nym/issues/1175)
|
||||||
|
- Get coconut credential for deposited tokens [\#1138](https://github.com/nymtech/nym/issues/1138)
|
||||||
|
- Make payments lazy [\#1135](https://github.com/nymtech/nym/issues/1135)
|
||||||
|
- Uptime on node selection for sets [\#1049](https://github.com/nymtech/nym/issues/1049)
|
||||||
|
|
||||||
|
## [nym-binaries-1.0.0-rc.1](https://github.com/nymtech/nym/tree/nym-binaries-1.0.0-rc.1) (2022-03-28)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/nym-wallet-v1.0.0...nym-binaries-1.0.0-rc.1)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- \[Issue\]cargo build --release issue [\#1101](https://github.com/nymtech/nym/issues/1101)
|
||||||
|
- appimage fail to load in Fedora [\#1098](https://github.com/nymtech/nym/issues/1098)
|
||||||
|
- \[Issue\] React Example project does not compile when using @nymproject/nym-client-wasm v0.9.0-1 [\#878](https://github.com/nymtech/nym/issues/878)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Make mainnet coin transfers work [\#1096](https://github.com/nymtech/nym/issues/1096)
|
||||||
|
- Make Nym wallet validators configurable at runtime [\#1026](https://github.com/nymtech/nym/issues/1026)
|
||||||
|
- Project Platypus e2e / integration testing [\#942](https://github.com/nymtech/nym/issues/942)
|
||||||
|
- \[Coconut\]: Replace ElGamal with Pedersen commitments [\#901](https://github.com/nymtech/nym/issues/901)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Different values for mixes and gateways [\#1169](https://github.com/nymtech/nym/pull/1169) ([durch](https://github.com/durch))
|
||||||
|
- Add global blacklist to validator-cache [\#1168](https://github.com/nymtech/nym/pull/1168) ([durch](https://github.com/durch))
|
||||||
|
- Feature/upgrade rewarding sandbox [\#1167](https://github.com/nymtech/nym/pull/1167) ([durch](https://github.com/durch))
|
||||||
|
- Bump node-forge from 1.2.1 to 1.3.0 [\#1165](https://github.com/nymtech/nym/pull/1165) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump minimist from 1.2.5 to 1.2.6 in /nym-wallet/webdriver [\#1164](https://github.com/nymtech/nym/pull/1164) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump minimist from 1.2.5 to 1.2.6 in /clients/tauri-client [\#1163](https://github.com/nymtech/nym/pull/1163) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump minimist from 1.2.5 to 1.2.6 in /clients/webassembly/js-example [\#1162](https://github.com/nymtech/nym/pull/1162) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump minimist from 1.2.5 to 1.2.6 in /clients/native/examples/js-examples/websocket [\#1160](https://github.com/nymtech/nym/pull/1160) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump minimist from 1.2.5 to 1.2.6 in /docker/typescript\_client/upload\_contract [\#1159](https://github.com/nymtech/nym/pull/1159) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Feature/vesting full [\#1158](https://github.com/nymtech/nym/pull/1158) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- get\_current\_epoch tauri [\#1156](https://github.com/nymtech/nym/pull/1156) ([durch](https://github.com/durch))
|
||||||
|
- Cleanup [\#1155](https://github.com/nymtech/nym/pull/1155) ([durch](https://github.com/durch))
|
||||||
|
- Feature flag reward payments [\#1154](https://github.com/nymtech/nym/pull/1154) ([durch](https://github.com/durch))
|
||||||
|
- Add Query endpoints for calculating rewards [\#1152](https://github.com/nymtech/nym/pull/1152) ([durch](https://github.com/durch))
|
||||||
|
- Pending endpoints [\#1150](https://github.com/nymtech/nym/pull/1150) ([durch](https://github.com/durch))
|
||||||
|
- wallet: add logging [\#1149](https://github.com/nymtech/nym/pull/1149) ([octol](https://github.com/octol))
|
||||||
|
- wallet: use Urls rather than Strings for validator urls [\#1148](https://github.com/nymtech/nym/pull/1148) ([octol](https://github.com/octol))
|
||||||
|
- Change accumulated reward to Option, migrate delegations [\#1147](https://github.com/nymtech/nym/pull/1147) ([durch](https://github.com/durch))
|
||||||
|
- wallet: fetch validators url remotely if available [\#1146](https://github.com/nymtech/nym/pull/1146) ([octol](https://github.com/octol))
|
||||||
|
- Fix delegated\_free calculation [\#1145](https://github.com/nymtech/nym/pull/1145) ([durch](https://github.com/durch))
|
||||||
|
- Update Nym wallet dependencies to use `ts-packages` [\#1144](https://github.com/nymtech/nym/pull/1144) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- wallet: try validators one by one if available [\#1143](https://github.com/nymtech/nym/pull/1143) ([octol](https://github.com/octol))
|
||||||
|
- Update Network Explorer Packages and add mix node identity key copy [\#1142](https://github.com/nymtech/nym/pull/1142) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Feature/vesting token pool selector [\#1140](https://github.com/nymtech/nym/pull/1140) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Add `ts-packages` for shared Typescript packages [\#1139](https://github.com/nymtech/nym/pull/1139) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- allow main-net prefix and denom to work [\#1137](https://github.com/nymtech/nym/pull/1137) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Upgrade blake3 to v1.3.1 and tauri to 1.0.0-rc.3 [\#1136](https://github.com/nymtech/nym/pull/1136) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Bump url-parse from 1.5.7 to 1.5.10 in /clients/native/examples/js-examples/websocket [\#1134](https://github.com/nymtech/nym/pull/1134) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Use network explorer map data with disputed areas [\#1133](https://github.com/nymtech/nym/pull/1133) ([Baro1905](https://github.com/Baro1905))
|
||||||
|
- Feature/vesting UI [\#1132](https://github.com/nymtech/nym/pull/1132) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Refactor to a lazy rewarding system [\#1127](https://github.com/nymtech/nym/pull/1127) ([durch](https://github.com/durch))
|
||||||
|
- Bump ws from 6.2.1 to 6.2.2 in /clients/webassembly/js-example [\#1126](https://github.com/nymtech/nym/pull/1126) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump url-parse from 1.4.7 to 1.5.7 in /clients/webassembly/react-example [\#1125](https://github.com/nymtech/nym/pull/1125) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump url-parse from 1.5.4 to 1.5.7 in /clients/native/examples/js-examples/websocket [\#1124](https://github.com/nymtech/nym/pull/1124) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump url-parse from 1.5.1 to 1.5.7 in /clients/webassembly/js-example [\#1122](https://github.com/nymtech/nym/pull/1122) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- update contract address [\#1121](https://github.com/nymtech/nym/pull/1121) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Refactor GitHub Actions notifications [\#1119](https://github.com/nymtech/nym/pull/1119) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Change `pledge` to `bond` in gateway list [\#1118](https://github.com/nymtech/nym/pull/1118) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Bump follow-redirects from 1.14.7 to 1.14.8 in /contracts/basic-bandwidth-generation [\#1117](https://github.com/nymtech/nym/pull/1117) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.3 to 1.14.8 in /explorer [\#1116](https://github.com/nymtech/nym/pull/1116) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.5 to 1.14.8 in /nym-wallet [\#1115](https://github.com/nymtech/nym/pull/1115) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.7 to 1.14.8 in /clients/native/examples/js-examples/websocket [\#1114](https://github.com/nymtech/nym/pull/1114) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.7 to 1.14.8 in /testnet-faucet [\#1113](https://github.com/nymtech/nym/pull/1113) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.1 to 1.14.8 in /clients/webassembly/js-example [\#1112](https://github.com/nymtech/nym/pull/1112) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Feature/vesting get current period [\#1111](https://github.com/nymtech/nym/pull/1111) ([durch](https://github.com/durch))
|
||||||
|
- Bump simple-get from 2.8.1 to 2.8.2 in /contracts/basic-bandwidth-generation [\#1110](https://github.com/nymtech/nym/pull/1110) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump simple-get from 3.1.0 to 3.1.1 in /explorer [\#1109](https://github.com/nymtech/nym/pull/1109) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump simple-get from 3.1.0 to 3.1.1 in /clients/tauri-client [\#1108](https://github.com/nymtech/nym/pull/1108) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump simple-get from 3.1.0 to 3.1.1 in /nym-wallet [\#1107](https://github.com/nymtech/nym/pull/1107) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump node-sass from 4.14.1 to 7.0.0 in /clients/webassembly/react-example [\#1105](https://github.com/nymtech/nym/pull/1105) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Fix hardcoded period logic [\#1104](https://github.com/nymtech/nym/pull/1104) ([durch](https://github.com/durch))
|
||||||
|
- Fixed underflow in rewarding all delegators [\#1099](https://github.com/nymtech/nym/pull/1099) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Emit original bond as part of rewarding event [\#1094](https://github.com/nymtech/nym/pull/1094) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Add UpdateMixnodeConfigOnBehalf to vestng contract [\#1091](https://github.com/nymtech/nym/pull/1091) ([durch](https://github.com/durch))
|
||||||
|
- Fixes infinite loops in requests involving pagination [\#1085](https://github.com/nymtech/nym/pull/1085) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Removes migration code [\#1071](https://github.com/nymtech/nym/pull/1071) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- feature/pedersen-commitments [\#1048](https://github.com/nymtech/nym/pull/1048) ([danielementary](https://github.com/danielementary))
|
||||||
|
- Feature/reuse init owner [\#970](https://github.com/nymtech/nym/pull/970) ([neacsu](https://github.com/neacsu))
|
||||||
|
|
||||||
|
## [nym-wallet-v1.0.0](https://github.com/nymtech/nym/tree/nym-wallet-v1.0.0) (2022-02-03)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.1...nym-wallet-v1.0.0)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- \[Feature Request\] Please enable registration without need for Telegram account [\#1016](https://github.com/nymtech/nym/issues/1016)
|
||||||
|
- Fast mixnode launch with a pre-built ISO + VM software [\#1001](https://github.com/nymtech/nym/issues/1001)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- \[Issue\] [\#1000](https://github.com/nymtech/nym/issues/1000)
|
||||||
|
- \[Issue\] `nym-client` requires multiple attempts to run a server [\#869](https://github.com/nymtech/nym/issues/869)
|
||||||
|
- De-'float'-ing `Interval` \(`Display` impl + `serde`\) [\#1065](https://github.com/nymtech/nym/pull/1065) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- display client address on wallet creation [\#1058](https://github.com/nymtech/nym/pull/1058) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Rewarded set inclusion probability API endpoint [\#1037](https://github.com/nymtech/nym/issues/1037)
|
||||||
|
- Update cw-storage-plus to 0.11 [\#1032](https://github.com/nymtech/nym/issues/1032)
|
||||||
|
- Change `u128` fields in `RewardEstimationResponse` to `u64` [\#1029](https://github.com/nymtech/nym/issues/1029)
|
||||||
|
- Test out the mainnet Gravity Bridge [\#1006](https://github.com/nymtech/nym/issues/1006)
|
||||||
|
- Add vesting contract interface to nym-wallet [\#959](https://github.com/nymtech/nym/issues/959)
|
||||||
|
- Mixnode crash [\#486](https://github.com/nymtech/nym/issues/486)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- create custom urls for mainnet [\#1095](https://github.com/nymtech/nym/pull/1095) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Wallet signing on MacOS [\#1093](https://github.com/nymtech/nym/pull/1093) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Fix rust 2018 idioms warnings [\#1092](https://github.com/nymtech/nym/pull/1092) ([octol](https://github.com/octol))
|
||||||
|
- Prevent contract overwriting [\#1090](https://github.com/nymtech/nym/pull/1090) ([durch](https://github.com/durch))
|
||||||
|
- Logout operation [\#1087](https://github.com/nymtech/nym/pull/1087) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Update to rust edition 2021 everywhere [\#1086](https://github.com/nymtech/nym/pull/1086) ([octol](https://github.com/octol))
|
||||||
|
- Tag contract errors, and print out lines for easier QA [\#1084](https://github.com/nymtech/nym/pull/1084) ([durch](https://github.com/durch))
|
||||||
|
- Feature/flexible vesting + utility queries [\#1083](https://github.com/nymtech/nym/pull/1083) ([durch](https://github.com/durch))
|
||||||
|
- Bump @openzeppelin/contracts from 4.3.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1082](https://github.com/nymtech/nym/pull/1082) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump nth-check from 2.0.0 to 2.0.1 in /clients/native/examples/js-examples/websocket [\#1081](https://github.com/nymtech/nym/pull/1081) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump url-parse from 1.5.1 to 1.5.4 in /clients/native/examples/js-examples/websocket [\#1080](https://github.com/nymtech/nym/pull/1080) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.1 to 1.14.7 in /clients/native/examples/js-examples/websocket [\#1079](https://github.com/nymtech/nym/pull/1079) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump nanoid from 3.1.23 to 3.2.0 in /clients/native/examples/js-examples/websocket [\#1078](https://github.com/nymtech/nym/pull/1078) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Setup basic test for mixnode stats reporting [\#1077](https://github.com/nymtech/nym/pull/1077) ([octol](https://github.com/octol))
|
||||||
|
- Make wallet\_address mandatory for mixnode init [\#1076](https://github.com/nymtech/nym/pull/1076) ([octol](https://github.com/octol))
|
||||||
|
- Tidy nym-mixnode module visibility [\#1075](https://github.com/nymtech/nym/pull/1075) ([octol](https://github.com/octol))
|
||||||
|
- Feature/wallet login with password [\#1074](https://github.com/nymtech/nym/pull/1074) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Add trait to mock client dependency in DelayForwarder [\#1073](https://github.com/nymtech/nym/pull/1073) ([octol](https://github.com/octol))
|
||||||
|
- Bump rust-version to latest stable for nym-mixnode [\#1072](https://github.com/nymtech/nym/pull/1072) ([octol](https://github.com/octol))
|
||||||
|
- Fixes CI for our wasm build [\#1069](https://github.com/nymtech/nym/pull/1069) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Add @octol as codeowner [\#1068](https://github.com/nymtech/nym/pull/1068) ([octol](https://github.com/octol))
|
||||||
|
- set-up inclusion probability [\#1067](https://github.com/nymtech/nym/pull/1067) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Feature/wasm client [\#1066](https://github.com/nymtech/nym/pull/1066) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Changed bech32\_prefix from punk to nymt [\#1064](https://github.com/nymtech/nym/pull/1064) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Bump nanoid from 3.1.30 to 3.2.0 in /testnet-faucet [\#1063](https://github.com/nymtech/nym/pull/1063) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump nanoid from 3.1.30 to 3.2.0 in /nym-wallet [\#1062](https://github.com/nymtech/nym/pull/1062) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Rework vesting contract storage [\#1061](https://github.com/nymtech/nym/pull/1061) ([durch](https://github.com/durch))
|
||||||
|
- Mixnet Contract constants extraction [\#1060](https://github.com/nymtech/nym/pull/1060) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- fix: make explorer footer year dynamic [\#1059](https://github.com/nymtech/nym/pull/1059) ([martinyung](https://github.com/martinyung))
|
||||||
|
- Add mnemonic just on creation, to display it [\#1057](https://github.com/nymtech/nym/pull/1057) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Network Explorer: updates to API and UI to show the active set [\#1056](https://github.com/nymtech/nym/pull/1056) ([mmsinclair](https://github.com/mmsinclair))
|
||||||
|
- Made contract addresses for query NymdClient construction optional [\#1055](https://github.com/nymtech/nym/pull/1055) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Introduced RPC query for total token supply [\#1053](https://github.com/nymtech/nym/pull/1053) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Feature/tokio console [\#1052](https://github.com/nymtech/nym/pull/1052) ([durch](https://github.com/durch))
|
||||||
|
- Implemented beta clippy lint recommendations [\#1051](https://github.com/nymtech/nym/pull/1051) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- add new function to update profit percentage [\#1050](https://github.com/nymtech/nym/pull/1050) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Upgrade Clap and use declarative argument parsing for nym-mixnode [\#1047](https://github.com/nymtech/nym/pull/1047) ([octol](https://github.com/octol))
|
||||||
|
- Feature/additional bond validation [\#1046](https://github.com/nymtech/nym/pull/1046) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Fix clippy on relevant lints [\#1044](https://github.com/nymtech/nym/pull/1044) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Bump shelljs from 0.8.4 to 0.8.5 in /contracts/basic-bandwidth-generation [\#1043](https://github.com/nymtech/nym/pull/1043) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Endpoint for rewarded set inclusion probabilities [\#1042](https://github.com/nymtech/nym/pull/1042) ([durch](https://github.com/durch))
|
||||||
|
- Bump follow-redirects from 1.14.4 to 1.14.7 in /contracts/basic-bandwidth-generation [\#1041](https://github.com/nymtech/nym/pull/1041) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Bump follow-redirects from 1.14.5 to 1.14.7 in /testnet-faucet [\#1040](https://github.com/nymtech/nym/pull/1040) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Feature/node settings update [\#1036](https://github.com/nymtech/nym/pull/1036) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Migrate to cw-storage-plus 0.11.1 [\#1035](https://github.com/nymtech/nym/pull/1035) ([durch](https://github.com/durch))
|
||||||
|
- Bump @openzeppelin/contracts from 4.4.1 to 4.4.2 in /contracts/basic-bandwidth-generation [\#1034](https://github.com/nymtech/nym/pull/1034) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Feature/configurable wallet [\#1033](https://github.com/nymtech/nym/pull/1033) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Feature/downcast reward estimation [\#1031](https://github.com/nymtech/nym/pull/1031) ([durch](https://github.com/durch))
|
||||||
|
- Wallet UI updates [\#1028](https://github.com/nymtech/nym/pull/1028) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Remove migration code [\#1027](https://github.com/nymtech/nym/pull/1027) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Chore/stricter dependency requirements [\#1025](https://github.com/nymtech/nym/pull/1025) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Feature/validator api client endpoints [\#1024](https://github.com/nymtech/nym/pull/1024) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Updated cosmrs to 0.4.1 [\#1023](https://github.com/nymtech/nym/pull/1023) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Feature/testnet deploy scripts [\#1022](https://github.com/nymtech/nym/pull/1022) ([mfahampshire](https://github.com/mfahampshire))
|
||||||
|
- Changed wallet's client to a full validator client [\#1021](https://github.com/nymtech/nym/pull/1021) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Fix 404 link [\#1020](https://github.com/nymtech/nym/pull/1020) ([RiccardoMasutti](https://github.com/RiccardoMasutti))
|
||||||
|
- Feature/additional mixnode endpoints [\#1019](https://github.com/nymtech/nym/pull/1019) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Introduced denom check when trying to withdraw vested coins [\#1018](https://github.com/nymtech/nym/pull/1018) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Add network defaults for qa [\#1017](https://github.com/nymtech/nym/pull/1017) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Feature/expanded events [\#1015](https://github.com/nymtech/nym/pull/1015) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- update frontend to use new profit update api [\#1014](https://github.com/nymtech/nym/pull/1014) ([fmtabbara](https://github.com/fmtabbara))
|
||||||
|
- Feature/node state endpoint [\#1013](https://github.com/nymtech/nym/pull/1013) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Feature/hourly set updates [\#1012](https://github.com/nymtech/nym/pull/1012) ([durch](https://github.com/durch))
|
||||||
|
- Feature/remove unused profit margin [\#1011](https://github.com/nymtech/nym/pull/1011) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Feature/explorer node status [\#1010](https://github.com/nymtech/nym/pull/1010) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Use serial integer instead of random [\#1009](https://github.com/nymtech/nym/pull/1009) ([durch](https://github.com/durch))
|
||||||
|
- Feature/configure profit [\#1008](https://github.com/nymtech/nym/pull/1008) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Feature/fix gateway sign [\#1004](https://github.com/nymtech/nym/pull/1004) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Fix clippy [\#1003](https://github.com/nymtech/nym/pull/1003) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Update wallet version [\#998](https://github.com/nymtech/nym/pull/998) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Fix wallet build instructions [\#997](https://github.com/nymtech/nym/pull/997) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
|
- Make the separation between testnet-mode and erc20 bandwidth mode clearer [\#994](https://github.com/nymtech/nym/pull/994) ([neacsu](https://github.com/neacsu))
|
||||||
|
- Bump @openzeppelin/contracts from 3.4.0 to 4.4.1 in /contracts/basic-bandwidth-generation [\#983](https://github.com/nymtech/nym/pull/983) ([dependabot[bot]](https://github.com/apps/dependabot))
|
||||||
|
- Feature/implicit runtime [\#973](https://github.com/nymtech/nym/pull/973) ([jstuczyn](https://github.com/jstuczyn))
|
||||||
|
- Differentiate staking and ownership [\#961](https://github.com/nymtech/nym/pull/961) ([durch](https://github.com/durch))
|
||||||
|
|
||||||
|
## [v0.12.1](https://github.com/nymtech/nym/tree/v0.12.1) (2021-12-23)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/nymtech/nym/compare/v0.12.0...v0.12.1)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Add version check to binaries [\#967](https://github.com/nymtech/nym/issues/967)
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- \[Issue\] NYM wallet doesn't work after login [\#995](https://github.com/nymtech/nym/issues/995)
|
||||||
|
- \[Issue\] [\#993](https://github.com/nymtech/nym/issues/993)
|
||||||
|
- NYM wallet setup trouble\[Issue\] [\#958](https://github.com/nymtech/nym/issues/958)
|
||||||
|
|
||||||
## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21)
|
## [v0.12.0](https://github.com/nymtech/nym/tree/v0.12.0) (2021-12-21)
|
||||||
|
|
||||||
[Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0)
|
[Full Changelog](https://github.com/nymtech/nym/compare/v0.11.0...v0.12.0)
|
||||||
@@ -58,6 +379,7 @@
|
|||||||
|
|
||||||
**Merged pull requests:**
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Update wallet to align with versioning on nodes and gateways [\#991](https://github.com/nymtech/nym/pull/991) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
- Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987))
|
- Fix success view messages. [\#990](https://github.com/nymtech/nym/pull/990) ([tommyv1987](https://github.com/tommyv1987))
|
||||||
- Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu))
|
- Feature/enable signature check [\#989](https://github.com/nymtech/nym/pull/989) ([neacsu](https://github.com/neacsu))
|
||||||
- Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu))
|
- Update mixnet contract address [\#988](https://github.com/nymtech/nym/pull/988) ([neacsu](https://github.com/neacsu))
|
||||||
|
|||||||
Generated
+412
-187
File diff suppressed because it is too large
Load Diff
+8
-1
@@ -9,23 +9,30 @@ overflow-checks = true
|
|||||||
[profile.dev]
|
[profile.dev]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.test]
|
||||||
|
# equivalent of running in `--release` (but since we're in test profile we're keeping overflow checks and all of those by default)
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"clients/client-core",
|
"clients/client-core",
|
||||||
|
"clients/credential",
|
||||||
"clients/native",
|
"clients/native",
|
||||||
"clients/native/websocket-requests",
|
"clients/native/websocket-requests",
|
||||||
"clients/socks5",
|
"clients/socks5",
|
||||||
# "clients/tauri-client/src-tauri",
|
|
||||||
"common/client-libs/gateway-client",
|
"common/client-libs/gateway-client",
|
||||||
"common/client-libs/mixnet-client",
|
"common/client-libs/mixnet-client",
|
||||||
"common/client-libs/validator-client",
|
"common/client-libs/validator-client",
|
||||||
|
"common/credential-storage",
|
||||||
"common/coconut-interface",
|
"common/coconut-interface",
|
||||||
"common/config",
|
"common/config",
|
||||||
"common/credentials",
|
"common/credentials",
|
||||||
"common/crypto",
|
"common/crypto",
|
||||||
|
"common/crypto/dkg",
|
||||||
"common/bandwidth-claim-contract",
|
"common/bandwidth-claim-contract",
|
||||||
|
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
|
||||||
"common/cosmwasm-smart-contracts/contracts-common",
|
"common/cosmwasm-smart-contracts/contracts-common",
|
||||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||||
|
|||||||
@@ -40,36 +40,40 @@ Node, node operator and delegator rewards are determined according to the princi
|
|||||||
|
|
||||||
|Symbol|Definition|
|
|Symbol|Definition|
|
||||||
|---|---|
|
|---|---|
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=R">|global share of rewards available, starts at 2% of the reward pool.
|
|<img src="https://render.githubusercontent.com/render/math?math=R#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R#gh-dark-mode-only">|global share of rewards available, starts at 2% of the reward pool.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}">|node reward for mixnode `i`.
|
|<img src="https://render.githubusercontent.com/render/math?math=R_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}#gh-dark-mode-only">|node reward for mixnode `i`.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|
|<img src="https://render.githubusercontent.com/render/math?math=\sigma_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma_{i}#gh-dark-mode-only">|ratio of total node stake (node bond + all delegations) to the token circulating supply.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}">|ratio of stake operator has pledged to their node to the token circulating supply.
|
|<img src="https://render.githubusercontent.com/render/math?math=\lambda_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda_{i}#gh-dark-mode-only">|ratio of stake operator has pledged to their node to the token circulating supply.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}">|fraction of total effort undertaken by node `i`, set to `1/k`.
|
|<img src="https://render.githubusercontent.com/render/math?math=\omega_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\omega_{i}#gh-dark-mode-only">|fraction of total effort undertaken by node `i`, set to `1/k`.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=k">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox.
|
|<img src="https://render.githubusercontent.com/render/math?math=k#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}k#gh-dark-mode-only">|number of nodes stakeholders are incentivised to create, set by the validators, a matter of governance. Currently determined by the `reward set` size, and set to 720 in testnet Sandbox.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=\alpha">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|
|<img src="https://render.githubusercontent.com/render/math?math=\alpha#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}\alpha#gh-dark-mode-only">|Sybil attack resistance parameter - the higher this parameter is set the stronger the reduction in competitivness gets for a Sybil attacker.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}">|declared profit margin of operator `i`, defaults to 10% in.
|
|<img src="https://render.githubusercontent.com/render/math?math=PM_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PM_{i}#gh-dark-mode-only">|declared profit margin of operator `i`, defaults to 10% in.
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|
|<img src="https://render.githubusercontent.com/render/math?math=PF_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PF_{i}#gh-dark-mode-only">|uptime of node `i`, scaled to 0 - 1, for the rewarding epoch
|
||||||
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}">|cost of operating node `i` for the duration of the rewarding eopoch, set to 40 NYMT.
|
|<img src="https://render.githubusercontent.com/render/math?math=PP_{i}#gh-light-mode-only"><img src="https://render.githubusercontent.com/render/math?math=\color{white}PP_{i}#gh-dark-mode-only">|cost of operating node `i` for the duration of the rewarding epoch, set to 40 NYMT.
|
||||||
|
|
||||||
Node reward for node `i` is determined as:
|
Node reward for node `i` is determined as:
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)">
|
<img src="https://render.githubusercontent.com/render/math?math=R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-light-mode-only">
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}R_{i}=PF_{i} \cdot R \cdot (\sigma^'_{i} \cdot \omega_{i} \cdot k %2b \alpha \cdot \lambda^'_{i} \cdot \sigma^'_{i} \cdot k)/(1 %2b \alpha)#gh-dark-mode-only">
|
||||||
where:
|
where:
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}">
|
<img src="https://render.githubusercontent.com/render/math?math=\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-light-mode-only">
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\sigma^'_{i} = min\{\sigma_{i}, 1/k\}#gh-dark-mode-only">
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}">
|
<img src="https://render.githubusercontent.com/render/math?math=\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-light-mode-only">
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}\lambda^'_{i} = min\{\lambda_{i}, 1/k\}#gh-dark-mode-only">
|
||||||
|
|
||||||
Operator of node `i` is credited with the following amount:
|
Operator of node `i` is credited with the following amount:
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}">
|
<img src="https://render.githubusercontent.com/render/math?math=min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}min\{PP_{i},R_{i})\} %2b max\{0, (PM_{i} %2b (1 - PM_{i}) \cdot \lambda_{i}/\delta_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
||||||
|
|
||||||
Delegate with stake `s` recieves:
|
Delegate with stake `s` recieves:
|
||||||
|
|
||||||
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}">
|
<img src="https://render.githubusercontent.com/render/math?math=max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-light-mode-only">
|
||||||
|
<img src="https://render.githubusercontent.com/render/math?math=\color{white}max\{0, (1-PM_{i}) \cdot (s^'/\sigma_{i}) \cdot (R_{i} - PP_{i})\}#gh-dark-mode-only">
|
||||||
|
|
||||||
where `s'` is stake `s` scaled over total token circulating supply.
|
where `s'` is stake `s` scaled over total token circulating supply.
|
||||||
|
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
Critical bug or security issue 💥
|
||||||
|
|
||||||
|
If you're here because you're trying to figure out how to notify us of a security issue, go to Discord, and alert the core engineers:
|
||||||
|
|
||||||
|
Dave Hrycyszyn futurechimp#5430
|
||||||
|
Drazen Urch drazen#4873
|
||||||
|
Jedrzej Stuczynski "Jedrzej | Nym#5666"
|
||||||
|
|
||||||
|
|
||||||
|
Please avoid opening public issues on GitHub that contain information about a potential security vulnerability as this makes it difficult to reduce the impact and harm of valid security issues.
|
||||||
@@ -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="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="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"/>
|
<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,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "client-core"
|
name = "client-core"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.1"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
@@ -32,4 +32,4 @@ validator-client = { path = "../../common/client-libs/validator-client" }
|
|||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = []
|
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
|
||||||
@@ -103,22 +103,15 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
self::Client::<T>::default_reply_encryption_key_store_path(&id);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
if self.client.database_path.as_os_str().is_empty() {
|
||||||
if self
|
self.client.database_path = self::Client::<T>::default_database_path(&id);
|
||||||
.client
|
|
||||||
.backup_bandwidth_token_keys_dir
|
|
||||||
.as_os_str()
|
|
||||||
.is_empty()
|
|
||||||
{
|
|
||||||
self.client.backup_bandwidth_token_keys_dir =
|
|
||||||
self::Client::<T>::default_backup_bandwidth_token_keys_dir(&id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.client.id = id;
|
self.client.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_testnet_mode(&mut self, testnet_mode: bool) {
|
pub fn with_disabled_credentials(&mut self, disabled_credentials_mode: bool) {
|
||||||
self.client.testnet_mode = testnet_mode;
|
self.client.disabled_credentials_mode = disabled_credentials_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
|
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
|
||||||
@@ -161,8 +154,8 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.id.clone()
|
self.client.id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_testnet_mode(&self) -> bool {
|
pub fn get_disabled_credentials_mode(&self) -> bool {
|
||||||
self.client.testnet_mode
|
self.client.disabled_credentials_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nym_root_directory(&self) -> PathBuf {
|
pub fn get_nym_root_directory(&self) -> PathBuf {
|
||||||
@@ -213,9 +206,8 @@ impl<T: NymConfig> Config<T> {
|
|||||||
self.client.gateway_endpoint.gateway_listener.clone()
|
self.client.gateway_endpoint.gateway_listener.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
pub fn get_database_path(&self) -> PathBuf {
|
||||||
pub fn get_backup_bandwidth_token_keys_dir(&self) -> PathBuf {
|
self.client.database_path.clone()
|
||||||
self.client.backup_bandwidth_token_keys_dir.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
@@ -302,10 +294,10 @@ pub struct Client<T> {
|
|||||||
/// ID specifies the human readable ID of this particular client.
|
/// ID specifies the human readable ID of this particular client.
|
||||||
id: String,
|
id: String,
|
||||||
|
|
||||||
/// Indicates whether this client is running in a testnet mode, thus attempting
|
/// Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||||
/// to claim bandwidth without presenting bandwidth credentials.
|
/// to claim bandwidth without presenting bandwidth credentials.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
testnet_mode: bool,
|
disabled_credentials_mode: bool,
|
||||||
|
|
||||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls: Vec<Url>,
|
validator_api_urls: Vec<Url>,
|
||||||
@@ -337,11 +329,8 @@ pub struct Client<T> {
|
|||||||
/// Information regarding how the client should send data to gateway.
|
/// Information regarding how the client should send data to gateway.
|
||||||
gateway_endpoint: GatewayEndpoint,
|
gateway_endpoint: GatewayEndpoint,
|
||||||
|
|
||||||
/// Path to directory containing public/private keys used for bandwidth token purchase.
|
/// Path to the database containing bandwidth credentials of this client.
|
||||||
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
database_path: PathBuf,
|
||||||
/// The public key is the name of the file, while the private key is the content.
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
backup_bandwidth_token_keys_dir: PathBuf,
|
|
||||||
|
|
||||||
/// Ethereum private key.
|
/// Ethereum private key.
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
@@ -365,7 +354,7 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
Client {
|
Client {
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
id: "".to_string(),
|
id: "".to_string(),
|
||||||
testnet_mode: false,
|
disabled_credentials_mode: true,
|
||||||
validator_api_urls: default_api_endpoints(),
|
validator_api_urls: default_api_endpoints(),
|
||||||
private_identity_key_file: Default::default(),
|
private_identity_key_file: Default::default(),
|
||||||
public_identity_key_file: Default::default(),
|
public_identity_key_file: Default::default(),
|
||||||
@@ -375,8 +364,7 @@ impl<T: NymConfig> Default for Client<T> {
|
|||||||
ack_key_file: Default::default(),
|
ack_key_file: Default::default(),
|
||||||
reply_encryption_key_store_path: Default::default(),
|
reply_encryption_key_store_path: Default::default(),
|
||||||
gateway_endpoint: Default::default(),
|
gateway_endpoint: Default::default(),
|
||||||
#[cfg(not(feature = "coconut"))]
|
database_path: Default::default(),
|
||||||
backup_bandwidth_token_keys_dir: Default::default(),
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
eth_private_key: "".to_string(),
|
eth_private_key: "".to_string(),
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
@@ -415,10 +403,8 @@ impl<T: NymConfig> Client<T> {
|
|||||||
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
|
||||||
T::default_data_directory(Some(id)).join("reply_key_store")
|
T::default_data_directory(Some(id)).join("reply_key_store")
|
||||||
}
|
}
|
||||||
|
fn default_database_path(id: &str) -> PathBuf {
|
||||||
#[cfg(not(feature = "coconut"))]
|
T::default_data_directory(Some(id)).join("db.sqlite")
|
||||||
fn default_backup_bandwidth_token_keys_dir(id: &str) -> PathBuf {
|
|
||||||
T::default_data_directory(Some(id)).join("backup_bandwidth_token_keys")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+3453
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
|||||||
|
[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" }
|
||||||
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
|
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"]
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// 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 credential_storage::storage::Storage;
|
||||||
|
use credential_storage::PersistentStorage;
|
||||||
|
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, shared_storage: PersistentStorage) -> 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, _shared_storage: PersistentStorage) -> 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, _shared_storage: PersistentStorage) -> 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, shared_storage: PersistentStorage) -> 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(
|
||||||
|
¶ms,
|
||||||
|
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(¶ms, &bandwidth_credential_attributes, &urls).await?;
|
||||||
|
shared_storage
|
||||||
|
.insert_coconut_credential(
|
||||||
|
state.amount.to_string(),
|
||||||
|
VOUCHER_INFO.to_string(),
|
||||||
|
bandwidth_credential_attributes.get_private_attributes()[0].to_bs58(),
|
||||||
|
bandwidth_credential_attributes.get_private_attributes()[1].to_bs58(),
|
||||||
|
signature.to_bs58(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
state.signature = Some(signature.to_bs58());
|
||||||
|
db.set(&self.tx_hash, &state).unwrap();
|
||||||
|
|
||||||
|
println!("Signature: {:?}", state.signature);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use credential_storage::error::StorageError;
|
||||||
|
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),
|
||||||
|
|
||||||
|
#[error("Could not use shared storage")]
|
||||||
|
SharedStorageError(#[from] StorageError),
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// 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 = "jazz fatigue diagram account outer wrist slide cherry mother grid network pause wolf pig round answer mail junior better hair dismiss toward access end";
|
||||||
|
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
|
||||||
|
pub const CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||||
|
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 shared_storage = credential_storage::initialise_storage(std::path::PathBuf::from("/tmp/credential.db")).await;
|
||||||
|
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, shared_storage).await?,
|
||||||
|
Commands::ListDeposits(m) => m.execute(&mut db, shared_storage).await?,
|
||||||
|
Commands::GetCredential(m) => m.execute(&mut db, shared_storage).await?,
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fn main() {
|
||||||
|
println!("Crate only designed for coconut feature");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client"
|
name = "nym-client"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.1"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.56"
|
rust-version = "1.56"
|
||||||
@@ -34,6 +34,7 @@ tokio-tungstenite = "0.14" # websocket
|
|||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
credentials = { path = "../../common/credentials", optional = true }
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
@@ -42,12 +43,12 @@ nymsphinx = { path = "../../common/nymsphinx" }
|
|||||||
pemstore = { path = "../../common/pemstore" }
|
pemstore = { path = "../../common/pemstore" }
|
||||||
topology = { path = "../../common/topology" }
|
topology = { path = "../../common/topology" }
|
||||||
websocket-requests = { path = "websocket-requests" }
|
websocket-requests = { path = "websocket-requests" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
network-defaults = { path = "../../common/network-defaults" }
|
network-defaults = { path = "../../common/network-defaults" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
|
||||||
eth = []
|
eth = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -592,9 +592,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
@@ -4825,9 +4825,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# Indicates whether this client is running in a testnet mode, thus attempting
|
# Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||||
# to claim bandwidth without presenting bandwidth credentials.
|
# to claim bandwidth without presenting bandwidth credentials.
|
||||||
testnet_mode = {{ client.testnet_mode }}
|
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
|
||||||
|
|
||||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls = [
|
validator_api_urls = [
|
||||||
@@ -46,10 +46,8 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
# Path to directory containing public/private keys used for bandwidth token purchase.
|
# Path to the database containing bandwidth credentials
|
||||||
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
database_path = '{{ client.database_path }}'
|
||||||
# The public key is the name of the file, while the private key is the content.
|
|
||||||
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
|
||||||
|
|
||||||
# Ethereum private key.
|
# Ethereum private key.
|
||||||
eth_private_key = '{{ client.eth_private_key }}'
|
eth_private_key = '{{ client.eth_private_key }}'
|
||||||
|
|||||||
@@ -174,14 +174,16 @@ impl NymClient {
|
|||||||
|
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
let bandwidth_controller = BandwidthController::new(
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||||
|
.await,
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
*self.key_manager.identity_keypair().public_key(),
|
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let bandwidth_controller = BandwidthController::new(
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||||
|
.await,
|
||||||
self.config.get_base().get_eth_endpoint(),
|
self.config.get_base().get_eth_endpoint(),
|
||||||
self.config.get_base().get_eth_private_key(),
|
self.config.get_base().get_eth_private_key(),
|
||||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
|
||||||
)
|
)
|
||||||
.expect("Could not create bandwidth controller");
|
.expect("Could not create bandwidth controller");
|
||||||
|
|
||||||
@@ -197,8 +199,8 @@ impl NymClient {
|
|||||||
Some(bandwidth_controller),
|
Some(bandwidth_controller),
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.config.get_base().get_testnet_mode() {
|
if self.config.get_base().get_disabled_credentials_mode() {
|
||||||
gateway_client.set_testnet_mode(true)
|
gateway_client.set_disabled_credentials_mode(true)
|
||||||
}
|
}
|
||||||
gateway_client
|
gateway_client
|
||||||
.authenticate_and_start()
|
.authenticate_and_start()
|
||||||
|
|||||||
@@ -4,20 +4,10 @@
|
|||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::client::key_manager::KeyManager;
|
use client_core::client::key_manager::KeyManager;
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use coconut_interface::{hash_to_scalar, Credential, Parameters};
|
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::coconut::bandwidth::{
|
|
||||||
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::obtain_aggregate_verification_key;
|
|
||||||
use crypto::asymmetric::{encryption, identity};
|
use crypto::asymmetric::{encryption, identity};
|
||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
@@ -34,8 +24,8 @@ use crate::commands::override_config;
|
|||||||
#[cfg(feature = "eth")]
|
#[cfg(feature = "eth")]
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use crate::commands::{
|
use crate::commands::{
|
||||||
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ENABLED_CREDENTIALS_MODE_ARG_NAME,
|
||||||
TESTNET_MODE_ARG_NAME,
|
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
@@ -76,58 +66,28 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let app = app
|
let app = app
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
.help("Set this client to work in a disabled credentials mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
.long(ETH_ENDPOINT_ARG_NAME)
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
||||||
.required(true))
|
.required(true))
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||||
.required(true)
|
.required(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
// this behaviour should definitely be changed, we shouldn't
|
|
||||||
// need to get bandwidth credential for registration
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
|
|
||||||
let verification_key = obtain_aggregate_verification_key(validators)
|
|
||||||
.await
|
|
||||||
.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 bandwidth_credential =
|
|
||||||
obtain_signature(¶ms, &bandwidth_credential_attributes, validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain bandwidth credential");
|
|
||||||
|
|
||||||
prepare_for_spending(
|
|
||||||
raw_identity,
|
|
||||||
&bandwidth_credential,
|
|
||||||
&bandwidth_credential_attributes,
|
|
||||||
&verification_key,
|
|
||||||
)
|
|
||||||
.expect("could not prepare out bandwidth credential for spending")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
gateway: &gateway::Node,
|
gateway: &gateway::Node,
|
||||||
our_identity: Arc<identity::KeyPair>,
|
our_identity: Arc<identity::KeyPair>,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::client::config::{Config, SocketType};
|
|||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
|
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode";
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
|
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
@@ -72,8 +72,8 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
|||||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
|
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||||
config.get_base_mut().with_testnet_mode(true)
|
config.get_base_mut().with_disabled_credentials(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use crate::client::NymClient;
|
|||||||
use crate::commands::override_config;
|
use crate::commands::override_config;
|
||||||
#[cfg(feature = "eth")]
|
#[cfg(feature = "eth")]
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
|
use crate::commands::{
|
||||||
|
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||||
|
};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use log::*;
|
use log::*;
|
||||||
@@ -46,9 +48,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let app = app
|
let app = app
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, App, ArgMatches};
|
use clap::{crate_version, App, ArgMatches};
|
||||||
|
use network_defaults::DEFAULT_NETWORK;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
@@ -67,6 +68,7 @@ fn long_version() -> String {
|
|||||||
{:<20}{}
|
{:<20}{}
|
||||||
{:<20}{}
|
{:<20}{}
|
||||||
{:<20}{}
|
{:<20}{}
|
||||||
|
{:<20}{}
|
||||||
"#,
|
"#,
|
||||||
"Build Timestamp:",
|
"Build Timestamp:",
|
||||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||||
@@ -84,6 +86,8 @@ fn long_version() -> String {
|
|||||||
env!("VERGEN_RUSTC_CHANNEL"),
|
env!("VERGEN_RUSTC_CHANNEL"),
|
||||||
"cargo Profile:",
|
"cargo Profile:",
|
||||||
env!("VERGEN_CARGO_PROFILE"),
|
env!("VERGEN_CARGO_PROFILE"),
|
||||||
|
"Network:",
|
||||||
|
DEFAULT_NETWORK
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-socks5-client"
|
name = "nym-socks5-client"
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.1"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.56"
|
rust-version = "1.56"
|
||||||
@@ -27,6 +27,7 @@ url = "2.2"
|
|||||||
client-core = { path = "../client-core" }
|
client-core = { path = "../client-core" }
|
||||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||||
credentials = { path = "../../common/credentials", optional = true }
|
credentials = { path = "../../common/credentials", optional = true }
|
||||||
|
credential-storage = { path = "../../common/credential-storage" }
|
||||||
config = { path = "../../common/config" }
|
config = { path = "../../common/config" }
|
||||||
crypto = { path = "../../common/crypto" }
|
crypto = { path = "../../common/crypto" }
|
||||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||||
@@ -37,12 +38,12 @@ socks5-requests = { path = "../../common/socks5/requests" }
|
|||||||
topology = { path = "../../common/topology" }
|
topology = { path = "../../common/topology" }
|
||||||
pemstore = { path = "../../common/pemstore" }
|
pemstore = { path = "../../common/pemstore" }
|
||||||
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
proxy-helpers = { path = "../../common/socks5/proxy-helpers" }
|
||||||
validator-client = { path = "../../common/client-libs/validator-client" }
|
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||||
version-checker = { path = "../../common/version-checker" }
|
version-checker = { path = "../../common/version-checker" }
|
||||||
network-defaults = { path = "../../common/network-defaults" }
|
network-defaults = { path = "../../common/network-defaults" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
|
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "client-core/coconut"]
|
||||||
eth = []
|
eth = []
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ version = '{{ client.version }}'
|
|||||||
# Human readable ID of this particular client.
|
# Human readable ID of this particular client.
|
||||||
id = '{{ client.id }}'
|
id = '{{ client.id }}'
|
||||||
|
|
||||||
# Indicates whether this client is running in a testnet mode, thus attempting
|
# Indicates whether this client is running in a disabled credentials mode, thus attempting
|
||||||
# to claim bandwidth without presenting bandwidth credentials.
|
# to claim bandwidth without presenting bandwidth credentials.
|
||||||
testnet_mode = {{ client.testnet_mode }}
|
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
|
||||||
|
|
||||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||||
validator_api_urls = [
|
validator_api_urls = [
|
||||||
@@ -46,10 +46,8 @@ public_encryption_key_file = '{{ client.public_encryption_key_file }}'
|
|||||||
# sent but not received back.
|
# sent but not received back.
|
||||||
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||||
|
|
||||||
# Path to directory containing public/private keys used for bandwidth token purchase.
|
# Path to the database containing bandwidth credentials
|
||||||
# Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
database_path = '{{ client.database_path }}'
|
||||||
# The public key is the name of the file, while the private key is the content.
|
|
||||||
backup_bandwidth_token_keys_dir = '{{ client.backup_bandwidth_token_keys_dir }}'
|
|
||||||
|
|
||||||
# Ethereum private key.
|
# Ethereum private key.
|
||||||
eth_private_key = '{{ client.eth_private_key }}'
|
eth_private_key = '{{ client.eth_private_key }}'
|
||||||
|
|||||||
@@ -162,14 +162,16 @@ impl NymClient {
|
|||||||
|
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
let bandwidth_controller = BandwidthController::new(
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||||
|
.await,
|
||||||
self.config.get_base().get_validator_api_endpoints(),
|
self.config.get_base().get_validator_api_endpoints(),
|
||||||
*self.key_manager.identity_keypair().public_key(),
|
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let bandwidth_controller = BandwidthController::new(
|
let bandwidth_controller = BandwidthController::new(
|
||||||
|
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||||
|
.await,
|
||||||
self.config.get_base().get_eth_endpoint(),
|
self.config.get_base().get_eth_endpoint(),
|
||||||
self.config.get_base().get_eth_private_key(),
|
self.config.get_base().get_eth_private_key(),
|
||||||
self.config.get_base().get_backup_bandwidth_token_keys_dir(),
|
|
||||||
)
|
)
|
||||||
.expect("Could not create bandwidth controller");
|
.expect("Could not create bandwidth controller");
|
||||||
|
|
||||||
@@ -185,8 +187,8 @@ impl NymClient {
|
|||||||
Some(bandwidth_controller),
|
Some(bandwidth_controller),
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.config.get_base().get_testnet_mode() {
|
if self.config.get_base().get_disabled_credentials_mode() {
|
||||||
gateway_client.set_testnet_mode(true)
|
gateway_client.set_disabled_credentials_mode(true)
|
||||||
}
|
}
|
||||||
gateway_client
|
gateway_client
|
||||||
.authenticate_and_start()
|
.authenticate_and_start()
|
||||||
|
|||||||
@@ -4,20 +4,10 @@
|
|||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use client_core::client::key_manager::KeyManager;
|
use client_core::client::key_manager::KeyManager;
|
||||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use coconut_interface::{hash_to_scalar, Credential, Parameters};
|
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::coconut::bandwidth::{
|
|
||||||
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use credentials::obtain_aggregate_verification_key;
|
|
||||||
use crypto::asymmetric::{encryption, identity};
|
use crypto::asymmetric::{encryption, identity};
|
||||||
use gateway_client::GatewayClient;
|
use gateway_client::GatewayClient;
|
||||||
use gateway_requests::registration::handshake::SharedKeys;
|
use gateway_requests::registration::handshake::SharedKeys;
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
|
||||||
use nymsphinx::addressing::clients::Recipient;
|
use nymsphinx::addressing::clients::Recipient;
|
||||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||||
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
use rand::{prelude::SliceRandom, rngs::OsRng, thread_rng};
|
||||||
@@ -32,8 +22,8 @@ use crate::commands::override_config;
|
|||||||
#[cfg(feature = "eth")]
|
#[cfg(feature = "eth")]
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use crate::commands::{
|
use crate::commands::{
|
||||||
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY, ENABLED_CREDENTIALS_MODE_ARG_NAME,
|
||||||
TESTNET_MODE_ARG_NAME,
|
ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
||||||
@@ -76,58 +66,28 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let app = app
|
let app = app
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
.help("Set this client to work in a enabled credentials mode that would attempt to use gateway with bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
||||||
.long(ETH_ENDPOINT_ARG_NAME)
|
.long(ETH_ENDPOINT_ARG_NAME)
|
||||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_ENDPOINT)
|
||||||
.required(true))
|
.required(true))
|
||||||
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
.arg(Arg::with_name(ETH_PRIVATE_KEY_ARG_NAME)
|
||||||
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
.long(ETH_PRIVATE_KEY_ARG_NAME)
|
||||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't want to set this value, use --testnet-mode instead")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value_if(TESTNET_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
.default_value_if(ENABLED_CREDENTIALS_MODE_ARG_NAME, None, DEFAULT_ETH_PRIVATE_KEY)
|
||||||
.required(true)
|
.required(true)
|
||||||
);
|
);
|
||||||
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
// this behaviour should definitely be changed, we shouldn't
|
|
||||||
// need to get bandwidth credential for registration
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8]) -> Credential {
|
|
||||||
let verification_key = obtain_aggregate_verification_key(validators)
|
|
||||||
.await
|
|
||||||
.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 bandwidth_credential =
|
|
||||||
obtain_signature(¶ms, &bandwidth_credential_attributes, validators)
|
|
||||||
.await
|
|
||||||
.expect("could not obtain bandwidth credential");
|
|
||||||
|
|
||||||
prepare_for_spending(
|
|
||||||
raw_identity,
|
|
||||||
&bandwidth_credential,
|
|
||||||
&bandwidth_credential_attributes,
|
|
||||||
&verification_key,
|
|
||||||
)
|
|
||||||
.expect("could not prepare out bandwidth credential for spending")
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn register_with_gateway(
|
async fn register_with_gateway(
|
||||||
gateway: &gateway::Node,
|
gateway: &gateway::Node,
|
||||||
our_identity: Arc<identity::KeyPair>,
|
our_identity: Arc<identity::KeyPair>,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub(crate) mod init;
|
|||||||
pub(crate) mod run;
|
pub(crate) mod run;
|
||||||
pub(crate) mod upgrade;
|
pub(crate) mod upgrade;
|
||||||
|
|
||||||
pub(crate) const TESTNET_MODE_ARG_NAME: &str = "testnet-mode";
|
pub(crate) const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode";
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
|
pub(crate) const ETH_ENDPOINT_ARG_NAME: &str = "eth_endpoint";
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
@@ -68,8 +68,8 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
|||||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cfg!(feature = "eth") || matches.is_present(TESTNET_MODE_ARG_NAME) {
|
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||||
config.get_base_mut().with_testnet_mode(true)
|
config.get_base_mut().with_disabled_credentials(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ use crate::client::NymClient;
|
|||||||
use crate::commands::override_config;
|
use crate::commands::override_config;
|
||||||
#[cfg(feature = "eth")]
|
#[cfg(feature = "eth")]
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use crate::commands::{ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME, TESTNET_MODE_ARG_NAME};
|
use crate::commands::{
|
||||||
|
ENABLED_CREDENTIALS_MODE_ARG_NAME, ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME,
|
||||||
|
};
|
||||||
use clap::{App, Arg, ArgMatches};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use config::NymConfig;
|
use config::NymConfig;
|
||||||
use log::*;
|
use log::*;
|
||||||
@@ -52,9 +54,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
let app = app
|
let app = app
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(TESTNET_MODE_ARG_NAME)
|
Arg::with_name(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.long(TESTNET_MODE_ARG_NAME)
|
.long(ENABLED_CREDENTIALS_MODE_ARG_NAME)
|
||||||
.help("Set this client to work in a testnet mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
.help("Set this client to work in a disabled credentials mode that would attempt to use gateway without bandwidth credential requirement. If this value is set, --eth_endpoint and --eth_private_key don't need to be set.")
|
||||||
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
.conflicts_with_all(&[ETH_ENDPOINT_ARG_NAME, ETH_PRIVATE_KEY_ARG_NAME])
|
||||||
)
|
)
|
||||||
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
.arg(Arg::with_name(ETH_ENDPOINT_ARG_NAME)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use clap::{crate_version, App, ArgMatches};
|
use clap::{crate_version, App, ArgMatches};
|
||||||
|
use network_defaults::DEFAULT_NETWORK;
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
mod commands;
|
mod commands;
|
||||||
@@ -67,6 +68,7 @@ fn long_version() -> String {
|
|||||||
{:<20}{}
|
{:<20}{}
|
||||||
{:<20}{}
|
{:<20}{}
|
||||||
{:<20}{}
|
{:<20}{}
|
||||||
|
{:<20}{}
|
||||||
"#,
|
"#,
|
||||||
"Build Timestamp:",
|
"Build Timestamp:",
|
||||||
env!("VERGEN_BUILD_TIMESTAMP"),
|
env!("VERGEN_BUILD_TIMESTAMP"),
|
||||||
@@ -84,6 +86,8 @@ fn long_version() -> String {
|
|||||||
env!("VERGEN_RUSTC_CHANNEL"),
|
env!("VERGEN_RUSTC_CHANNEL"),
|
||||||
"cargo Profile:",
|
"cargo Profile:",
|
||||||
env!("VERGEN_CARGO_PROFILE"),
|
env!("VERGEN_CARGO_PROFILE"),
|
||||||
|
"Network:",
|
||||||
|
DEFAULT_NETWORK
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "nym-client-wasm"
|
name = "nym-client-wasm"
|
||||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||||
version = "1.0.0-rc.1"
|
version = "1.0.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|||||||
+7
-8
@@ -24,8 +24,7 @@
|
|||||||
},
|
},
|
||||||
"../pkg": {
|
"../pkg": {
|
||||||
"name": "@nymproject/nym-client-wasm",
|
"name": "@nymproject/nym-client-wasm",
|
||||||
"version": "0.12.0",
|
"version": "0.0.1"
|
||||||
"license": "Apache-2.0"
|
|
||||||
},
|
},
|
||||||
"node_modules/@discoveryjs/json-ext": {
|
"node_modules/@discoveryjs/json-ext": {
|
||||||
"version": "0.5.7",
|
"version": "0.5.7",
|
||||||
@@ -586,9 +585,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/async": {
|
"node_modules/async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
@@ -4305,9 +4304,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async": {
|
"async": {
|
||||||
"version": "2.6.3",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash": "^4.17.14"
|
"lodash": "^4.17.14"
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1_500);
|
|||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct NymClient {
|
pub struct NymClient {
|
||||||
validator_server: Url,
|
validator_server: Url,
|
||||||
testnet_mode: bool,
|
disabled_credentials_mode: bool,
|
||||||
|
|
||||||
// TODO: technically this doesn't need to be an Arc since wasm is run on a single thread
|
// TODO: technically this doesn't need to be an Arc since wasm is run on a single thread
|
||||||
// however, once we eventually combine this code with the native-client's, it will make things
|
// however, once we eventually combine this code with the native-client's, it will make things
|
||||||
@@ -72,7 +72,7 @@ impl NymClient {
|
|||||||
|
|
||||||
on_message: None,
|
on_message: None,
|
||||||
on_gateway_connect: None,
|
on_gateway_connect: None,
|
||||||
testnet_mode: true,
|
disabled_credentials_mode: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +85,12 @@ impl NymClient {
|
|||||||
self.on_gateway_connect = Some(on_connect)
|
self.on_gateway_connect = Some(on_connect)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
|
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
|
||||||
console_log!("Setting testnet mode to {}", testnet_mode);
|
console_log!(
|
||||||
self.testnet_mode = testnet_mode;
|
"Setting disabled credentials mode to {}",
|
||||||
|
disabled_credentials_mode
|
||||||
|
);
|
||||||
|
self.disabled_credentials_mode = disabled_credentials_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_recipient(&self) -> Recipient {
|
fn self_recipient(&self) -> Recipient {
|
||||||
@@ -107,14 +110,8 @@ impl NymClient {
|
|||||||
|
|
||||||
// Right now it's impossible to have async exported functions to take `&self` rather than self
|
// Right now it's impossible to have async exported functions to take `&self` rather than self
|
||||||
pub async fn initial_setup(self) -> Self {
|
pub async fn initial_setup(self) -> Self {
|
||||||
let testnet_mode = self.testnet_mode;
|
let disabled_credentials_mode = self.disabled_credentials_mode;
|
||||||
|
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
let bandwidth_controller = Some(gateway_client::bandwidth::BandwidthController::new(
|
|
||||||
vec![self.validator_server.clone()],
|
|
||||||
*self.identity.public_key(),
|
|
||||||
));
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
let bandwidth_controller = None;
|
let bandwidth_controller = None;
|
||||||
|
|
||||||
let mut client = self.get_and_update_topology().await;
|
let mut client = self.get_and_update_topology().await;
|
||||||
@@ -135,8 +132,8 @@ impl NymClient {
|
|||||||
bandwidth_controller,
|
bandwidth_controller,
|
||||||
);
|
);
|
||||||
|
|
||||||
if testnet_mode {
|
if disabled_credentials_mode {
|
||||||
gateway_client.set_testnet_mode(true)
|
gateway_client.set_disabled_credentials_mode(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
gateway_client
|
gateway_client
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ url = "2.2"
|
|||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||||
secp256k1 = "0.20.3"
|
secp256k1 = "0.20.3"
|
||||||
web3 = { version = "0.17.0", default-features = false }
|
web3 = { version = "0.17.0", default-features = false }
|
||||||
|
async-trait = { version = "0.1.51" }
|
||||||
|
|
||||||
# internal
|
# internal
|
||||||
credentials = { path = "../../credentials" }
|
credentials = { path = "../../credentials" }
|
||||||
@@ -26,6 +27,7 @@ nymsphinx = { path = "../../nymsphinx" }
|
|||||||
pemstore = { path = "../../pemstore" }
|
pemstore = { path = "../../pemstore" }
|
||||||
coconut-interface = { path = "../../coconut-interface", optional = true }
|
coconut-interface = { path = "../../coconut-interface", optional = true }
|
||||||
network-defaults = { path = "../../network-defaults" }
|
network-defaults = { path = "../../network-defaults" }
|
||||||
|
validator-client = { path = "../validator-client", optional = true }
|
||||||
|
|
||||||
[dependencies.tungstenite]
|
[dependencies.tungstenite]
|
||||||
version = "0.13"
|
version = "0.13"
|
||||||
@@ -39,6 +41,9 @@ features = ["macros", "rt", "net", "sync", "time"]
|
|||||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||||
version = "0.14"
|
version = "0.14"
|
||||||
|
|
||||||
|
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
|
||||||
|
path = "../../credential-storage"
|
||||||
|
|
||||||
# wasm-only dependencies
|
# wasm-only dependencies
|
||||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
@@ -67,6 +72,6 @@ features = ["js"]
|
|||||||
#url = "2.1"
|
#url = "2.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
coconut = ["gateway-requests/coconut", "coconut-interface"]
|
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
|
||||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||||
default = ["web3/default"]
|
default = ["web3/default"]
|
||||||
@@ -1,24 +1,28 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::wasm_storage::{Storage, StorageError};
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
use coconut_interface::Base58;
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use credential_storage::error::StorageError;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use credential_storage::storage::Storage;
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
use credentials::coconut::{
|
use credentials::coconut::{
|
||||||
bandwidth::{
|
bandwidth::prepare_for_spending, utils::obtain_aggregate_verification_key,
|
||||||
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
|
|
||||||
},
|
|
||||||
utils::obtain_aggregate_verification_key,
|
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use credentials::token::bandwidth::TokenCredential;
|
use credentials::token::bandwidth::TokenCredential;
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
use crypto::asymmetric::identity::PublicKey;
|
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use network_defaults::{
|
use network_defaults::{
|
||||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME,
|
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, BANDWIDTH_VALUE,
|
||||||
ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME, ETH_ERC20_CONTRACT_ADDRESS,
|
ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||||
ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
ETH_ERC20_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||||
};
|
};
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use pemstore::traits::PemStorableKeyPair;
|
use pemstore::traits::PemStorableKeyPair;
|
||||||
@@ -26,9 +30,6 @@ use pemstore::traits::PemStorableKeyPair;
|
|||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use secp256k1::SecretKey;
|
use secp256k1::SecretKey;
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use web3::{
|
use web3::{
|
||||||
@@ -69,35 +70,35 @@ pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct BandwidthController {
|
pub struct BandwidthController<St: Storage> {
|
||||||
|
storage: St,
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
validator_endpoints: Vec<url::Url>,
|
validator_endpoints: Vec<url::Url>,
|
||||||
#[cfg(feature = "coconut")]
|
|
||||||
identity: PublicKey,
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
contract: Contract<Http>,
|
contract: Contract<Http>,
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
erc20_contract: Contract<Http>,
|
erc20_contract: Contract<Http>,
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
eth_private_key: SecretKey,
|
eth_private_key: SecretKey,
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
backup_bandwidth_token_keys_dir: std::path::PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BandwidthController {
|
impl<St> BandwidthController<St>
|
||||||
|
where
|
||||||
|
St: Storage + Clone + 'static,
|
||||||
|
{
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
pub fn new(validator_endpoints: Vec<url::Url>, identity: PublicKey) -> Self {
|
pub fn new(storage: St, validator_endpoints: Vec<url::Url>) -> Self {
|
||||||
BandwidthController {
|
BandwidthController {
|
||||||
|
storage,
|
||||||
validator_endpoints,
|
validator_endpoints,
|
||||||
identity,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
storage: St,
|
||||||
eth_endpoint: String,
|
eth_endpoint: String,
|
||||||
eth_private_key: String,
|
eth_private_key: String,
|
||||||
backup_bandwidth_token_keys_dir: std::path::PathBuf,
|
|
||||||
) -> Result<Self, GatewayClientError> {
|
) -> Result<Self, GatewayClientError> {
|
||||||
// Fail early, on invalid url
|
// Fail early, on invalid url
|
||||||
let transport =
|
let transport =
|
||||||
@@ -110,60 +111,42 @@ impl BandwidthController {
|
|||||||
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
||||||
|
|
||||||
Ok(BandwidthController {
|
Ok(BandwidthController {
|
||||||
|
storage,
|
||||||
contract,
|
contract,
|
||||||
erc20_contract,
|
erc20_contract,
|
||||||
eth_private_key,
|
eth_private_key,
|
||||||
backup_bandwidth_token_keys_dir,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||||
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
|
self.storage
|
||||||
let file_path = self
|
.insert_erc20_credential(
|
||||||
.backup_bandwidth_token_keys_dir
|
keypair.public_key().to_base58_string(),
|
||||||
.join(keypair.public_key().to_base58_string());
|
keypair.private_key().to_base58_string(),
|
||||||
let mut file = std::fs::File::create(file_path)?;
|
)
|
||||||
file.write_all(&keypair.private_key().to_bytes())?;
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
async fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
||||||
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
|
let data = self.storage.get_next_erc20_credential().await?;
|
||||||
let file = std::fs::read_dir(&self.backup_bandwidth_token_keys_dir)?
|
let public_key = identity::PublicKey::from_base58_string(data.public_key).unwrap();
|
||||||
.find(|entry| {
|
let private_key = identity::PrivateKey::from_base58_string(data.private_key).unwrap();
|
||||||
entry
|
|
||||||
.as_ref()
|
Ok(identity::KeyPair::from_keys(private_key, public_key))
|
||||||
.map(|entry| entry.path().is_file())
|
|
||||||
.unwrap_or(false)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| Err(std::io::Error::from(std::io::ErrorKind::NotFound)))?;
|
|
||||||
let file_path = file.path();
|
|
||||||
let pub_key = file_path
|
|
||||||
.file_name()
|
|
||||||
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?
|
|
||||||
.to_str()
|
|
||||||
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::NotFound))?;
|
|
||||||
let mut priv_key = vec![];
|
|
||||||
std::fs::File::open(file_path.clone())?.read_to_end(&mut priv_key)?;
|
|
||||||
Ok(identity::KeyPair::from_keys(
|
|
||||||
identity::PrivateKey::from_bytes(&priv_key).unwrap(),
|
|
||||||
identity::PublicKey::from_base58_string(pub_key).unwrap(),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
fn mark_keypair_as_spent(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
async fn mark_keypair_as_spent(
|
||||||
let mut spent_dir = self.backup_bandwidth_token_keys_dir.clone();
|
&self,
|
||||||
spent_dir.push("spent");
|
keypair: &identity::KeyPair,
|
||||||
std::fs::create_dir_all(&spent_dir)?;
|
) -> Result<(), GatewayClientError> {
|
||||||
let file_path_old = self
|
self.storage
|
||||||
.backup_bandwidth_token_keys_dir
|
.consume_erc20_credential(keypair.public_key().to_base58_string())
|
||||||
.join(keypair.public_key().to_base58_string());
|
.await?;
|
||||||
let file_path_new = spent_dir.join(keypair.public_key().to_base58_string());
|
|
||||||
std::fs::rename(file_path_old, file_path_new)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -173,31 +156,24 @@ impl BandwidthController {
|
|||||||
&self,
|
&self,
|
||||||
) -> Result<coconut_interface::Credential, GatewayClientError> {
|
) -> Result<coconut_interface::Credential, GatewayClientError> {
|
||||||
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
|
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
|
||||||
let params = coconut_interface::Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
|
||||||
|
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
|
||||||
// TODO: Decide what is the value and additional info associated with the bandwidth voucher
|
.map_err(|_| StorageError::InconsistentData)?;
|
||||||
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
|
let voucher_info = bandwidth_credential.voucher_info.clone();
|
||||||
serial_number: params.random_scalar(),
|
let serial_number =
|
||||||
binding_number: params.random_scalar(),
|
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
|
||||||
voucher_value: coconut_interface::hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
|
let binding_number =
|
||||||
voucher_info: coconut_interface::hash_to_scalar(
|
coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
|
||||||
String::from("BandwidthVoucher").as_bytes(),
|
let signature =
|
||||||
),
|
coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
|
||||||
};
|
|
||||||
|
|
||||||
let bandwidth_credential = obtain_signature(
|
|
||||||
¶ms,
|
|
||||||
&bandwidth_credential_attributes,
|
|
||||||
&self.validator_endpoints,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
// the above would presumably be loaded from a file
|
|
||||||
|
|
||||||
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
|
// the below would only be executed once we know where we want to spend it (i.e. which gateway and stuff)
|
||||||
Ok(prepare_for_spending(
|
Ok(prepare_for_spending(
|
||||||
&self.identity.to_bytes(),
|
voucher_value,
|
||||||
&bandwidth_credential,
|
voucher_info,
|
||||||
&bandwidth_credential_attributes,
|
serial_number,
|
||||||
|
binding_number,
|
||||||
|
&signature,
|
||||||
&verification_key,
|
&verification_key,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
@@ -205,15 +181,15 @@ impl BandwidthController {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
pub async fn prepare_token_credential(
|
pub async fn prepare_token_credential(
|
||||||
&self,
|
&self,
|
||||||
gateway_identity: PublicKey,
|
gateway_identity: identity::PublicKey,
|
||||||
gateway_owner: String,
|
gateway_owner: String,
|
||||||
) -> Result<TokenCredential, GatewayClientError> {
|
) -> Result<TokenCredential, GatewayClientError> {
|
||||||
let kp = match self.restore_keypair() {
|
let kp = match self.restore_keypair().await {
|
||||||
Ok(kp) => kp,
|
Ok(kp) => kp,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
let kp = identity::KeyPair::new(&mut rng);
|
let kp = identity::KeyPair::new(&mut rng);
|
||||||
self.backup_keypair(&kp)?;
|
self.backup_keypair(&kp).await?;
|
||||||
kp
|
kp
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -223,7 +199,7 @@ impl BandwidthController {
|
|||||||
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self.mark_keypair_as_spent(&kp)?;
|
self.mark_keypair_as_spent(&kp).await?;
|
||||||
|
|
||||||
let message: Vec<u8> = verification_key
|
let message: Vec<u8> = verification_key
|
||||||
.to_bytes()
|
.to_bytes()
|
||||||
@@ -244,7 +220,7 @@ impl BandwidthController {
|
|||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
pub async fn buy_token_credential(
|
pub async fn buy_token_credential(
|
||||||
&self,
|
&self,
|
||||||
verification_key: PublicKey,
|
verification_key: identity::PublicKey,
|
||||||
signed_verification_key: identity::Signature,
|
signed_verification_key: identity::Signature,
|
||||||
gateway_owner: String,
|
gateway_owner: String,
|
||||||
) -> Result<(), GatewayClientError> {
|
) -> Result<(), GatewayClientError> {
|
||||||
|
|||||||
@@ -9,8 +9,12 @@ pub use crate::packet_router::{
|
|||||||
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
|
AcknowledgementReceiver, AcknowledgementSender, MixnetMessageReceiver, MixnetMessageSender,
|
||||||
};
|
};
|
||||||
use crate::socket_state::{PartiallyDelegated, SocketState};
|
use crate::socket_state::{PartiallyDelegated, SocketState};
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::wasm_storage::PersistentStorage;
|
||||||
#[cfg(feature = "coconut")]
|
#[cfg(feature = "coconut")]
|
||||||
use coconut_interface::Credential;
|
use coconut_interface::Credential;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use credential_storage::PersistentStorage;
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
use credentials::token::bandwidth::TokenCredential;
|
use credentials::token::bandwidth::TokenCredential;
|
||||||
use crypto::asymmetric::identity;
|
use crypto::asymmetric::identity;
|
||||||
@@ -41,7 +45,7 @@ const DEFAULT_RECONNECTION_BACKOFF: Duration = Duration::from_secs(5);
|
|||||||
|
|
||||||
pub struct GatewayClient {
|
pub struct GatewayClient {
|
||||||
authenticated: bool,
|
authenticated: bool,
|
||||||
testnet_mode: bool,
|
disabled_credentials_mode: bool,
|
||||||
bandwidth_remaining: i64,
|
bandwidth_remaining: i64,
|
||||||
gateway_address: String,
|
gateway_address: String,
|
||||||
gateway_identity: identity::PublicKey,
|
gateway_identity: identity::PublicKey,
|
||||||
@@ -51,7 +55,7 @@ pub struct GatewayClient {
|
|||||||
connection: SocketState,
|
connection: SocketState,
|
||||||
packet_router: PacketRouter,
|
packet_router: PacketRouter,
|
||||||
response_timeout_duration: Duration,
|
response_timeout_duration: Duration,
|
||||||
bandwidth_controller: Option<BandwidthController>,
|
bandwidth_controller: Option<BandwidthController<PersistentStorage>>,
|
||||||
|
|
||||||
// reconnection related variables
|
// reconnection related variables
|
||||||
/// Specifies whether client should try to reconnect to gateway on connection failure.
|
/// Specifies whether client should try to reconnect to gateway on connection failure.
|
||||||
@@ -75,11 +79,11 @@ impl GatewayClient {
|
|||||||
mixnet_message_sender: MixnetMessageSender,
|
mixnet_message_sender: MixnetMessageSender,
|
||||||
ack_sender: AcknowledgementSender,
|
ack_sender: AcknowledgementSender,
|
||||||
response_timeout_duration: Duration,
|
response_timeout_duration: Duration,
|
||||||
bandwidth_controller: Option<BandwidthController>,
|
bandwidth_controller: Option<BandwidthController<PersistentStorage>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
GatewayClient {
|
GatewayClient {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
testnet_mode: false,
|
disabled_credentials_mode: true,
|
||||||
bandwidth_remaining: 0,
|
bandwidth_remaining: 0,
|
||||||
gateway_address,
|
gateway_address,
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
@@ -96,8 +100,8 @@ impl GatewayClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_testnet_mode(&mut self, testnet_mode: bool) {
|
pub fn set_disabled_credentials_mode(&mut self, disabled_credentials_mode: bool) {
|
||||||
self.testnet_mode = testnet_mode
|
self.disabled_credentials_mode = disabled_credentials_mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: later convert into proper builder methods
|
// TODO: later convert into proper builder methods
|
||||||
@@ -130,7 +134,7 @@ impl GatewayClient {
|
|||||||
|
|
||||||
GatewayClient {
|
GatewayClient {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
testnet_mode: false,
|
disabled_credentials_mode: true,
|
||||||
bandwidth_remaining: 0,
|
bandwidth_remaining: 0,
|
||||||
gateway_address,
|
gateway_address,
|
||||||
gateway_identity,
|
gateway_identity,
|
||||||
@@ -544,13 +548,13 @@ impl GatewayClient {
|
|||||||
if self.shared_key.is_none() {
|
if self.shared_key.is_none() {
|
||||||
return Err(GatewayClientError::NoSharedKeyAvailable);
|
return Err(GatewayClientError::NoSharedKeyAvailable);
|
||||||
}
|
}
|
||||||
if self.bandwidth_controller.is_none() && !self.testnet_mode {
|
if self.bandwidth_controller.is_none() && !self.disabled_credentials_mode {
|
||||||
return Err(GatewayClientError::NoBandwidthControllerAvailable);
|
return Err(GatewayClientError::NoBandwidthControllerAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
warn!("Not enough bandwidth. Trying to get more bandwidth, this might take a while");
|
||||||
if self.testnet_mode {
|
if self.disabled_credentials_mode {
|
||||||
info!("The client is running in testnet mode - attempting to claim bandwidth without a credential");
|
info!("The client is running in disabled credentials mode - attempting to claim bandwidth without a credential");
|
||||||
return self.try_claim_testnet_bandwidth().await;
|
return self.try_claim_testnet_bandwidth().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
use crate::wasm_storage::StorageError;
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
use credential_storage::error::StorageError;
|
||||||
use gateway_requests::registration::handshake::error::HandshakeError;
|
use gateway_requests::registration::handshake::error::HandshakeError;
|
||||||
use std::io;
|
use std::io;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@@ -21,15 +25,18 @@ pub enum GatewayClientError {
|
|||||||
#[error("There was a network error - {0}")]
|
#[error("There was a network error - {0}")]
|
||||||
NetworkError(#[from] WsError),
|
NetworkError(#[from] WsError),
|
||||||
|
|
||||||
|
#[error("There was a credential storage error - {0}")]
|
||||||
|
CredentialStorageError(#[from] StorageError),
|
||||||
|
|
||||||
|
#[cfg(feature = "coconut")]
|
||||||
|
#[error("Coconut error - {0}")]
|
||||||
|
CoconutError(#[from] coconut_interface::CoconutError),
|
||||||
|
|
||||||
// TODO: see if `JsValue` is a reasonable type for this
|
// TODO: see if `JsValue` is a reasonable type for this
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
#[error("There was a network error")]
|
#[error("There was a network error")]
|
||||||
NetworkErrorWasm(JsValue),
|
NetworkErrorWasm(JsValue),
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
|
||||||
#[error("Keypair IO error - {0}")]
|
|
||||||
IOError(#[from] std::io::Error),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "coconut"))]
|
#[cfg(not(feature = "coconut"))]
|
||||||
#[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
|
#[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
|
||||||
BurnTokenError(#[from] Web3Error),
|
BurnTokenError(#[from] Web3Error),
|
||||||
@@ -69,6 +76,9 @@ pub enum GatewayClientError {
|
|||||||
#[error("Client does not have enough bandwidth: estimated {0}, remaining: {1}")]
|
#[error("Client does not have enough bandwidth: estimated {0}, remaining: {1}")]
|
||||||
NotEnoughBandwidth(i64, i64),
|
NotEnoughBandwidth(i64, i64),
|
||||||
|
|
||||||
|
#[error("There are no more bandwidth credentials acquired. Please buy some more if you want to use the mixnet")]
|
||||||
|
NoMoreBandwidthCredentials,
|
||||||
|
|
||||||
#[error("Received an unexpected response")]
|
#[error("Received an unexpected response")]
|
||||||
UnexpectedResponse,
|
UnexpectedResponse,
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ pub mod client;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod packet_router;
|
pub mod packet_router;
|
||||||
pub mod socket_state;
|
pub mod socket_state;
|
||||||
|
#[cfg(feature = "wasm")]
|
||||||
|
mod wasm_storage;
|
||||||
|
|
||||||
/// Helper method for reading from websocket stream. Helps to flatten the structure.
|
/// Helper method for reading from websocket stream. Helps to flatten the structure.
|
||||||
pub(crate) fn cleanup_socket_message(
|
pub(crate) fn cleanup_socket_message(
|
||||||
|
|||||||
@@ -72,7 +72,12 @@ impl PacketRouter {
|
|||||||
|
|
||||||
if !received_acks.is_empty() {
|
if !received_acks.is_empty() {
|
||||||
trace!("routing acks");
|
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.
|
// This would also require NOT discarding any text responses here.
|
||||||
|
|
||||||
// TODO: those can return the "send confirmations" - perhaps it should be somehow worked around?
|
// 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! - {}",
|
"received a text message - probably a response to some previous query! - {}",
|
||||||
text
|
text
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum StorageError {
|
||||||
|
#[error("Wasm client is not yet supported")]
|
||||||
|
WasmNotSupported,
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[error("Code shouldn't reach this point")]
|
||||||
|
InconsistentData,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PersistentStorage {}
|
||||||
|
|
||||||
|
pub struct CoconutCredential {
|
||||||
|
pub id: i64,
|
||||||
|
pub voucher_value: String,
|
||||||
|
pub voucher_info: String,
|
||||||
|
pub serial_number: String,
|
||||||
|
pub binding_number: String,
|
||||||
|
pub signature: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ERC20Credential {
|
||||||
|
pub id: i64,
|
||||||
|
pub public_key: String,
|
||||||
|
pub private_key: String,
|
||||||
|
pub consumed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Storage: Send + Sync {
|
||||||
|
async fn insert_coconut_credential(
|
||||||
|
&self,
|
||||||
|
voucher_value: String,
|
||||||
|
voucher_info: String,
|
||||||
|
serial_number: String,
|
||||||
|
binding_number: String,
|
||||||
|
signature: String,
|
||||||
|
) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||||
|
|
||||||
|
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
async fn insert_erc20_credential(
|
||||||
|
&self,
|
||||||
|
public_key: String,
|
||||||
|
private_key: String,
|
||||||
|
) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError>;
|
||||||
|
|
||||||
|
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Storage for PersistentStorage {
|
||||||
|
async fn insert_coconut_credential(
|
||||||
|
&self,
|
||||||
|
_voucher_value: String,
|
||||||
|
_voucher_info: String,
|
||||||
|
_serial_number: String,
|
||||||
|
_binding_number: String,
|
||||||
|
_signature: String,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert_erc20_credential(
|
||||||
|
&self,
|
||||||
|
_public_key: String,
|
||||||
|
_private_key: String,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn consume_erc20_credential(&self, _public_key: String) -> Result<(), StorageError> {
|
||||||
|
Err(StorageError::WasmNotSupported)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ rust-version = "1.56"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
colored = "2.0"
|
||||||
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
|
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
|
||||||
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
|
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
|
||||||
vesting-contract = { path = "../../../contracts/vesting" }
|
vesting-contract = { path = "../../../contracts/vesting" }
|
||||||
@@ -18,6 +19,8 @@ reqwest = { version = "0.11", features = ["json"] }
|
|||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
url = { version = "2.2", features = ["serde"] }
|
url = { version = "2.2", features = ["serde"] }
|
||||||
|
tokio = { version = "1.10", features = ["sync", "time"] }
|
||||||
|
futures = "0.3"
|
||||||
|
|
||||||
coconut-interface = { path = "../../coconut-interface" }
|
coconut-interface = { path = "../../coconut-interface" }
|
||||||
network-defaults = { path = "../../network-defaults" }
|
network-defaults = { path = "../../network-defaults" }
|
||||||
@@ -29,16 +32,12 @@ validator-api-requests = { path = "../../../validator-api/validator-api-requests
|
|||||||
async-trait = { version = "0.1.51", optional = true }
|
async-trait = { version = "0.1.51", optional = true }
|
||||||
bip39 = { version = "1", features = ["rand"], optional = true }
|
bip39 = { version = "1", features = ["rand"], optional = true }
|
||||||
config = { path = "../../config", optional = true }
|
config = { path = "../../config", optional = true }
|
||||||
cosmrs = { version = "0.4.1", features = [
|
cosmrs = { git = "https://github.com/nymtech/cosmos-rust", branch = "bugfix/account-id-length-validation", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||||
"rpc",
|
|
||||||
"bip32",
|
|
||||||
"cosmwasm",
|
|
||||||
], optional = true }
|
|
||||||
prost = { version = "0.9", default-features = false, optional = true }
|
prost = { version = "0.9", default-features = false, optional = true }
|
||||||
flate2 = { version = "1.0.20", optional = true }
|
flate2 = { version = "1.0.20", optional = true }
|
||||||
sha2 = { version = "0.9.5", optional = true }
|
sha2 = { version = "0.9.5", optional = true }
|
||||||
itertools = { version = "0.10", optional = true }
|
itertools = { version = "0.10", optional = true }
|
||||||
cosmwasm-std = { version = "1.0.0-beta6", optional = true }
|
cosmwasm-std = { version = "1.0.0-beta8", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ts-rs = "6.1.2"
|
ts-rs = "6.1.2"
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ use crate::{validator_api, ValidatorClientError};
|
|||||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
#[cfg(feature = "nymd-client")]
|
||||||
|
use validator_api_requests::models::UptimeResponse;
|
||||||
use validator_api_requests::models::{
|
use validator_api_requests::models::{
|
||||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||||
StakeSaturationResponse,
|
StakeSaturationResponse,
|
||||||
@@ -153,6 +156,10 @@ impl Client<SigningNymdClient> {
|
|||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_nymd_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
||||||
|
self.nymd.set_simulated_gas_multiplier(multiplier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
@@ -260,13 +267,14 @@ impl<C> Client<C> {
|
|||||||
&self,
|
&self,
|
||||||
address: String,
|
address: String,
|
||||||
mix_identity: IdentityKey,
|
mix_identity: IdentityKey,
|
||||||
|
proxy: Option<String>,
|
||||||
) -> Result<u128, ValidatorClientError>
|
) -> Result<u128, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
Ok(self
|
Ok(self
|
||||||
.nymd
|
.nymd
|
||||||
.get_delegator_rewards(address, mix_identity)
|
.get_delegator_rewards(address, mix_identity, proxy)
|
||||||
.await?
|
.await?
|
||||||
.u128())
|
.u128())
|
||||||
}
|
}
|
||||||
@@ -274,13 +282,14 @@ impl<C> Client<C> {
|
|||||||
pub async fn get_pending_delegation_events(
|
pub async fn get_pending_delegation_events(
|
||||||
&self,
|
&self,
|
||||||
owner_address: String,
|
owner_address: String,
|
||||||
|
proxy_address: Option<String>,
|
||||||
) -> Result<Vec<DelegationEvent>, ValidatorClientError>
|
) -> Result<Vec<DelegationEvent>, ValidatorClientError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
Ok(self
|
Ok(self
|
||||||
.nymd
|
.nymd
|
||||||
.get_pending_delegation_events(owner_address)
|
.get_pending_delegation_events(owner_address, proxy_address)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -576,6 +585,12 @@ impl<C> Client<C> {
|
|||||||
Ok(delegations)
|
Ok(delegations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_mixnode_avg_uptimes(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<UptimeResponse>, ValidatorClientError> {
|
||||||
|
Ok(self.validator_api.get_mixnode_avg_uptimes().await?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn blind_sign(
|
pub async fn blind_sign(
|
||||||
&self,
|
&self,
|
||||||
request_body: &BlindSignRequestBody,
|
request_body: &BlindSignRequestBody,
|
||||||
@@ -685,6 +700,16 @@ impl ApiClient {
|
|||||||
Ok(self.validator_api.blind_sign(request_body).await?)
|
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(
|
pub async fn get_coconut_verification_key(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
) -> 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
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
#[cfg(feature = "nymd-client")]
|
||||||
|
pub mod connection_tester;
|
||||||
mod error;
|
mod error;
|
||||||
#[cfg(feature = "nymd-client")]
|
#[cfg(feature = "nymd-client")]
|
||||||
pub mod nymd;
|
pub mod nymd;
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ use crate::nymd::cosmwasm_client::types::{
|
|||||||
};
|
};
|
||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::proto::cosmos::auth::v1beta1::{
|
use cosmrs::proto::cosmos::auth::v1beta1::{QueryAccountRequest, QueryAccountResponse};
|
||||||
BaseAccount, QueryAccountRequest, QueryAccountResponse,
|
|
||||||
};
|
|
||||||
use cosmrs::proto::cosmos::bank::v1beta1::{
|
use cosmrs::proto::cosmos::bank::v1beta1::{
|
||||||
QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
|
QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
|
||||||
QueryTotalSupplyRequest, QueryTotalSupplyResponse,
|
QueryTotalSupplyRequest, QueryTotalSupplyResponse,
|
||||||
@@ -32,12 +30,26 @@ use cosmwasm_std::Coin as CosmWasmCoin;
|
|||||||
use prost::Message;
|
use prost::Message;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CosmWasmClient for HttpClient {}
|
impl CosmWasmClient for HttpClient {
|
||||||
|
fn broadcast_polling_rate(&self) -> Duration {
|
||||||
|
Duration::from_secs(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast_timeout(&self) -> Duration {
|
||||||
|
Duration::from_secs(60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait CosmWasmClient: rpc::Client {
|
pub trait CosmWasmClient: rpc::Client {
|
||||||
|
// this should probably get redesigned, but I'm leaving those like that temporarily to fix
|
||||||
|
// the underlying issue more quickly
|
||||||
|
fn broadcast_polling_rate(&self) -> Duration;
|
||||||
|
fn broadcast_timeout(&self) -> Duration;
|
||||||
|
|
||||||
// helper method to remove duplicate code involved in making abci requests with protobuf messages
|
// helper method to remove duplicate code involved in making abci requests with protobuf messages
|
||||||
// TODO: perhaps it should have an additional argument to determine whether the response should
|
// TODO: perhaps it should have an additional argument to determine whether the response should
|
||||||
// require proof?
|
// require proof?
|
||||||
@@ -83,21 +95,16 @@ pub trait CosmWasmClient: rpc::Client {
|
|||||||
.make_abci_query::<_, QueryAccountResponse>(path, req)
|
.make_abci_query::<_, QueryAccountResponse>(path, req)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let base_account = res
|
res.account.map(TryFrom::try_from).transpose()
|
||||||
.account
|
|
||||||
.map(|account| BaseAccount::decode(account.value.as_ref()))
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
base_account
|
|
||||||
.map(|base_account| base_account.try_into())
|
|
||||||
.transpose()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_sequence(&self, address: &AccountId) -> Result<SequenceResponse, NymdError> {
|
async fn get_sequence(&self, address: &AccountId) -> Result<SequenceResponse, NymdError> {
|
||||||
let base_account = self
|
let account = self
|
||||||
.get_account(address)
|
.get_account(address)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| NymdError::NonExistentAccountError(address.clone()))?;
|
.ok_or_else(|| NymdError::NonExistentAccountError(address.clone()))?;
|
||||||
|
let base_account = account.try_get_base_account()?;
|
||||||
|
|
||||||
Ok(SequenceResponse {
|
Ok(SequenceResponse {
|
||||||
account_number: base_account.account_number,
|
account_number: base_account.account_number,
|
||||||
sequence: base_account.sequence,
|
sequence: base_account.sequence,
|
||||||
@@ -260,6 +267,42 @@ pub trait CosmWasmClient: rpc::Client {
|
|||||||
Ok(rpc::Client::broadcast_tx_commit(self, tx).await?)
|
Ok(rpc::Client::broadcast_tx_commit(self, tx).await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn broadcast_tx(&self, tx: Transaction) -> Result<TxResponse, NymdError> {
|
||||||
|
let broadcasted = CosmWasmClient::broadcast_tx_sync(self, tx).await?;
|
||||||
|
|
||||||
|
if broadcasted.code.is_err() {
|
||||||
|
let code_val = broadcasted.code.value();
|
||||||
|
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
||||||
|
hash: broadcasted.hash,
|
||||||
|
height: None,
|
||||||
|
code: code_val,
|
||||||
|
raw_log: broadcasted.log.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx_hash = broadcasted.hash;
|
||||||
|
|
||||||
|
let start = tokio::time::Instant::now();
|
||||||
|
loop {
|
||||||
|
log::debug!(
|
||||||
|
"Polling for result of including {} in a block...",
|
||||||
|
broadcasted.hash
|
||||||
|
);
|
||||||
|
if tokio::time::Instant::now().duration_since(start) >= self.broadcast_timeout() {
|
||||||
|
return Err(NymdError::BroadcastTimeout {
|
||||||
|
hash: tx_hash,
|
||||||
|
timeout: self.broadcast_timeout(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(poll_res) = self.get_tx(tx_hash).await {
|
||||||
|
return Ok(poll_res);
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio::time::sleep(self.broadcast_polling_rate()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_codes(&self) -> Result<Vec<Code>, NymdError> {
|
async fn get_codes(&self) -> Result<Vec<Code>, NymdError> {
|
||||||
let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap());
|
let path = Some("/cosmwasm.wasm.v1.Query/Codes".parse().unwrap());
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
|
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
|
||||||
|
use cosmrs::proto::cosmos::base::v1beta1::Coin as ProtoCoin;
|
||||||
use cosmrs::rpc::endpoint::broadcast;
|
use cosmrs::rpc::endpoint::broadcast;
|
||||||
|
use cosmrs::Coin;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -17,7 +19,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
|
|||||||
if self.check_tx.code.is_err() {
|
if self.check_tx.code.is_err() {
|
||||||
return Err(NymdError::BroadcastTxErrorCheckTx {
|
return Err(NymdError::BroadcastTxErrorCheckTx {
|
||||||
hash: self.hash,
|
hash: self.hash,
|
||||||
height: self.height,
|
height: Some(self.height),
|
||||||
code: self.check_tx.code.value(),
|
code: self.check_tx.code.value(),
|
||||||
raw_log: self.check_tx.log.value().to_owned(),
|
raw_log: self.check_tx.log.value().to_owned(),
|
||||||
});
|
});
|
||||||
@@ -26,7 +28,7 @@ impl CheckResponse for broadcast::tx_commit::Response {
|
|||||||
if self.deliver_tx.code.is_err() {
|
if self.deliver_tx.code.is_err() {
|
||||||
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
||||||
hash: self.hash,
|
hash: self.hash,
|
||||||
height: self.height,
|
height: Some(self.height),
|
||||||
code: self.deliver_tx.code.value(),
|
code: self.deliver_tx.code.value(),
|
||||||
raw_log: self.deliver_tx.log.value().to_owned(),
|
raw_log: self.deliver_tx.log.value().to_owned(),
|
||||||
});
|
});
|
||||||
@@ -36,6 +38,21 @@ impl CheckResponse for broadcast::tx_commit::Response {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CheckResponse for crate::nymd::TxResponse {
|
||||||
|
fn check_response(self) -> Result<Self, NymdError> {
|
||||||
|
if self.tx_result.code.is_err() {
|
||||||
|
return Err(NymdError::BroadcastTxErrorDeliverTx {
|
||||||
|
hash: self.hash,
|
||||||
|
height: Some(self.height),
|
||||||
|
code: self.tx_result.code.value(),
|
||||||
|
raw_log: self.tx_result.log.value().to_owned(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NymdError> {
|
pub(crate) fn compress_wasm_code(code: &[u8]) -> Result<Vec<u8>, NymdError> {
|
||||||
// using compression level 9, same as cosmjs, that optimises for size
|
// using compression level 9, same as cosmjs, that optimises for size
|
||||||
let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
|
let mut encoder = GzEncoder::new(Vec::new(), Compression::best());
|
||||||
@@ -65,3 +82,14 @@ pub(crate) fn next_page_key(pagination_info: Option<PageResponse>) -> Option<Vec
|
|||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_proto_coin_vec(value: Vec<ProtoCoin>) -> Result<Vec<Coin>, NymdError> {
|
||||||
|
value
|
||||||
|
.into_iter()
|
||||||
|
.map(|proto_coin| {
|
||||||
|
Coin::try_from(&proto_coin).map_err(|_| NymdError::MalformedCoin {
|
||||||
|
coin_representation: format!("{:?}", proto_coin),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cosmrs::bank::MsgSend;
|
use cosmrs::bank::MsgSend;
|
||||||
@@ -24,7 +25,7 @@ use crate::nymd::cosmwasm_client::types::*;
|
|||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
use crate::nymd::fee::{Fee, DEFAULT_SIMULATED_GAS_MULTIPLIER};
|
||||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||||
use crate::nymd::{CosmosCoin, GasPrice};
|
use crate::nymd::{CosmosCoin, GasPrice, TxResponse};
|
||||||
|
|
||||||
// we need to have **a** valid secp256k1 signature for simulation purposes.
|
// we need to have **a** valid secp256k1 signature for simulation purposes.
|
||||||
// it doesn't matter what it is as long as it parses correctly
|
// it doesn't matter what it is as long as it parses correctly
|
||||||
@@ -35,6 +36,9 @@ const DUMMY_SECP256K1_SIGNATURE: &[u8] = &[
|
|||||||
91,
|
91,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
||||||
|
const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait SigningCosmWasmClient: CosmWasmClient {
|
pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||||
fn signer(&self) -> &DirectSecp256k1HdWallet;
|
fn signer(&self) -> &DirectSecp256k1HdWallet;
|
||||||
@@ -111,12 +115,12 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgStoreCode".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![upload_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![upload_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let logs = parse_raw_logs(tx_res.deliver_tx.log)?;
|
let logs = parse_raw_logs(tx_res.tx_result.log)?;
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
// TODO: should those strings be extracted into some constants?
|
// TODO: should those strings be extracted into some constants?
|
||||||
// the reason I think unwrap here is fine is that if the transaction succeeded and those
|
// the reason I think unwrap here is fine is that if the transaction succeeded and those
|
||||||
@@ -172,12 +176,12 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgInstantiateContract".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![init_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![init_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let logs = parse_raw_logs(tx_res.deliver_tx.log)?;
|
let logs = parse_raw_logs(tx_res.tx_result.log)?;
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
// TODO: should those strings be extracted into some constants?
|
// TODO: should those strings be extracted into some constants?
|
||||||
// the reason I think unwrap here is fine is that if the transaction succeeded and those
|
// the reason I think unwrap here is fine is that if the transaction succeeded and those
|
||||||
@@ -214,14 +218,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgUpdateAdmin".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![change_admin_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
Ok(ChangeAdminResult {
|
Ok(ChangeAdminResult {
|
||||||
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
transaction_hash: tx_res.hash,
|
transaction_hash: tx_res.hash,
|
||||||
gas_info,
|
gas_info,
|
||||||
})
|
})
|
||||||
@@ -242,14 +246,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgClearAdmin".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![change_admin_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![change_admin_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
Ok(ChangeAdminResult {
|
Ok(ChangeAdminResult {
|
||||||
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
transaction_hash: tx_res.hash,
|
transaction_hash: tx_res.hash,
|
||||||
gas_info,
|
gas_info,
|
||||||
})
|
})
|
||||||
@@ -277,14 +281,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgMigrateContract".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![migrate_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![migrate_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
Ok(MigrateResult {
|
Ok(MigrateResult {
|
||||||
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
transaction_hash: tx_res.hash,
|
transaction_hash: tx_res.hash,
|
||||||
gas_info,
|
gas_info,
|
||||||
})
|
})
|
||||||
@@ -312,14 +316,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgExecuteContract".to_owned()))?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, vec![execute_msg], fee, memo)
|
.sign_and_broadcast(sender_address, vec![execute_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
Ok(ExecuteResult {
|
Ok(ExecuteResult {
|
||||||
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
transaction_hash: tx_res.hash,
|
transaction_hash: tx_res.hash,
|
||||||
gas_info,
|
gas_info,
|
||||||
})
|
})
|
||||||
@@ -352,14 +356,14 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let tx_res = self
|
let tx_res = self
|
||||||
.sign_and_broadcast_commit(sender_address, messages, fee, memo)
|
.sign_and_broadcast(sender_address, messages, fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()?;
|
.check_response()?;
|
||||||
|
|
||||||
let gas_info = GasInfo::new(tx_res.deliver_tx.gas_wanted, tx_res.deliver_tx.gas_used);
|
let gas_info = GasInfo::new(tx_res.tx_result.gas_wanted, tx_res.tx_result.gas_used);
|
||||||
|
|
||||||
Ok(ExecuteResult {
|
Ok(ExecuteResult {
|
||||||
logs: parse_raw_logs(tx_res.deliver_tx.log)?,
|
logs: parse_raw_logs(tx_res.tx_result.log)?,
|
||||||
transaction_hash: tx_res.hash,
|
transaction_hash: tx_res.hash,
|
||||||
gas_info,
|
gas_info,
|
||||||
})
|
})
|
||||||
@@ -372,7 +376,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
amount: Vec<Coin>,
|
amount: Vec<Coin>,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError> {
|
) -> Result<TxResponse, NymdError> {
|
||||||
let send_msg = MsgSend {
|
let send_msg = MsgSend {
|
||||||
from_address: sender_address.clone(),
|
from_address: sender_address.clone(),
|
||||||
to_address: recipient_address.clone(),
|
to_address: recipient_address.clone(),
|
||||||
@@ -381,7 +385,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.to_any()
|
.to_any()
|
||||||
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgSend".to_owned()))?;
|
||||||
|
|
||||||
self.sign_and_broadcast_commit(sender_address, vec![send_msg], fee, memo)
|
self.sign_and_broadcast(sender_address, vec![send_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
@@ -392,7 +396,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
msgs: I,
|
msgs: I,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError>
|
) -> Result<TxResponse, NymdError>
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = (AccountId, Vec<Coin>)> + Send,
|
I: IntoIterator<Item = (AccountId, Vec<Coin>)> + Send,
|
||||||
{
|
{
|
||||||
@@ -409,7 +413,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
})
|
})
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
self.sign_and_broadcast_commit(sender_address, messages, fee, memo)
|
self.sign_and_broadcast(sender_address, messages, fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
@@ -421,7 +425,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError> {
|
) -> Result<TxResponse, NymdError> {
|
||||||
let delegate_msg = MsgDelegate {
|
let delegate_msg = MsgDelegate {
|
||||||
delegator_address: delegator_address.to_owned(),
|
delegator_address: delegator_address.to_owned(),
|
||||||
validator_address: validator_address.to_owned(),
|
validator_address: validator_address.to_owned(),
|
||||||
@@ -430,7 +434,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.to_any()
|
.to_any()
|
||||||
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgDelegate".to_owned()))?;
|
||||||
|
|
||||||
self.sign_and_broadcast_commit(delegator_address, vec![delegate_msg], fee, memo)
|
self.sign_and_broadcast(delegator_address, vec![delegate_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
@@ -442,7 +446,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
amount: Coin,
|
amount: Coin,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError> {
|
) -> Result<TxResponse, NymdError> {
|
||||||
let undelegate_msg = MsgUndelegate {
|
let undelegate_msg = MsgUndelegate {
|
||||||
delegator_address: delegator_address.to_owned(),
|
delegator_address: delegator_address.to_owned(),
|
||||||
validator_address: validator_address.to_owned(),
|
validator_address: validator_address.to_owned(),
|
||||||
@@ -451,7 +455,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.to_any()
|
.to_any()
|
||||||
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgUndelegate".to_owned()))?;
|
||||||
|
|
||||||
self.sign_and_broadcast_commit(delegator_address, vec![undelegate_msg], fee, memo)
|
self.sign_and_broadcast(delegator_address, vec![undelegate_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
@@ -462,7 +466,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
validator_address: &AccountId,
|
validator_address: &AccountId,
|
||||||
fee: Fee,
|
fee: Fee,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError> {
|
) -> Result<TxResponse, NymdError> {
|
||||||
let withdraw_msg = MsgWithdrawDelegatorReward {
|
let withdraw_msg = MsgWithdrawDelegatorReward {
|
||||||
delegator_address: delegator_address.to_owned(),
|
delegator_address: delegator_address.to_owned(),
|
||||||
validator_address: validator_address.to_owned(),
|
validator_address: validator_address.to_owned(),
|
||||||
@@ -470,7 +474,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
.to_any()
|
.to_any()
|
||||||
.map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?;
|
.map_err(|_| NymdError::SerializationError("MsgWithdrawDelegatorReward".to_owned()))?;
|
||||||
|
|
||||||
self.sign_and_broadcast_commit(delegator_address, vec![withdraw_msg], fee, memo)
|
self.sign_and_broadcast(delegator_address, vec![withdraw_msg], fee, memo)
|
||||||
.await?
|
.await?
|
||||||
.check_response()
|
.check_response()
|
||||||
}
|
}
|
||||||
@@ -573,6 +577,27 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
|||||||
CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await
|
CosmWasmClient::broadcast_tx_commit(self, tx_bytes.into()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Broadcast a transaction to the network and monitors its inclusion in a block.
|
||||||
|
async fn sign_and_broadcast(
|
||||||
|
&self,
|
||||||
|
signer_address: &AccountId,
|
||||||
|
messages: Vec<Any>,
|
||||||
|
fee: Fee,
|
||||||
|
memo: impl Into<String> + Send + 'static,
|
||||||
|
) -> Result<TxResponse, NymdError> {
|
||||||
|
let memo = memo.into();
|
||||||
|
let fee = self
|
||||||
|
.determine_transaction_fee(signer_address, &messages, fee, &memo)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let tx_raw = self.sign(signer_address, messages, fee, memo).await?;
|
||||||
|
let tx_bytes = tx_raw
|
||||||
|
.to_bytes()
|
||||||
|
.map_err(|_| NymdError::SerializationError("Tx".to_owned()))?;
|
||||||
|
|
||||||
|
self.broadcast_tx(tx_bytes.into()).await
|
||||||
|
}
|
||||||
|
|
||||||
fn sign_direct(
|
fn sign_direct(
|
||||||
&self,
|
&self,
|
||||||
signer_address: &AccountId,
|
signer_address: &AccountId,
|
||||||
@@ -638,6 +663,9 @@ pub struct Client {
|
|||||||
rpc_client: HttpClient,
|
rpc_client: HttpClient,
|
||||||
signer: DirectSecp256k1HdWallet,
|
signer: DirectSecp256k1HdWallet,
|
||||||
gas_price: GasPrice,
|
gas_price: GasPrice,
|
||||||
|
|
||||||
|
broadcast_polling_rate: Duration,
|
||||||
|
broadcast_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
@@ -654,8 +682,18 @@ impl Client {
|
|||||||
rpc_client,
|
rpc_client,
|
||||||
signer,
|
signer,
|
||||||
gas_price,
|
gas_price,
|
||||||
|
broadcast_polling_rate: DEFAULT_BROADCAST_POLLING_RATE,
|
||||||
|
broadcast_timeout: DEFAULT_BROADCAST_TIMEOUT,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_broadcast_polling_rate(&mut self, broadcast_polling_rate: Duration) {
|
||||||
|
self.broadcast_polling_rate = broadcast_polling_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_broadcast_timeout(&mut self, broadcast_timeout: Duration) {
|
||||||
|
self.broadcast_timeout = broadcast_timeout
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -669,7 +707,15 @@ impl rpc::Client for Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl CosmWasmClient for Client {}
|
impl CosmWasmClient for Client {
|
||||||
|
fn broadcast_polling_rate(&self) -> Duration {
|
||||||
|
self.broadcast_polling_rate
|
||||||
|
}
|
||||||
|
|
||||||
|
fn broadcast_timeout(&self) -> Duration {
|
||||||
|
self.broadcast_timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl SigningCosmWasmClient for Client {
|
impl SigningCosmWasmClient for Client {
|
||||||
|
|||||||
@@ -1,23 +1,34 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/
|
// TODO: There's a significant argument to pull those out of the package and make a PR on https://github.com/cosmos/cosmos-rust/
|
||||||
|
|
||||||
|
use crate::nymd::cosmwasm_client::helpers::parse_proto_coin_vec;
|
||||||
use crate::nymd::cosmwasm_client::logs::Log;
|
use crate::nymd::cosmwasm_client::logs::Log;
|
||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use cosmrs::crypto::PublicKey;
|
use cosmrs::crypto::PublicKey;
|
||||||
use cosmrs::proto::cosmos::auth::v1beta1::BaseAccount;
|
use cosmrs::proto::cosmos::auth::v1beta1::{
|
||||||
|
BaseAccount as ProtoBaseAccount, ModuleAccount as ProtoModuleAccount,
|
||||||
|
};
|
||||||
use cosmrs::proto::cosmos::base::abci::v1beta1::{
|
use cosmrs::proto::cosmos::base::abci::v1beta1::{
|
||||||
GasInfo as ProtoGasInfo, Result as ProtoAbciResult,
|
GasInfo as ProtoGasInfo, Result as ProtoAbciResult,
|
||||||
};
|
};
|
||||||
use cosmrs::proto::cosmos::tx::v1beta1::SimulateResponse as ProtoSimulateResponse;
|
use cosmrs::proto::cosmos::tx::v1beta1::SimulateResponse as ProtoSimulateResponse;
|
||||||
|
use cosmrs::proto::cosmos::vesting::v1beta1::{
|
||||||
|
BaseVestingAccount as ProtoBaseVestingAccount,
|
||||||
|
ContinuousVestingAccount as ProtoContinuousVestingAccount,
|
||||||
|
DelayedVestingAccount as ProtoDelayedVestingAccount, Period as ProtoPeriod,
|
||||||
|
PeriodicVestingAccount as ProtoPeriodicVestingAccount,
|
||||||
|
PermanentLockedAccount as ProtoPermanentLockedAccount,
|
||||||
|
};
|
||||||
use cosmrs::proto::cosmwasm::wasm::v1::{
|
use cosmrs::proto::cosmwasm::wasm::v1::{
|
||||||
CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry,
|
CodeInfoResponse, ContractCodeHistoryEntry as ProtoContractCodeHistoryEntry,
|
||||||
ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo,
|
ContractCodeHistoryOperationType, ContractInfo as ProtoContractInfo,
|
||||||
};
|
};
|
||||||
use cosmrs::tendermint::{abci, chain};
|
use cosmrs::tendermint::{abci, chain};
|
||||||
use cosmrs::tx::{AccountNumber, Gas, SequenceNumber};
|
use cosmrs::tx::{AccountNumber, Gas, SequenceNumber};
|
||||||
use cosmrs::{tx, AccountId, Coin};
|
use cosmrs::{tx, AccountId, Any, Coin};
|
||||||
|
use prost::Message;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
@@ -32,8 +43,11 @@ pub struct SequenceResponse {
|
|||||||
pub sequence: SequenceNumber,
|
pub sequence: SequenceNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// BaseAccount defines a base account type. It contains all the necessary fields
|
||||||
|
/// for basic account functionality. Any custom account type should extend this
|
||||||
|
/// type for additional functionality (e.g. vesting).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Account {
|
pub struct BaseAccount {
|
||||||
/// Bech32 account address
|
/// Bech32 account address
|
||||||
pub address: AccountId,
|
pub address: AccountId,
|
||||||
pub pubkey: Option<PublicKey>,
|
pub pubkey: Option<PublicKey>,
|
||||||
@@ -41,10 +55,10 @@ pub struct Account {
|
|||||||
pub sequence: SequenceNumber,
|
pub sequence: SequenceNumber,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<BaseAccount> for Account {
|
impl TryFrom<ProtoBaseAccount> for BaseAccount {
|
||||||
type Error = NymdError;
|
type Error = NymdError;
|
||||||
|
|
||||||
fn try_from(value: BaseAccount) -> Result<Self, Self::Error> {
|
fn try_from(value: ProtoBaseAccount) -> Result<Self, Self::Error> {
|
||||||
let address: AccountId = value
|
let address: AccountId = value
|
||||||
.address
|
.address
|
||||||
.parse()
|
.parse()
|
||||||
@@ -56,7 +70,7 @@ impl TryFrom<BaseAccount> for Account {
|
|||||||
.transpose()
|
.transpose()
|
||||||
.map_err(|_| NymdError::InvalidPublicKey(address.clone()))?;
|
.map_err(|_| NymdError::InvalidPublicKey(address.clone()))?;
|
||||||
|
|
||||||
Ok(Account {
|
Ok(BaseAccount {
|
||||||
address,
|
address,
|
||||||
pubkey,
|
pubkey,
|
||||||
account_number: value.account_number,
|
account_number: value.account_number,
|
||||||
@@ -65,6 +79,261 @@ impl TryFrom<BaseAccount> for Account {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ModuleAccount defines an account for modules that holds coins on a pool.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModuleAccount {
|
||||||
|
pub base_account: Option<BaseAccount>,
|
||||||
|
pub name: String,
|
||||||
|
pub permissions: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoModuleAccount> for ModuleAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoModuleAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_account = value.base_account.map(TryFrom::try_from).transpose()?;
|
||||||
|
|
||||||
|
Ok(ModuleAccount {
|
||||||
|
base_account,
|
||||||
|
name: value.name,
|
||||||
|
permissions: value.permissions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// BaseVestingAccount implements the VestingAccount interface. It contains all
|
||||||
|
/// the necessary fields needed for any vesting account implementation.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BaseVestingAccount {
|
||||||
|
pub base_account: Option<BaseAccount>,
|
||||||
|
pub original_vesting: Vec<Coin>,
|
||||||
|
pub delegated_free: Vec<Coin>,
|
||||||
|
pub delegated_vesting: Vec<Coin>,
|
||||||
|
pub end_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoBaseVestingAccount> for BaseVestingAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoBaseVestingAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_account = value.base_account.map(TryFrom::try_from).transpose()?;
|
||||||
|
|
||||||
|
let original_vesting = parse_proto_coin_vec(value.original_vesting)?;
|
||||||
|
let delegated_free = parse_proto_coin_vec(value.delegated_free)?;
|
||||||
|
let delegated_vesting = parse_proto_coin_vec(value.delegated_vesting)?;
|
||||||
|
|
||||||
|
Ok(BaseVestingAccount {
|
||||||
|
base_account,
|
||||||
|
original_vesting,
|
||||||
|
delegated_free,
|
||||||
|
delegated_vesting,
|
||||||
|
end_time: value.end_time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ContinuousVestingAccount implements the VestingAccount interface. It
|
||||||
|
/// continuously vests by unlocking coins linearly with respect to time.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ContinuousVestingAccount {
|
||||||
|
pub base_vesting_account: Option<BaseVestingAccount>,
|
||||||
|
pub start_time: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoContinuousVestingAccount> for ContinuousVestingAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoContinuousVestingAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_vesting_account = value
|
||||||
|
.base_vesting_account
|
||||||
|
.map(TryFrom::try_from)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
Ok(ContinuousVestingAccount {
|
||||||
|
base_vesting_account,
|
||||||
|
start_time: value.start_time,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DelayedVestingAccount implements the VestingAccount interface. It vests all
|
||||||
|
/// coins after a specific time, but non prior. In other words, it keeps them
|
||||||
|
/// locked until a specified time.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DelayedVestingAccount {
|
||||||
|
pub base_vesting_account: Option<BaseVestingAccount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoDelayedVestingAccount> for DelayedVestingAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoDelayedVestingAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_vesting_account = value
|
||||||
|
.base_vesting_account
|
||||||
|
.map(TryFrom::try_from)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
Ok(DelayedVestingAccount {
|
||||||
|
base_vesting_account,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Period defines a length of time and amount of coins that will vest.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Period {
|
||||||
|
pub length: i64,
|
||||||
|
pub amount: Vec<Coin>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoPeriod> for Period {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoPeriod) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Period {
|
||||||
|
length: value.length,
|
||||||
|
amount: parse_proto_coin_vec(value.amount)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PeriodicVestingAccount implements the VestingAccount interface. It
|
||||||
|
/// periodically vests by unlocking coins during each specified period.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PeriodicVestingAccount {
|
||||||
|
pub base_vesting_account: Option<BaseVestingAccount>,
|
||||||
|
pub start_time: i64,
|
||||||
|
pub vesting_periods: Vec<Period>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoPeriodicVestingAccount> for PeriodicVestingAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoPeriodicVestingAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_vesting_account = value
|
||||||
|
.base_vesting_account
|
||||||
|
.map(TryFrom::try_from)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
let vesting_periods = value
|
||||||
|
.vesting_periods
|
||||||
|
.into_iter()
|
||||||
|
.map(TryFrom::try_from)
|
||||||
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
Ok(PeriodicVestingAccount {
|
||||||
|
base_vesting_account,
|
||||||
|
start_time: value.start_time,
|
||||||
|
vesting_periods,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PermanentLockedAccount implements the VestingAccount interface. It does
|
||||||
|
/// not ever release coins, locking them indefinitely. Coins in this account can
|
||||||
|
/// still be used for delegating and for governance votes even while locked.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PermanentLockedAccount {
|
||||||
|
pub base_vesting_account: Option<BaseVestingAccount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ProtoPermanentLockedAccount> for PermanentLockedAccount {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(value: ProtoPermanentLockedAccount) -> Result<Self, Self::Error> {
|
||||||
|
let base_vesting_account = value
|
||||||
|
.base_vesting_account
|
||||||
|
.map(TryFrom::try_from)
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
Ok(PermanentLockedAccount {
|
||||||
|
base_vesting_account,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Account {
|
||||||
|
Base(BaseAccount),
|
||||||
|
Module(ModuleAccount),
|
||||||
|
BaseVesting(BaseVestingAccount),
|
||||||
|
ContinuousVesting(ContinuousVestingAccount),
|
||||||
|
DelayedVesting(DelayedVestingAccount),
|
||||||
|
PeriodicVesting(PeriodicVestingAccount),
|
||||||
|
PermanentLockedVesting(PermanentLockedAccount),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Account {
|
||||||
|
pub fn try_get_base_account(&self) -> Result<&BaseAccount, NymdError> {
|
||||||
|
match self {
|
||||||
|
Account::Base(acc) => Ok(acc),
|
||||||
|
Account::Module(acc) => acc
|
||||||
|
.base_account
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
Account::BaseVesting(acc) => acc
|
||||||
|
.base_account
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
Account::ContinuousVesting(acc) => acc
|
||||||
|
.base_vesting_account
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|vesting_acc| vesting_acc.base_account.as_ref())
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
Account::DelayedVesting(acc) => acc
|
||||||
|
.base_vesting_account
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|vesting_acc| vesting_acc.base_account.as_ref())
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
Account::PeriodicVesting(acc) => acc
|
||||||
|
.base_vesting_account
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|vesting_acc| vesting_acc.base_account.as_ref())
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
Account::PermanentLockedVesting(acc) => acc
|
||||||
|
.base_vesting_account
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|vesting_acc| vesting_acc.base_account.as_ref())
|
||||||
|
.ok_or(NymdError::NoBaseAccountInformationAvailable),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Any> for Account {
|
||||||
|
type Error = NymdError;
|
||||||
|
|
||||||
|
fn try_from(raw_account: Any) -> Result<Self, Self::Error> {
|
||||||
|
match raw_account.type_url.as_ref() {
|
||||||
|
"/cosmos.auth.v1beta1.BaseAccount" => Ok(Account::Base(
|
||||||
|
ProtoBaseAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.auth.v1beta1.ModuleAccount" => Ok(Account::Module(
|
||||||
|
ProtoModuleAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.vesting.v1beta1.BaseVestingAccount" => Ok(Account::BaseVesting(
|
||||||
|
ProtoBaseVestingAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.vesting.v1beta1.ContinuousVestingAccount" => Ok(Account::ContinuousVesting(
|
||||||
|
ProtoContinuousVestingAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.vesting.v1beta1.DelayedVestingAccount" => Ok(Account::DelayedVesting(
|
||||||
|
ProtoDelayedVestingAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.vesting.v1beta1.PeriodicVestingAccount" => Ok(Account::PeriodicVesting(
|
||||||
|
ProtoPeriodicVestingAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
)),
|
||||||
|
"/cosmos.vesting.v1beta1.PermanentLockedAccount" => {
|
||||||
|
Ok(Account::PermanentLockedVesting(
|
||||||
|
ProtoPermanentLockedAccount::decode(raw_account.value.as_ref())?.try_into()?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(NymdError::UnsupportedAccountType {
|
||||||
|
type_url: raw_account.type_url,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Code {
|
pub struct Code {
|
||||||
pub code_id: ContractCodeId,
|
pub code_id: ContractCodeId,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::nymd::cosmwasm_client::types::ContractCodeId;
|
|||||||
use cosmrs::tendermint::{abci, block};
|
use cosmrs::tendermint::{abci, block};
|
||||||
use cosmrs::{bip32, tx, AccountId};
|
use cosmrs::{bip32, tx, AccountId};
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use cosmrs::rpc::error::{
|
pub use cosmrs::rpc::error::{
|
||||||
@@ -81,21 +82,21 @@ pub enum NymdError {
|
|||||||
MalformedLogString,
|
MalformedLogString,
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
"Error when broadcasting tx {hash} at height {height}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}"
|
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during CheckTx phase. Code: {code}; Raw log: {raw_log}"
|
||||||
)]
|
)]
|
||||||
BroadcastTxErrorCheckTx {
|
BroadcastTxErrorCheckTx {
|
||||||
hash: tx::Hash,
|
hash: tx::Hash,
|
||||||
height: block::Height,
|
height: Option<block::Height>,
|
||||||
code: u32,
|
code: u32,
|
||||||
raw_log: String,
|
raw_log: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[error(
|
#[error(
|
||||||
"Error when broadcasting tx {hash} at height {height}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}"
|
"Error when broadcasting tx {hash} at height {height:?}. Error occurred during DeliverTx phase. Code: {code}; Raw log: {raw_log}"
|
||||||
)]
|
)]
|
||||||
BroadcastTxErrorDeliverTx {
|
BroadcastTxErrorDeliverTx {
|
||||||
hash: tx::Hash,
|
hash: tx::Hash,
|
||||||
height: block::Height,
|
height: Option<block::Height>,
|
||||||
code: u32,
|
code: u32,
|
||||||
raw_log: String,
|
raw_log: String,
|
||||||
},
|
},
|
||||||
@@ -108,6 +109,18 @@ pub enum NymdError {
|
|||||||
|
|
||||||
#[error("Abci query failed with code {0} - {1}")]
|
#[error("Abci query failed with code {0} - {1}")]
|
||||||
AbciError(u32, abci::Log),
|
AbciError(u32, abci::Log),
|
||||||
|
|
||||||
|
#[error("Unsupported account type: {type_url}")]
|
||||||
|
UnsupportedAccountType { type_url: String },
|
||||||
|
|
||||||
|
#[error("{coin_representation} is not a valid Cosmos Coin")]
|
||||||
|
MalformedCoin { coin_representation: String },
|
||||||
|
|
||||||
|
#[error("This account does not have BaseAccount information available to it")]
|
||||||
|
NoBaseAccountInformationAvailable,
|
||||||
|
|
||||||
|
#[error("Transaction with ID {hash} has been submitted but not yet found on the chain. You might want to check for it later. There was a total wait of {} seconds", .timeout.as_secs())]
|
||||||
|
BroadcastTimeout { hash: tx::Hash, timeout: Duration },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NymdError {
|
impl NymdError {
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
|
|
||||||
use crate::nymd::cosmwasm_client::signing_client;
|
use crate::nymd::cosmwasm_client::signing_client;
|
||||||
use crate::nymd::cosmwasm_client::types::{
|
use crate::nymd::cosmwasm_client::types::{
|
||||||
ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions, InstantiateResult,
|
Account, ChangeAdminResult, ContractCodeId, ExecuteResult, InstantiateOptions,
|
||||||
MigrateResult, SequenceResponse, UploadResult,
|
InstantiateResult, MigrateResult, SequenceResponse, UploadResult,
|
||||||
};
|
};
|
||||||
use crate::nymd::error::NymdError;
|
use crate::nymd::error::NymdError;
|
||||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||||
use cosmrs::rpc::endpoint::broadcast;
|
|
||||||
use cosmrs::rpc::Error as TendermintRpcError;
|
use cosmrs::rpc::Error as TendermintRpcError;
|
||||||
use cosmrs::rpc::HttpClientUrl;
|
use cosmrs::rpc::HttpClientUrl;
|
||||||
use cosmwasm_std::{Coin, Uint128};
|
use cosmwasm_std::{Coin, Uint128};
|
||||||
@@ -23,15 +22,20 @@ use mixnet_contract_common::{
|
|||||||
PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
|
PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||||
pub use crate::nymd::fee::Fee;
|
pub use crate::nymd::fee::Fee;
|
||||||
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
|
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
|
||||||
|
pub use cosmrs::bank::MsgSend;
|
||||||
|
pub use cosmrs::rpc::endpoint::tx::Response as TxResponse;
|
||||||
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
|
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
|
||||||
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
|
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
|
||||||
pub use cosmrs::rpc::Paging;
|
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::block::Height;
|
||||||
pub use cosmrs::tendermint::hash;
|
pub use cosmrs::tendermint::hash;
|
||||||
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||||
@@ -40,7 +44,6 @@ pub use cosmrs::tx::{self, Gas};
|
|||||||
pub use cosmrs::Coin as CosmosCoin;
|
pub use cosmrs::Coin as CosmosCoin;
|
||||||
pub use cosmrs::{AccountId, Decimal, Denom};
|
pub use cosmrs::{AccountId, Decimal, Denom};
|
||||||
pub use signing_client::Client as SigningNymdClient;
|
pub use signing_client::Client as SigningNymdClient;
|
||||||
use std::collections::HashMap;
|
|
||||||
pub use traits::{VestingQueryClient, VestingSigningClient};
|
pub use traits::{VestingQueryClient, VestingSigningClient};
|
||||||
|
|
||||||
pub mod cosmwasm_client;
|
pub mod cosmwasm_client;
|
||||||
@@ -168,6 +171,10 @@ impl<C> NymdClient<C> {
|
|||||||
.ok_or(NymdError::NoContractAddressAvailable)
|
.ok_or(NymdError::NoContractAddressAvailable)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
|
||||||
|
self.simulated_gas_multiplier = multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn address(&self) -> &AccountId
|
pub fn address(&self) -> &AccountId
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient,
|
C: SigningCosmWasmClient,
|
||||||
@@ -223,6 +230,16 @@ impl<C> NymdClient<C> {
|
|||||||
self.client.get_sequence(self.address()).await
|
self.client.get_sequence(self.address()).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_account_details(
|
||||||
|
&self,
|
||||||
|
address: &AccountId,
|
||||||
|
) -> Result<Option<Account>, NymdError>
|
||||||
|
where
|
||||||
|
C: SigningCosmWasmClient + Sync,
|
||||||
|
{
|
||||||
|
self.client.get_account(address).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
|
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
@@ -274,6 +291,13 @@ impl<C> NymdClient<C> {
|
|||||||
self.client.get_balance(address, denom).await
|
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>
|
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
@@ -305,6 +329,7 @@ impl<C> NymdClient<C> {
|
|||||||
&self,
|
&self,
|
||||||
address: String,
|
address: String,
|
||||||
mix_identity: IdentityKey,
|
mix_identity: IdentityKey,
|
||||||
|
proxy: Option<String>,
|
||||||
) -> Result<Uint128, NymdError>
|
) -> Result<Uint128, NymdError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
@@ -312,6 +337,7 @@ impl<C> NymdClient<C> {
|
|||||||
let request = QueryMsg::QueryDelegatorReward {
|
let request = QueryMsg::QueryDelegatorReward {
|
||||||
address,
|
address,
|
||||||
mix_identity,
|
mix_identity,
|
||||||
|
proxy,
|
||||||
};
|
};
|
||||||
self.client
|
self.client
|
||||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||||
@@ -321,11 +347,15 @@ impl<C> NymdClient<C> {
|
|||||||
pub async fn get_pending_delegation_events(
|
pub async fn get_pending_delegation_events(
|
||||||
&self,
|
&self,
|
||||||
owner_address: String,
|
owner_address: String,
|
||||||
|
proxy_address: Option<String>,
|
||||||
) -> Result<Vec<DelegationEvent>, NymdError>
|
) -> Result<Vec<DelegationEvent>, NymdError>
|
||||||
where
|
where
|
||||||
C: CosmWasmClient + Sync,
|
C: CosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
let request = QueryMsg::GetPendingDelegationEvents { owner_address };
|
let request = QueryMsg::GetPendingDelegationEvents {
|
||||||
|
owner_address,
|
||||||
|
proxy_address,
|
||||||
|
};
|
||||||
self.client
|
self.client
|
||||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||||
.await
|
.await
|
||||||
@@ -610,7 +640,7 @@ impl<C> NymdClient<C> {
|
|||||||
recipient: &AccountId,
|
recipient: &AccountId,
|
||||||
amount: Vec<CosmosCoin>,
|
amount: Vec<CosmosCoin>,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError>
|
) -> Result<TxResponse, NymdError>
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient + Sync,
|
C: SigningCosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
@@ -625,7 +655,7 @@ impl<C> NymdClient<C> {
|
|||||||
&self,
|
&self,
|
||||||
msgs: Vec<(AccountId, Vec<CosmosCoin>)>,
|
msgs: Vec<(AccountId, Vec<CosmosCoin>)>,
|
||||||
memo: impl Into<String> + Send + 'static,
|
memo: impl Into<String> + Send + 'static,
|
||||||
) -> Result<broadcast::tx_commit::Response, NymdError>
|
) -> Result<TxResponse, NymdError>
|
||||||
where
|
where
|
||||||
C: SigningCosmWasmClient + Sync,
|
C: SigningCosmWasmClient + Sync,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
|||||||
self.vesting_contract_address()?,
|
self.vesting_contract_address()?,
|
||||||
&req,
|
&req,
|
||||||
fee,
|
fee,
|
||||||
"VestingContract::DeledateToMixnode",
|
"VestingContract::DelegateToMixnode",
|
||||||
vec![],
|
vec![],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -197,38 +197,45 @@ impl DirectSecp256k1HdWalletBuilder {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use network_defaults::DEFAULT_NETWORK;
|
use network_defaults::all::Network::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn generating_account_addresses() {
|
fn generating_account_addresses() {
|
||||||
let (addr1, addr2, addr3) = match DEFAULT_NETWORK.bech32_prefix() {
|
|
||||||
"punk" => (
|
|
||||||
"punk1jw6mp7d5xqc7w6xm79lha27glmd0vdt32a3fj2",
|
|
||||||
"punk1h5hgn94nsq4kh99rjj794hr5h5q6yfm22mcqqn",
|
|
||||||
"punk17n9flp6jflljg6fp05dsy07wcprf2uuujse962",
|
|
||||||
),
|
|
||||||
"nymt" => (
|
|
||||||
"nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94",
|
|
||||||
"nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv",
|
|
||||||
"nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4",
|
|
||||||
),
|
|
||||||
_ => panic!("Test needs to be updated with new bech32 prefix"),
|
|
||||||
};
|
|
||||||
// test vectors produced from our js wallet
|
// test vectors produced from our js wallet
|
||||||
let mnemonic_address = vec![
|
let mnemonics = vec![
|
||||||
("crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove", addr1),
|
"crush minute paddle tobacco message debate cabin peace bar jacket execute twenty winner view sure mask popular couch penalty fragile demise fresh pizza stove",
|
||||||
("acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel", addr2),
|
"acquire rebel spot skin gun such erupt pull swear must define ill chief turtle today flower chunk truth battle claw rigid detail gym feel",
|
||||||
("step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball", addr3)
|
"step income throw wheat mobile ship wave drink pool sudden upset jaguar bar globe rifle spice frost bless glimpse size regular carry aspect ball"
|
||||||
|
];
|
||||||
|
let prefixes = vec![
|
||||||
|
MAINNET.bech32_prefix(),
|
||||||
|
SANDBOX.bech32_prefix(),
|
||||||
|
QA.bech32_prefix(),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (mnemonic, address) in mnemonic_address.into_iter() {
|
for prefix in prefixes {
|
||||||
let prefix = DEFAULT_NETWORK.bech32_prefix();
|
let addrs = match prefix {
|
||||||
let wallet =
|
"nymt" => vec![
|
||||||
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap()).unwrap();
|
"nymt1jw6mp7d5xqc7w6xm79lha27glmd0vdt339me94",
|
||||||
assert_eq!(
|
"nymt1h5hgn94nsq4kh99rjj794hr5h5q6yfm23rjshv",
|
||||||
wallet.try_derive_accounts().unwrap()[0].address,
|
"nymt17n9flp6jflljg6fp05dsy07wcprf2uuufgn4d4",
|
||||||
address.parse().unwrap()
|
],
|
||||||
)
|
"n" => vec![
|
||||||
|
"n1jw6mp7d5xqc7w6xm79lha27glmd0vdt3l9artf",
|
||||||
|
"n1h5hgn94nsq4kh99rjj794hr5h5q6yfm2lr52es",
|
||||||
|
"n17n9flp6jflljg6fp05dsy07wcprf2uuu8g40rf",
|
||||||
|
],
|
||||||
|
_ => panic!("Test needs to be updated with new bech32 prefix"),
|
||||||
|
};
|
||||||
|
for (idx, mnemonic) in mnemonics.iter().enumerate() {
|
||||||
|
let wallet =
|
||||||
|
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap())
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
wallet.try_derive_accounts().unwrap()[0].address,
|
||||||
|
addrs[idx].parse().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,7 @@ pub enum ValidatorAPIError {
|
|||||||
#[from]
|
#[from]
|
||||||
source: reqwest::Error,
|
source: reqwest::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[error("Request failed with error message - {0}")]
|
||||||
|
GenericRequestFailure(String),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ use std::collections::HashMap;
|
|||||||
use url::Url;
|
use url::Url;
|
||||||
use validator_api_requests::models::{
|
use validator_api_requests::models::{
|
||||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
||||||
RewardEstimationResponse, StakeSaturationResponse,
|
RewardEstimationResponse, StakeSaturationResponse, UptimeResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub(crate) mod routes;
|
pub mod routes;
|
||||||
|
|
||||||
type PathSegments<'a> = &'a [&'a str];
|
type PathSegments<'a> = &'a [&'a str];
|
||||||
type Params<'a, K, V> = &'a [(K, V)];
|
type Params<'a, K, V> = &'a [(K, V)];
|
||||||
@@ -39,6 +39,10 @@ impl Client {
|
|||||||
self.url = new_url
|
self.url = new_url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_url(&self) -> &Url {
|
||||||
|
&self.url
|
||||||
|
}
|
||||||
|
|
||||||
async fn query_validator_api<T, K, V>(
|
async fn query_validator_api<T, K, V>(
|
||||||
&self,
|
&self,
|
||||||
path: PathSegments<'_>,
|
path: PathSegments<'_>,
|
||||||
@@ -66,14 +70,14 @@ impl Client {
|
|||||||
V: AsRef<str>,
|
V: AsRef<str>,
|
||||||
{
|
{
|
||||||
let url = create_api_url(&self.url, path, params);
|
let url = create_api_url(&self.url, path, params);
|
||||||
Ok(self
|
let response = self.reqwest_client.post(url).json(json_body).send().await?;
|
||||||
.reqwest_client
|
if response.status().is_success() {
|
||||||
.post(url)
|
Ok(response.json().await?)
|
||||||
.json(json_body)
|
} else {
|
||||||
.send()
|
Err(ValidatorAPIError::GenericRequestFailure(
|
||||||
.await?
|
response.text().await?,
|
||||||
.json()
|
))
|
||||||
.await?)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||||
@@ -249,12 +253,64 @@ impl Client {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_mixnode_avg_uptime(
|
||||||
|
&self,
|
||||||
|
identity: IdentityKeyRef<'_>,
|
||||||
|
) -> Result<UptimeResponse, ValidatorAPIError> {
|
||||||
|
self.query_validator_api(
|
||||||
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::STATUS_ROUTES,
|
||||||
|
routes::MIXNODE,
|
||||||
|
identity,
|
||||||
|
routes::AVG_UPTIME,
|
||||||
|
],
|
||||||
|
NO_PARAMS,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_mixnode_avg_uptimes(&self) -> Result<Vec<UptimeResponse>, ValidatorAPIError> {
|
||||||
|
self.query_validator_api(
|
||||||
|
&[
|
||||||
|
routes::API_VERSION,
|
||||||
|
routes::STATUS_ROUTES,
|
||||||
|
routes::MIXNODES,
|
||||||
|
routes::AVG_UPTIME,
|
||||||
|
],
|
||||||
|
NO_PARAMS,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn blind_sign(
|
pub async fn blind_sign(
|
||||||
&self,
|
&self,
|
||||||
request_body: &BlindSignRequestBody,
|
request_body: &BlindSignRequestBody,
|
||||||
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
|
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
|
||||||
self.post_validator_api(
|
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,
|
NO_PARAMS,
|
||||||
request_body,
|
request_body,
|
||||||
)
|
)
|
||||||
@@ -265,7 +321,12 @@ impl Client {
|
|||||||
&self,
|
&self,
|
||||||
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
|
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
|
||||||
self.query_validator_api(
|
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,
|
NO_PARAMS,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ pub const GATEWAYS: &str = "gateways";
|
|||||||
pub const ACTIVE: &str = "active";
|
pub const ACTIVE: &str = "active";
|
||||||
pub const REWARDED: &str = "rewarded";
|
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_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 COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||||
|
|
||||||
pub const STATUS_ROUTES: &str = "status";
|
pub const STATUS_ROUTES: &str = "status";
|
||||||
@@ -22,5 +26,6 @@ pub const SINCE_ARG: &str = "since";
|
|||||||
|
|
||||||
pub const STATUS: &str = "status";
|
pub const STATUS: &str = "status";
|
||||||
pub const REWARD_ESTIMATION: &str = "reward-estimation";
|
pub const REWARD_ESTIMATION: &str = "reward-estimation";
|
||||||
|
pub const AVG_UPTIME: &str = "avg_uptime";
|
||||||
pub const STAKE_SATURATION: &str = "stake-saturation";
|
pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ edition = "2021"
|
|||||||
description = "Crutch library until there is proper SerDe support for coconut structs"
|
description = "Crutch library until there is proper SerDe support for coconut structs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
bs58 = "0.4.0"
|
||||||
getset = "0.1.1"
|
getset = "0.1.1"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
thiserror = "1"
|
||||||
|
|
||||||
nymcoconut = {path = "../nymcoconut" }
|
nymcoconut = {path = "../nymcoconut" }
|
||||||
|
|||||||
@@ -5,21 +5,15 @@ use thiserror::Error;
|
|||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum CoconutInterfaceError {
|
pub enum CoconutInterfaceError {
|
||||||
#[error("could not parse validator URL: {source}")]
|
#[error("not enough bytes: {0} received, minimum {1} required")]
|
||||||
UrlParsingError {
|
InvalidByteLength(usize, usize),
|
||||||
#[from]
|
|
||||||
source: url::ParseError,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[error("could not aggregate verification key: {0}")]
|
#[error("Could not decode base 58 string - {0}")]
|
||||||
AggregateVerificationKeyError(coconut_rs::CoconutError),
|
MalformedString(#[from] bs58::decode::Error),
|
||||||
|
|
||||||
#[error("could not prove credential: {0}")]
|
#[error("Not enough public attributes were specified")]
|
||||||
ProveCredentialError(coconut_rs::CoconutError),
|
NotEnoughPublicAttributes,
|
||||||
|
|
||||||
#[error("got invalid signature index: {0}")]
|
#[error("Could not recover bandwidth value")]
|
||||||
InvalidSignatureIdx(usize),
|
InvalidBandwidth,
|
||||||
|
|
||||||
#[error("got too many total attributes(public + private): {0} received, {1} is the maximum")]
|
|
||||||
TooManyTotalAttributes(usize, u32),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
use getset::{CopyGetters, Getters};
|
use getset::{CopyGetters, Getters};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use error::CoconutInterfaceError;
|
||||||
|
|
||||||
pub use nymcoconut::*;
|
pub use nymcoconut::*;
|
||||||
|
|
||||||
@@ -20,9 +25,11 @@ impl Credential {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
n_params: u32,
|
n_params: u32,
|
||||||
theta: Theta,
|
theta: Theta,
|
||||||
public_attributes: Vec<Vec<u8>>,
|
voucher_value: String,
|
||||||
|
voucher_info: String,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
) -> Credential {
|
) -> Credential {
|
||||||
|
let public_attributes = vec![voucher_value.into_bytes(), voucher_info.into_bytes()];
|
||||||
Credential {
|
Credential {
|
||||||
n_params,
|
n_params,
|
||||||
theta,
|
theta,
|
||||||
@@ -31,8 +38,18 @@ impl Credential {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn public_attributes(&self) -> Vec<Vec<u8>> {
|
pub fn voucher_value(&self) -> Result<u64, CoconutInterfaceError> {
|
||||||
self.public_attributes.clone()
|
let bandwidth_vec = self
|
||||||
|
.public_attributes
|
||||||
|
.get(0)
|
||||||
|
.ok_or(CoconutInterfaceError::NotEnoughPublicAttributes)?
|
||||||
|
.to_owned();
|
||||||
|
let bandwidth_str = String::from_utf8(bandwidth_vec)
|
||||||
|
.map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
|
||||||
|
let value =
|
||||||
|
u64::from_str(&bandwidth_str).map_err(|_| CoconutInterfaceError::InvalidBandwidth)?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
|
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
|
||||||
@@ -79,27 +96,39 @@ impl VerifyCredentialBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// All strings are base58 encoded representations of structs
|
// All strings are base58 encoded representations of structs
|
||||||
#[derive(Serialize, Deserialize, Debug, Getters, CopyGetters)]
|
#[derive(Clone, Serialize, Deserialize, Debug, Getters, CopyGetters)]
|
||||||
pub struct BlindSignRequestBody {
|
pub struct BlindSignRequestBody {
|
||||||
#[getset(get = "pub")]
|
#[getset(get = "pub")]
|
||||||
blind_sign_request: BlindSignRequest,
|
blind_sign_request: BlindSignRequest,
|
||||||
|
#[getset(get = "pub")]
|
||||||
|
tx_hash: String,
|
||||||
|
#[getset(get = "pub")]
|
||||||
|
signature: String,
|
||||||
public_attributes: Vec<String>,
|
public_attributes: Vec<String>,
|
||||||
#[getset(get = "pub")]
|
#[getset(get = "pub")]
|
||||||
|
public_attributes_plain: Vec<String>,
|
||||||
|
#[getset(get = "pub")]
|
||||||
total_params: u32,
|
total_params: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlindSignRequestBody {
|
impl BlindSignRequestBody {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
blind_sign_request: &BlindSignRequest,
|
blind_sign_request: &BlindSignRequest,
|
||||||
|
tx_hash: String,
|
||||||
|
signature: String,
|
||||||
public_attributes: &[Attribute],
|
public_attributes: &[Attribute],
|
||||||
|
public_attributes_plain: Vec<String>,
|
||||||
total_params: u32,
|
total_params: u32,
|
||||||
) -> BlindSignRequestBody {
|
) -> BlindSignRequestBody {
|
||||||
BlindSignRequestBody {
|
BlindSignRequestBody {
|
||||||
blind_sign_request: blind_sign_request.clone(),
|
blind_sign_request: blind_sign_request.clone(),
|
||||||
|
tx_hash,
|
||||||
|
signature,
|
||||||
public_attributes: public_attributes
|
public_attributes: public_attributes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|attr| attr.to_bs58())
|
.map(|attr| attr.to_bs58())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
public_attributes_plain,
|
||||||
total_params,
|
total_params,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,14 +141,46 @@ impl BlindSignRequestBody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct BlindedSignatureResponse {
|
pub struct BlindedSignatureResponse {
|
||||||
pub blinded_signature: BlindedSignature,
|
pub remote_key: [u8; 32],
|
||||||
|
pub encrypted_signature: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlindedSignatureResponse {
|
impl BlindedSignatureResponse {
|
||||||
pub fn new(blinded_signature: BlindedSignature) -> BlindedSignatureResponse {
|
pub fn new(encrypted_signature: Vec<u8>, remote_key: [u8; 32]) -> BlindedSignatureResponse {
|
||||||
BlindedSignatureResponse { blinded_signature }
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
handlebars = "3.0.1"
|
handlebars = "3.0.1"
|
||||||
humantime-serde = "1.0"
|
humantime-serde = "1.0"
|
||||||
|
log = "0.4"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
toml = "0.5.6"
|
toml = "0.5.6"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
|
||||||
network-defaults = { path = "../network-defaults" }
|
network-defaults = { path = "../network-defaults" }
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
use handlebars::Handlebars;
|
use handlebars::Handlebars;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
#[cfg(unix)]
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
|
|
||||||
@@ -13,6 +15,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
fn template() -> &'static str;
|
fn template() -> &'static str;
|
||||||
|
|
||||||
fn config_file_name() -> String {
|
fn config_file_name() -> String {
|
||||||
|
log::trace!("NymdConfig::config_file_name");
|
||||||
"config.toml".to_string()
|
"config.toml".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,6 +23,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
|
|
||||||
// default, most probable, implementations; can be easily overridden where required
|
// default, most probable, implementations; can be easily overridden where required
|
||||||
fn default_config_directory(id: Option<&str>) -> PathBuf {
|
fn default_config_directory(id: Option<&str>) -> PathBuf {
|
||||||
|
log::trace!("NymdConfig::default_config_directory");
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
Self::default_root_directory().join(id).join("config")
|
Self::default_root_directory().join(id).join("config")
|
||||||
} else {
|
} else {
|
||||||
@@ -28,6 +32,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_data_directory(id: Option<&str>) -> PathBuf {
|
fn default_data_directory(id: Option<&str>) -> PathBuf {
|
||||||
|
log::trace!("NymdConfig::default_data_path");
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
Self::default_root_directory().join(id).join("data")
|
Self::default_root_directory().join(id).join("data")
|
||||||
} else {
|
} else {
|
||||||
@@ -36,6 +41,7 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_config_file_path(id: Option<&str>) -> PathBuf {
|
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())
|
Self::default_config_directory(id).join(Self::config_file_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,15 +66,25 @@ pub trait NymConfig: Default + Serialize + DeserializeOwned {
|
|||||||
None => fs::create_dir_all(self.config_directory()),
|
None => fs::create_dir_all(self.config_directory()),
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
fs::write(
|
let location = custom_location
|
||||||
custom_location
|
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name()));
|
||||||
.unwrap_or_else(|| self.config_directory().join(Self::config_file_name())),
|
|
||||||
templated_config,
|
fs::write(location.clone(), templated_config)?;
|
||||||
)
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mut perms = fs::metadata(location.clone())?.permissions();
|
||||||
|
#[cfg(unix)]
|
||||||
|
perms.set_mode(0o600);
|
||||||
|
#[cfg(unix)]
|
||||||
|
fs::set_permissions(location, perms)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_from_file(id: Option<&str>) -> io::Result<Self> {
|
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)
|
toml::from_str(&config_contents)
|
||||||
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[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]
|
||||||
|
cosmwasm-std = "1.0.0-beta6"
|
||||||
|
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,29 @@
|
|||||||
|
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use cosmwasm_std::Coin;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::deposit::DepositData;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
|
pub struct InstantiateMsg {
|
||||||
|
pub multisig_addr: String,
|
||||||
|
pub pool_addr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ExecuteMsg {
|
||||||
|
DepositFunds { data: DepositData },
|
||||||
|
ReleaseFunds { funds: Coin },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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,4 +7,4 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmwasm-std = "1.0.0-beta6"
|
cosmwasm-std = "1.0.0-beta8"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmwasm-std = "1.0.0-beta6"
|
cosmwasm-std = "1.0.0-beta8"
|
||||||
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_repr = "0.1"
|
serde_repr = "0.1"
|
||||||
|
|||||||
@@ -13,4 +13,11 @@ pub enum MixnetContractError {
|
|||||||
},
|
},
|
||||||
#[error("Error casting from U128")]
|
#[error("Error casting from U128")]
|
||||||
CastError,
|
CastError,
|
||||||
|
#[error("{source}")]
|
||||||
|
StdErr {
|
||||||
|
#[from]
|
||||||
|
source: cosmwasm_std::StdError,
|
||||||
|
},
|
||||||
|
#[error("Division by zero at {}", line!())]
|
||||||
|
DivisionByZero,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use cosmwasm_std::Env;
|
use cosmwasm_std::Env;
|
||||||
|
use schemars::gen::SchemaGenerator;
|
||||||
|
use schemars::schema::{InstanceType, Schema, SchemaObject};
|
||||||
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
@@ -64,6 +67,39 @@ pub struct Interval {
|
|||||||
length: Duration,
|
length: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl JsonSchema for Interval {
|
||||||
|
fn schema_name() -> String {
|
||||||
|
"Interval".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_schema(gen: &mut SchemaGenerator) -> Schema {
|
||||||
|
let mut schema_object = SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::Object.into()),
|
||||||
|
..SchemaObject::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let object_validation = schema_object.object();
|
||||||
|
object_validation
|
||||||
|
.properties
|
||||||
|
.insert("id".to_owned(), gen.subschema_for::<u32>());
|
||||||
|
object_validation.required.insert("id".to_owned());
|
||||||
|
|
||||||
|
// PrimitiveDateTime does not implement JsonSchema. However it has a custom
|
||||||
|
// serialization to string, so we just specify the schema to be String.
|
||||||
|
object_validation
|
||||||
|
.properties
|
||||||
|
.insert("start".to_owned(), gen.subschema_for::<String>());
|
||||||
|
object_validation.required.insert("start".to_owned());
|
||||||
|
|
||||||
|
object_validation
|
||||||
|
.properties
|
||||||
|
.insert("length".to_owned(), gen.subschema_for::<Duration>());
|
||||||
|
object_validation.required.insert("length".to_owned());
|
||||||
|
|
||||||
|
Schema::Object(schema_object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Interval {
|
impl Interval {
|
||||||
/// Initialize epoch in the contract with default values.
|
/// Initialize epoch in the contract with default values.
|
||||||
pub fn init_epoch(env: Env) -> Self {
|
pub fn init_epoch(env: Env) -> Self {
|
||||||
|
|||||||
@@ -222,13 +222,19 @@ impl DelegatorRewardParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
|
pub fn determine_delegation_reward(&self, delegation_amount: Uint128) -> u128 {
|
||||||
|
if self.sigma == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// change all values into their fixed representations
|
// change all values into their fixed representations
|
||||||
let delegation_amount = U128::from_num(delegation_amount.u128());
|
let delegation_amount = U128::from_num(delegation_amount.u128());
|
||||||
let circulating_supply = U128::from_num(self.reward_params.circulating_supply());
|
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 / circulating_supply;
|
||||||
|
|
||||||
|
// Div by zero checked above
|
||||||
let delegator_reward =
|
let delegator_reward =
|
||||||
(ONE - self.profit_margin) * scaled_delegation_amount / self.sigma * self.node_profit;
|
(ONE - self.profit_margin) * (scaled_delegation_amount / self.sigma) * self.node_profit;
|
||||||
|
|
||||||
let reward = delegator_reward.max(U128::ZERO);
|
let reward = delegator_reward.max(U128::ZERO);
|
||||||
if let Some(int_reward) = reward.checked_cast() {
|
if let Some(int_reward) = reward.checked_cast() {
|
||||||
@@ -250,8 +256,14 @@ impl DelegatorRewardParams {
|
|||||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
||||||
pub struct StoredNodeRewardResult {
|
pub struct StoredNodeRewardResult {
|
||||||
reward: Uint128,
|
reward: Uint128,
|
||||||
lambda: Uint128,
|
|
||||||
sigma: Uint128,
|
#[schemars(with = "String")]
|
||||||
|
#[serde(with = "fixed_U128_as_string")]
|
||||||
|
lambda: U128,
|
||||||
|
|
||||||
|
#[schemars(with = "String")]
|
||||||
|
#[serde(with = "fixed_U128_as_string")]
|
||||||
|
sigma: U128,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StoredNodeRewardResult {
|
impl StoredNodeRewardResult {
|
||||||
@@ -259,11 +271,11 @@ impl StoredNodeRewardResult {
|
|||||||
self.reward
|
self.reward
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lambda(&self) -> Uint128 {
|
pub fn lambda(&self) -> U128 {
|
||||||
self.lambda
|
self.lambda
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigma(&self) -> Uint128 {
|
pub fn sigma(&self) -> U128 {
|
||||||
self.sigma
|
self.sigma
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,18 +291,8 @@ impl TryFrom<NodeRewardResult> for StoredNodeRewardResult {
|
|||||||
.checked_cast()
|
.checked_cast()
|
||||||
.ok_or(MixnetContractError::CastError)?,
|
.ok_or(MixnetContractError::CastError)?,
|
||||||
),
|
),
|
||||||
lambda: Uint128::new(
|
lambda: node_reward_result.lambda(),
|
||||||
node_reward_result
|
sigma: node_reward_result.sigma(),
|
||||||
.lambda()
|
|
||||||
.checked_cast()
|
|
||||||
.ok_or(MixnetContractError::CastError)?,
|
|
||||||
),
|
|
||||||
sigma: Uint128::new(
|
|
||||||
node_reward_result
|
|
||||||
.sigma()
|
|
||||||
.checked_cast()
|
|
||||||
.ok_or(MixnetContractError::CastError)?,
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,17 +428,17 @@ impl MixNodeBond {
|
|||||||
&self,
|
&self,
|
||||||
params: &RewardParams,
|
params: &RewardParams,
|
||||||
) -> Result<(u64, u64, u64), MixnetContractError> {
|
) -> Result<(u64, u64, u64), MixnetContractError> {
|
||||||
let total_node_reward = self.reward(params);
|
let total_node_reward = self
|
||||||
|
.reward(params)
|
||||||
|
.reward()
|
||||||
|
.checked_to_num::<u128>()
|
||||||
|
.unwrap_or_default();
|
||||||
let operator_reward = self.operator_reward(params);
|
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
|
// Total reward has to be the sum of operator and delegator rewards
|
||||||
let delegators_reward = self.reward_delegation(self.total_delegation().amount, params);
|
let delegators_reward = total_node_reward - operator_reward;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
total_node_reward
|
total_node_reward.try_into()?,
|
||||||
.reward()
|
|
||||||
.checked_to_num::<u128>()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.try_into()?,
|
|
||||||
operator_reward.try_into()?,
|
operator_reward.try_into()?,
|
||||||
delegators_reward.try_into()?,
|
delegators_reward.try_into()?,
|
||||||
))
|
))
|
||||||
@@ -469,12 +471,16 @@ impl MixNodeBond {
|
|||||||
|
|
||||||
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
|
pub fn operator_reward(&self, params: &RewardParams) -> u128 {
|
||||||
let reward = self.reward(params);
|
let reward = self.reward(params);
|
||||||
|
if reward.sigma == 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let profit = if reward.reward < params.node.operator_cost() {
|
let profit = if reward.reward < params.node.operator_cost() {
|
||||||
U128::from_num(0u128)
|
U128::from_num(0u128)
|
||||||
} else {
|
} else {
|
||||||
reward.reward - params.node.operator_cost()
|
reward.reward - params.node.operator_cost()
|
||||||
};
|
};
|
||||||
let operator_base_reward = reward.reward.min(params.node.operator_cost());
|
let operator_base_reward = reward.reward.min(params.node.operator_cost());
|
||||||
|
// Div by zero checked above
|
||||||
let operator_reward = (self.profit_margin()
|
let operator_reward = (self.profit_margin()
|
||||||
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
|
+ (ONE - self.profit_margin()) * reward.lambda / reward.sigma)
|
||||||
* profit;
|
* profit;
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ pub struct InstantiateMsg {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ExecuteMsg {
|
pub enum ExecuteMsg {
|
||||||
|
UpdateRewardingValidatorAddress {
|
||||||
|
address: String,
|
||||||
|
},
|
||||||
InitEpoch {},
|
InitEpoch {},
|
||||||
ReconcileDelegations {},
|
ReconcileDelegations {},
|
||||||
CheckpointMixnodes {},
|
CheckpointMixnodes {},
|
||||||
@@ -101,6 +104,7 @@ pub enum ExecuteMsg {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum QueryMsg {
|
pub enum QueryMsg {
|
||||||
|
GetRewardingValidatorAddress {},
|
||||||
GetAllDelegationKeys {},
|
GetAllDelegationKeys {},
|
||||||
DebugGetAllDelegationValues {},
|
DebugGetAllDelegationValues {},
|
||||||
GetContractVersion {},
|
GetContractVersion {},
|
||||||
@@ -167,9 +171,18 @@ pub enum QueryMsg {
|
|||||||
QueryDelegatorReward {
|
QueryDelegatorReward {
|
||||||
address: String,
|
address: String,
|
||||||
mix_identity: IdentityKey,
|
mix_identity: IdentityKey,
|
||||||
|
proxy: Option<String>,
|
||||||
},
|
},
|
||||||
GetPendingDelegationEvents {
|
GetPendingDelegationEvents {
|
||||||
owner_address: String,
|
owner_address: String,
|
||||||
|
proxy_address: Option<String>,
|
||||||
|
},
|
||||||
|
GetCheckpointsForMixnode {
|
||||||
|
mix_identity: IdentityKey,
|
||||||
|
},
|
||||||
|
GetMixnodeAtHeight {
|
||||||
|
mix_identity: IdentityKey,
|
||||||
|
height: u64,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ impl NodeEpochRewards {
|
|||||||
self.epoch_id
|
self.epoch_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sigma(&self) -> Uint128 {
|
pub fn sigma(&self) -> U128 {
|
||||||
self.result.sigma()
|
self.result.sigma()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lambda(&self) -> Uint128 {
|
pub fn lambda(&self) -> U128 {
|
||||||
self.result.lambda()
|
self.result.lambda()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,20 +47,19 @@ impl NodeEpochRewards {
|
|||||||
|
|
||||||
pub fn node_profit(&self) -> U128 {
|
pub fn node_profit(&self) -> U128 {
|
||||||
let reward = U128::from_num(self.reward().u128());
|
let reward = U128::from_num(self.reward().u128());
|
||||||
if reward < self.operator_cost() {
|
// if operating cost is higher then the reward node profit is 0
|
||||||
U128::from_num(0u128)
|
reward.saturating_sub(self.operator_cost())
|
||||||
} else {
|
|
||||||
reward - self.operator_cost()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
|
pub fn operator_reward(&self, profit_margin: U128) -> Result<Uint128, MixnetContractError> {
|
||||||
let reward = self.node_profit();
|
let reward = self.node_profit();
|
||||||
let operator_base_reward = reward.min(self.operator_cost());
|
let operator_base_reward = reward.min(self.operator_cost());
|
||||||
let operator_reward = (profit_margin
|
let div_by_zero_check = if let Some(value) = self.lambda().checked_div(self.sigma()) {
|
||||||
+ (ONE - profit_margin) * U128::from_num(self.lambda().u128())
|
value
|
||||||
/ U128::from_num(self.sigma().u128()))
|
} else {
|
||||||
* reward;
|
return Err(MixnetContractError::DivisionByZero);
|
||||||
|
};
|
||||||
|
let operator_reward = (profit_margin + (ONE - profit_margin) * div_by_zero_check) * reward;
|
||||||
|
|
||||||
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
|
let reward = (operator_reward + operator_base_reward).max(U128::from_num(0u128));
|
||||||
|
|
||||||
@@ -82,9 +81,15 @@ impl NodeEpochRewards {
|
|||||||
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
|
let circulating_supply = U128::from_num(epoch_reward_params.circulating_supply());
|
||||||
|
|
||||||
let scaled_delegation_amount = delegation_amount / circulating_supply;
|
let scaled_delegation_amount = delegation_amount / circulating_supply;
|
||||||
let delegator_reward = (ONE - profit_margin) * scaled_delegation_amount
|
|
||||||
/ U128::from_num(self.sigma().u128())
|
let check_div_by_zero =
|
||||||
* self.node_profit();
|
if let Some(value) = scaled_delegation_amount.checked_div(self.sigma()) {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(MixnetContractError::DivisionByZero);
|
||||||
|
};
|
||||||
|
|
||||||
|
let delegator_reward = (ONE - profit_margin) * check_div_by_zero * self.node_profit();
|
||||||
|
|
||||||
let reward = delegator_reward.max(U128::ZERO);
|
let reward = delegator_reward.max(U128::ZERO);
|
||||||
if let Some(int_reward) = reward.checked_cast() {
|
if let Some(int_reward) = reward.checked_cast() {
|
||||||
@@ -201,6 +206,11 @@ impl RewardParams {
|
|||||||
let denom = self.active_set_work_factor() * U128::from_num(self.rewarded_set_size())
|
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());
|
- (self.active_set_work_factor() - ONE) * U128::from_num(self.idle_nodes().u128());
|
||||||
|
|
||||||
|
if denom == 0 {
|
||||||
|
return U128::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Div by zero checked above
|
||||||
if self.in_active_set() {
|
if self.in_active_set() {
|
||||||
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
// work_active = factor / (factor * self.network.k[month] - (factor - 1) * idle_nodes)
|
||||||
self.active_set_work_factor() / denom * self.rewarded_set_size()
|
self.active_set_work_factor() / denom * self.rewarded_set_size()
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmwasm-std = "1.0.0-beta6"
|
cosmwasm-std = "1.0.0-beta8"
|
||||||
mixnet-contract-common = { path = "../mixnet-contract" }
|
mixnet-contract-common = { path = "../mixnet-contract" }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
schemars = "0.8"
|
schemars = "0.8"
|
||||||
cw-storage-plus = "0.13.1"
|
cw-storage-plus = "0.13.2"
|
||||||
config = { path = "../../config" }
|
config = { path = "../../config" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ impl VestingSpecification {
|
|||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum ExecuteMsg {
|
pub enum ExecuteMsg {
|
||||||
|
CompoundDelegatorReward {
|
||||||
|
mix_identity: String,
|
||||||
|
},
|
||||||
|
CompoundOperatorReward {},
|
||||||
UpdateMixnodeConfig {
|
UpdateMixnodeConfig {
|
||||||
profit_margin_percent: u8,
|
profit_margin_percent: u8,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "credential-storage"
|
||||||
|
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 = { version = "0.1.51" }
|
||||||
|
nymcoconut = { path = "../nymcoconut" }
|
||||||
|
|
||||||
|
log = "0.4"
|
||||||
|
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||||
|
thiserror = "1.0"
|
||||||
|
tokio = { version = "1.4", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
|
||||||
|
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||||
|
tokio = { version = "1.4", features = ["rt-multi-thread", "macros"] }
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let out_dir = env::var("OUT_DIR").unwrap();
|
||||||
|
let database_path = format!("{}/coconut-credential-example.sqlite", out_dir);
|
||||||
|
|
||||||
|
let mut conn = SqliteConnection::connect(&*format!("sqlite://{}?mode=rwc", database_path))
|
||||||
|
.await
|
||||||
|
.expect("Failed to create SQLx database connection");
|
||||||
|
|
||||||
|
sqlx::migrate!("./migrations")
|
||||||
|
.run(&mut conn)
|
||||||
|
.await
|
||||||
|
.expect("Failed to perform SQLx migrations");
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
|
||||||
|
|
||||||
|
#[cfg(target_family = "windows")]
|
||||||
|
// for some strange reason we need to add a leading `/` to the windows path even though it's
|
||||||
|
// not a valid windows path... but hey, it works...
|
||||||
|
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
CREATE TABLE coconut_credentials
|
||||||
|
(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
voucher_value TEXT NOT NULL,
|
||||||
|
voucher_info TEXT NOT NULL,
|
||||||
|
serial_number TEXT NOT NULL,
|
||||||
|
binding_number TEXT NOT NULL,
|
||||||
|
signature TEXT NOT NULL UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE erc20_credentials
|
||||||
|
(
|
||||||
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
public_key TEXT NOT NULL,
|
||||||
|
private_key TEXT NOT NULL,
|
||||||
|
consumed BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::models::CoconutCredential;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct CoconutCredentialManager {
|
||||||
|
connection_pool: sqlx::SqlitePool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoconutCredentialManager {
|
||||||
|
/// Creates new instance of the `CoconutCredentialManager` with the provided sqlite connection pool.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `connection_pool`: database connection pool to use.
|
||||||
|
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||||
|
CoconutCredentialManager { connection_pool }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts provided signature into the database.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `voucher_value`: Plaintext bandwidth value of the credential.
|
||||||
|
/// * `voucher_info`: Plaintext information of the credential.
|
||||||
|
/// * `serial_number`: Base58 representation of the serial number attribute.
|
||||||
|
/// * `binding_number`: Base58 representation of the binding number attribute.
|
||||||
|
/// * `signature`: Coconut credential in the form of a signature.
|
||||||
|
pub(crate) async fn insert_coconut_credential(
|
||||||
|
&self,
|
||||||
|
voucher_value: String,
|
||||||
|
voucher_info: String,
|
||||||
|
serial_number: String,
|
||||||
|
binding_number: String,
|
||||||
|
signature: String,
|
||||||
|
) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO coconut_credentials(voucher_value, voucher_info, serial_number, binding_number, signature) VALUES (?, ?, ?, ?, ?)",
|
||||||
|
voucher_value, voucher_info, serial_number, binding_number, signature
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve one of the stored, unused credentials.
|
||||||
|
pub(crate) async fn get_next_coconut_credential(
|
||||||
|
&self,
|
||||||
|
) -> Result<CoconutCredential, sqlx::Error> {
|
||||||
|
sqlx::query_as!(CoconutCredential, "SELECT * FROM coconut_credentials")
|
||||||
|
.fetch_one(&self.connection_pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes from the database the specified credential.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `id`: Database id.
|
||||||
|
pub(crate) async fn remove_coconut_credential(&self, id: i64) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!("DELETE FROM coconut_credentials WHERE id = ?", id)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::models::ERC20Credential;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct ERC20CredentialManager {
|
||||||
|
connection_pool: sqlx::SqlitePool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ERC20CredentialManager {
|
||||||
|
/// Creates new instance of the `ERC20CredentialManager` with the provided sqlite connection pool.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `connection_pool`: database connection pool to use.
|
||||||
|
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||||
|
ERC20CredentialManager { connection_pool }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts provided signature into the database.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `public_key`: Base58 representation of a public key.
|
||||||
|
/// * `private_key`: Base58 representation of a private key.
|
||||||
|
pub(crate) async fn insert_erc20_credential(
|
||||||
|
&self,
|
||||||
|
public_key: String,
|
||||||
|
private_key: String,
|
||||||
|
) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
"INSERT INTO erc20_credentials(public_key, private_key, consumed) VALUES (?, ?, ?)",
|
||||||
|
public_key,
|
||||||
|
private_key,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to retrieve one of the stored, unused credentials.
|
||||||
|
pub(crate) async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, sqlx::Error> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
ERC20Credential,
|
||||||
|
"SELECT * FROM erc20_credentials WHERE consumed = false"
|
||||||
|
)
|
||||||
|
.fetch_one(&self.connection_pool)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark a credential as being consumed.
|
||||||
|
pub(crate) async fn consume_erc20_credential(
|
||||||
|
&self,
|
||||||
|
public_key: String,
|
||||||
|
) -> Result<(), sqlx::Error> {
|
||||||
|
sqlx::query!(
|
||||||
|
r#"
|
||||||
|
UPDATE erc20_credentials
|
||||||
|
SET consumed = true
|
||||||
|
WHERE public_key = ?
|
||||||
|
"#,
|
||||||
|
public_key
|
||||||
|
)
|
||||||
|
.execute(&self.connection_pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum StorageError {
|
||||||
|
#[error("Database experienced an internal error - {0}")]
|
||||||
|
InternalDatabaseError(#[from] sqlx::Error),
|
||||||
|
|
||||||
|
#[error("Failed to perform database migration - {0}")]
|
||||||
|
MigrationError(#[from] sqlx::migrate::MigrateError),
|
||||||
|
|
||||||
|
#[error("Inconsistent data in database")]
|
||||||
|
InconsistentData,
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::coconut::CoconutCredentialManager;
|
||||||
|
use crate::erc20::ERC20CredentialManager;
|
||||||
|
use crate::error::StorageError;
|
||||||
|
use crate::storage::Storage;
|
||||||
|
|
||||||
|
use crate::models::{CoconutCredential, ERC20Credential};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use log::{debug, error};
|
||||||
|
use sqlx::ConnectOptions;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
mod coconut;
|
||||||
|
mod erc20;
|
||||||
|
pub mod error;
|
||||||
|
mod models;
|
||||||
|
pub mod storage;
|
||||||
|
|
||||||
|
// note that clone here is fine as upon cloning the same underlying pool will be used
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PersistentStorage {
|
||||||
|
coconut_credential_manager: CoconutCredentialManager,
|
||||||
|
erc20_credential_manager: ERC20CredentialManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersistentStorage {
|
||||||
|
/// Initialises `PersistentStorage` using the provided path.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `database_path`: path to the database.
|
||||||
|
pub async fn init<P: AsRef<Path> + Send>(database_path: P) -> Result<Self, StorageError> {
|
||||||
|
debug!(
|
||||||
|
"Attempting to connect to database {:?}",
|
||||||
|
database_path.as_ref().as_os_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||||
|
.filename(database_path)
|
||||||
|
.create_if_missing(true);
|
||||||
|
|
||||||
|
opts.disable_statement_logging();
|
||||||
|
|
||||||
|
let connection_pool = match sqlx::SqlitePool::connect_with(opts).await {
|
||||||
|
Ok(db) => db,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to connect to SQLx database: {}", err);
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = sqlx::migrate!("./migrations").run(&connection_pool).await {
|
||||||
|
error!("Failed to perform migration on the SQLx database: {}", err);
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PersistentStorage {
|
||||||
|
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
|
||||||
|
erc20_credential_manager: ERC20CredentialManager::new(connection_pool),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Storage for PersistentStorage {
|
||||||
|
async fn insert_coconut_credential(
|
||||||
|
&self,
|
||||||
|
voucher_value: String,
|
||||||
|
voucher_info: String,
|
||||||
|
serial_number: String,
|
||||||
|
binding_number: String,
|
||||||
|
signature: String,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
self.coconut_credential_manager
|
||||||
|
.insert_coconut_credential(
|
||||||
|
voucher_value,
|
||||||
|
voucher_info,
|
||||||
|
serial_number,
|
||||||
|
binding_number,
|
||||||
|
signature,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError> {
|
||||||
|
let credential = self
|
||||||
|
.coconut_credential_manager
|
||||||
|
.get_next_coconut_credential()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(credential)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError> {
|
||||||
|
self.coconut_credential_manager
|
||||||
|
.remove_coconut_credential(id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn insert_erc20_credential(
|
||||||
|
&self,
|
||||||
|
public_key: String,
|
||||||
|
private_key: String,
|
||||||
|
) -> Result<(), StorageError> {
|
||||||
|
self.erc20_credential_manager
|
||||||
|
.insert_erc20_credential(public_key, private_key)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError> {
|
||||||
|
let credential = self
|
||||||
|
.erc20_credential_manager
|
||||||
|
.get_next_erc20_credential()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(credential)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError> {
|
||||||
|
let credential = self
|
||||||
|
.erc20_credential_manager
|
||||||
|
.consume_erc20_credential(public_key)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(credential)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn initialise_storage(path: PathBuf) -> PersistentStorage {
|
||||||
|
match PersistentStorage::init(path).await {
|
||||||
|
Err(err) => panic!("failed to initialise credential storage - {}", err),
|
||||||
|
Ok(storage) => storage,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
pub struct CoconutCredential {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub id: i64,
|
||||||
|
pub voucher_value: String,
|
||||||
|
pub voucher_info: String,
|
||||||
|
pub serial_number: String,
|
||||||
|
pub binding_number: String,
|
||||||
|
pub signature: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ERC20Credential {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub id: i64,
|
||||||
|
pub public_key: String,
|
||||||
|
pub private_key: String,
|
||||||
|
pub consumed: bool,
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::models::{CoconutCredential, ERC20Credential};
|
||||||
|
use crate::StorageError;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Storage: Send + Sync {
|
||||||
|
/// Inserts provided signature into the database.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `signature`: Coconut credential in the form of a signature.
|
||||||
|
async fn insert_coconut_credential(
|
||||||
|
&self,
|
||||||
|
voucher_value: String,
|
||||||
|
voucher_info: String,
|
||||||
|
serial_number: String,
|
||||||
|
binding_number: String,
|
||||||
|
signature: String,
|
||||||
|
) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
/// Tries to retrieve one of the stored, unused credentials.
|
||||||
|
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||||
|
|
||||||
|
/// Removes from the database the specified credential.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `signature`: Coconut credential in the form of a signature.
|
||||||
|
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
/// Inserts provided signature into the database.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `public_key`: Base58 representation of a public key.
|
||||||
|
/// * `private_key`: Base58 representation of a private key.
|
||||||
|
async fn insert_erc20_credential(
|
||||||
|
&self,
|
||||||
|
public_key: String,
|
||||||
|
private_key: String,
|
||||||
|
) -> Result<(), StorageError>;
|
||||||
|
|
||||||
|
/// Tries to retrieve one of the stored, unused credential data.
|
||||||
|
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError>;
|
||||||
|
|
||||||
|
/// Mark a credential as being consumed.
|
||||||
|
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError>;
|
||||||
|
}
|
||||||
@@ -7,11 +7,18 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||||
|
cosmrs = { version = "0.4.1", optional = true }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
|
|
||||||
# I guess temporarily until we get serde support in coconut up and running
|
# I guess temporarily until we get serde support in coconut up and running
|
||||||
coconut-interface = { path = "../coconut-interface" }
|
coconut-interface = { path = "../coconut-interface" }
|
||||||
crypto = { path = "../crypto", features = ["asymmetric"] }
|
crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] }
|
||||||
network-defaults = { path = "../network-defaults" }
|
network-defaults = { path = "../network-defaults" }
|
||||||
validator-client = { path = "../client-libs/validator-client" }
|
validator-client = { path = "../client-libs/validator-client" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
rand = "0.7.3"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
coconut = ["cosmrs"]
|
||||||
@@ -7,71 +7,238 @@
|
|||||||
// it's the simplest possible case
|
// it's the simplest possible case
|
||||||
|
|
||||||
use coconut_interface::{
|
use coconut_interface::{
|
||||||
Credential, Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey,
|
hash_to_scalar, prepare_blind_sign, Attribute, BlindSignRequest, Credential, Parameters,
|
||||||
|
PrivateAttribute, PublicAttribute, Signature, VerificationKey,
|
||||||
};
|
};
|
||||||
use network_defaults::BANDWIDTH_VALUE;
|
use crypto::asymmetric::{encryption, identity};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
|
use cosmrs::tx::Hash;
|
||||||
|
|
||||||
|
use super::utils::prepare_credential_for_spending;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
|
||||||
use super::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
|
|
||||||
|
|
||||||
pub const PUBLIC_ATTRIBUTES: u32 = 2;
|
pub const PUBLIC_ATTRIBUTES: u32 = 2;
|
||||||
pub const PRIVATE_ATTRIBUTES: u32 = 2;
|
pub const PRIVATE_ATTRIBUTES: u32 = 2;
|
||||||
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
|
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
|
// 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
|
// 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
|
// 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.
|
// 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> {
|
pub fn get_public_attributes(&self) -> Vec<PublicAttribute> {
|
||||||
vec![self.voucher_value, self.voucher_info]
|
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> {
|
pub fn get_private_attributes(&self) -> Vec<PrivateAttribute> {
|
||||||
vec![self.serial_number, self.binding_number]
|
vec![self.serial_number, self.binding_number]
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this definitely has to be moved somewhere else. It's just a temporary solution
|
pub fn sign(&self, request: &BlindSignRequest) -> identity::Signature {
|
||||||
pub async fn obtain_signature(
|
let mut message = request.to_bytes();
|
||||||
params: &Parameters,
|
message.extend_from_slice(self.tx_hash.to_string().as_bytes());
|
||||||
attributes: &BandwidthVoucherAttributes,
|
self.signing_key.sign(&message)
|
||||||
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 prepare_for_spending(
|
pub fn prepare_for_spending(
|
||||||
raw_identity: &[u8],
|
voucher_value: u64,
|
||||||
|
voucher_info: String,
|
||||||
|
serial_number: PrivateAttribute,
|
||||||
|
binding_number: PrivateAttribute,
|
||||||
signature: &Signature,
|
signature: &Signature,
|
||||||
attributes: &BandwidthVoucherAttributes,
|
|
||||||
verification_key: &VerificationKey,
|
verification_key: &VerificationKey,
|
||||||
) -> Result<Credential, Error> {
|
) -> Result<Credential, Error> {
|
||||||
let public_attributes = vec![
|
|
||||||
raw_identity.to_vec(),
|
|
||||||
BANDWIDTH_VALUE.to_be_bytes().to_vec(),
|
|
||||||
];
|
|
||||||
|
|
||||||
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
|
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
|
||||||
|
|
||||||
prepare_credential_for_spending(
|
prepare_credential_for_spending(
|
||||||
¶ms,
|
¶ms,
|
||||||
public_attributes,
|
voucher_value,
|
||||||
attributes.serial_number,
|
voucher_info,
|
||||||
attributes.binding_number,
|
serial_number,
|
||||||
|
binding_number,
|
||||||
signature,
|
signature,
|
||||||
verification_key,
|
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(
|
||||||
|
¶ms,
|
||||||
|
"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()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user