Compare commits
100 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a31f7de7c9 | |||
| e7c8de8eec | |||
| e27a2ae857 | |||
| 0de0ad91bd | |||
| db2769b59b | |||
| d1c12c0b22 | |||
| 2e0c0bfdc5 | |||
| ccf97e8570 | |||
| 56a8b82c4d | |||
| 876beed97d | |||
| de450f87de | |||
| 2d55d09f24 | |||
| aa8926ed5d | |||
| 5df820db6c | |||
| 20c144c236 | |||
| 6604d927c5 | |||
| 4fedf05b64 | |||
| fa00f85a31 | |||
| abf5a2d307 | |||
| 8e0000cb0b | |||
| cc27911671 | |||
| b598f1d1db | |||
| d4f10c1d68 | |||
| 1d7ff40d02 | |||
| 72703b36e1 | |||
| ecf7a40010 | |||
| 272e9187d2 | |||
| 69a1f3773c | |||
| 15c97b1cae | |||
| 2660ef59f7 | |||
| 12a06d9dfe | |||
| 6399cd24c8 | |||
| f30b5162e8 | |||
| 229329ae4c | |||
| a3bb5e466d | |||
| 65d893a70b | |||
| e0944e5238 | |||
| b451d4b709 | |||
| 1507e48158 | |||
| 6e7a7ed4a6 | |||
| af4801ac47 | |||
| b748c55d4e | |||
| 84e5bfc286 | |||
| 96bd9ecf49 | |||
| 092851976f | |||
| 9da1f82cca | |||
| 8c60927fe2 | |||
| aa6f5ad664 | |||
| 201b5cc56f | |||
| 510553a697 | |||
| b81967c9f5 | |||
| fadff7888b | |||
| 24854f9254 | |||
| 8ea7726e81 | |||
| ea32a897b2 | |||
| 5d7a14c5ab | |||
| cad68b8d6c | |||
| 1d125ed97f | |||
| 36d4ec5a72 | |||
| 44981b8621 | |||
| bd68797432 | |||
| 636759789c | |||
| c03a859223 | |||
| b269cdae31 | |||
| 2177a396b9 | |||
| 4c50dd64f4 | |||
| be9d3a48bb | |||
| 37d890c634 | |||
| 6287583f4b | |||
| c7760c6c10 | |||
| 2c0058c477 | |||
| 02fb2e56ca | |||
| 8b2c72eb91 | |||
| 805eb31728 | |||
| 7f45cbd5df | |||
| 8ce709bbb4 | |||
| 46db139f79 | |||
| 069c5bf332 | |||
| 54a4653e7d | |||
| ea1f689c71 | |||
| 3d60299b22 | |||
| 5b5063676f | |||
| 833556020d | |||
| aa00eb70d5 | |||
| 2caf79fba0 | |||
| eda1822653 | |||
| 0f1ff5ffd2 | |||
| 9512fc34f0 | |||
| 938eb2d0f0 | |||
| ede51fa589 | |||
| 7e0711ce12 | |||
| 822f40239f | |||
| 31149e6ba1 | |||
| ba233b6016 | |||
| 3d17ab6a4b | |||
| f22bb282ab | |||
| d907b6b81a | |||
| 1903938035 | |||
| bb646c4f6a | |||
| 4c65ef3a2f |
@@ -9,28 +9,14 @@ on:
|
||||
- 'explorer/**'
|
||||
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from build_matrix_includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/build_matrix_includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
build:
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.os == 'windows-latest' }}
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
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
|
||||
@@ -39,7 +25,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
@@ -63,25 +49,16 @@ jobs:
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
# if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all binaries with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
@@ -96,7 +73,6 @@ jobs:
|
||||
|
||||
- name: Run clippy with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --features=coconut -- -D warnings
|
||||
args: --features=coconut -- -D warnings
|
||||
|
||||
@@ -10,41 +10,10 @@
|
||||
"rust":"stable",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"pull_request"
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -41,9 +41,10 @@ jobs:
|
||||
EXCLUDE: "/dist/, /node_modules/"
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files/messages
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: network-explorer
|
||||
NYM_PROJECT_NAME: "Network Explorer"
|
||||
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
|
||||
NYM_CI_WWW_LOCATION: "network-explorer-${{ env.GITHUB_REF_SLUG }}"
|
||||
@@ -56,4 +57,4 @@ jobs:
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/messages/entry_point_notifications.sh
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
name: Nightly builds
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '14 4 * * *'
|
||||
jobs:
|
||||
matrix_prep:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
# creates the matrix strategy from nightly_build_matrix_includes.json
|
||||
- uses: actions/checkout@v2
|
||||
- id: set-matrix
|
||||
uses: JoshuaTheMiller/conditional-build-matrix@main
|
||||
with:
|
||||
inputFile: '.github/workflows/nightly_build_matrix_includes.json'
|
||||
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
|
||||
build:
|
||||
needs: matrix_prep
|
||||
strategy:
|
||||
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.rust == 'nightly' || matrix.rust == 'beta' || matrix.rust == 'stable' }}
|
||||
steps:
|
||||
- name: Install Dependencies (Linux)
|
||||
run: sudo apt-get update && sudo apt-get install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev squashfs-tools
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Build all binaries
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all
|
||||
|
||||
- name: Run all tests
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all
|
||||
|
||||
- name: Check formatting
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
name: Clippy checks
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-features
|
||||
|
||||
- name: Run clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
# COCONUT stuff
|
||||
- name: Reclaim some disk space (because Windows is being annoying)
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
with:
|
||||
command: clean
|
||||
|
||||
- name: Build all binaries with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --all --features=coconut
|
||||
|
||||
- name: Run all tests with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all --features=coconut
|
||||
|
||||
- name: Run clippy with coconut enabled
|
||||
uses: actions-rs/cargo@v1
|
||||
if: ${{ matrix.rust != 'nightly' }}
|
||||
with:
|
||||
command: clippy
|
||||
args: --features=coconut -- -D warnings
|
||||
notification:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Collect jobs status
|
||||
uses: technote-space/workflow-conclusion-action@v2
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@v2
|
||||
- name: Keybase - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files
|
||||
- name: Keybase - Send Notification
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: nightly
|
||||
NYM_PROJECT_NAME: "Nym nightly build"
|
||||
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-nightly"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
@@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"stable",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"beta",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
|
||||
{
|
||||
"os":"ubuntu-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"windows-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
},
|
||||
{
|
||||
"os":"macos-latest",
|
||||
"rust":"nightly",
|
||||
"runOnEvent":"schedule"
|
||||
}
|
||||
]
|
||||
@@ -10,7 +10,10 @@ on:
|
||||
|
||||
jobs:
|
||||
nym-wallet-types:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [ self-hosted, custom-linux-exoscale ]
|
||||
# Enable sccache
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
steps:
|
||||
- name: Prepare
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
KEYBASE_NYM_CHANNEL=
|
||||
KEYBASE_NYMBOT_USERNAME=
|
||||
KEYBASE_NYMBOT_PAPERKEY=
|
||||
|
||||
NYM_NOTIFICATION_KIND=nightly
|
||||
NYM_PROJECT_NAME=Nightly Build
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Custom GitHub Actions mock env vars
|
||||
IS_SUCCESS=true
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# GitHub Actions context mock env vars
|
||||
GITHUB_SHA=abcdef
|
||||
GITHUB_RUN_ID=123456
|
||||
GITHUB_REPOSITORY=nymtech/nym
|
||||
GITHUB_SERVER_URL=https://github.com
|
||||
GIT_BRANCH_NAME=feature/testing-support-files
|
||||
GIT_BRANCH=feature/testing-support-files
|
||||
GIT_COMMIT_MESSAGE=This is the commit message
|
||||
GITHUB_ACTOR=octocat
|
||||
|
||||
# add a Personal Access Token (PAT) generated from GitHub here for use in testing
|
||||
GITHUB_TOKEN=
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Network Explorer
|
||||
NYM_CI_WWW_LOCATION=some-branch
|
||||
NYM_CI_WWW_BASE=example.com
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# Nightly builds
|
||||
WORKFLOW_CONCLUSION=success
|
||||
|
||||
SHOW_DEBUG=true
|
||||
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
.idea
|
||||
|
||||
# don't commit the lock file to avoid cross-platform issues
|
||||
package-lock.json
|
||||
@@ -0,0 +1 @@
|
||||
16
|
||||
@@ -0,0 +1,58 @@
|
||||
# GitHub Actions Support Files
|
||||
|
||||
This is a collection of scripts and files to support GitHub Actions.
|
||||
|
||||
## Sending Notifications
|
||||
|
||||
These scripts send CI notifications to Keybase by creating messages from templates and env vars passed from GitHub Actions.
|
||||
|
||||
### Adding notifications to a GitHub Action
|
||||
|
||||
```
|
||||
jobs:
|
||||
build:
|
||||
...
|
||||
- name: Notifications - Node Install
|
||||
run: npm install
|
||||
working-directory: .github/workflows/support-files/notifications
|
||||
- name: Notifications - Send
|
||||
env:
|
||||
NYM_NOTIFICATION_KIND: "my-component"
|
||||
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-network-explorer"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
```
|
||||
|
||||
Notifications are run by adding the snippet above to a GitHub Action, and:
|
||||
|
||||
1. Installing node packages needed at run time
|
||||
2. Set the env vars as required:
|
||||
- `NYM_NOTIFICATION_KIND` matches the directory in `.github/workflows/support-files/${NYM_NOTIFICATION_KIND}` to provide the templates and extra scripting in `index.js`
|
||||
- Keybase credentials, channel and other env vars for the status of the build and repo
|
||||
3. Replacing the default entry point shell script on the `keybaseio/client:stable-node` docker image to run `.github/workflows/support-files/notifications/entry_point.sh`
|
||||
|
||||
### Running locally
|
||||
|
||||
You will need:
|
||||
- Node 16 LTS
|
||||
- npm
|
||||
|
||||
Copy `.github/workflows/support-files/.env.example` to `.github/workflows/support-files/.env` and valid Keybase credentials.
|
||||
|
||||
Then run `npm install` to get dependencies.
|
||||
|
||||
Start development mode for the notification type you want either by passing the value as an env var called `NYM_NOTIFICATION_KIND` or set the `.env` file values correctly.
|
||||
|
||||
```bash
|
||||
cd .github/workflows/support-files
|
||||
npm install
|
||||
cp .env.example .env
|
||||
vi .env
|
||||
npm run dev
|
||||
```
|
||||
@@ -0,0 +1 @@
|
||||
require('./notifications/send_message');
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
.idea
|
||||
@@ -1,69 +0,0 @@
|
||||
const Bot = require('keybase-bot');
|
||||
const Handlebars = require('handlebars');
|
||||
const fs = require('fs');
|
||||
|
||||
async function main() {
|
||||
const data = { env: process.env };
|
||||
// const data = { ...PASTE TEST DATA HERE ... }; // -- DEV: uncomment to set test data
|
||||
|
||||
// validation of environment
|
||||
if(!(process.env.NYM_PROJECT_NAME || data.env.NYM_PROJECT_NAME)) {
|
||||
throw new Error('Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages');
|
||||
}
|
||||
const keybaseChannel = process.env.KEYBASE_NYM_CHANNEL || data.env.KEYBASE_NYM_CHANNEL;
|
||||
if(!keybaseChannel) {
|
||||
throw new Error('Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message');
|
||||
}
|
||||
|
||||
// extract the git branch name
|
||||
const GIT_BRANCH_NAME = (process.env.GITHUB_REF || data.env.GITHUB_REF).split('/').slice(2).join('/');
|
||||
|
||||
data.env.GIT_BRANCH_NAME = GIT_BRANCH_NAME;
|
||||
const source = fs
|
||||
.readFileSync(process.env.IS_SUCCESS === 'true' ? 'success' : 'failure')
|
||||
.toString();
|
||||
const template = Handlebars.compile(source);
|
||||
const result = template(data);
|
||||
|
||||
// -- DEV: uncomment to show what is available in the handlebars template / show the result
|
||||
// console.dir({ data }, { depth: null });
|
||||
// console.log(result);
|
||||
|
||||
const bot = new Bot();
|
||||
try {
|
||||
const username = process.env.KEYBASE_NYMBOT_USERNAME;
|
||||
const paperkey = process.env.KEYBASE_NYMBOT_PAPERKEY;
|
||||
|
||||
if(!username) {
|
||||
throw new Error('Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME');
|
||||
}
|
||||
if(!paperkey) {
|
||||
throw new Error('Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY');
|
||||
}
|
||||
|
||||
console.log(`Initialising keybase with user "${username}" and key: "${'*'.repeat(paperkey.length)}"...`);
|
||||
await bot.init(username, paperkey, { verbose: false });
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: keybaseChannel,
|
||||
topic_type: 'CHAT',
|
||||
};
|
||||
const message = {
|
||||
body: result,
|
||||
};
|
||||
|
||||
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
|
||||
await bot.chat.send(channel, message);
|
||||
|
||||
console.log('Message sent!');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exitCode = -1;
|
||||
} finally {
|
||||
await bot.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -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,
|
||||
};
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
> `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:
|
||||
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
|
||||
```
|
||||
{{ env.GIT_COMMIT_MESSAGE }}
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
const Handlebars = require('handlebars');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Octokit, App } = require('octokit');
|
||||
|
||||
async function addToContextAndValidate(context) {
|
||||
if (!context.env.WORKFLOW_CONCLUSION) {
|
||||
throw new Error('Please ensure the env var WORKFLOW_CONCLUSION is set');
|
||||
}
|
||||
if (!context.env.GITHUB_TOKEN) {
|
||||
throw new Error('Please ensure the env var GITHUB_TOKEN is set');
|
||||
}
|
||||
if (!context.env.GITHUB_RUN_ID) {
|
||||
throw new Error('Please ensure the env var GITHUB_RUN_ID is set');
|
||||
}
|
||||
if (!context.env.GITHUB_REPOSITORY) {
|
||||
throw new Error('Please ensure the env var GITHUB_REPOSITORY is set');
|
||||
}
|
||||
}
|
||||
|
||||
async function getMessageBody(context) {
|
||||
const source = fs
|
||||
.readFileSync(
|
||||
context.env.WORKFLOW_CONCLUSION === 'success'
|
||||
? path.resolve(__dirname, 'templates', 'success')
|
||||
: path.resolve(__dirname, 'templates', 'failure'),
|
||||
)
|
||||
.toString();
|
||||
const template = Handlebars.compile(source);
|
||||
|
||||
// get job details from GitHub API
|
||||
const octokit = new Octokit({ auth: context.env.GITHUB_TOKEN });
|
||||
const [owner, repo] = context.env.GITHUB_REPOSITORY.split('/');
|
||||
const {
|
||||
data: { jobs },
|
||||
} = await octokit.rest.actions.listJobsForWorkflowRun({
|
||||
run_id: context.env.GITHUB_RUN_ID,
|
||||
owner,
|
||||
repo,
|
||||
});
|
||||
|
||||
// uncomment this to see what is available for each job
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
console.dir(jobs, { depth: null });
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
a sample of the response is:
|
||||
|
||||
{
|
||||
total_count: 10,
|
||||
jobs: [
|
||||
{
|
||||
id: 5182940024,
|
||||
run_id: 1840752095,
|
||||
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
|
||||
run_attempt: 1,
|
||||
node_id: 'CR_kwDODdjOis8AAAABNO1jeA',
|
||||
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
|
||||
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182940024',
|
||||
html_url: 'https://github.com/nymtech/nym/runs/5182940024?check_suite_focus=true',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
started_at: '2022-02-14T11:28:34Z',
|
||||
completed_at: '2022-02-14T11:28:38Z',
|
||||
name: 'matrix_prep',
|
||||
steps: [
|
||||
{
|
||||
name: 'Set up job',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 1,
|
||||
started_at: '2022-02-14T13:28:34.000+02:00',
|
||||
completed_at: '2022-02-14T13:28:36.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Run actions/checkout@v2',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 2,
|
||||
started_at: '2022-02-14T13:28:36.000+02:00',
|
||||
completed_at: '2022-02-14T13:28:37.000+02:00'
|
||||
},
|
||||
...
|
||||
],
|
||||
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182940024',
|
||||
labels: [ 'ubuntu-latest' ],
|
||||
runner_id: 1,
|
||||
runner_name: 'Hosted Agent',
|
||||
runner_group_id: 2,
|
||||
runner_group_name: 'GitHub Actions'
|
||||
},
|
||||
{
|
||||
id: 5182943473,
|
||||
run_id: 1840752095,
|
||||
run_url: 'https://api.github.com/repos/nymtech/nym/actions/runs/1840752095',
|
||||
run_attempt: 1,
|
||||
node_id: 'CR_kwDODdjOis8AAAABNO1w8Q',
|
||||
head_sha: 'aa00eb70d57751bfa556bd3602df87c7473367fc',
|
||||
url: 'https://api.github.com/repos/nymtech/nym/actions/jobs/5182943473',
|
||||
html_url: 'https://github.com/nymtech/nym/runs/5182943473?check_suite_focus=true',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
started_at: '2022-02-14T11:29:04Z',
|
||||
completed_at: '2022-02-14T11:55:45Z',
|
||||
name: 'build (macos-latest, stable, schedule)',
|
||||
steps: [
|
||||
{
|
||||
name: 'Set up job',
|
||||
status: 'completed',
|
||||
conclusion: 'success',
|
||||
number: 1,
|
||||
started_at: '2022-02-14T13:29:04.000+02:00',
|
||||
completed_at: '2022-02-14T13:29:26.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Install Dependencies (Linux)',
|
||||
status: 'completed',
|
||||
conclusion: 'skipped',
|
||||
number: 2,
|
||||
started_at: '2022-02-14T13:29:26.000+02:00',
|
||||
completed_at: '2022-02-14T13:29:26.000+02:00'
|
||||
},
|
||||
{
|
||||
name: 'Keybase - Send Notification',
|
||||
status: 'completed',
|
||||
conclusion: 'failure',
|
||||
number: 15,
|
||||
started_at: '2022-02-14T13:55:44.000+02:00',
|
||||
completed_at: '2022-02-14T13:55:44.000+02:00'
|
||||
},
|
||||
],
|
||||
check_run_url: 'https://api.github.com/repos/nymtech/nym/check-runs/5182943473',
|
||||
labels: [ 'macos-latest' ],
|
||||
runner_id: 4,
|
||||
runner_name: 'GitHub Actions 4',
|
||||
runner_group_id: 2,
|
||||
runner_group_name: 'GitHub Actions'
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
*/
|
||||
|
||||
const jobResults = jobs
|
||||
.map((job) => {
|
||||
const icon = job.conclusion === 'success' ? '🟩' : '🟥';
|
||||
|
||||
// each job is converted into formatted markdown text
|
||||
return `${icon} ${job.conclusion}: ${job.name} - ${job.html_url}`;
|
||||
})
|
||||
// and join with newlines for display in the template
|
||||
.join('\n');
|
||||
|
||||
return template({ ...context, jobResults });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addToContextAndValidate,
|
||||
getMessageBody,
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }}
|
||||
> 🔴 **FAILURE** :cry:
|
||||
> `when` {{ timestamp }}
|
||||
> `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 }}
|
||||
|
||||
{{ jobResults }}
|
||||
@@ -0,0 +1,9 @@
|
||||
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
|
||||
> :rocket: {{ env.NYM_PROJECT_NAME }}
|
||||
> ✅ **SUCCESS**
|
||||
> `when` {{ timestamp }}
|
||||
> `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 }}
|
||||
|
||||
{{ jobResults }}
|
||||
@@ -0,0 +1,153 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const Bot = require('keybase-bot');
|
||||
|
||||
let context = {
|
||||
kinds: ['network-explorer', 'nightly'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Validate that all required env and context vars are available
|
||||
*/
|
||||
function validateContext() {
|
||||
if (!context.env.NYM_NOTIFICATION_KIND) {
|
||||
throw new Error(
|
||||
'Please set env var NYM_NOTIFICATION_KIND with the project kind that matches a directory in ".github/workflows/support-files"',
|
||||
);
|
||||
}
|
||||
if (!context.kinds.includes(context.env.NYM_NOTIFICATION_KIND)) {
|
||||
throw new Error(`Env var NYM_NOTIFICATION_KIND is not in ${context.kinds}`);
|
||||
}
|
||||
if (!context.env.NYM_PROJECT_NAME) {
|
||||
throw new Error(
|
||||
'Please set env var NYM_PROJECT_NAME with the project name for displaying in notification messages',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYM_CHANNEL) {
|
||||
throw new Error(
|
||||
'Please set env var KEYBASE_NYM_CHANNEL with the channel name for the notification message',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYMBOT_USERNAME) {
|
||||
throw new Error(
|
||||
'Username is not defined. Please set env var KEYBASE_NYMBOT_USERNAME',
|
||||
);
|
||||
}
|
||||
if (!context.env.KEYBASE_NYMBOT_PAPERKEY) {
|
||||
throw new Error(
|
||||
'Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a context that will be available in the templates for rendering notifications
|
||||
*/
|
||||
function createTemplateContext() {
|
||||
const options = { dateStyle: 'full', timeStyle: 'long' };
|
||||
context.timestamp = new Date().toLocaleString(undefined, options);
|
||||
|
||||
// add environment to template context and validate
|
||||
context.env = process.env;
|
||||
try {
|
||||
validateContext();
|
||||
} catch (e) {
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
// recursively print the context for easy debugging and rethrow the error
|
||||
console.dir({ context }, { depth: null });
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
context.kind = context.env.NYM_NOTIFICATION_KIND;
|
||||
|
||||
context.keybase = {
|
||||
channel: context.env.KEYBASE_NYM_CHANNEL,
|
||||
username: context.env.KEYBASE_NYMBOT_USERNAME,
|
||||
paperkey: context.env.KEYBASE_NYMBOT_PAPERKEY,
|
||||
};
|
||||
|
||||
if (!context.env.GIT_BRANCH_NAME) {
|
||||
context.env.GIT_BRANCH_NAME = context.env.GITHUB_REF.split('/')
|
||||
.slice(2)
|
||||
.join('/');
|
||||
}
|
||||
|
||||
context.status = process.env.IS_SUCCESS === 'true' ? 'success' : 'failure';
|
||||
}
|
||||
|
||||
async function sendKeybaseMessage(messageBody) {
|
||||
const bot = new Bot();
|
||||
try {
|
||||
console.log(
|
||||
`Initialising keybase with user "${
|
||||
context.keybase.username
|
||||
}" and key: "${'*'.repeat(context.keybase.paperkey.length)}"...`,
|
||||
);
|
||||
await bot.init(context.keybase.username, context.keybase.paperkey, {
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
const channel = {
|
||||
name: 'nymtech_bot',
|
||||
membersType: 'team',
|
||||
topicName: context.keybase.channel,
|
||||
topic_type: 'CHAT',
|
||||
};
|
||||
const message = {
|
||||
body: messageBody,
|
||||
};
|
||||
|
||||
console.log(`Sending to ${channel.name}#${channel.topicName}...`);
|
||||
await bot.chat.send(channel, message);
|
||||
|
||||
console.log('Message sent!');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
process.exitCode = -1;
|
||||
} finally {
|
||||
await bot.deinit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the `kind` set in the context to process the context and generate a notification message
|
||||
* @returns {Promise<string>} A string notification message body
|
||||
*/
|
||||
async function processKindScript() {
|
||||
const script = require(`../${context.kind}`);
|
||||
if (!script.addToContextAndValidate) {
|
||||
throw new Error(
|
||||
`"./${context.kind}/index.js" does not export a method called "async addToContextAndValidate(context)"`,
|
||||
);
|
||||
}
|
||||
if (!script.getMessageBody) {
|
||||
throw new Error(
|
||||
`"./${context.kind}/index.js" does not export a method called "async getMessageBody(context)"`,
|
||||
);
|
||||
}
|
||||
|
||||
// call the script to modify and validate the context
|
||||
await script.addToContextAndValidate(context);
|
||||
|
||||
// let the script create a message body and return the result as a string for sending
|
||||
return await script.getMessageBody(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function, as async so that await syntax is available
|
||||
*/
|
||||
async function main() {
|
||||
createTemplateContext();
|
||||
console.log(`Sending notification for kind "${context.kind}"...`);
|
||||
const messageBody = await processKindScript();
|
||||
if(process.env.SHOW_DEBUG) {
|
||||
console.log('-----------------------------------------');
|
||||
console.log(messageBody);
|
||||
console.log('-----------------------------------------');
|
||||
}
|
||||
await sendKeybaseMessage(messageBody);
|
||||
}
|
||||
|
||||
// call main function and let NodeJS handle the promise
|
||||
main();
|
||||
+5
-2
@@ -4,11 +4,14 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"format": "prettier --write send_message.js"
|
||||
"dev": "node dev.js",
|
||||
"format": "prettier --write **/*.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"keybase-bot": "^3.6.1"
|
||||
"keybase-bot": "^3.6.1",
|
||||
"octokit": "^1.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.3.2"
|
||||
Generated
+117
-11
@@ -68,16 +68,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.44"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
|
||||
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
|
||||
|
||||
[[package]]
|
||||
name = "app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bandwidth-claim-contract",
|
||||
"bip39",
|
||||
"coconut-interface",
|
||||
"cosmwasm-std",
|
||||
"credentials",
|
||||
"cw3-flex-multisig",
|
||||
"network-defaults",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
@@ -1002,9 +1007,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
version = "1.0.0-beta4"
|
||||
version = "1.0.0-beta5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f903ebbabc0d4880dbc76148efb8be8fc29fa4bf294c440c3d70da1c8bcafff7"
|
||||
checksum = "8904127a5b9e325ef5d6b2b3f997dcd74943cd35097139b1a4d15b1b6bccae66"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"ed25519-zebra",
|
||||
@@ -1015,18 +1020,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-derive"
|
||||
version = "1.0.0-beta4"
|
||||
version = "1.0.0-beta5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "832bebef577ecb394603de8e2bf0de429b74aa364e17dec18e15ce37e71b0cae"
|
||||
checksum = "a14364ac4d9d085867929d0cf3e94b1d2100121ce02c33c72961406830002613"
|
||||
dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-std"
|
||||
version = "1.0.0-beta4"
|
||||
version = "1.0.0-beta5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6238c45840cc9de5a39f0f619e3a4f7c38c5d2c6ac9e3e4d72751ee045e6d7da"
|
||||
checksum = "e2ece12e5bbde434b93937d7b2107e6291f11d69ffa72398c50e8bab41d451d3"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"cosmwasm-crypto",
|
||||
@@ -1336,6 +1341,99 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c087ff98fb0475db4c2b5298a5fd12b2848d2854b39d1115d930ee6da24d1eed"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3396c7aff5a0e3fb6dcc6cc89f56862c1d212b40d93ed725a6962955b1887ff"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f8a6500c396e33f6a7b05d35a5124eb3e394cdb6ca901f7e88332870407896c"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.12.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "017b6263414c58081cea97626f4d8da2dc8f7267337bba0eb5770260d26251e0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3-fixed-multisig"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949d65e60dbe8a24fc58b6b33143328af0abcc72e108a7ff80d2f3f8cabdf63b"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.12.1",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw3",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3-flex-multisig"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "297fe0521cf453ed58b582262cdb045862f126eb9390432e5689774badcfa370"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.12.1",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw3",
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f22da7845e99e5a277523d61c19bda3171ec3dd1084b9ca6ebac6b2fd0cdf084"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus 0.12.1",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.10.2"
|
||||
@@ -2268,6 +2366,7 @@ dependencies = [
|
||||
"log",
|
||||
"network-defaults",
|
||||
"nymsphinx",
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
"secp256k1",
|
||||
"thiserror",
|
||||
@@ -3845,10 +3944,11 @@ dependencies = [
|
||||
name = "nym-gateway"
|
||||
version = "0.12.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bandwidth-claim-contract",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 2.33.3",
|
||||
"clap 3.0.10",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"config",
|
||||
@@ -3866,6 +3966,7 @@ dependencies = [
|
||||
"mixnode-common",
|
||||
"network-defaults",
|
||||
"nymsphinx",
|
||||
"once_cell",
|
||||
"pemstore",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
@@ -3984,6 +4085,8 @@ version = "0.12.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"attohttpc 0.18.0",
|
||||
"bandwidth-claim-contract",
|
||||
"bip39",
|
||||
"cfg-if 1.0.0",
|
||||
"clap 2.33.3",
|
||||
"coconut-interface",
|
||||
@@ -3991,6 +4094,8 @@ dependencies = [
|
||||
"console-subscriber",
|
||||
"credentials",
|
||||
"crypto",
|
||||
"cw3",
|
||||
"cw3-flex-multisig",
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"futures",
|
||||
@@ -7723,7 +7828,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 0.11.1",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -7737,10 +7842,11 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-storage-plus 0.11.1",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
all: clippy-all test wasm fmt
|
||||
happy: clippy-happy test fmt
|
||||
test: build clippy-all cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet
|
||||
test: test-main test-contracts test-wallet
|
||||
cargo-test: test-main test-contracts test-wallet
|
||||
build: build-main build-contracts build-wallet
|
||||
fmt: fmt-main fmt-contracts fmt-wallet
|
||||
|
||||
clippy-happy-main:
|
||||
@@ -32,6 +33,15 @@ test-contracts:
|
||||
test-wallet:
|
||||
cargo test --manifest-path nym-wallet/Cargo.toml --all-features
|
||||
|
||||
build-main:
|
||||
cargo build
|
||||
|
||||
build-contracts:
|
||||
cargo build --manifest-path contracts/Cargo.toml
|
||||
|
||||
build-wallet:
|
||||
cargo build --manifest-path nym-wallet/Cargo.toml
|
||||
|
||||
fmt-main:
|
||||
cargo fmt --all
|
||||
|
||||
|
||||
@@ -121,12 +121,16 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.testnet_mode = testnet_mode;
|
||||
}
|
||||
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.client.gateway_id = id.into();
|
||||
pub fn with_gateway_endpoint<S: Into<String>>(&mut self, id: S, owner: S, listener: S) {
|
||||
self.client.gateway_endpoint = GatewayEndpoint {
|
||||
gateway_id: id.into(),
|
||||
gateway_owner: owner.into(),
|
||||
gateway_listener: listener.into(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn with_gateway_listener<S: Into<String>>(&mut self, gateway_listener: S) {
|
||||
self.client.gateway_listener = gateway_listener.into();
|
||||
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
@@ -198,11 +202,15 @@ impl<T: NymConfig> Config<T> {
|
||||
}
|
||||
|
||||
pub fn get_gateway_id(&self) -> String {
|
||||
self.client.gateway_id.clone()
|
||||
self.client.gateway_endpoint.gateway_id.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_owner(&self) -> String {
|
||||
self.client.gateway_endpoint.gateway_owner.clone()
|
||||
}
|
||||
|
||||
pub fn get_gateway_listener(&self) -> String {
|
||||
self.client.gateway_listener.clone()
|
||||
self.client.gateway_endpoint.gateway_listener.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
@@ -272,6 +280,19 @@ impl<T: NymConfig> Default for Config<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||
struct GatewayEndpoint {
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
gateway_id: String,
|
||||
|
||||
/// Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Serialize)]
|
||||
pub struct Client<T> {
|
||||
/// Version of the client for which this configuration was created.
|
||||
@@ -313,12 +334,8 @@ pub struct Client<T> {
|
||||
/// sent but not received back.
|
||||
reply_encryption_key_store_path: PathBuf,
|
||||
|
||||
/// gateway_id specifies ID of the gateway to which the client should send messages.
|
||||
/// If initially omitted, a random gateway will be chosen from the available topology.
|
||||
gateway_id: String,
|
||||
|
||||
/// Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener: String,
|
||||
/// Information regarding how the client should send data to gateway.
|
||||
gateway_endpoint: GatewayEndpoint,
|
||||
|
||||
/// Path to directory containing public/private keys used for bandwidth token purchase.
|
||||
/// Those are saved in case of emergency, to be able to reclaim bandwidth tokens.
|
||||
@@ -357,8 +374,7 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
gateway_shared_key_file: Default::default(),
|
||||
ack_key_file: Default::default(),
|
||||
reply_encryption_key_store_path: Default::default(),
|
||||
gateway_id: "".to_string(),
|
||||
gateway_listener: "".to_string(),
|
||||
gateway_endpoint: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
backup_bandwidth_token_keys_dir: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
|
||||
+12
-12
@@ -2441,9 +2441,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -6085,9 +6085,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
|
||||
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
@@ -8853,9 +8853,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"dev": true
|
||||
},
|
||||
"for-in": {
|
||||
@@ -11733,9 +11733,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.4.tgz",
|
||||
"integrity": "sha512-ITeAByWWoqutFClc/lRZnFplgXgEZr3WJ6XngMM/N9DMIm4K8zXPCZ1Jdu0rERwO84w1WC5wkle2ubwTA4NTBg==",
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
|
||||
@@ -59,12 +59,6 @@ eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_id }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_listener }}'
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
# communication with particular gateway.
|
||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
@@ -78,6 +72,17 @@ ack_key_file = '{{ client.ack_key_file }}'
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_root_directory = '{{ client.nym_root_directory }}'
|
||||
|
||||
[client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
@@ -160,6 +160,10 @@ impl NymClient {
|
||||
if gateway_id.is_empty() {
|
||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_owner = self.config.get_base().get_gateway_owner();
|
||||
if gateway_owner.is_empty() {
|
||||
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||
if gateway_address.is_empty() {
|
||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||
@@ -185,6 +189,7 @@ impl NymClient {
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
|
||||
@@ -136,6 +136,7 @@ async fn register_with_gateway(
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
@@ -255,15 +256,14 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_id(gateway_details.identity_key.to_base58_string());
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_listener(gateway_details.clients_address());
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
|
||||
@@ -58,7 +58,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
} else {
|
||||
} else if !cfg!(feature = "eth") {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
|
||||
@@ -66,7 +66,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
} else {
|
||||
} else if !cfg!(feature = "eth") {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||
|
||||
@@ -59,12 +59,6 @@ eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_id }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_listener }}'
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
# communication with particular gateway.
|
||||
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
@@ -72,12 +66,22 @@ gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
|
||||
# Path to file containing key used for encrypting and decrypting the content of an
|
||||
# acknowledgement so that nobody besides the client knows which packet it refers to.
|
||||
ack_key_file = '{{ client.ack_key_file }}'
|
||||
|
||||
|
||||
##### advanced configuration options #####
|
||||
|
||||
# Absolute path to the home Nym Clients directory.
|
||||
nym_root_directory = '{{ client.nym_root_directory }}'
|
||||
|
||||
[client.gateway_endpoint]
|
||||
# ID of the gateway from which the client should be fetching messages.
|
||||
gateway_id = '{{ client.gateway_endpoint.gateway_id }}'
|
||||
|
||||
# Address of the gateway owner to which the client should send messages.
|
||||
gateway_owner = '{{ client.gateway_endpoint.gateway_owner }}'
|
||||
|
||||
# Address of the gateway listener to which all client requests should be sent.
|
||||
gateway_listener = '{{ client.gateway_endpoint.gateway_listener }}'
|
||||
|
||||
|
||||
##### socket config options #####
|
||||
|
||||
|
||||
@@ -148,6 +148,10 @@ impl NymClient {
|
||||
if gateway_id.is_empty() {
|
||||
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_owner = self.config.get_base().get_gateway_owner();
|
||||
if gateway_owner.is_empty() {
|
||||
panic!("The owner of the gateway is unknown - did you run `nym-client` init?")
|
||||
}
|
||||
let gateway_address = self.config.get_base().get_gateway_listener();
|
||||
if gateway_address.is_empty() {
|
||||
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
|
||||
@@ -173,6 +177,7 @@ impl NymClient {
|
||||
gateway_address,
|
||||
self.key_manager.identity_keypair(),
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
Some(self.key_manager.gateway_shared_key()),
|
||||
mixnet_message_sender,
|
||||
ack_sender,
|
||||
|
||||
@@ -136,6 +136,7 @@ async fn register_with_gateway(
|
||||
let mut gateway_client = GatewayClient::new_init(
|
||||
gateway.clients_address(),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
our_identity.clone(),
|
||||
timeout,
|
||||
);
|
||||
@@ -256,15 +257,14 @@ pub async fn execute(matches: ArgMatches<'static>) {
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await;
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_id(gateway_details.identity_key.to_base58_string());
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await;
|
||||
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_gateway_listener(gateway_details.clients_address());
|
||||
config.get_base_mut().with_gateway_endpoint(
|
||||
gateway_details.identity_key.to_base58_string(),
|
||||
gateway_details.owner.clone(),
|
||||
gateway_details.clients_address(),
|
||||
);
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
|
||||
|
||||
@@ -54,7 +54,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_endpoint) = matches.value_of(ETH_ENDPOINT_ARG_NAME) {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
} else {
|
||||
} else if !cfg!(feature = "eth") {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
|
||||
@@ -62,7 +62,7 @@ pub(crate) fn override_config(mut config: Config, matches: &ArgMatches<'_>) -> C
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_private_key) = matches.value_of(ETH_PRIVATE_KEY_ARG_NAME) {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
} else {
|
||||
} else if !cfg!(feature = "eth") {
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||
|
||||
Generated
+17
-7
@@ -11,6 +11,7 @@
|
||||
"@tauri-apps/api": "^1.0.0-beta.4",
|
||||
"compression": "^1.7.1",
|
||||
"polka": "next",
|
||||
"qrious": "^4.0.2",
|
||||
"sirv": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -6758,6 +6759,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/qrious": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
|
||||
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
|
||||
},
|
||||
"node_modules/query-string": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
@@ -7579,11 +7585,10 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"decompress-response": "^4.2.0",
|
||||
"once": "^1.3.1",
|
||||
@@ -13474,6 +13479,11 @@
|
||||
"escape-goat": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"qrious": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/qrious/-/qrious-4.0.2.tgz",
|
||||
"integrity": "sha512-xWPJIrK1zu5Ypn898fBp8RHkT/9ibquV2Kv24S/JY9VYEhMBMKur1gHVsOiNUh7PHP9uCgejjpZUHUIXXKoU/g=="
|
||||
},
|
||||
"query-string": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
@@ -14036,9 +14046,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz",
|
||||
"integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
|
||||
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"decompress-response": "^4.2.0",
|
||||
|
||||
@@ -15,6 +15,9 @@ build = "src/build.rs"
|
||||
tauri-build = { version = "1.0.0-beta.2" }
|
||||
|
||||
[dependencies]
|
||||
cw3-flex-multisig = "0.12.1"
|
||||
cosmwasm-std = "1.0.0-beta5"
|
||||
bip39 = "1.0.1"
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.0.0-beta.4", features = [] }
|
||||
@@ -23,7 +26,9 @@ url = "2.2"
|
||||
|
||||
coconut-interface = { path = "../../../common/coconut-interface" }
|
||||
credentials = { path = "../../../common/credentials" }
|
||||
validator-client = {path = "../../../common/client-libs/validator-client"}
|
||||
validator-client = {path = "../../../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
bandwidth-claim-contract = { path = "../../../common/bandwidth-claim-contract" }
|
||||
network-defaults = { path = "../../../common/network-defaults" }
|
||||
|
||||
[features]
|
||||
default = ["custom-protocol"]
|
||||
|
||||
@@ -3,18 +3,28 @@
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
use bip39::Mnemonic;
|
||||
use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use bandwidth_claim_contract::msg::ExecuteMsg;
|
||||
use tokio::sync::RwLock;
|
||||
use url::Url;
|
||||
|
||||
use coconut_interface::{
|
||||
self, hash_to_scalar, Attribute, Credential, Parameters, Signature, Theta, VerificationKey,
|
||||
self, hash_to_scalar, Attribute, Base58, Bytable, Credential, Parameters, Signature, Theta,
|
||||
VerificationKey,
|
||||
};
|
||||
use credentials::{obtain_aggregate_signature, obtain_aggregate_verification_key};
|
||||
use credentials::coconut::bandwidth::{
|
||||
obtain_signature, verify_credential_remote, BandwidthVoucherAttributes,
|
||||
};
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
use validator_client::nymd::{AccountId, CosmosCoin, Decimal, Denom, NymdClient};
|
||||
|
||||
struct State {
|
||||
signatures: Vec<Signature>,
|
||||
last_tx_hash: String,
|
||||
n_attributes: u32,
|
||||
params: Parameters,
|
||||
serial_number: Attribute,
|
||||
@@ -25,19 +35,12 @@ struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn init(public_attributes_bytes: Vec<Vec<u8>>, private_attributes_bytes: Vec<Vec<u8>>) -> State {
|
||||
let n_attributes = (public_attributes_bytes.len() + private_attributes_bytes.len()) as u32;
|
||||
fn init(public_attributes: Vec<Attribute>, private_attributes: Vec<Attribute>) -> State {
|
||||
let n_attributes = (public_attributes.len() + private_attributes.len()) as u32;
|
||||
let params = Parameters::new(n_attributes).unwrap();
|
||||
let public_attributes = public_attributes_bytes
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
let private_attributes = private_attributes_bytes
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
State {
|
||||
signatures: Vec::new(),
|
||||
last_tx_hash: String::new(),
|
||||
n_attributes,
|
||||
params,
|
||||
serial_number: private_attributes[0],
|
||||
@@ -61,15 +64,40 @@ fn parse_url_validators(raw: &[String]) -> Result<Vec<Url>, String> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn randomise_credential(
|
||||
idx: usize,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<Signature>, String> {
|
||||
async fn deposit_funds(state: tauri::State<'_, Arc<RwLock<State>>>) -> Result<String, String> {
|
||||
let nymd_url = Url::from_str("http://127.0.0.1:26657").unwrap();
|
||||
let mnemonic = Mnemonic::from_str(&"sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter").unwrap();
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
network_defaults::all::Network::SANDBOX,
|
||||
nymd_url.as_ref(),
|
||||
None,
|
||||
None,
|
||||
AccountId::from_str("nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps").ok(),
|
||||
mnemonic,
|
||||
None,
|
||||
)
|
||||
.expect("Could not create nymd client");
|
||||
let req = ExecuteMsg::BuyBandwidth {};
|
||||
let funds = vec![CosmosCoin {
|
||||
denom: Denom::from_str(network_defaults::sandbox::DENOM).unwrap(),
|
||||
amount: Decimal::from(1000000u64),
|
||||
}];
|
||||
let last_tx_hash = nymd_client
|
||||
.execute(
|
||||
nymd_client.erc20_bridge_contract_address().unwrap(),
|
||||
&req,
|
||||
Default::default(),
|
||||
"",
|
||||
funds,
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.transaction_hash
|
||||
.to_string();
|
||||
println!("Tx hash: {}", last_tx_hash);
|
||||
let mut state = state.write().await;
|
||||
let signature = state.signatures.remove(idx);
|
||||
let (new_signature, _) = signature.randomise(&state.params);
|
||||
state.signatures.insert(idx, new_signature);
|
||||
Ok(state.signatures.clone())
|
||||
state.last_tx_hash = last_tx_hash.clone();
|
||||
Ok(last_tx_hash)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -145,26 +173,100 @@ async fn verify_credential(
|
||||
// the API needs to be improved but at least it should compile (in theory)
|
||||
let verification_key =
|
||||
get_aggregated_verification_key(validator_urls.clone(), state.clone()).await?;
|
||||
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||
println!("Verification key {:?}", verification_key.to_bs58());
|
||||
let theta = prove_credential(idx, validator_urls, state.clone()).await?;
|
||||
|
||||
let state = state.read().await;
|
||||
|
||||
let public_attributes_bytes = vec![
|
||||
state.voucher_value.to_bytes().to_vec(),
|
||||
state.voucher_info.to_bytes().to_vec(),
|
||||
state.voucher_value.to_byte_vec(),
|
||||
state.voucher_info.to_byte_vec(),
|
||||
];
|
||||
|
||||
let credential = Credential::new(
|
||||
let mut credential = Credential::new(
|
||||
state.n_attributes,
|
||||
theta,
|
||||
theta.clone(),
|
||||
public_attributes_bytes,
|
||||
state
|
||||
.signatures
|
||||
.get(idx)
|
||||
.ok_or("Got invalid signature idx")?,
|
||||
0,
|
||||
);
|
||||
println!("Using verification key: {:?}", verification_key.to_bs58());
|
||||
let local_check = credential.verify(&verification_key);
|
||||
println!("Local check: {}", local_check);
|
||||
if !local_check {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(credential.verify(&verification_key))
|
||||
let mnemonic = Mnemonic::from_str("sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter").unwrap();
|
||||
let nymd_url = Url::from_str("http://127.0.0.1:26657").unwrap();
|
||||
let nymd_client = NymdClient::connect_with_mnemonic(
|
||||
network_defaults::all::Network::SANDBOX,
|
||||
nymd_url.as_ref(),
|
||||
None,
|
||||
None,
|
||||
AccountId::from_str("nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps").ok(),
|
||||
mnemonic,
|
||||
None,
|
||||
)
|
||||
.expect("Could not create nymd client");
|
||||
let req = cw3_flex_multisig::msg::ExecuteMsg::Propose {
|
||||
title: "Spend coconut".to_string(),
|
||||
description: "Propose to spend a coconut cred".to_string(),
|
||||
msgs: vec![CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: "nymt1sthrn5ep8ls5vzz8f9gp89khhmedahhdqdmmps".to_string(),
|
||||
msg: to_binary(&ExecuteMsg::SpendCredential { amount: 1000000u64 }).unwrap(),
|
||||
funds: vec![],
|
||||
})],
|
||||
latest: None,
|
||||
};
|
||||
let tx = nymd_client
|
||||
.execute(
|
||||
&AccountId::from_str("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd").unwrap(),
|
||||
&req,
|
||||
Default::default(),
|
||||
"",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let event = tx.logs[0]
|
||||
.events
|
||||
.iter()
|
||||
.find(|event| event.ty == "wasm")
|
||||
.unwrap();
|
||||
let proposal_id = u64::from_str(
|
||||
&event
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|attr| attr.key == "proposal_id")
|
||||
.unwrap()
|
||||
.value,
|
||||
)
|
||||
.unwrap();
|
||||
println!("Got proposal id {}", proposal_id);
|
||||
credential.set_proposal_id(proposal_id);
|
||||
let remote_check = verify_credential_remote(&parsed_urls, credential)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("Remote check: {}", remote_check);
|
||||
|
||||
let req = cw3_flex_multisig::msg::ExecuteMsg::Execute { proposal_id };
|
||||
nymd_client
|
||||
.execute(
|
||||
&AccountId::from_str("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd").unwrap(),
|
||||
&req,
|
||||
Default::default(),
|
||||
"",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Ok(remote_check)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -172,19 +274,25 @@ async fn get_credential(
|
||||
validator_urls: Vec<String>,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<Vec<Signature>, String> {
|
||||
let guard = state.read().await;
|
||||
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||
let public_attributes = vec![guard.voucher_value, guard.voucher_info];
|
||||
let private_attributes = vec![guard.serial_number, guard.binding_number];
|
||||
let signature = {
|
||||
let guard = state.read().await;
|
||||
let parsed_urls = parse_url_validators(&validator_urls)?;
|
||||
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
|
||||
serial_number: guard.serial_number,
|
||||
binding_number: guard.binding_number,
|
||||
voucher_value: guard.voucher_value,
|
||||
voucher_info: guard.voucher_info,
|
||||
};
|
||||
|
||||
let signature = obtain_aggregate_signature(
|
||||
&guard.params,
|
||||
&public_attributes,
|
||||
&private_attributes,
|
||||
&parsed_urls,
|
||||
)
|
||||
.await
|
||||
.map_err(|err| format!("failed to obtain aggregate signature - {:?}", err))?;
|
||||
obtain_signature(
|
||||
&guard.params,
|
||||
&bandwidth_credential_attributes,
|
||||
&parsed_urls,
|
||||
guard.last_tx_hash.clone(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| format!("failed to obtain aggregate signature - {:?}", err))?
|
||||
};
|
||||
|
||||
let mut state = state.write().await;
|
||||
state.signatures.push(signature);
|
||||
@@ -192,16 +300,21 @@ async fn get_credential(
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let public_attributes = vec![b"public_key".to_vec()];
|
||||
let private_attributes = vec![b"private_key".to_vec()];
|
||||
let params = coconut_interface::Parameters::new(4).unwrap();
|
||||
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
|
||||
serial_number: params.random_scalar(),
|
||||
binding_number: params.random_scalar(),
|
||||
voucher_value: Attribute::from(1000000u64),
|
||||
voucher_info: hash_to_scalar("BandwidthVoucher"),
|
||||
};
|
||||
tauri::Builder::default()
|
||||
.manage(Arc::new(RwLock::new(State::init(
|
||||
public_attributes,
|
||||
private_attributes,
|
||||
bandwidth_credential_attributes.get_public_attributes(),
|
||||
bandwidth_credential_attributes.get_private_attributes(),
|
||||
))))
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
get_credential,
|
||||
randomise_credential,
|
||||
deposit_funds,
|
||||
delete_credential,
|
||||
list_credentials,
|
||||
verify_credential
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
import {onMount} from "svelte";
|
||||
import QRious from "qrious";
|
||||
|
||||
const validator_urls = ["http://localhost:8080"];
|
||||
const validator_urls = ["http://localhost:8080", "http://localhost:8081", "http://localhost:8082"];
|
||||
let signatures = [];
|
||||
let qrVisible = false;
|
||||
let tx_hash = "";
|
||||
|
||||
async function getCredential() {
|
||||
signatures = await invoke("get_credential", {
|
||||
@@ -13,10 +14,8 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function randomiseCredential(idx) {
|
||||
signatures = await invoke("randomise_credential", {
|
||||
idx: idx,
|
||||
});
|
||||
async function depositFunds() {
|
||||
tx_hash = await invoke("deposit_funds");
|
||||
}
|
||||
|
||||
async function verifyCredential(idx) {
|
||||
@@ -24,7 +23,7 @@
|
||||
idx: idx,
|
||||
validatorUrls: validator_urls,
|
||||
});
|
||||
alert(response);
|
||||
qrVisible = !response;
|
||||
}
|
||||
|
||||
async function deleteCredential(idx) {
|
||||
@@ -58,6 +57,7 @@
|
||||
<title>Coconut</title>
|
||||
</svelte:head>
|
||||
|
||||
<button class="btn btn-success" on:click={depositFunds}>Deposit</button>
|
||||
<button class="btn btn-success" on:click={getCredential}>Get Credential</button>
|
||||
<hr />
|
||||
<table class="table table-dark">
|
||||
@@ -67,11 +67,6 @@
|
||||
<td>
|
||||
<div class="btn-group" role="group" aria-label="Basic example">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
on:click={() => {
|
||||
randomiseCredential(idx);
|
||||
}}>Randomize</button
|
||||
><button
|
||||
class="btn btn-danger"
|
||||
on:click={() => {
|
||||
deleteCredential(idx);
|
||||
|
||||
@@ -1813,7 +1813,7 @@ decompress-response@^3.2.0, decompress-response@^3.3.0:
|
||||
|
||||
decompress-response@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986"
|
||||
integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
@@ -3130,7 +3130,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
|
||||
mimic-response@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
@@ -4056,13 +4056,13 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
|
||||
simple-concat@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
|
||||
integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
|
||||
|
||||
simple-get@^3.0.3, simple-get@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz"
|
||||
integrity sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55"
|
||||
integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==
|
||||
dependencies:
|
||||
decompress-response "^4.2.0"
|
||||
once "^1.3.1"
|
||||
@@ -4673,7 +4673,7 @@ wrap-ansi@^7.0.0:
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
||||
write-file-atomic@^3.0.0:
|
||||
|
||||
+19
-16
@@ -22,7 +22,10 @@
|
||||
"webpack-dev-server": "^3.11.0"
|
||||
}
|
||||
},
|
||||
"../pkg": {},
|
||||
"../pkg": {
|
||||
"name": "@nymproject/nym-client-wasm",
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"node_modules/@nymproject/nym-client-wasm": {
|
||||
"resolved": "../pkg",
|
||||
"link": true
|
||||
@@ -2351,9 +2354,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5859,9 +5862,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
@@ -6500,9 +6503,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "6.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
|
||||
"integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async-limiter": "~1.0.0"
|
||||
@@ -8542,9 +8545,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==",
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"dev": true
|
||||
},
|
||||
"for-in": {
|
||||
@@ -11399,9 +11402,9 @@
|
||||
}
|
||||
},
|
||||
"url-parse": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.1.tgz",
|
||||
"integrity": "sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==",
|
||||
"version": "1.5.7",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.7.tgz",
|
||||
"integrity": "sha512-HxWkieX+STA38EDk7CE9MEryFeHCKzgagxlGvsdS7WBImq9Mk+PGwiT56w82WI3aicwJA8REp42Cxo98c8FZMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"querystringify": "^2.1.1",
|
||||
|
||||
+510
-629
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"bootstrap": "^4.5.0",
|
||||
"node-sass": "^4.14.1",
|
||||
"node-sass": "^7.0.0",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-scripts": "^4.0.0",
|
||||
|
||||
@@ -127,6 +127,7 @@ impl NymClient {
|
||||
gateway.clients_address(),
|
||||
Arc::clone(&client.identity),
|
||||
gateway.identity_key,
|
||||
gateway.owner.clone(),
|
||||
None,
|
||||
mixnet_messages_sender,
|
||||
ack_sender,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// event types
|
||||
pub const VOUCHER_ACQUIRED_EVENT_TYPE: &str = "coconut_acquired";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const VOUCHER_VALUE: &str = "value";
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod events;
|
||||
pub mod keys;
|
||||
pub mod msg;
|
||||
pub mod payment;
|
||||
|
||||
@@ -14,6 +14,8 @@ pub struct InstantiateMsg {}
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
LinkPayment { data: LinkPaymentData },
|
||||
BuyBandwidth {},
|
||||
SpendCredential { amount: u64 },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
|
||||
@@ -23,6 +23,7 @@ credentials = { path = "../../credentials" }
|
||||
crypto = { path = "../../crypto" }
|
||||
gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
nymsphinx = { path = "../../nymsphinx" }
|
||||
pemstore = { path = "../../pemstore" }
|
||||
coconut-interface = { path = "../../coconut-interface", optional = true }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
|
||||
|
||||
@@ -16,22 +16,27 @@ use crypto::asymmetric::identity::PublicKey;
|
||||
use network_defaults::BANDWIDTH_VALUE;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use network_defaults::{
|
||||
eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH,
|
||||
TOKENS_TO_BURN,
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, ETH_BURN_FUNCTION_NAME,
|
||||
ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME, ETH_ERC20_CONTRACT_ADDRESS,
|
||||
ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use pemstore::traits::PemStorableKeyPair;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use rand::rngs::OsRng;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use secp256k1::SecretKey;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use std::io::Write;
|
||||
use std::io::{Read, Write};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use std::str::FromStr;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, Bytes, U256, U64},
|
||||
types::{Address, U256, U64},
|
||||
Web3,
|
||||
};
|
||||
|
||||
@@ -50,6 +55,19 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_ERC20_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_ERC20_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BandwidthController {
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -59,6 +77,8 @@ pub struct BandwidthController {
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: SecretKey,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
backup_bandwidth_token_keys_dir: std::path::PathBuf,
|
||||
@@ -84,12 +104,14 @@ impl BandwidthController {
|
||||
Http::new(ð_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// Fail early, on invalid abi
|
||||
let contract = eth_contract(web3);
|
||||
let contract = eth_contract(web3.clone());
|
||||
let erc20_contract = eth_erc20_contract(web3);
|
||||
let eth_private_key = secp256k1::SecretKey::from_str(ð_private_key)
|
||||
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
||||
|
||||
Ok(BandwidthController {
|
||||
contract,
|
||||
erc20_contract,
|
||||
eth_private_key,
|
||||
backup_bandwidth_token_keys_dir,
|
||||
})
|
||||
@@ -107,6 +129,45 @@ impl BandwidthController {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
|
||||
std::fs::create_dir_all(&self.backup_bandwidth_token_keys_dir)?;
|
||||
let file = std::fs::read_dir(&self.backup_bandwidth_token_keys_dir)?
|
||||
.find(|entry| {
|
||||
entry
|
||||
.as_ref()
|
||||
.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"))]
|
||||
fn mark_keypair_as_spent(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||
let mut spent_dir = self.backup_bandwidth_token_keys_dir.clone();
|
||||
spent_dir.push("spent");
|
||||
std::fs::create_dir_all(&spent_dir)?;
|
||||
let file_path_old = self
|
||||
.backup_bandwidth_token_keys_dir
|
||||
.join(keypair.public_key().to_base58_string());
|
||||
let file_path_new = spent_dir.join(keypair.public_key().to_base58_string());
|
||||
std::fs::rename(file_path_old, file_path_new)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub async fn prepare_coconut_credential(
|
||||
&self,
|
||||
@@ -128,6 +189,7 @@ impl BandwidthController {
|
||||
¶ms,
|
||||
&bandwidth_credential_attributes,
|
||||
&self.validator_endpoints,
|
||||
String::new(),
|
||||
)
|
||||
.await?;
|
||||
// the above would presumably be loaded from a file
|
||||
@@ -145,17 +207,25 @@ impl BandwidthController {
|
||||
pub async fn prepare_token_credential(
|
||||
&self,
|
||||
gateway_identity: PublicKey,
|
||||
gateway_owner: String,
|
||||
) -> Result<TokenCredential, GatewayClientError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp)?;
|
||||
let kp = match self.restore_keypair() {
|
||||
Ok(kp) => kp,
|
||||
Err(_) => {
|
||||
let mut rng = OsRng;
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp)?;
|
||||
kp
|
||||
}
|
||||
};
|
||||
|
||||
let verification_key = *kp.public_key();
|
||||
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
|
||||
self.buy_token_credential(verification_key, signed_verification_key)
|
||||
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
||||
.await?;
|
||||
|
||||
self.mark_keypair_as_spent(&kp)?;
|
||||
|
||||
let message: Vec<u8> = verification_key
|
||||
.to_bytes()
|
||||
.iter()
|
||||
@@ -177,28 +247,109 @@ impl BandwidthController {
|
||||
&self,
|
||||
verification_key: PublicKey,
|
||||
signed_verification_key: identity::Signature,
|
||||
gateway_owner: String,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
// 0 means a transaction failure, 1 means success
|
||||
let confirmations = if cfg!(debug_assertions) {
|
||||
1
|
||||
} else {
|
||||
ETH_MIN_BLOCK_DEPTH
|
||||
};
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead + 20 seconds of wait for kill
|
||||
log::info!(
|
||||
"Waiting for Ethereum transaction. This should take about {} seconds",
|
||||
confirmations * 15 + 10
|
||||
(confirmations + 1) * 15 + 30
|
||||
);
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.erc20_contract
|
||||
.estimate_gas(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Calling ERC20 approve in 10 seconds with an estimated gas of {}. Kill the process if you want to abort", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.erc20_contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
options,
|
||||
1, // One confirmation is enough, as we'll be consuming the approved token next anyway
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
return Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Approve transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
));
|
||||
} else {
|
||||
log::info!(
|
||||
"Approved {} tokens for bandwidth use on Ethereum",
|
||||
TOKENS_TO_BURN
|
||||
);
|
||||
}
|
||||
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.contract
|
||||
.estimate_gas(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner.clone()),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Generating bandwidth on ETH contract in 10 seconds with an estimated gas of {}. \
|
||||
Kill the process if you want to abort. Keep in mind that if you abort now, you'll still have \
|
||||
some tokens approved for bandwidth spending from the previous action. \
|
||||
If you don't want that, you'll need to manually decreaseAllowance to revert the approval.", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
U256::from(TOKENS_TO_BURN),
|
||||
U256::from(&verification_key.to_bytes()),
|
||||
Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner),
|
||||
),
|
||||
Options::default(),
|
||||
options,
|
||||
confirmations,
|
||||
&self.eth_private_key,
|
||||
)
|
||||
@@ -236,6 +387,15 @@ mod tests {
|
||||
eth_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_erc20_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_erc20_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_event_name_constant_against_abi() {
|
||||
let transport =
|
||||
|
||||
@@ -20,6 +20,7 @@ use gateway_requests::iv::IV;
|
||||
use gateway_requests::registration::handshake::{client_handshake, SharedKeys};
|
||||
use gateway_requests::{BinaryRequest, ClientControlRequest, ServerResponse};
|
||||
use log::*;
|
||||
use network_defaults::{REMAINING_BANDWIDTH_THRESHOLD, TOKENS_TO_BURN};
|
||||
use nymsphinx::forwarding::packet::MixPacket;
|
||||
use rand::rngs::OsRng;
|
||||
use std::convert::TryFrom;
|
||||
@@ -44,6 +45,7 @@ pub struct GatewayClient {
|
||||
bandwidth_remaining: i64,
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
connection: SocketState,
|
||||
@@ -68,6 +70,7 @@ impl GatewayClient {
|
||||
gateway_address: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
shared_key: Option<Arc<SharedKeys>>,
|
||||
mixnet_message_sender: MixnetMessageSender,
|
||||
ack_sender: AcknowledgementSender,
|
||||
@@ -80,6 +83,7 @@ impl GatewayClient {
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
local_identity,
|
||||
shared_key,
|
||||
connection: SocketState::NotConnected,
|
||||
@@ -112,6 +116,7 @@ impl GatewayClient {
|
||||
pub fn new_init(
|
||||
gateway_address: String,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
response_timeout_duration: Duration,
|
||||
) -> Self {
|
||||
@@ -129,6 +134,7 @@ impl GatewayClient {
|
||||
bandwidth_remaining: 0,
|
||||
gateway_address,
|
||||
gateway_identity,
|
||||
gateway_owner,
|
||||
local_identity,
|
||||
shared_key: None,
|
||||
connection: SocketState::NotConnected,
|
||||
@@ -548,6 +554,8 @@ impl GatewayClient {
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
}
|
||||
|
||||
let _gateway_owner = self.gateway_owner.clone();
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let credential = self
|
||||
.bandwidth_controller
|
||||
@@ -560,7 +568,7 @@ impl GatewayClient {
|
||||
.bandwidth_controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.prepare_token_credential(self.gateway_identity)
|
||||
.prepare_token_credential(self.gateway_identity, _gateway_owner)
|
||||
.await?;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -584,15 +592,10 @@ impl GatewayClient {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
|
||||
// Try to claim more bandwidth first, and return an error only if that is still not
|
||||
// enough (the current granularity for bandwidth should be sufficient)
|
||||
self.claim_bandwidth().await?;
|
||||
if self.estimate_required_bandwidth(&packets) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
self.estimate_required_bandwidth(&packets),
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
self.estimate_required_bandwidth(&packets),
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -659,15 +662,10 @@ impl GatewayClient {
|
||||
return Err(GatewayClientError::NotAuthenticated);
|
||||
}
|
||||
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
|
||||
// Try to claim more bandwidth first, and return an error only if that is still not
|
||||
// enough
|
||||
self.claim_bandwidth().await?;
|
||||
if (mix_packet.sphinx_packet().len() as i64) > self.bandwidth_remaining {
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
mix_packet.sphinx_packet().len() as i64,
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
return Err(GatewayClientError::NotEnoughBandwidth(
|
||||
mix_packet.sphinx_packet().len() as i64,
|
||||
self.bandwidth_remaining,
|
||||
));
|
||||
}
|
||||
if !self.connection.is_established() {
|
||||
return Err(GatewayClientError::ConnectionNotEstablished);
|
||||
@@ -737,6 +735,12 @@ impl GatewayClient {
|
||||
}
|
||||
let shared_key = self.perform_initial_authentication().await?;
|
||||
|
||||
if self.bandwidth_remaining < REMAINING_BANDWIDTH_THRESHOLD {
|
||||
info!("Claiming more bandwidth for your tokens. This will use {} token(s) from your wallet. \
|
||||
Stop the process now if you don't want that to happen.", TOKENS_TO_BURN);
|
||||
self.claim_bandwidth().await?;
|
||||
}
|
||||
|
||||
// this call is NON-blocking
|
||||
self.start_listening_for_mixnet_messages()?;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use tungstenite::Error as WsError;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::JsValue;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use web3::Error as Web3Error;
|
||||
use web3::{contract::Error as Web3ContractError, Error as Web3Error};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum GatewayClientError {
|
||||
@@ -27,13 +27,17 @@ pub enum GatewayClientError {
|
||||
NetworkErrorWasm(JsValue),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Could not backup keypair - {0}")]
|
||||
#[error("Keypair IO error - {0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Could not burn ERC20 token in Ethereum smart contract - {0}")]
|
||||
BurnTokenError(#[from] Web3Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Could not run web3 contract - {0}")]
|
||||
Web3ContractError(#[from] Web3ContractError),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Invalid Ethereum private key")]
|
||||
InvalidEthereumPrivateKey,
|
||||
|
||||
@@ -39,7 +39,8 @@ flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0-beta3", optional = true }
|
||||
ts-rs = { version = "5.1", optional = true }
|
||||
# Leaving it as * so that it inherits whatever the wallet is using
|
||||
ts-rs = { version = "*", optional = true }
|
||||
|
||||
[features]
|
||||
nymd-client = [
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use coconut_interface::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, Credential, VerificationKeyResponse,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
@@ -35,6 +38,7 @@ pub struct Config {
|
||||
nymd_url: Url,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
@@ -50,12 +54,14 @@ impl Config {
|
||||
api_url: Url,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
) -> Self {
|
||||
Config {
|
||||
network,
|
||||
nymd_url,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
api_url,
|
||||
mixnode_page_limit: None,
|
||||
gateway_page_limit: None,
|
||||
@@ -90,6 +96,7 @@ pub struct Client<C> {
|
||||
network: network_defaults::all::Network,
|
||||
mixnet_contract_address: Option<cosmrs::AccountId>,
|
||||
vesting_contract_address: Option<cosmrs::AccountId>,
|
||||
erc20_bridge_contract_address: Option<cosmrs::AccountId>,
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
|
||||
mixnode_page_limit: Option<u32>,
|
||||
@@ -114,6 +121,7 @@ impl Client<SigningNymdClient> {
|
||||
config.nymd_url.as_str(),
|
||||
config.mixnet_contract_address.clone(),
|
||||
config.vesting_contract_address.clone(),
|
||||
config.erc20_bridge_contract_address.clone(),
|
||||
mnemonic.clone(),
|
||||
None,
|
||||
)?;
|
||||
@@ -122,6 +130,7 @@ impl Client<SigningNymdClient> {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
|
||||
mnemonic: Some(mnemonic),
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
@@ -138,6 +147,7 @@ impl Client<SigningNymdClient> {
|
||||
new_endpoint.as_ref(),
|
||||
self.mixnet_contract_address.clone(),
|
||||
self.vesting_contract_address.clone(),
|
||||
self.erc20_bridge_contract_address.clone(),
|
||||
self.mnemonic.clone().unwrap(),
|
||||
None,
|
||||
)?;
|
||||
@@ -159,12 +169,24 @@ impl Client<QueryNymdClient> {
|
||||
cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS)
|
||||
.unwrap()
|
||||
})),
|
||||
Some(
|
||||
config
|
||||
.erc20_bridge_contract_address
|
||||
.clone()
|
||||
.unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(
|
||||
network_defaults::DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||
)
|
||||
.unwrap()
|
||||
}),
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(Client {
|
||||
network: config.network,
|
||||
mixnet_contract_address: config.mixnet_contract_address,
|
||||
vesting_contract_address: config.vesting_contract_address,
|
||||
erc20_bridge_contract_address: config.erc20_bridge_contract_address,
|
||||
mnemonic: None,
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
@@ -180,6 +202,7 @@ impl Client<QueryNymdClient> {
|
||||
new_endpoint.as_ref(),
|
||||
self.mixnet_contract_address.clone(),
|
||||
self.vesting_contract_address.clone(),
|
||||
self.erc20_bridge_contract_address.clone(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -232,7 +255,7 @@ impl<C> Client<C> {
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_mixnet_contract_version().await?)
|
||||
self.nymd.get_mixnet_contract_version().await
|
||||
}
|
||||
|
||||
pub async fn get_rewarding_status(
|
||||
@@ -551,6 +574,13 @@ impl<C> Client<C> {
|
||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
}
|
||||
|
||||
pub async fn verify_credential(
|
||||
&self,
|
||||
request_body: &Credential,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.verify_credential(request_body).await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ApiClient {
|
||||
@@ -653,4 +683,11 @@ impl ApiClient {
|
||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
}
|
||||
|
||||
pub async fn verify_credential(
|
||||
&self,
|
||||
request_body: &Credential,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.verify_credential(request_body).await?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::cosmwasm_client::helpers::create_pagination;
|
||||
use crate::nymd::cosmwasm_client::helpers::{create_pagination, next_page_key};
|
||||
use crate::nymd::cosmwasm_client::types::{
|
||||
Account, Code, CodeDetails, Contract, ContractCodeHistoryEntry, ContractCodeId,
|
||||
SequenceResponse, SimulateResponse,
|
||||
@@ -150,8 +150,8 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
raw_balances.append(&mut res.balances);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -182,8 +182,8 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
supply.append(&mut res.supply);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -274,8 +274,8 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
raw_codes.append(&mut res.code_infos);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -319,8 +319,8 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
raw_contracts.append(&mut res.contracts);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -375,8 +375,8 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.await?;
|
||||
|
||||
raw_entries.append(&mut res.entries);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
if let Some(next_key) = next_page_key(res.pagination) {
|
||||
pagination = Some(create_pagination(next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::error::NymdError;
|
||||
use cosmrs::proto::cosmos::base::query::v1beta1::PageRequest;
|
||||
use cosmrs::proto::cosmos::base::query::v1beta1::{PageRequest, PageResponse};
|
||||
use cosmrs::rpc::endpoint::broadcast;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
@@ -53,3 +53,15 @@ pub(crate) fn create_pagination(key: Vec<u8>) -> PageRequest {
|
||||
count_total: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next_page_key(pagination_info: Option<PageResponse>) -> Option<Vec<u8>> {
|
||||
if let Some(next_page_info) = pagination_info {
|
||||
// it turns out, even though `PageResponse` is always returned wrapped in an `Option`,
|
||||
// the `next_key` can still be empty, so check whether we actually need to perform another call
|
||||
if !next_page_info.next_key.is_empty() {
|
||||
return Some(next_page_info.next_key);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct Log {
|
||||
// and launchpad cosmos validator was setting it to what essentially is just the raw version of what
|
||||
// we received (and we don't care about launchpad, we, as the time of writing this, work on the stargate)
|
||||
// log: String,
|
||||
events: Vec<cosmwasm_std::Event>,
|
||||
pub events: Vec<cosmwasm_std::Event>,
|
||||
}
|
||||
|
||||
/// Searches in logs for the first event of the given event type and in that event
|
||||
|
||||
@@ -46,12 +46,14 @@ pub mod error;
|
||||
pub mod fee;
|
||||
pub mod traits;
|
||||
pub mod wallet;
|
||||
use cosmrs::rpc::endpoint::tx::Response as TxResponse;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NymdClient<C> {
|
||||
client: C,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
client_address: Option<Vec<AccountId>>,
|
||||
custom_gas_limits: HashMap<Operation, Gas>,
|
||||
simulated_gas_multiplier: f32,
|
||||
@@ -62,6 +64,7 @@ impl NymdClient<QueryNymdClient> {
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
) -> Result<NymdClient<QueryNymdClient>, NymdError>
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
@@ -70,6 +73,7 @@ impl NymdClient<QueryNymdClient> {
|
||||
client: QueryNymdClient::new(endpoint)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: None,
|
||||
custom_gas_limits: Default::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
@@ -83,6 +87,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
signer: DirectSecp256k1HdWallet,
|
||||
gas_price: Option<GasPrice>,
|
||||
) -> Result<NymdClient<SigningNymdClient>, NymdError>
|
||||
@@ -99,6 +104,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
custom_gas_limits: Default::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
@@ -110,6 +116,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
erc20_bridge_contract_address: Option<AccountId>,
|
||||
mnemonic: bip39::Mnemonic,
|
||||
gas_price: Option<GasPrice>,
|
||||
) -> Result<NymdClient<SigningNymdClient>, NymdError>
|
||||
@@ -128,6 +135,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?,
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
custom_gas_limits: Default::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
@@ -148,6 +156,12 @@ impl<C> NymdClient<C> {
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
}
|
||||
|
||||
pub fn erc20_bridge_contract_address(&self) -> Result<&AccountId, NymdError> {
|
||||
self.erc20_bridge_contract_address
|
||||
.as_ref()
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
}
|
||||
|
||||
// now the question is as follows: will denom always be in the format of `u{prefix}`?
|
||||
pub fn denom(&self) -> Result<Denom, NymdError> {
|
||||
Ok(format!("u{}", self.mixnet_contract_address()?.prefix())
|
||||
@@ -261,6 +275,13 @@ impl<C> NymdClient<C> {
|
||||
self.client.get_balance(address, denom).await
|
||||
}
|
||||
|
||||
pub async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.client.get_tx(id).await
|
||||
}
|
||||
|
||||
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
|
||||
@@ -6,8 +6,10 @@ use crate::nymd::error::NymdError;
|
||||
use crate::nymd::NymdClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use vesting_contract::vesting::{Account, PledgeData};
|
||||
use vesting_contract_common::messages::QueryMsg as VestingQueryMsg;
|
||||
use vesting_contract::vesting::Account;
|
||||
use vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, OriginalVestingResponse, Period, PledgeData,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait VestingQueryClient {
|
||||
@@ -43,7 +45,10 @@ pub trait VestingQueryClient {
|
||||
async fn vesting_end_time(&self, vesting_account_address: &str)
|
||||
-> Result<Timestamp, NymdError>;
|
||||
|
||||
async fn original_vesting(&self, vesting_account_address: &str) -> Result<Coin, NymdError>;
|
||||
async fn original_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<OriginalVestingResponse, NymdError>;
|
||||
|
||||
async fn delegated_free(
|
||||
&self,
|
||||
@@ -60,6 +65,10 @@ pub trait VestingQueryClient {
|
||||
async fn get_account(&self, address: &str) -> Result<Account, NymdError>;
|
||||
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError>;
|
||||
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NymdError>;
|
||||
async fn get_current_vesting_period(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Period, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -142,7 +151,10 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn original_vesting(&self, vesting_account_address: &str) -> Result<Coin, NymdError> {
|
||||
async fn original_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<OriginalVestingResponse, NymdError> {
|
||||
let request = VestingQueryMsg::GetOriginalVesting {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
@@ -203,4 +215,13 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NymdClient<C> {
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_vesting_period(&self, address: &str) -> Result<Period, NymdError> {
|
||||
let request = VestingQueryMsg::GetCurrentVestingPeriod {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ use vesting_contract_common::messages::{ExecuteMsg as VestingExecuteMsg, Vesting
|
||||
|
||||
#[async_trait]
|
||||
pub trait VestingSigningClient {
|
||||
async fn vesting_update_mixnode_config(
|
||||
&self,
|
||||
profix_margin_percent: u8,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn update_mixnet_address(&self, address: &str) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn vesting_bond_gateway(
|
||||
@@ -316,4 +321,24 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NymdClient
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_update_mixnode_config(
|
||||
&self,
|
||||
profit_margin_percent: u8,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
|
||||
let req = VestingExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.vesting_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"VestingContract::UpdateMixnetConfig",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
use crate::validator_api::error::ValidatorAPIError;
|
||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use coconut_interface::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, Credential, VerificationKeyResponse,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
@@ -270,6 +273,18 @@ impl Client {
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_credential(
|
||||
&self,
|
||||
request_body: &Credential,
|
||||
) -> Result<VerifyCredentialResponse, ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[routes::API_VERSION, routes::COCONUT_VERIFY_CREDENTIAL],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
|
||||
@@ -12,6 +12,7 @@ pub const REWARDED: &str = "rewarded";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||
pub const COCONUT_VERIFY_CREDENTIAL: &str = "verify-credential";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use getset::{CopyGetters, Getters};
|
||||
use getset::{CopyGetters, Getters, Setters};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub use nymcoconut::*;
|
||||
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
|
||||
#[derive(Serialize, Deserialize, Getters, CopyGetters, Setters, Clone)]
|
||||
pub struct Credential {
|
||||
#[getset(get = "pub")]
|
||||
n_params: u32,
|
||||
@@ -15,6 +15,8 @@ pub struct Credential {
|
||||
public_attributes: Vec<Vec<u8>>,
|
||||
#[getset(get = "pub")]
|
||||
signature: Signature,
|
||||
#[getset(get = "pub", set = "pub")]
|
||||
proposal_id: u64,
|
||||
}
|
||||
impl Credential {
|
||||
pub fn new(
|
||||
@@ -22,12 +24,14 @@ impl Credential {
|
||||
theta: Theta,
|
||||
public_attributes: Vec<Vec<u8>>,
|
||||
signature: &Signature,
|
||||
proposal_id: u64,
|
||||
) -> Credential {
|
||||
Credential {
|
||||
n_params,
|
||||
theta,
|
||||
public_attributes,
|
||||
signature: *signature,
|
||||
proposal_id,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +41,13 @@ impl Credential {
|
||||
|
||||
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
|
||||
let params = Parameters::new(self.n_params).unwrap();
|
||||
let public_attributes = self
|
||||
.public_attributes
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
let mut public_attributes = vec![];
|
||||
for attr in &self.public_attributes {
|
||||
match Attribute::try_from_byte_slice(attr) {
|
||||
Ok(attr) => public_attributes.push(attr),
|
||||
Err(_) => return false,
|
||||
}
|
||||
}
|
||||
nymcoconut::verify_credential(¶ms, verification_key, &self.theta, &public_attributes)
|
||||
}
|
||||
}
|
||||
@@ -88,6 +94,7 @@ pub struct BlindSignRequestBody {
|
||||
public_attributes: Vec<String>,
|
||||
#[getset(get = "pub")]
|
||||
total_params: u32,
|
||||
tx_hash: String,
|
||||
}
|
||||
|
||||
impl BlindSignRequestBody {
|
||||
@@ -96,6 +103,7 @@ impl BlindSignRequestBody {
|
||||
public_key: &nymcoconut::PublicKey,
|
||||
public_attributes: &[Attribute],
|
||||
total_params: u32,
|
||||
tx_hash: String,
|
||||
) -> BlindSignRequestBody {
|
||||
BlindSignRequestBody {
|
||||
blind_sign_request: blind_sign_request.clone(),
|
||||
@@ -105,6 +113,7 @@ impl BlindSignRequestBody {
|
||||
.map(|attr| attr.to_bs58())
|
||||
.collect(),
|
||||
total_params,
|
||||
tx_hash,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,6 +123,10 @@ impl BlindSignRequestBody {
|
||||
.map(|x| Attribute::try_from_bs58(x).unwrap())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn tx_hash(&self) -> &str {
|
||||
&self.tx_hash
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -137,3 +150,8 @@ impl VerificationKeyResponse {
|
||||
VerificationKeyResponse { key }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct VerifyCredentialResponse {
|
||||
pub response: bool,
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ cosmwasm-std = "1.0.0-beta3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
ts-rs = { version = "5.1", optional = true }
|
||||
# Leaving it as * so that it inherits whatever the wallet is using
|
||||
ts-rs = { version = "*", optional = true }
|
||||
thiserror = "1.0"
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
fixed = { version = "1.1", features = ["serde"] }
|
||||
|
||||
@@ -52,6 +52,8 @@ pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size";
|
||||
pub const INTERVAL_ID_KEY: &str = "interval_id";
|
||||
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
|
||||
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
|
||||
pub const TOTAL_PLEDGE_KEY: &str = "pledge";
|
||||
pub const TOTAL_DELEGATIONS_KEY: &str = "delegated";
|
||||
pub const LAMBDA_KEY: &str = "lambda";
|
||||
pub const SIGMA_KEY: &str = "sigma";
|
||||
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
|
||||
@@ -271,10 +273,13 @@ pub fn new_zero_uptime_mix_operator_rewarding_event(
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
node_reward_result: NodeRewardResult,
|
||||
node_pledge: Uint128,
|
||||
node_delegation: Uint128,
|
||||
operator_reward: Uint128,
|
||||
delegation_rewards_distributed: Uint128,
|
||||
further_delegations: bool,
|
||||
@@ -282,6 +287,8 @@ pub fn new_mix_operator_rewarding_event(
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(TOTAL_PLEDGE_KEY, node_pledge)
|
||||
.add_attribute(TOTAL_DELEGATIONS_KEY, node_delegation)
|
||||
.add_attribute(
|
||||
TOTAL_MIXNODE_REWARD_KEY,
|
||||
node_reward_result.reward().to_string(),
|
||||
|
||||
@@ -23,6 +23,10 @@ pub enum ExecuteMsg {
|
||||
UpdateMixnodeConfig {
|
||||
profit_margin_percent: u8,
|
||||
},
|
||||
UpdateMixnodeConfigOnBehalf {
|
||||
profit_margin_percent: u8,
|
||||
owner: String,
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
|
||||
@@ -11,4 +11,5 @@ mixnet-contract-common = { path = "../mixnet-contract" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
cw-storage-plus = "0.11.1"
|
||||
config = {path = "../../config"}
|
||||
config = { path = "../../config" }
|
||||
ts-rs = { version = "*", optional = true }
|
||||
|
||||
@@ -15,6 +15,7 @@ pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
|
||||
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
|
||||
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
||||
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
||||
pub const VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE: &str = "vesting_update_mixnode_config";
|
||||
|
||||
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
||||
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
||||
@@ -108,6 +109,10 @@ pub fn new_vesting_mixnode_bonding_event() -> Event {
|
||||
Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_update_mixnode_config_event() -> Event {
|
||||
Event::new(VESTING_UPDATE_MIXNODE_CONFIG_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_mixnode_unbonding_event() -> Event {
|
||||
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,70 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::Coin;
|
||||
use cosmwasm_std::{Coin, Timestamp};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod events;
|
||||
pub mod messages;
|
||||
|
||||
pub fn one_unym() -> Coin {
|
||||
pub fn one_ucoin() -> Coin {
|
||||
Coin::new(1, DENOM)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub enum Period {
|
||||
Before,
|
||||
In(usize),
|
||||
After,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct PledgeData {
|
||||
amount: Coin,
|
||||
block_time: Timestamp,
|
||||
}
|
||||
|
||||
impl PledgeData {
|
||||
pub fn amount(&self) -> Coin {
|
||||
self.amount.clone()
|
||||
}
|
||||
|
||||
pub fn block_time(&self) -> Timestamp {
|
||||
self.block_time
|
||||
}
|
||||
|
||||
pub fn new(amount: Coin, block_time: Timestamp) -> Self {
|
||||
Self { amount, block_time }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct OriginalVestingResponse {
|
||||
amount: Coin,
|
||||
number_of_periods: usize,
|
||||
period_duration: u64,
|
||||
}
|
||||
|
||||
impl OriginalVestingResponse {
|
||||
pub fn amount(&self) -> Coin {
|
||||
self.amount.clone()
|
||||
}
|
||||
|
||||
pub fn number_of_periods(&self) -> usize {
|
||||
self.number_of_periods
|
||||
}
|
||||
|
||||
pub fn period_duration(&self) -> u64 {
|
||||
self.period_duration
|
||||
}
|
||||
|
||||
pub fn new(amount: Coin, number_of_periods: usize, period_duration: u64) -> Self {
|
||||
Self {
|
||||
amount,
|
||||
number_of_periods,
|
||||
period_duration,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,9 @@ impl VestingSpecification {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ExecuteMsg {
|
||||
UpdateMixnodeConfig {
|
||||
profit_margin_percent: u8,
|
||||
},
|
||||
UpdateMixnetAddress {
|
||||
address: String,
|
||||
},
|
||||
@@ -145,4 +148,7 @@ pub enum QueryMsg {
|
||||
GetGateway {
|
||||
address: String,
|
||||
},
|
||||
GetCurrentVestingPeriod {
|
||||
address: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ use url::Url;
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
use super::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
|
||||
use super::utils::{
|
||||
obtain_aggregate_signature, obtain_aggregate_verify_credential, prepare_credential_for_spending,
|
||||
};
|
||||
|
||||
pub const PUBLIC_ATTRIBUTES: u32 = 2;
|
||||
pub const PRIVATE_ATTRIBUTES: u32 = 2;
|
||||
@@ -46,11 +48,26 @@ pub async fn obtain_signature(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucherAttributes,
|
||||
validators: &[Url],
|
||||
tx_hash: String,
|
||||
) -> 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
|
||||
obtain_aggregate_signature(
|
||||
params,
|
||||
&public_attributes,
|
||||
&private_attributes,
|
||||
validators,
|
||||
tx_hash,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_credential_remote(
|
||||
validators: &[Url],
|
||||
credential: Credential,
|
||||
) -> Result<bool, Error> {
|
||||
obtain_aggregate_verify_credential(validators, credential).await
|
||||
}
|
||||
|
||||
pub fn prepare_for_spending(
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
use coconut_interface::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign,
|
||||
prove_bandwidth_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature,
|
||||
SignatureShare, VerificationKey,
|
||||
prove_bandwidth_credential, Attribute, Base58, BlindSignRequest, BlindSignRequestBody,
|
||||
Credential, ElGamalKeyPair, Parameters, Signature, SignatureShare, VerificationKey,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
@@ -47,17 +47,41 @@ pub async fn obtain_aggregate_verification_key(
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
let response = client.get_coconut_verification_key().await?;
|
||||
|
||||
indices.push(0);
|
||||
indices.push(1);
|
||||
shares.push(response.key);
|
||||
|
||||
for (id, validator_url) in validators.iter().enumerate().skip(1) {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let response = client.get_coconut_verification_key().await?;
|
||||
indices.push(id as u64);
|
||||
indices.push((id + 1) as u64);
|
||||
shares.push(response.key);
|
||||
}
|
||||
let ret = aggregate_verification_keys(&shares, Some(&indices))?;
|
||||
|
||||
Ok(aggregate_verification_keys(&shares, Some(&indices))?)
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn obtain_aggregate_verify_credential(
|
||||
validators: &[Url],
|
||||
credential: Credential,
|
||||
) -> Result<bool, Error> {
|
||||
if validators.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
}
|
||||
|
||||
let mut ret = true;
|
||||
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
let response = client.verify_credential(&credential).await?;
|
||||
ret &= response.response;
|
||||
|
||||
for validator_url in validators.iter().skip(1) {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let response = client.verify_credential(&credential).await?;
|
||||
ret &= response.response;
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
async fn obtain_partial_credential(
|
||||
@@ -66,20 +90,16 @@ async fn obtain_partial_credential(
|
||||
private_attributes: &[Attribute],
|
||||
client: &validator_client::ApiClient,
|
||||
validator_vk: &VerificationKey,
|
||||
blind_sign_request: &BlindSignRequest,
|
||||
elgamal_keypair: &ElGamalKeyPair,
|
||||
tx_hash: String,
|
||||
) -> Result<Signature, Error> {
|
||||
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
|
||||
let blind_sign_request = prepare_blind_sign(
|
||||
params,
|
||||
&elgamal_keypair,
|
||||
private_attributes,
|
||||
public_attributes,
|
||||
)?;
|
||||
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
&blind_sign_request,
|
||||
blind_sign_request,
|
||||
elgamal_keypair.public_key(),
|
||||
public_attributes,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
tx_hash,
|
||||
);
|
||||
|
||||
let blinded_signature = client
|
||||
@@ -104,6 +124,7 @@ pub async fn obtain_aggregate_signature(
|
||||
public_attributes: &[Attribute],
|
||||
private_attributes: &[Attribute],
|
||||
validators: &[Url],
|
||||
tx_hash: String,
|
||||
) -> Result<Signature, Error> {
|
||||
if validators.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
@@ -116,15 +137,26 @@ pub async fn obtain_aggregate_signature(
|
||||
let validator_partial_vk = client.get_coconut_verification_key().await?;
|
||||
validators_partial_vks.push(validator_partial_vk.key.clone());
|
||||
|
||||
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
|
||||
let blind_sign_request = prepare_blind_sign(
|
||||
params,
|
||||
&elgamal_keypair,
|
||||
private_attributes,
|
||||
public_attributes,
|
||||
)?;
|
||||
|
||||
let first = obtain_partial_credential(
|
||||
params,
|
||||
public_attributes,
|
||||
private_attributes,
|
||||
&client,
|
||||
&validator_partial_vk.key,
|
||||
&blind_sign_request,
|
||||
&elgamal_keypair,
|
||||
tx_hash.clone(),
|
||||
)
|
||||
.await?;
|
||||
shares.push(SignatureShare::new(first, 0));
|
||||
shares.push(SignatureShare::new(first, 1));
|
||||
|
||||
for (id, validator_url) in validators.iter().enumerate().skip(1) {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
@@ -136,9 +168,12 @@ pub async fn obtain_aggregate_signature(
|
||||
private_attributes,
|
||||
&client,
|
||||
&validator_partial_vk.key,
|
||||
&blind_sign_request,
|
||||
&elgamal_keypair,
|
||||
tx_hash.clone(),
|
||||
)
|
||||
.await?;
|
||||
let share = SignatureShare::new(signature, id as u64);
|
||||
let share = SignatureShare::new(signature, (id + 1) as u64);
|
||||
shares.push(share)
|
||||
}
|
||||
|
||||
@@ -147,11 +182,12 @@ pub async fn obtain_aggregate_signature(
|
||||
attributes.extend_from_slice(public_attributes);
|
||||
|
||||
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
|
||||
for i in 1..validators_partial_vks.len() {
|
||||
indices.push(i as u64);
|
||||
for i in 0..validators_partial_vks.len() {
|
||||
indices.push((i + 1) as u64);
|
||||
}
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
println!("Verification key: {}", verification_key.to_bs58());
|
||||
|
||||
Ok(aggregate_signature_shares(
|
||||
params,
|
||||
@@ -183,5 +219,6 @@ pub fn prepare_credential_for_spending(
|
||||
theta,
|
||||
public_attributes,
|
||||
signature,
|
||||
0,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -4,129 +4,546 @@
|
||||
// This should be modified whenever an updated Ethereum contract is uploaded
|
||||
pub const ETH_JSON_ABI: &str = r#"
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract ERC20Burnable",
|
||||
"name": "_erc20",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "Bandwidth",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "VerificationKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes",
|
||||
"name": "SignedVerificationKey",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "Burned",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "previousOwner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnershipTransferred",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "verificationKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "signedVerificationKey",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "burnTokenForAccessCode",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "erc20",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract ERC20Burnable",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "owner",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "renounceOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "transferOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract CosmosERC20",
|
||||
"name": "_erc20",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "contract Gravity",
|
||||
"name": "_gravityBridge",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "Bandwidth",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "VerificationKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes",
|
||||
"name": "SignedVerificationKey",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "string",
|
||||
"name": "CosmosRecipient",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "BBCredentialPurchased",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bool",
|
||||
"name": "Enabled",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"name": "CredentialGenerationSwitch",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "previousOwner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "OwnershipTransferred",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "uint256",
|
||||
"name": "NewBytesPerToken",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "RatioChanged",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "BytesPerToken",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "bandwidthFromToken",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_newBytesPerTokenAmount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "changeRatio",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "credentialGenerationEnabled",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "_generation",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"name": "credentialGenerationSwitch",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "erc20",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract CosmosERC20",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_amount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_verificationKey",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_signedVerificationKey",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "_cosmosRecipient",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"name": "generateBasicBandwidthCredential",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "gravityBridge",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract Gravity",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "owner",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "renounceOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "newOwner",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "transferOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
"#;
|
||||
|
||||
pub const ETH_ERC20_JSON_ABI: &str = r#"
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "name_",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "symbol_",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "account",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "subtractedValue",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "decreaseAllowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "addedValue",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "increaseAllowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
"#;
|
||||
|
||||
@@ -17,6 +17,8 @@ cfg_if::cfg_if! {
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = mainnet::MIXNET_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = mainnet::VESTING_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
|
||||
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = mainnet::REWARDING_VALIDATOR_ADDRESS;
|
||||
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
@@ -33,6 +35,8 @@ cfg_if::cfg_if! {
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = qa::MIXNET_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = qa::VESTING_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS;
|
||||
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_REWARDING_VALIDATOR: &str = qa::REWARDING_VALIDATOR_ADDRESS;
|
||||
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
@@ -49,6 +53,8 @@ cfg_if::cfg_if! {
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = sandbox::MIXNET_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = sandbox::VESTING_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS;
|
||||
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_REWARDING_VALIDATOR: &str = sandbox::REWARDING_VALIDATOR_ADDRESS;
|
||||
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
@@ -106,18 +112,22 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
pub const ETH_EVENT_NAME: &str = "BBCredentialPurchased";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "generateBasicBandwidthCredential";
|
||||
pub const ETH_ERC20_APPROVE_FUNCTION_NAME: &str = "approve";
|
||||
|
||||
// Ethereum constants used for token bridge
|
||||
/// How much bandwidth (in bytes) one token can buy
|
||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||
|
||||
/// Threshold for claiming more bandwidth: 1 MB
|
||||
pub const REMAINING_BANDWIDTH_THRESHOLD: i64 = 1024 * 1024;
|
||||
/// How many ERC20 tokens should be burned to buy bandwidth
|
||||
pub const TOKENS_TO_BURN: u64 = 10;
|
||||
pub const TOKENS_TO_BURN: u64 = 1;
|
||||
/// How many ERC20 utokens should be burned to buy bandwidth
|
||||
pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
|
||||
/// Default bandwidth (in bytes) that we try to buy
|
||||
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "n17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
|
||||
@@ -6,10 +6,14 @@ use crate::ValidatorDetails;
|
||||
pub(crate) const BECH32_PREFIX: &str = "nymt";
|
||||
pub const DENOM: &str = "unymt";
|
||||
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt17x6pt4msccvawgxjeg5nmnygttu56tftg5l6j3";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1t4dmskxea0avvrj8xtmu66hv7dkyg9s8059t3c";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("0000000000000000000000000000000000000000");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
|
||||
@@ -7,10 +7,14 @@ pub(crate) const BECH32_PREFIX: &str = "nymt";
|
||||
pub const DENOM: &str = "unymt";
|
||||
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt14ejqjyq8um4p3xfqj74yld5waqljf88fn549lh";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
pub(crate) const _ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("8e0DcFF7F3085235C32E845f3667aEB3f1e83133");
|
||||
pub(crate) const _ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("E8883BAeF3869e14E4823F46662e81D4F7d2A81F");
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1jh0s6qu6tuw9ut438836mmn7f3f2wencrnmdj4";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
extern crate core;
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::Scalar;
|
||||
|
||||
pub use crate::traits::Bytable;
|
||||
pub use elgamal::elgamal_keygen;
|
||||
pub use elgamal::ElGamalKeyPair;
|
||||
pub use elgamal::PublicKey;
|
||||
@@ -28,8 +31,6 @@ pub use scheme::SignatureShare;
|
||||
pub use traits::Base58;
|
||||
pub use utils::hash_to_scalar;
|
||||
|
||||
use crate::traits::Bytable;
|
||||
|
||||
pub mod elgamal;
|
||||
mod error;
|
||||
mod impls;
|
||||
|
||||
@@ -206,10 +206,17 @@ pub fn verify_credential(
|
||||
public_attributes: &[Attribute],
|
||||
) -> bool {
|
||||
if public_attributes.len() + theta.pi_v.private_attributes_len() > verification_key.beta.len() {
|
||||
println!(
|
||||
"Len fail: {} + {} > {}",
|
||||
public_attributes.len(),
|
||||
theta.pi_v.private_attributes_len(),
|
||||
verification_key.beta.len()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if !theta.verify_proof(params, verification_key) {
|
||||
println!("Proof fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -230,12 +237,14 @@ pub fn verify_credential(
|
||||
theta.blinded_message + signed_public_attributes
|
||||
};
|
||||
|
||||
check_bilinear_pairing(
|
||||
let ret = check_bilinear_pairing(
|
||||
&theta.credential.0.to_affine(),
|
||||
&G2Prepared::from(kappa.to_affine()),
|
||||
&(theta.credential.1).to_affine(),
|
||||
params.prepared_miller_g2(),
|
||||
) && !bool::from(theta.credential.0.is_identity())
|
||||
);
|
||||
println!("Ret value {}", ret);
|
||||
ret && !bool::from(theta.credential.0.is_identity())
|
||||
}
|
||||
|
||||
// Used in tests only
|
||||
|
||||
@@ -8,11 +8,9 @@ edition = "2021"
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
config = { path = "../../common/config"}
|
||||
|
||||
[dependencies]
|
||||
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
|
||||
config = { path = "../../common/config"}
|
||||
|
||||
cosmwasm-std = "1.0.0-beta3"
|
||||
cosmwasm-storage = "1.0.0-beta3"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use config::defaults::DENOM;
|
||||
|
||||
use cosmwasm_std::{StdError, VerificationError};
|
||||
use thiserror::Error;
|
||||
|
||||
@@ -24,4 +26,16 @@ pub enum ContractError {
|
||||
|
||||
#[error("The payment is not properly signed")]
|
||||
BadSignature,
|
||||
|
||||
#[error("Received multiple coin types")]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("No coin was sent for voucher")]
|
||||
NoCoin,
|
||||
|
||||
#[error("Wrong coin denomination, you must send {}", DENOM)]
|
||||
WrongDenom,
|
||||
|
||||
#[error("The sender is not authorized to perform this action")]
|
||||
Unauthorized,
|
||||
}
|
||||
|
||||
@@ -39,6 +39,10 @@ pub fn execute(
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::LinkPayment { data } => transactions::link_payment(deps, env, info, data),
|
||||
ExecuteMsg::BuyBandwidth {} => transactions::buy_bandwidth(deps, env, info),
|
||||
ExecuteMsg::SpendCredential { amount } => {
|
||||
transactions::spend_credential(deps, env, info, amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use bandwidth_claim_contract::payment::Payment;
|
||||
// buckets
|
||||
const PREFIX_PAYMENTS: &[u8] = b"payments";
|
||||
const PREFIX_STATUS: &[u8] = b"status";
|
||||
const PREFIX_COCONUT: &[u8] = b"coconut";
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub enum Status {
|
||||
@@ -31,6 +32,14 @@ pub fn status(storage: &mut dyn Storage) -> Bucket<'_, Status> {
|
||||
bucket(storage, PREFIX_STATUS)
|
||||
}
|
||||
|
||||
pub fn coconut(storage: &mut dyn Storage) -> Bucket<'_, Payment> {
|
||||
bucket(storage, PREFIX_COCONUT)
|
||||
}
|
||||
|
||||
pub fn coconut_read(storage: &dyn Storage) -> ReadonlyBucket<'_, Payment> {
|
||||
bucket_read(storage, PREFIX_COCONUT)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
use cosmwasm_std::{Addr, BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Response};
|
||||
|
||||
use crate::error::ContractError;
|
||||
use crate::storage::{payments, status, Status};
|
||||
use crate::storage::{coconut, payments, status, Status};
|
||||
use bandwidth_claim_contract::events::{VOUCHER_ACQUIRED_EVENT_TYPE, VOUCHER_VALUE};
|
||||
use bandwidth_claim_contract::payment::{LinkPaymentData, Payment};
|
||||
use config::defaults::DENOM;
|
||||
|
||||
pub(crate) fn link_payment(
|
||||
deps: DepsMut<'_>,
|
||||
@@ -44,6 +46,43 @@ pub(crate) fn link_payment(
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
pub(crate) fn buy_bandwidth(
|
||||
_deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
if info.funds.is_empty() {
|
||||
return Err(ContractError::NoCoin);
|
||||
}
|
||||
if info.funds.len() > 1 {
|
||||
return Err(ContractError::MultipleDenoms);
|
||||
}
|
||||
if info.funds[0].denom != DENOM {
|
||||
return Err(ContractError::WrongDenom);
|
||||
}
|
||||
|
||||
let voucher_value = info.funds.last().unwrap();
|
||||
let event =
|
||||
Event::new(VOUCHER_ACQUIRED_EVENT_TYPE).add_attribute(VOUCHER_VALUE, voucher_value.amount);
|
||||
Ok(Response::new().add_event(event))
|
||||
}
|
||||
|
||||
pub(crate) fn spend_credential(
|
||||
_deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
info: MessageInfo,
|
||||
amount: u64,
|
||||
) -> Result<Response, ContractError> {
|
||||
if info.sender != Addr::unchecked(String::from("nymt1qwlgtx52gsdu7dtp0cekka5zehdl0uj3vqx3jd")) {
|
||||
return Err(ContractError::Unauthorized);
|
||||
}
|
||||
let return_tokens = BankMsg::Send {
|
||||
to_address: String::from("nymt1t6p4dl8nnlftvehz3jsklrd0aw458p4l6n9n4t"),
|
||||
amount: vec![Coin::new(amount as u128, DENOM)],
|
||||
};
|
||||
Ok(Response::new().add_message(return_tokens))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"
|
||||
},
|
||||
"mainnet":{
|
||||
"NYM_ERC20":"0xCf6DeE9947fdDc958985E5283e63d41CBC83Ff61",
|
||||
"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
|
||||
"NYMT": "0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
|
||||
"BANDWIDTH_GENERATOR":"",
|
||||
"BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be",
|
||||
|
||||
+7
-21
@@ -6524,9 +6524,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||
"version": "1.14.8",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
|
||||
"integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
|
||||
"dev": true
|
||||
},
|
||||
"for-each": {
|
||||
@@ -14257,21 +14257,7 @@
|
||||
"simple-concat": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"snapdragon": {
|
||||
"version": "0.8.2",
|
||||
@@ -19376,9 +19362,9 @@
|
||||
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="
|
||||
},
|
||||
"simple-get": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz",
|
||||
"integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==",
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.2.tgz",
|
||||
"integrity": "sha512-Ijd/rV5o+mSBBs4F/x9oDPtTx9Zb6X9brmnXvMW4J7IR15ngi9q5xxqWBKU744jTZiaXtxaPL7uHG6vtN8kUkw==",
|
||||
"requires": {
|
||||
"decompress-response": "^3.3.0",
|
||||
"once": "^1.3.1",
|
||||
|
||||
@@ -122,6 +122,16 @@ pub fn execute(
|
||||
info,
|
||||
profit_margin_percent,
|
||||
),
|
||||
ExecuteMsg::UpdateMixnodeConfigOnBehalf {
|
||||
profit_margin_percent,
|
||||
owner,
|
||||
} => crate::mixnodes::transactions::try_update_mixnode_config_on_behalf(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
profit_margin_percent,
|
||||
owner,
|
||||
),
|
||||
ExecuteMsg::BondGateway {
|
||||
gateway,
|
||||
owner_signature,
|
||||
@@ -315,64 +325,9 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
|
||||
Ok(query_res?)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(deps: DepsMut<'_>, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
use cw_storage_plus::Item;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// needed migration:
|
||||
/*
|
||||
1. removal of rewarding_interval_starting_block field from ContractState
|
||||
2. removal of latest_rewarding_interval_nonce field from ContractState
|
||||
3. removal of rewarding_in_progress field from ContractState
|
||||
4. interval_storage::CURRENT_INTERVAL.save(deps.storage, &Interval::default())?;
|
||||
5. interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
|
||||
6. removal of active_set_work_factor fields from ContractStateParams
|
||||
*/
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct OldContractStateParams {
|
||||
pub minimum_mixnode_pledge: Uint128,
|
||||
pub minimum_gateway_pledge: Uint128,
|
||||
pub mixnode_rewarded_set_size: u32,
|
||||
pub mixnode_active_set_size: u32,
|
||||
pub active_set_work_factor: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct OldContractState {
|
||||
pub owner: Addr, // only the owner account can update state
|
||||
pub rewarding_validator_address: Addr,
|
||||
pub params: OldContractStateParams,
|
||||
pub rewarding_interval_starting_block: u64,
|
||||
pub latest_rewarding_interval_nonce: u32,
|
||||
pub rewarding_in_progress: bool,
|
||||
}
|
||||
|
||||
let old_contract_state: Item<'_, OldContractState> = Item::new("config");
|
||||
|
||||
let old_state = old_contract_state.load(deps.storage)?;
|
||||
|
||||
let new_params = mixnet_contract_common::ContractStateParams {
|
||||
minimum_mixnode_pledge: old_state.params.minimum_mixnode_pledge,
|
||||
minimum_gateway_pledge: old_state.params.minimum_mixnode_pledge,
|
||||
mixnode_rewarded_set_size: old_state.params.mixnode_rewarded_set_size,
|
||||
mixnode_active_set_size: old_state.params.mixnode_active_set_size,
|
||||
};
|
||||
|
||||
let new_state = crate::mixnet_contract_settings::models::ContractState {
|
||||
owner: old_state.owner,
|
||||
rewarding_validator_address: old_state.rewarding_validator_address,
|
||||
params: new_params,
|
||||
};
|
||||
let rewarding_interval =
|
||||
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
|
||||
|
||||
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
|
||||
|
||||
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
|
||||
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
|
||||
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use cw_storage_plus::PrimaryKey;
|
||||
use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event};
|
||||
use mixnet_contract_common::{Delegation, IdentityKey};
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
use vesting_contract_common::one_unym;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
|
||||
// check if anything was put as delegation
|
||||
@@ -213,7 +213,7 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
|
||||
amount: old_delegation.amount.clone(),
|
||||
});
|
||||
|
||||
let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_unym()])?;
|
||||
let track_undelegation_msg = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
|
||||
|
||||
response = response.add_message(track_undelegation_msg);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use cosmwasm_std::{
|
||||
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
|
||||
use mixnet_contract_common::{Gateway, GatewayBond, Layer};
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
use vesting_contract_common::one_unym;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
pub fn try_add_gateway(
|
||||
deps: DepsMut<'_>,
|
||||
@@ -177,7 +177,7 @@ pub(crate) fn _try_remove_gateway(
|
||||
amount: gateway_bond.pledge_amount(),
|
||||
};
|
||||
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?;
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
|
||||
response = response.add_message(track_unbond_message);
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use cosmwasm_std::{
|
||||
use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event};
|
||||
use mixnet_contract_common::MixNode;
|
||||
use vesting_contract_common::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
use vesting_contract_common::one_unym;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
pub fn try_add_mixnode(
|
||||
deps: DepsMut<'_>,
|
||||
@@ -201,7 +201,7 @@ pub(crate) fn _try_remove_mixnode(
|
||||
amount: mixnode_bond.pledge_amount(),
|
||||
};
|
||||
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_unym()])?;
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, vec![one_ucoin()])?;
|
||||
response = response.add_message(track_unbond_message);
|
||||
}
|
||||
|
||||
@@ -222,14 +222,43 @@ pub(crate) fn try_update_mixnode_config(
|
||||
profit_margin_percent: u8,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = deps.api.addr_validate(info.sender.as_ref())?;
|
||||
let mix_identity = storage::mixnodes()
|
||||
_try_update_mixnode_config(deps, env, profit_margin_percent, owner, None)
|
||||
}
|
||||
|
||||
pub(crate) fn try_update_mixnode_config_on_behalf(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
profit_margin_percent: u8,
|
||||
owner: String,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = deps.api.addr_validate(&owner)?;
|
||||
let proxy = deps.api.addr_validate(info.sender.as_ref())?;
|
||||
_try_update_mixnode_config(deps, env, profit_margin_percent, owner, Some(proxy))
|
||||
}
|
||||
|
||||
pub(crate) fn _try_update_mixnode_config(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
profit_margin_percent: u8,
|
||||
owner: Addr,
|
||||
proxy: Option<Addr>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let mixnode_bond = storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, owner.clone())?
|
||||
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
|
||||
.1
|
||||
.identity()
|
||||
.clone();
|
||||
.1;
|
||||
|
||||
if proxy != mixnode_bond.proxy {
|
||||
return Err(ContractError::ProxyMismatch {
|
||||
existing: mixnode_bond
|
||||
.proxy
|
||||
.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
|
||||
incoming: proxy.map_or_else(|| "None".to_string(), |a| a.as_str().to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
// We don't have to check lower bound as its an u8
|
||||
if profit_margin_percent > 100 {
|
||||
@@ -238,7 +267,7 @@ pub(crate) fn try_update_mixnode_config(
|
||||
));
|
||||
}
|
||||
|
||||
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
|
||||
storage::mixnodes().update(deps.storage, mixnode_bond.identity(), |mixnode_bond_opt| {
|
||||
mixnode_bond_opt
|
||||
.map(|mut mixnode_bond| {
|
||||
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
|
||||
@@ -248,7 +277,19 @@ pub(crate) fn try_update_mixnode_config(
|
||||
.ok_or(ContractError::NoBondFound)
|
||||
})?;
|
||||
|
||||
Ok(Response::new())
|
||||
let mut response = Response::new();
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
// Returns one_ucoin proxy had to send in order to execute the contract to contract transaction, this is potentially leaky as anyone can say that they're a proxy,
|
||||
// and they could potentially leak 1 unym per transaction, altough I'm pretty sure transaction fees make that silly.
|
||||
let return_one_ucoint = BankMsg::Send {
|
||||
to_address: proxy.as_str().to_string(),
|
||||
amount: vec![one_ucoin()],
|
||||
};
|
||||
response = response.add_message(return_one_ucoint);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn validate_mixnode_pledge(
|
||||
|
||||
@@ -260,6 +260,9 @@ pub(crate) fn try_reward_mixnode(
|
||||
);
|
||||
}
|
||||
|
||||
let node_pledge = current_bond.pledge_amount.amount;
|
||||
let node_delegation = current_bond.total_delegation.amount;
|
||||
|
||||
// check if it has non-zero uptime
|
||||
if params.uptime() == 0 {
|
||||
storage::REWARDING_STATUS.save(
|
||||
@@ -315,6 +318,8 @@ pub(crate) fn try_reward_mixnode(
|
||||
interval_id,
|
||||
&mix_identity,
|
||||
node_reward_result,
|
||||
node_pledge,
|
||||
node_delegation,
|
||||
operator_reward,
|
||||
total_delegator_reward,
|
||||
further_delegations,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::storage::{account_from_address, ADMIN, MIXNET_CONTRACT_ADDRESS};
|
||||
use crate::traits::{
|
||||
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
|
||||
};
|
||||
use crate::vesting::{populate_vesting_periods, Account, PledgeData};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{
|
||||
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, QueryResponse,
|
||||
@@ -18,6 +18,7 @@ use vesting_contract_common::events::{
|
||||
use vesting_contract_common::messages::{
|
||||
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
|
||||
};
|
||||
use vesting_contract_common::{OriginalVestingResponse, Period, PledgeData};
|
||||
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
@@ -45,6 +46,9 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
} => try_update_mixnode_config(profit_margin_percent, info, deps),
|
||||
ExecuteMsg::UpdateMixnetAddress { address } => {
|
||||
try_update_mixnet_address(address, info, deps)
|
||||
}
|
||||
@@ -102,6 +106,15 @@ pub fn execute(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_update_mixnode_config(
|
||||
profit_margin_percent: u8,
|
||||
info: MessageInfo,
|
||||
deps: DepsMut,
|
||||
) -> Result<Response, ContractError> {
|
||||
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
account.try_update_mixnode_config(profit_margin_percent, deps.storage)
|
||||
}
|
||||
|
||||
// Only contract admin, set at init
|
||||
pub fn try_update_mixnet_address(
|
||||
address: String,
|
||||
@@ -431,11 +444,23 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
|
||||
QueryMsg::GetAccount { address } => to_binary(&try_get_account(&address, deps)?),
|
||||
QueryMsg::GetMixnode { address } => to_binary(&try_get_mixnode(&address, deps)?),
|
||||
QueryMsg::GetGateway { address } => to_binary(&try_get_gateway(&address, deps)?),
|
||||
QueryMsg::GetCurrentVestingPeriod { address } => {
|
||||
to_binary(&try_get_current_vesting_period(&address, deps, env)?)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
}
|
||||
|
||||
pub fn try_get_current_vesting_period(
|
||||
address: &str,
|
||||
deps: Deps<'_>,
|
||||
env: Env,
|
||||
) -> Result<Period, ContractError> {
|
||||
let account = account_from_address(address, deps.storage, deps.api)?;
|
||||
Ok(account.get_current_vesting_period(env.block.time))
|
||||
}
|
||||
|
||||
pub fn try_get_mixnode(address: &str, deps: Deps<'_>) -> Result<Option<PledgeData>, ContractError> {
|
||||
let account = account_from_address(address, deps.storage, deps.api)?;
|
||||
account.load_mixnode_pledge(deps.storage)
|
||||
@@ -509,7 +534,7 @@ pub fn try_get_end_time(
|
||||
pub fn try_get_original_vesting(
|
||||
vesting_account_address: &str,
|
||||
deps: Deps<'_>,
|
||||
) -> Result<Coin, ContractError> {
|
||||
) -> Result<OriginalVestingResponse, ContractError> {
|
||||
let account = account_from_address(vesting_account_address, deps.storage, deps.api)?;
|
||||
Ok(account.get_original_vesting())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::errors::ContractError;
|
||||
use crate::vesting::Account;
|
||||
use crate::vesting::PledgeData;
|
||||
use cosmwasm_std::{Addr, Api, Storage, Uint128};
|
||||
use cw_storage_plus::{Item, Map};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use vesting_contract_common::PledgeData;
|
||||
|
||||
type BlockHeight = u64;
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ pub trait MixnodeBondingAccount {
|
||||
amount: Coin,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError>;
|
||||
|
||||
fn try_update_mixnode_config(
|
||||
&self,
|
||||
profit_margin_percent: u8,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<Response, ContractError>;
|
||||
}
|
||||
|
||||
pub trait GatewayBondingAccount {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::errors::ContractError;
|
||||
use cosmwasm_std::{Addr, Coin, Env, Storage, Timestamp};
|
||||
use vesting_contract_common::OriginalVestingResponse;
|
||||
|
||||
pub trait VestingAccount {
|
||||
// locked_coins returns the set of coins that are not spendable (can still be delegated tough) (i.e. locked),
|
||||
@@ -37,7 +38,7 @@ pub trait VestingAccount {
|
||||
fn get_start_time(&self) -> Timestamp;
|
||||
fn get_end_time(&self) -> Timestamp;
|
||||
|
||||
fn get_original_vesting(&self) -> Coin;
|
||||
fn get_original_vesting(&self) -> OriginalVestingResponse;
|
||||
fn get_delegated_free(
|
||||
&self,
|
||||
block_time: Option<Timestamp>,
|
||||
|
||||
@@ -8,7 +8,7 @@ use mixnet_contract_common::IdentityKey;
|
||||
use vesting_contract_common::events::{
|
||||
new_vesting_delegation_event, new_vesting_undelegation_event,
|
||||
};
|
||||
use vesting_contract_common::one_unym;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
use super::Account;
|
||||
|
||||
@@ -70,7 +70,7 @@ impl DelegatingAccount for Account {
|
||||
let undelegate_from_mixnode = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_unym()],
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new()
|
||||
|
||||
@@ -7,7 +7,7 @@ use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, Gateway};
|
||||
use vesting_contract_common::events::{
|
||||
new_vesting_gateway_bonding_event, new_vesting_gateway_unbonding_event,
|
||||
};
|
||||
use vesting_contract_common::one_unym;
|
||||
use vesting_contract_common::one_ucoin;
|
||||
|
||||
use super::Account;
|
||||
|
||||
@@ -34,10 +34,7 @@ impl GatewayBondingAccount for Account {
|
||||
self.owner_address().as_str().to_string(),
|
||||
));
|
||||
} else {
|
||||
PledgeData {
|
||||
block_time: env.block.time,
|
||||
amount: pledge.amount,
|
||||
}
|
||||
PledgeData::new(pledge.clone(), env.block.time)
|
||||
};
|
||||
|
||||
let msg = MixnetExecuteMsg::BondGatewayOnBehalf {
|
||||
@@ -68,7 +65,7 @@ impl GatewayBondingAccount for Account {
|
||||
let unbond_msg = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_unym()],
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new()
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use super::PledgeData;
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||
use crate::traits::MixnodeBondingAccount;
|
||||
@@ -6,12 +5,36 @@ use cosmwasm_std::{wasm_execute, Coin, Env, Response, Storage, Uint128};
|
||||
use mixnet_contract_common::{ExecuteMsg as MixnetExecuteMsg, MixNode};
|
||||
use vesting_contract_common::events::{
|
||||
new_vesting_mixnode_bonding_event, new_vesting_mixnode_unbonding_event,
|
||||
new_vesting_update_mixnode_config_event,
|
||||
};
|
||||
use vesting_contract_common::one_unym;
|
||||
|
||||
use vesting_contract_common::one_ucoin;
|
||||
use vesting_contract_common::PledgeData;
|
||||
|
||||
use super::Account;
|
||||
|
||||
impl MixnodeBondingAccount for Account {
|
||||
fn try_update_mixnode_config(
|
||||
&self,
|
||||
profit_margin_percent: u8,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<Response, ContractError> {
|
||||
let msg = MixnetExecuteMsg::UpdateMixnodeConfigOnBehalf {
|
||||
profit_margin_percent,
|
||||
owner: self.owner_address().into_string(),
|
||||
};
|
||||
|
||||
let update_mixnode_config_msg = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_message(update_mixnode_config_msg)
|
||||
.add_event(new_vesting_update_mixnode_config_event()))
|
||||
}
|
||||
|
||||
fn try_bond_mixnode(
|
||||
&self,
|
||||
mix_node: MixNode,
|
||||
@@ -34,10 +57,7 @@ impl MixnodeBondingAccount for Account {
|
||||
self.owner_address().as_str().to_string(),
|
||||
));
|
||||
} else {
|
||||
PledgeData {
|
||||
block_time: env.block.time,
|
||||
amount: pledge.amount,
|
||||
}
|
||||
PledgeData::new(pledge.clone(), env.block.time)
|
||||
};
|
||||
|
||||
let msg = MixnetExecuteMsg::BondMixnodeOnBehalf {
|
||||
@@ -68,7 +88,7 @@ impl MixnodeBondingAccount for Account {
|
||||
let unbond_msg = wasm_execute(
|
||||
MIXNET_CONTRACT_ADDRESS.load(storage)?,
|
||||
&msg,
|
||||
vec![one_unym()],
|
||||
vec![one_ucoin()],
|
||||
)?;
|
||||
|
||||
Ok(Response::new()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PledgeData, VestingPeriod};
|
||||
use super::VestingPeriod;
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::{
|
||||
load_balance, load_bond_pledge, load_gateway_pledge, remove_bond_pledge, remove_delegation,
|
||||
@@ -10,6 +10,7 @@ use cw_storage_plus::Bound;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vesting_contract_common::{Period, PledgeData};
|
||||
|
||||
mod delegating_account;
|
||||
mod gateway_bonding_account;
|
||||
@@ -60,6 +61,10 @@ impl Account {
|
||||
self.periods.len()
|
||||
}
|
||||
|
||||
pub fn period_duration(&self) -> u64 {
|
||||
self.periods.get(0).unwrap().period_seconds
|
||||
}
|
||||
|
||||
pub fn storage_key(&self) -> u32 {
|
||||
self.storage_key
|
||||
}
|
||||
@@ -92,24 +97,23 @@ impl Account {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> usize {
|
||||
pub fn get_current_vesting_period(&self, block_time: Timestamp) -> Period {
|
||||
// Returns the index of the next vesting period. Unless the current time is somehow in the past or vesting has not started yet.
|
||||
// In case vesting is over it will always return NUM_VESTING_PERIODS.
|
||||
let period = match self
|
||||
.periods
|
||||
.iter()
|
||||
.map(|period| period.start_time)
|
||||
.collect::<Vec<u64>>()
|
||||
.binary_search(&block_time.seconds())
|
||||
{
|
||||
Ok(u) => u,
|
||||
Err(u) => u,
|
||||
};
|
||||
|
||||
if period > 0 {
|
||||
period - 1
|
||||
if block_time.seconds() < self.periods.first().unwrap().start_time {
|
||||
Period::Before
|
||||
} else if self.periods.last().unwrap().end_time() < block_time {
|
||||
Period::After
|
||||
} else {
|
||||
0
|
||||
let mut index = 0;
|
||||
for period in &self.periods {
|
||||
if block_time < period.end_time() {
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
Period::In(index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::storage::{delete_account, save_account, DELEGATIONS};
|
||||
use crate::traits::VestingAccount;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{Addr, Coin, Env, Order, Storage, Timestamp, Uint128};
|
||||
use vesting_contract_common::{OriginalVestingResponse, Period};
|
||||
|
||||
use super::Account;
|
||||
|
||||
@@ -61,17 +62,15 @@ impl VestingAccount for Account {
|
||||
let period = self.get_current_vesting_period(block_time);
|
||||
|
||||
let amount = match period {
|
||||
// We're in the first period, or the vesting has not started yet.
|
||||
0 => Coin {
|
||||
Period::Before => Coin {
|
||||
amount: Uint128::new(0),
|
||||
denom: DENOM.to_string(),
|
||||
},
|
||||
// We always have 8 vesting periods, so periods 1-7 are special
|
||||
1..=7 => Coin {
|
||||
amount: Uint128::new(self.tokens_per_period()? * period as u128),
|
||||
Period::In(idx) => Coin {
|
||||
amount: Uint128::new(self.tokens_per_period()? * idx as u128),
|
||||
denom: DENOM.to_string(),
|
||||
},
|
||||
_ => Coin {
|
||||
Period::After => Coin {
|
||||
amount: self.coin.amount,
|
||||
denom: DENOM.to_string(),
|
||||
},
|
||||
@@ -85,7 +84,7 @@ impl VestingAccount for Account {
|
||||
env: &Env,
|
||||
) -> Result<Coin, ContractError> {
|
||||
Ok(Coin {
|
||||
amount: self.get_original_vesting().amount
|
||||
amount: self.get_original_vesting().amount().amount
|
||||
- self.get_vested_coins(block_time, env)?.amount,
|
||||
denom: DENOM.to_string(),
|
||||
})
|
||||
@@ -99,8 +98,12 @@ impl VestingAccount for Account {
|
||||
self.periods[(self.num_vesting_periods() - 1) as usize].end_time()
|
||||
}
|
||||
|
||||
fn get_original_vesting(&self) -> Coin {
|
||||
self.coin.clone()
|
||||
fn get_original_vesting(&self) -> OriginalVestingResponse {
|
||||
OriginalVestingResponse::new(
|
||||
self.coin.clone(),
|
||||
self.num_vesting_periods(),
|
||||
self.period_duration(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_delegated_free(
|
||||
@@ -111,8 +114,12 @@ impl VestingAccount for Account {
|
||||
) -> Result<Coin, ContractError> {
|
||||
let block_time = block_time.unwrap_or(env.block.time);
|
||||
let period = self.get_current_vesting_period(block_time);
|
||||
let max_vested = self.tokens_per_period()? * period as u128;
|
||||
let start_time = self.periods[period].start_time;
|
||||
let max_vested = self.get_vested_coins(Some(block_time), env)?;
|
||||
let start_time = match period {
|
||||
Period::Before => 0,
|
||||
Period::After => u64::MAX,
|
||||
Period::In(idx) => self.periods[idx as usize].start_time,
|
||||
};
|
||||
|
||||
let coin = DELEGATIONS
|
||||
.sub_prefix(self.storage_key())
|
||||
@@ -123,7 +130,7 @@ impl VestingAccount for Account {
|
||||
acc + amount
|
||||
});
|
||||
|
||||
let amount = Uint128::new(coin.u128().min(max_vested));
|
||||
let amount = Uint128::new(coin.u128().min(max_vested.amount.u128()));
|
||||
|
||||
Ok(Coin {
|
||||
amount,
|
||||
@@ -157,15 +164,19 @@ impl VestingAccount for Account {
|
||||
) -> Result<Coin, ContractError> {
|
||||
let block_time = block_time.unwrap_or(env.block.time);
|
||||
let period = self.get_current_vesting_period(block_time);
|
||||
let max_vested = self.tokens_per_period()? * period as u128;
|
||||
let start_time = self.periods[period].start_time;
|
||||
let max_vested = self.get_vested_coins(Some(block_time), env)?;
|
||||
let start_time = match period {
|
||||
Period::Before => 0,
|
||||
Period::After => u64::MAX,
|
||||
Period::In(idx) => self.periods[idx as usize].start_time,
|
||||
};
|
||||
|
||||
let amount = if let Some(bond) = self
|
||||
.load_mixnode_pledge(storage)?
|
||||
.or(self.load_gateway_pledge(storage)?)
|
||||
{
|
||||
if bond.block_time.seconds() < start_time {
|
||||
bond.amount
|
||||
if bond.block_time().seconds() < start_time {
|
||||
bond.amount().amount
|
||||
} else {
|
||||
Uint128::zero()
|
||||
}
|
||||
@@ -173,7 +184,7 @@ impl VestingAccount for Account {
|
||||
Uint128::zero()
|
||||
};
|
||||
|
||||
let amount = Uint128::new(amount.u128().min(max_vested));
|
||||
let amount = Uint128::new(amount.u128().min(max_vested.amount.u128()));
|
||||
|
||||
Ok(Coin {
|
||||
amount,
|
||||
@@ -194,7 +205,7 @@ impl VestingAccount for Account {
|
||||
.load_mixnode_pledge(storage)?
|
||||
.or(self.load_gateway_pledge(storage)?)
|
||||
{
|
||||
let amount = bond.amount - bonded_free.amount;
|
||||
let amount = bond.amount().amount - bonded_free.amount;
|
||||
Ok(Coin {
|
||||
amount,
|
||||
denom: DENOM.to_string(),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use cosmwasm_std::{Timestamp, Uint128};
|
||||
use cosmwasm_std::Timestamp;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -19,12 +19,6 @@ impl VestingPeriod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct PledgeData {
|
||||
amount: Uint128,
|
||||
block_time: Timestamp,
|
||||
}
|
||||
|
||||
pub fn populate_vesting_periods(
|
||||
start_time: u64,
|
||||
vesting_spec: VestingSpecification,
|
||||
@@ -53,6 +47,7 @@ mod tests {
|
||||
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
|
||||
use mixnet_contract_common::{Gateway, MixNode};
|
||||
use vesting_contract_common::messages::ExecuteMsg;
|
||||
use vesting_contract_common::Period;
|
||||
|
||||
#[test]
|
||||
fn test_account_creation() {
|
||||
@@ -182,48 +177,63 @@ mod tests {
|
||||
assert_eq!(account.periods().len(), num_vesting_periods as usize);
|
||||
|
||||
let current_period = account.get_current_vesting_period(Timestamp::from_seconds(0));
|
||||
assert_eq!(0, current_period);
|
||||
assert_eq!(Period::Before, current_period);
|
||||
|
||||
let block_time =
|
||||
Timestamp::from_seconds(account.start_time().seconds() + vesting_period + 1);
|
||||
let current_period = account.get_current_vesting_period(block_time);
|
||||
assert_eq!(current_period, 1);
|
||||
assert_eq!(current_period, Period::In(1));
|
||||
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
|
||||
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
|
||||
assert_eq!(
|
||||
vested_coins.amount,
|
||||
Uint128::new(
|
||||
account.get_original_vesting().amount.u128() / num_vesting_periods as u128
|
||||
account.get_original_vesting().amount().amount.u128() / num_vesting_periods as u128
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
vesting_coins.amount,
|
||||
Uint128::new(
|
||||
account.get_original_vesting().amount.u128()
|
||||
- account.get_original_vesting().amount.u128() / num_vesting_periods as u128
|
||||
account.get_original_vesting().amount().amount.u128()
|
||||
- account.get_original_vesting().amount().amount.u128()
|
||||
/ num_vesting_periods as u128
|
||||
)
|
||||
);
|
||||
|
||||
let block_time =
|
||||
Timestamp::from_seconds(account.start_time().seconds() + 5 * vesting_period + 1);
|
||||
let current_period = account.get_current_vesting_period(block_time);
|
||||
assert_eq!(current_period, 5);
|
||||
assert_eq!(current_period, Period::In(5));
|
||||
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
|
||||
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
|
||||
assert_eq!(
|
||||
vested_coins.amount,
|
||||
Uint128::new(
|
||||
5 * account.get_original_vesting().amount.u128() / num_vesting_periods as u128
|
||||
5 * account.get_original_vesting().amount().amount.u128()
|
||||
/ num_vesting_periods as u128
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
vesting_coins.amount,
|
||||
Uint128::new(
|
||||
account.get_original_vesting().amount.u128()
|
||||
- 5 * account.get_original_vesting().amount.u128()
|
||||
account.get_original_vesting().amount().amount.u128()
|
||||
- 5 * account.get_original_vesting().amount().amount.u128()
|
||||
/ num_vesting_periods as u128
|
||||
)
|
||||
);
|
||||
let vesting_over_period = num_vesting_periods + 1;
|
||||
let block_time = Timestamp::from_seconds(
|
||||
account.start_time().seconds() + vesting_over_period * vesting_period + 1,
|
||||
);
|
||||
let current_period = account.get_current_vesting_period(block_time);
|
||||
assert_eq!(current_period, Period::After);
|
||||
let vested_coins = account.get_vested_coins(Some(block_time), &env).unwrap();
|
||||
let vesting_coins = account.get_vesting_coins(Some(block_time), &env).unwrap();
|
||||
assert_eq!(
|
||||
vested_coins.amount,
|
||||
Uint128::new(account.get_original_vesting().amount().amount.u128())
|
||||
);
|
||||
assert_eq!(vesting_coins.amount, Uint128::zero());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -396,7 +406,7 @@ mod tests {
|
||||
assert!(err.is_err());
|
||||
|
||||
let pledge = account.load_mixnode_pledge(&deps.storage).unwrap().unwrap();
|
||||
assert_eq!(Uint128::new(500_000_000_000), pledge.amount);
|
||||
assert_eq!(Uint128::new(500_000_000_000), pledge.amount().amount);
|
||||
|
||||
// Current period -> block_time: None
|
||||
let bonded_free = account.get_pledged_free(None, &env, &deps.storage).unwrap();
|
||||
@@ -405,7 +415,10 @@ mod tests {
|
||||
let bonded_vesting = account
|
||||
.get_pledged_vesting(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
|
||||
assert_eq!(
|
||||
pledge.amount().amount - bonded_free.amount,
|
||||
bonded_vesting.amount
|
||||
);
|
||||
|
||||
// All periods
|
||||
for (i, period) in account.periods().iter().enumerate() {
|
||||
@@ -417,7 +430,8 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
(account.tokens_per_period().unwrap() * i as u128).min(pledge.amount.u128()),
|
||||
(account.tokens_per_period().unwrap() * i as u128)
|
||||
.min(pledge.amount().amount.u128()),
|
||||
bonded_free.amount.u128()
|
||||
);
|
||||
|
||||
@@ -428,7 +442,10 @@ mod tests {
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
|
||||
assert_eq!(
|
||||
pledge.amount().amount - bonded_free.amount,
|
||||
bonded_vesting.amount
|
||||
);
|
||||
}
|
||||
|
||||
let bonded_free = account
|
||||
@@ -438,7 +455,7 @@ mod tests {
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount, bonded_free.amount);
|
||||
assert_eq!(pledge.amount().amount, bonded_free.amount);
|
||||
|
||||
let bonded_vesting = account
|
||||
.get_pledged_vesting(
|
||||
@@ -509,7 +526,7 @@ mod tests {
|
||||
assert!(err.is_err());
|
||||
|
||||
let pledge = account.load_gateway_pledge(&deps.storage).unwrap().unwrap();
|
||||
assert_eq!(Uint128::new(500_000_000_000), pledge.amount);
|
||||
assert_eq!(Uint128::new(500_000_000_000), pledge.amount().amount);
|
||||
|
||||
// Current period -> block_time: None
|
||||
let bonded_free = account.get_pledged_free(None, &env, &deps.storage).unwrap();
|
||||
@@ -518,7 +535,10 @@ mod tests {
|
||||
let bonded_vesting = account
|
||||
.get_pledged_vesting(None, &env, &deps.storage)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
|
||||
assert_eq!(
|
||||
pledge.amount().amount - bonded_free.amount,
|
||||
bonded_vesting.amount
|
||||
);
|
||||
|
||||
// All periods
|
||||
for (i, period) in account.periods().iter().enumerate() {
|
||||
@@ -530,7 +550,8 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
(account.tokens_per_period().unwrap() * i as u128).min(pledge.amount.u128()),
|
||||
(account.tokens_per_period().unwrap() * i as u128)
|
||||
.min(pledge.amount().amount.u128()),
|
||||
bonded_free.amount.u128()
|
||||
);
|
||||
|
||||
@@ -541,7 +562,10 @@ mod tests {
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount - bonded_free.amount, bonded_vesting.amount);
|
||||
assert_eq!(
|
||||
pledge.amount().amount - bonded_free.amount,
|
||||
bonded_vesting.amount
|
||||
);
|
||||
}
|
||||
|
||||
let bonded_free = account
|
||||
@@ -551,7 +575,7 @@ mod tests {
|
||||
&deps.storage,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(pledge.amount, bonded_free.amount);
|
||||
assert_eq!(pledge.amount().amount, bonded_free.amount);
|
||||
|
||||
let bonded_vesting = account
|
||||
.get_pledged_vesting(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user