Merge remote-tracking branch 'origin/develop' into feature/rust-sdk-initial-version
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ GEOIPUPDATE_LICENSE_KEY=xxx
|
||||
# List of space-separated database edition IDs. Edition IDs may
|
||||
# consist of letters, digits, and dashes. For example, GeoIP2-City
|
||||
# would download the GeoIP2 City database (GeoIP2-City).
|
||||
GEOIPUPDATE_EDITION_IDS=GeoLite2-Country
|
||||
GEOIPUPDATE_EDITION_IDS=GeoLite2-City
|
||||
# The number of hours between geoipupdate runs. If this is not set
|
||||
# or is set to 0, geoipupdate will run once and exit.
|
||||
GEOIPUPDATE_FREQUENCY=72
|
||||
|
||||
@@ -47,6 +47,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "security"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -58,6 +58,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-ts-packages"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [ self-hosted, custom-linux ]
|
||||
# Enable sccache via environment variable
|
||||
# Enable sccache via environment variable
|
||||
env:
|
||||
RUSTC_WRAPPER: /home/ubuntu/.cargo/bin/sccache
|
||||
steps:
|
||||
|
||||
@@ -73,6 +73,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-network-explorer"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -183,6 +183,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -198,6 +198,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -198,6 +198,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nightly-release"
|
||||
IS_SUCCESS: "${{ env.WORKFLOW_CONCLUSION == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -54,6 +54,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nym-connect"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -50,6 +50,11 @@ jobs:
|
||||
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
|
||||
KEYBASE_NYM_CHANNEL: "ci-nym-wallet"
|
||||
IS_SUCCESS: "${{ job.status == 'success' }}"
|
||||
MATRIX_SERVER: "${{ secrets.MATRIX_SERVER }}"
|
||||
MATRIX_ROOM: "${{ secrets.MATRIX_ROOM }}"
|
||||
MATRIX_USER_ID: "${{ secrets.MATRIX_USER_ID }}"
|
||||
MATRIX_TOKEN: "${{ secrets.MATRIX_TOKEN }}"
|
||||
MATRIX_DEVICE_ID: "${{ secrets.MATRIX_DEVICE_ID }}"
|
||||
uses: docker://keybaseio/client:stable-node
|
||||
with:
|
||||
args: .github/workflows/support-files/notifications/entry_point.sh
|
||||
|
||||
@@ -2,6 +2,13 @@ KEYBASE_NYM_CHANNEL=
|
||||
KEYBASE_NYMBOT_USERNAME=
|
||||
KEYBASE_NYMBOT_PAPERKEY=
|
||||
|
||||
MATRIX_SERVER=
|
||||
MATRIX_ROOM=
|
||||
MATRIX_ROOM_OF_SHAME=
|
||||
MATRIX_USER_ID=
|
||||
MATRIX_TOKEN=
|
||||
MATRIX_DEVICE_ID=
|
||||
|
||||
NYM_NOTIFICATION_KIND=nightly
|
||||
NYM_PROJECT_NAME=Nightly Build
|
||||
|
||||
|
||||
@@ -2,4 +2,6 @@ node_modules
|
||||
.idea
|
||||
|
||||
# don't commit the lock file to avoid cross-platform issues
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
|
||||
scratch
|
||||
@@ -1,6 +1,7 @@
|
||||
require('dotenv').config();
|
||||
|
||||
const Bot = require('keybase-bot');
|
||||
const { sendMatrixMessage } = require('./send_message_to_matrix');
|
||||
|
||||
let context = {
|
||||
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly', 'nym-connect','security'],
|
||||
@@ -38,6 +39,28 @@ function validateContext() {
|
||||
'Paperkey is not defined. Please set env var KEYBASE_NYMBOT_PAPERKEY',
|
||||
);
|
||||
}
|
||||
if (context.env.MATRIX_ROOM) {
|
||||
if (!context.env.MATRIX_SERVER) {
|
||||
throw new Error(
|
||||
'Matrix server is not defined. Please set env var MATRIX_SERVER',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_USER_ID) {
|
||||
throw new Error(
|
||||
'Matrix user id is not defined. Please set env var MATRIX_USER_ID',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_TOKEN) {
|
||||
throw new Error(
|
||||
'Matrix token is not defined. Please set env var MATRIX_TOKEN',
|
||||
);
|
||||
}
|
||||
if (!context.env.MATRIX_DEVICE_ID) {
|
||||
throw new Error(
|
||||
'Matrix device id is not defined. Please set env var MATRIX_DEVICE_ID',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +170,13 @@ async function main() {
|
||||
console.log('-----------------------------------------');
|
||||
}
|
||||
await sendKeybaseMessage(messageBody);
|
||||
if(context.env.MATRIX_ROOM) {
|
||||
await sendMatrixMessage(context, messageBody, context.env.MATRIX_ROOM)
|
||||
}
|
||||
if(context.env.MATRIX_ROOM_OF_SHAME && context.env.IS_SUCCESS !== 'true') {
|
||||
// when a job fails
|
||||
await sendMatrixMessage(context, messageBody, context.env.MATRIX_ROOM_OF_SHAME)
|
||||
}
|
||||
}
|
||||
|
||||
// call main function and let NodeJS handle the promise
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
const sdk = require('matrix-js-sdk');
|
||||
global.Olm = require('olm');
|
||||
const { LocalStorage } = require('node-localstorage');
|
||||
const localStorage = new LocalStorage('./scratch');
|
||||
const {
|
||||
LocalStorageCryptoStore,
|
||||
} = require('matrix-js-sdk/lib/crypto/store/localStorage-crypto-store');
|
||||
|
||||
const vfile = require('to-vfile');
|
||||
const unified = require('unified');
|
||||
const remarkParse = require('remark-parse');
|
||||
const remarkHtml = require('remark-html');
|
||||
const emoji = require('remark-emoji');
|
||||
|
||||
// hide all matrix client output
|
||||
console.error = (error) => console.log('❌ error: ', error);
|
||||
process.stderr.write = () => {};
|
||||
process.stdout.write = () => {};
|
||||
|
||||
|
||||
function createClient(context, room, message) {
|
||||
const server = context.env.MATRIX_SERVER;
|
||||
const token = context.env.MATRIX_TOKEN;
|
||||
const deviceId = context.env.MATRIX_DEVICE_ID;
|
||||
const userId = context.env.MATRIX_USER_ID;
|
||||
|
||||
const client = sdk.createClient({
|
||||
baseUrl: server,
|
||||
accessToken: token,
|
||||
userId,
|
||||
deviceId,
|
||||
sessionStore: new sdk.WebStorageSessionStore(localStorage),
|
||||
cryptoStore: new LocalStorageCryptoStore(localStorage),
|
||||
});
|
||||
|
||||
client.on('sync', async function(state, prevState, res) {
|
||||
if (state !== 'PREPARED') return;
|
||||
client.setGlobalErrorOnUnknownDevices(false);
|
||||
try {
|
||||
await client.joinRoom(room);
|
||||
await client.sendEvent(
|
||||
room,
|
||||
'm.room.message',
|
||||
{
|
||||
msgtype: 'm.text',
|
||||
format: 'org.matrix.custom.html',
|
||||
body: message,
|
||||
formatted_body: message,
|
||||
},
|
||||
'',
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Job failed: ' + error.message);
|
||||
}
|
||||
client.stopClient();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
async function markdownToHtml(messageAsMarkdown) {
|
||||
const file = await unified()
|
||||
.use(emoji)
|
||||
.use(remarkParse)
|
||||
.use(remarkHtml)
|
||||
.process(await vfile({ path: 'test.md', contents: messageAsMarkdown}));
|
||||
return String(file);
|
||||
}
|
||||
|
||||
async function sendMatrixMessage(contextArg, messageAsMarkdown, roomId) {
|
||||
const messageAsHtml = await markdownToHtml(messageAsMarkdown);
|
||||
const client = createClient(contextArg, roomId, messageAsHtml);
|
||||
await client.initCrypto();
|
||||
await client.startClient({ initialSyncLimit: 1 });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sendMatrixMessage,
|
||||
};
|
||||
@@ -11,7 +11,15 @@
|
||||
"dotenv": "^16.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"keybase-bot": "^3.6.1",
|
||||
"octokit": "^1.7.1"
|
||||
"matrix-js-sdk": "^9.3.0",
|
||||
"node-localstorage": "^2.1.6",
|
||||
"octokit": "^1.7.1",
|
||||
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
|
||||
"remark-emoji": "^2.2.0",
|
||||
"remark-html": "^13.0.2",
|
||||
"remark-parse": "^9.0.0",
|
||||
"to-vfile": "^6.1.0",
|
||||
"unified": "^9.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "2.3.2"
|
||||
|
||||
+3
-1
@@ -38,4 +38,6 @@ validator-config
|
||||
validator-api-config.toml
|
||||
dist
|
||||
storybook-static
|
||||
envs/qwerty.env
|
||||
envs/qwerty.env
|
||||
Cargo.lock
|
||||
nym-connect/Cargo.lock
|
||||
|
||||
+18
-6
@@ -6,27 +6,39 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- socks5: send status message for service ready, and network-requester error response
|
||||
- nym-sdk: added initial version of a Rust client sdk
|
||||
|
||||
### Changed
|
||||
|
||||
- renamed all references to validator_api to nym_api
|
||||
- renamed all references to nymd to nyxd
|
||||
- all-binaries: improved error logging ([#2686])
|
||||
- native client: bring shutdown logic up to the same level as socks5-client
|
||||
- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement ([#2670])
|
||||
- all-binaries: standarised argument names (note: old names should still be accepted) ([#2762]
|
||||
|
||||
### Fixed
|
||||
|
||||
- nym-api: should now correctly use `rewarding.enabled` config flag ([#2753])
|
||||
|
||||
[#2686]: https://github.com/nymtech/nym/pull/2686
|
||||
[#2670]: https://github.com/nymtech/nym/pull/2670
|
||||
[#2753]: https://github.com/nymtech/nym/pull/2753
|
||||
[#2762]: https://github.com/nymtech/nym/pull/2762
|
||||
|
||||
## [v1.1.5] (2022-01-10)
|
||||
|
||||
### Added
|
||||
|
||||
- socks5: send status message for service ready, and network-requester error response in https://github.com/nymtech/nym/pull/2715
|
||||
|
||||
### Changed
|
||||
|
||||
- all-binaries: improved error logging in https://github.com/nymtech/nym/pull/2686
|
||||
- native client: bring shutdown logic up to the same level as socks5-client in https://github.com/nymtech/nym/pull/2695
|
||||
- nym-api, coconut-dkg contract: automatic, time-based dkg epoch state advancement in https://github.com/nymtech/nym/pull/2670
|
||||
- DKG resharing unit test by @neacsu in https://github.com/nymtech/nym/pull/2668
|
||||
- Renaming validator-api to nym-api by @futurechimp in https://github.com/nymtech/nym/pull/1863
|
||||
- Modify wasm specific make targets by @neacsu in https://github.com/nymtech/nym/pull/2693
|
||||
- client: create websocket handler builder by @octol in https://github.com/nymtech/nym/pull/2700
|
||||
- Outfox and Lion by @durch in https://github.com/nymtech/nym/pull/2730
|
||||
- Feature/multi surb transmission lanes by @jstuczyn in https://github.com/nymtech/nym/pull/2723
|
||||
|
||||
## [v1.1.4] (2022-12-20)
|
||||
|
||||
This release adds multiple Single Use Reply Blocks (SURBs) to allow arbitrarily-sized anonymized replies.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "client-core"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-client"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
|
||||
let folderName = '.';
|
||||
|
||||
if (process.argv.length >= 3) {
|
||||
folderName = process.argv[2];
|
||||
if (!fs.existsSync(folderName)) {
|
||||
fs.mkdirSync(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
const clone = spawn("git", ["clone", "https://github.com/rustwasm/create-wasm-app.git", folderName]);
|
||||
|
||||
clone.on("close", code => {
|
||||
if (code !== 0) {
|
||||
console.error("cloning the template failed!")
|
||||
process.exit(code);
|
||||
} else {
|
||||
console.log("🦀 Rust + 🕸 Wasm = ❤");
|
||||
}
|
||||
});
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
@@ -1,5 +0,0 @@
|
||||
language: node_js
|
||||
node_js: "10"
|
||||
|
||||
script:
|
||||
- ./node_modules/.bin/webpack
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,28 +0,0 @@
|
||||
# Nym Sphinx Wasm Demo
|
||||
|
||||
This example application demonstrates how to use WebAssembly to create Sphinx packets, in the browser, and forward them to a Nym gateway.
|
||||
|
||||
## 🚴 Usage
|
||||
|
||||
Build the WASM package for bundling:
|
||||
|
||||
```
|
||||
wasm-pack build --scope nymproject --target no-modules
|
||||
```
|
||||
|
||||
in the `clients/webassembly` directory (one up).
|
||||
|
||||
Start the webpack dev server:
|
||||
|
||||
```
|
||||
npm install # set up dependencies
|
||||
npm run start # starts a web server at http://localhost:8001
|
||||
```
|
||||
|
||||
Check your dev console for output.
|
||||
|
||||
### Rebuild after Rust source changes
|
||||
|
||||
Install `wasm-pack`. Instruction are at the [Rust WASM tutorial](https://rustwasm.github.io/docs/book/game-of-life/hello-world.html).
|
||||
|
||||
`wasm-pack build --scope nymproject --target no-modules` in the `clients/webassembly` directory (one up) will rebuild the wasm package if you make changes to the Rust source. That will be automatically picked up (and reloaded, if need be) by the npm dev server.
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// A dependency graph that contains any wasm must all be imported
|
||||
// asynchronously. This `bootstrap.js` file does the single async import, so
|
||||
// that no one else needs to worry about it again.
|
||||
import('./index.js')
|
||||
.catch(e => console.error('Error importing `index.js`:', e));
|
||||
@@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Nym WebAssembly Demo</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>
|
||||
<label>Sender: </label><input disabled="true" size="85" type="text" id="sender" value="">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>Recipient: </label><input size="85" type="text" id="recipient" value="">
|
||||
</p>
|
||||
<p>
|
||||
<label>Message: </label><input type="text" id="message" value="Hello mixnet!">
|
||||
</p>
|
||||
<p>
|
||||
<button id="send-button">Send</button>
|
||||
</p>
|
||||
|
||||
<p>Send messages from your browser, through the mixnet, and to the recipient using the "send" button.</p>
|
||||
<p><span style='color: blue;'>Sent</span> messages show in blue, <span style='color: green;'>received</span>
|
||||
messages show in green.</p>
|
||||
|
||||
<hr>
|
||||
<p>
|
||||
<span id="output"></div>
|
||||
</p>
|
||||
<script src="./bootstrap.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,128 +0,0 @@
|
||||
// Copyright 2020-2022 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
class WebWorkerClient {
|
||||
worker = null;
|
||||
|
||||
constructor() {
|
||||
this.worker = new Worker('./worker.js');
|
||||
|
||||
this.worker.onmessage = (ev) => {
|
||||
if (ev.data && ev.data.kind) {
|
||||
switch (ev.data.kind) {
|
||||
case 'Ready':
|
||||
const { selfAddress } = ev.data.args;
|
||||
displaySenderAddress(selfAddress);
|
||||
break;
|
||||
case 'ReceiveMessage':
|
||||
const { message } = ev.data.args;
|
||||
displayReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sendMessage = (message, recipient) => {
|
||||
if (!this.worker) {
|
||||
console.error('Could not send message because worker does not exist');
|
||||
return;
|
||||
}
|
||||
|
||||
this.worker.postMessage({
|
||||
kind: 'SendMessage',
|
||||
args: {
|
||||
message, recipient,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
let client = null;
|
||||
|
||||
async function main() {
|
||||
client = new WebWorkerClient();
|
||||
|
||||
const sendButton = document.querySelector('#send-button');
|
||||
sendButton.onclick = function() {
|
||||
sendMessageTo();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Sphinx packet and send it to the mixnet through the gateway node.
|
||||
*
|
||||
* Message and recipient are taken from the values in the user interface.
|
||||
*
|
||||
*/
|
||||
async function sendMessageTo() {
|
||||
const message = document.getElementById('message').value;
|
||||
const recipient = document.getElementById('recipient').value;
|
||||
|
||||
await client.sendMessage(message, recipient);
|
||||
displaySend(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display messages that have been sent up the websocket. Colours them blue.
|
||||
*
|
||||
* @param {string} message
|
||||
*/
|
||||
function displaySend(message) {
|
||||
let timestamp = new Date().toISOString().substr(11, 12);
|
||||
|
||||
let sendDiv = document.createElement('div');
|
||||
let paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: blue');
|
||||
let paragraphContent = document.createTextNode(timestamp + ' sent >>> ' + message);
|
||||
paragraph.appendChild(paragraphContent);
|
||||
|
||||
sendDiv.appendChild(paragraph);
|
||||
document.getElementById('output').appendChild(sendDiv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display received text messages in the browser. Colour them green.
|
||||
*
|
||||
* @param {Uint8Array} raw
|
||||
*/
|
||||
function displayReceived(raw, sender_tag) {
|
||||
const content = new TextDecoder().decode(raw);
|
||||
if (sender_tag !== undefined) {
|
||||
console.log("this message also contained some surbs from", sender_tag)
|
||||
}
|
||||
|
||||
let timestamp = new Date().toISOString().substr(11, 12);
|
||||
let receivedDiv = document.createElement('div');
|
||||
let paragraph = document.createElement('p');
|
||||
paragraph.setAttribute('style', 'color: green');
|
||||
let paragraphContent = document.createTextNode(timestamp + ' received >>> ' + content);
|
||||
// let paragraphContent = document.createTextNode(timestamp + " received >>> " + content + ((replySurb != null) ? "Reply SURB was attached here (but we can't do anything with it yet" : " (NO REPLY-SURB AVAILABLE)"))
|
||||
paragraph.appendChild(paragraphContent);
|
||||
receivedDiv.appendChild(paragraph);
|
||||
document.getElementById('output').appendChild(receivedDiv);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the nymClient's sender address in the user interface
|
||||
*
|
||||
* @param {String} address
|
||||
*/
|
||||
function displaySenderAddress(address) {
|
||||
document.getElementById('sender').value = address;
|
||||
}
|
||||
|
||||
// Let's get started!
|
||||
main();
|
||||
-6872
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"name": "create-wasm-app",
|
||||
"version": "0.1.0",
|
||||
"description": "create an app to consume rust-generated wasm packages",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
"create-wasm-app": ".bin/create-wasm-app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"start": "webpack-dev-server --port 8001"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rustwasm/create-wasm-app.git"
|
||||
},
|
||||
"keywords": [
|
||||
"webassembly",
|
||||
"wasm",
|
||||
"rust",
|
||||
"webpack"
|
||||
],
|
||||
"author": "Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/nymtech/nym/issues"
|
||||
},
|
||||
"homepage": "https://nymtech.net/docs",
|
||||
"devDependencies": {
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"hello-wasm-pack": "^0.1.0",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nymproject/nym-client-wasm": "file:../pkg"
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
performance: {
|
||||
hints: false,
|
||||
maxEntrypointSize: 512000,
|
||||
maxAssetSize: 512000
|
||||
},
|
||||
entry: {
|
||||
bootstrap: './bootstrap.js',
|
||||
worker: './worker.js',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: '[name].js',
|
||||
},
|
||||
// mode: 'development',
|
||||
mode: 'production',
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
'index.html',
|
||||
{
|
||||
from: 'node_modules/@nymproject/nym-client-wasm/*.(js|wasm)',
|
||||
to: '[name][ext]',
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
],
|
||||
experiments: { syncWebAssembly: true },
|
||||
};
|
||||
@@ -1,101 +0,0 @@
|
||||
// Copyright 2020-2022 Nym Technologies SA
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
importScripts('nym_client_wasm.js');
|
||||
|
||||
console.log('Initializing worker');
|
||||
|
||||
// wasm_bindgen creates a global variable (with the exports attached) that is in scope after `importScripts`
|
||||
const { default_debug, NymClientBuilder, set_panic_hook, Config, GatewayEndpointConfig } = wasm_bindgen;
|
||||
|
||||
let client = null;
|
||||
|
||||
async function main() {
|
||||
// load WASM package
|
||||
await wasm_bindgen('nym_client_wasm_bg.wasm');
|
||||
|
||||
console.log('Loaded WASM');
|
||||
|
||||
// sets up better stack traces in case of in-rust panics
|
||||
set_panic_hook();
|
||||
|
||||
// validator server we will use to get topology from
|
||||
const validator = 'https://qwerty-validator-api.qa.nymte.ch/api';
|
||||
|
||||
const gatewayId = 'EVupP2tRUeZo5Y6RpBHAbm8kSntpgNyZNL6yCr7BDEoG';
|
||||
const gatewayOwner = 'n1rmlew3euapuq7rs4s4j9apv00whrsazr764kl7';
|
||||
const gatewayListener = 'ws://176.58.120.72:9000';
|
||||
const gatewayEndpoint = new GatewayEndpointConfig(gatewayId, gatewayOwner, gatewayListener)
|
||||
|
||||
// only really useful if you want to adjust some settings like traffic rate
|
||||
// (if not needed you can just pass a null)
|
||||
const debug = default_debug();
|
||||
|
||||
debug.disable_main_poisson_packet_distribution = true;
|
||||
debug.disable_loop_cover_traffic_stream = true;
|
||||
debug.use_extended_packet_size = true;
|
||||
// debug.average_packet_delay_ms = BigInt(10);
|
||||
// debug.average_ack_delay_ms = BigInt(10);
|
||||
// debug.ack_wait_addition_ms = BigInt(3000);
|
||||
// debug.ack_wait_multiplier = 10;
|
||||
|
||||
debug.topology_refresh_rate_ms = BigInt(60000)
|
||||
|
||||
const config = new Config('my-awesome-wasm-client', validator, gatewayEndpoint, debug);
|
||||
|
||||
const onMessageHandler = (message) => {
|
||||
self.postMessage({
|
||||
kind: 'ReceiveMessage',
|
||||
args: {
|
||||
message,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
console.log('Instantiating WASM client...');
|
||||
|
||||
let clientBuilder = new NymClientBuilder(config, onMessageHandler)
|
||||
console.log('Web worker creating WASM client...');
|
||||
let local_client = await clientBuilder.start_client();
|
||||
console.log('WASM client running!');
|
||||
|
||||
const selfAddress = local_client.self_address();
|
||||
|
||||
// set the global (I guess we don't have to anymore?)
|
||||
client = local_client;
|
||||
|
||||
console.log(`Client address is ${selfAddress}`);
|
||||
self.postMessage({
|
||||
kind: 'Ready',
|
||||
args: {
|
||||
selfAddress,
|
||||
},
|
||||
});
|
||||
|
||||
// Set callback to handle messages passed to the worker.
|
||||
self.onmessage = async event => {
|
||||
if (event.data && event.data.kind) {
|
||||
switch (event.data.kind) {
|
||||
case 'SendMessage': {
|
||||
const { message, recipient } = event.data.args;
|
||||
let uint8Array = new TextEncoder().encode(message);
|
||||
await client.send_regular_message(uint8Array, recipient);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Let's get started!
|
||||
main();
|
||||
@@ -27,6 +27,7 @@ nymsphinx = { path = "../../nymsphinx" }
|
||||
pemstore = { path = "../../pemstore" }
|
||||
validator-client = { path = "../validator-client", optional = true }
|
||||
task = { path = "../../task" }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
|
||||
|
||||
[dependencies.tungstenite]
|
||||
|
||||
@@ -16,6 +16,7 @@ use cosmrs::rpc::Error as TendermintRpcError;
|
||||
use cosmrs::rpc::HttpClientUrl;
|
||||
use cosmrs::tx::Msg;
|
||||
use execute::execute;
|
||||
use log::debug;
|
||||
use network_defaults::{ChainDetails, NymNetworkDetails};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
@@ -76,9 +77,12 @@ impl Config {
|
||||
expected_prefix: &str,
|
||||
) -> Result<Option<AccountId>, NyxdError> {
|
||||
if let Some(address) = raw {
|
||||
debug!("Raw address:{:?}", raw);
|
||||
debug!("Expected prefix:{:?}", expected_prefix);
|
||||
let parsed: AccountId = address
|
||||
.parse()
|
||||
.map_err(|_| NyxdError::MalformedAccountAddress(address.clone()))?;
|
||||
debug!("Parsed prefix:{:?}", parsed);
|
||||
if parsed.prefix() != expected_prefix {
|
||||
Err(NyxdError::UnexpectedBech32Prefix {
|
||||
got: parsed.prefix().into(),
|
||||
|
||||
@@ -12,8 +12,9 @@ use mixnet_contract_common::MixId;
|
||||
use serde::Deserialize;
|
||||
use vesting_contract::vesting::Account;
|
||||
use vesting_contract_common::{
|
||||
messages::QueryMsg as VestingQueryMsg, AllDelegationsResponse, DelegationTimesResponse,
|
||||
OriginalVestingResponse, Period, PledgeData, VestingDelegation,
|
||||
messages::QueryMsg as VestingQueryMsg, AccountVestingCoins, AccountsResponse,
|
||||
AllDelegationsResponse, BaseVestingAccountInfo, DelegationTimesResponse,
|
||||
OriginalVestingResponse, Period, PledgeData, VestingCoinsResponse, VestingDelegation,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
@@ -27,74 +28,187 @@ pub trait VestingQueryClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_accounts_paged(
|
||||
&self,
|
||||
start_next_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AccountsResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetAccountsPaged {
|
||||
start_next_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_accounts_vesting_coins_paged(
|
||||
&self,
|
||||
start_next_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<VestingCoinsResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetAccountsVestingCoinsPaged {
|
||||
start_next_after,
|
||||
limit,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn locked_coins(
|
||||
&self,
|
||||
address: &str,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::LockedCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn spendable_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::SpendableCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn vested_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetVestedCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn vesting_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetVestingCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn vesting_start_time(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Timestamp, NyxdError>;
|
||||
) -> Result<Timestamp, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetStartTime {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_end_time(&self, vesting_account_address: &str)
|
||||
-> Result<Timestamp, NyxdError>;
|
||||
async fn vesting_end_time(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Timestamp, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetEndTime {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn original_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<OriginalVestingResponse, NyxdError>;
|
||||
) -> Result<OriginalVestingResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetOriginalVesting {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delegated_free(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetDelegatedFree {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the total amount of delegated tokens that have vested
|
||||
async fn delegated_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError>;
|
||||
) -> Result<Coin, NyxdError> {
|
||||
self.query_vesting_contract::<CosmWasmCoin>(VestingQueryMsg::GetDelegatedVesting {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
})
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn get_account(&self, address: &str) -> Result<Account, NyxdError>;
|
||||
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError>;
|
||||
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError>;
|
||||
async fn get_current_vesting_period(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Period, NyxdError>;
|
||||
async fn get_account(&self, address: &str) -> Result<Account, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetAccount {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetMixnode {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetGateway {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_vesting_period(&self, address: &str) -> Result<Period, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetCurrentVestingPeriod {
|
||||
address: address.to_string(),
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
) -> Result<DelegationTimesResponse, NyxdError>;
|
||||
) -> Result<DelegationTimesResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetDelegationTimes {
|
||||
address: address.to_string(),
|
||||
mix_id,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, MixId, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NyxdError>;
|
||||
) -> Result<AllDelegationsResponse, NyxdError> {
|
||||
self.query_vesting_contract(VestingQueryMsg::GetAllDelegations { start_after, limit })
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_vesting_delegations(&self) -> Result<Vec<VestingDelegation>, NyxdError> {
|
||||
let mut delegations = Vec::new();
|
||||
@@ -114,6 +228,44 @@ pub trait VestingQueryClient {
|
||||
|
||||
Ok(delegations)
|
||||
}
|
||||
|
||||
async fn get_all_accounts_info(&self) -> Result<Vec<BaseVestingAccountInfo>, NyxdError> {
|
||||
let mut accounts = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.get_all_accounts_paged(start_after.take(), None)
|
||||
.await?;
|
||||
accounts.append(&mut paged_response.accounts);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res.into_string())
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(accounts)
|
||||
}
|
||||
|
||||
async fn get_all_accounts_vesting_coins(&self) -> Result<Vec<AccountVestingCoins>, NyxdError> {
|
||||
let mut accounts = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.get_all_accounts_vesting_coins_paged(start_after.take(), None)
|
||||
.await?;
|
||||
accounts.append(&mut paged_response.accounts);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res.into_string())
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(accounts)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -126,188 +278,4 @@ impl<C: CosmWasmClient + Sync + Send> VestingQueryClient for NyxdClient<C> {
|
||||
.query_contract_smart(self.vesting_contract_address(), &query)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn locked_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::LockedCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn spendable_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::SpendableCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
async fn vested_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::GetVestedCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
async fn vesting_coins(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::GetVestingCoins {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn vesting_start_time(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Timestamp, NyxdError> {
|
||||
let request = VestingQueryMsg::GetStartTime {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn vesting_end_time(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<Timestamp, NyxdError> {
|
||||
let request = VestingQueryMsg::GetEndTime {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn original_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
) -> Result<OriginalVestingResponse, NyxdError> {
|
||||
let request = VestingQueryMsg::GetOriginalVesting {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn delegated_free(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::GetDelegatedFree {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
/// Returns the total amount of delegated tokens that have vested
|
||||
async fn delegated_vesting(
|
||||
&self,
|
||||
vesting_account_address: &str,
|
||||
block_time: Option<Timestamp>,
|
||||
) -> Result<Coin, NyxdError> {
|
||||
let request = VestingQueryMsg::GetDelegatedVesting {
|
||||
vesting_account_address: vesting_account_address.to_string(),
|
||||
block_time,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart::<_, CosmWasmCoin>(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
|
||||
async fn get_account(&self, address: &str) -> Result<Account, NyxdError> {
|
||||
let request = VestingQueryMsg::GetAccount {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
async fn get_mixnode_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
|
||||
let request = VestingQueryMsg::GetMixnode {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
async fn get_gateway_pledge(&self, address: &str) -> Result<Option<PledgeData>, NyxdError> {
|
||||
let request = VestingQueryMsg::GetGateway {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_current_vesting_period(&self, address: &str) -> Result<Period, NyxdError> {
|
||||
let request = VestingQueryMsg::GetCurrentVestingPeriod {
|
||||
address: address.to_string(),
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_delegation_timestamps(
|
||||
&self,
|
||||
address: &str,
|
||||
mix_id: MixId,
|
||||
) -> Result<DelegationTimesResponse, NyxdError> {
|
||||
let request = VestingQueryMsg::GetDelegationTimes {
|
||||
address: address.to_string(),
|
||||
mix_id,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_all_vesting_delegations_paged(
|
||||
&self,
|
||||
start_after: Option<(u32, MixId, u64)>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AllDelegationsResponse, NyxdError> {
|
||||
let request = VestingQueryMsg::GetAllDelegations { start_after, limit };
|
||||
self.client
|
||||
.query_contract_smart(self.vesting_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,12 +106,14 @@ pub trait VestingSigningClient {
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn vesting_undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
@@ -367,6 +369,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NyxdClient
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
@@ -374,6 +377,7 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NyxdClient
|
||||
VestingExecuteMsg::DelegateToMixnode {
|
||||
mix_id,
|
||||
amount: amount.into(),
|
||||
on_behalf_of,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
@@ -383,11 +387,15 @@ impl<C: SigningCosmWasmClient + Sync + Send> VestingSigningClient for NyxdClient
|
||||
async fn vesting_undelegate_from_mixnode(
|
||||
&self,
|
||||
mix_id: MixId,
|
||||
on_behalf_of: Option<String>,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
self.execute_vesting_contract(
|
||||
fee,
|
||||
VestingExecuteMsg::UndelegateFromMixnode { mix_id },
|
||||
VestingExecuteMsg::UndelegateFromMixnode {
|
||||
mix_id,
|
||||
on_behalf_of,
|
||||
},
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -11,6 +11,7 @@ bs58 = "0.4"
|
||||
comfy-table = "6.0.0"
|
||||
cfg-if = "1.0.0"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
|
||||
@@ -31,3 +32,6 @@ validator-client = { path = "../client-libs/validator-client", features = ["nyxd
|
||||
network-defaults = { path = "../network-defaults" }
|
||||
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
|
||||
coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
|
||||
multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
|
||||
use coconut_bandwidth_contract_common::msg::InstantiateMsg;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub pool_addr: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub multisig_addr: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_denom: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
info!("Starting to generate vesting contract instantiate msg");
|
||||
|
||||
debug!("Received arguments: {:?}", args);
|
||||
|
||||
let multisig_addr = args.multisig_addr.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS)
|
||||
.expect("Multisig address has to be set")
|
||||
});
|
||||
|
||||
let mix_denom = args.mix_denom.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM).expect("Mix denom has to be set")
|
||||
});
|
||||
|
||||
let instantiate_msg = InstantiateMsg {
|
||||
pool_addr: args.pool_addr,
|
||||
multisig_addr,
|
||||
mix_denom,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
let res =
|
||||
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
|
||||
|
||||
println!("{}", res)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
use std::str::FromStr;
|
||||
|
||||
use coconut_dkg_common::msg::InstantiateMsg;
|
||||
use coconut_dkg_common::types::TimeConfiguration;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub group_addr: String,
|
||||
|
||||
#[clap(long)]
|
||||
pub multisig_addr: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub public_key_submission_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub dealing_exchange_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verification_key_submission_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verification_key_validation_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub verification_key_finalization_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub in_progress_time_secs: Option<u64>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_denom: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
info!("Starting to generate vesting contract instantiate msg");
|
||||
|
||||
debug!("Received arguments: {:?}", args);
|
||||
|
||||
let multisig_addr = args.multisig_addr.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS)
|
||||
.expect("Multisig address has to be set")
|
||||
});
|
||||
|
||||
let mix_denom = args.mix_denom.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM).expect("Mix denom has to be set")
|
||||
});
|
||||
|
||||
let mut time_configuration =
|
||||
if let Ok(config) = std::env::var(network_defaults::var_names::DKG_TIME_CONFIGURATION) {
|
||||
TimeConfiguration::from_str(&config).expect("Invalid env variable value")
|
||||
} else {
|
||||
TimeConfiguration::default()
|
||||
};
|
||||
if let Some(public_key_submission_time_secs) = args.public_key_submission_time_secs {
|
||||
time_configuration.public_key_submission_time_secs = public_key_submission_time_secs;
|
||||
}
|
||||
if let Some(dealing_exchange_time_secs) = args.dealing_exchange_time_secs {
|
||||
time_configuration.dealing_exchange_time_secs = dealing_exchange_time_secs;
|
||||
}
|
||||
if let Some(verification_key_submission_time_secs) = args.verification_key_submission_time_secs
|
||||
{
|
||||
time_configuration.verification_key_submission_time_secs =
|
||||
verification_key_submission_time_secs;
|
||||
}
|
||||
if let Some(verification_key_validation_time_secs) = args.verification_key_validation_time_secs
|
||||
{
|
||||
time_configuration.verification_key_validation_time_secs =
|
||||
verification_key_validation_time_secs;
|
||||
}
|
||||
if let Some(verification_key_finalization_time_secs) =
|
||||
args.verification_key_finalization_time_secs
|
||||
{
|
||||
time_configuration.verification_key_finalization_time_secs =
|
||||
verification_key_finalization_time_secs;
|
||||
}
|
||||
if let Some(in_progress_time_secs) = args.in_progress_time_secs {
|
||||
time_configuration.in_progress_time_secs = in_progress_time_secs;
|
||||
}
|
||||
|
||||
let instantiate_msg = InstantiateMsg {
|
||||
group_addr: args.group_addr,
|
||||
multisig_addr,
|
||||
time_configuration: Some(time_configuration),
|
||||
mix_denom,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
let res =
|
||||
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
|
||||
|
||||
println!("{}", res)
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
|
||||
use cosmwasm_std::Decimal;
|
||||
use mixnet_contract_common::{InitialRewardingParams, InstantiateMsg, Percent};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub rewarding_validator_address: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub vesting_contract_address: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub rewarding_denom: Option<String>,
|
||||
|
||||
#[clap(long, default_value_t = 720)]
|
||||
pub epochs_in_interval: u32,
|
||||
|
||||
#[clap(long, default_value_t = 60*60)]
|
||||
pub epoch_duration: u64,
|
||||
|
||||
#[clap(long, default_value_t = 244_817_525_850_285)]
|
||||
pub initial_reward_pool: u128,
|
||||
|
||||
#[clap(long, default_value_t = 100_000_000_000_000)]
|
||||
pub initial_staking_supply: u128,
|
||||
|
||||
#[clap(long, default_value_t = 50)]
|
||||
pub staking_supply_scale_factor: u64,
|
||||
|
||||
#[clap(long, default_value_t = 30)]
|
||||
pub sybil_resistance: u64,
|
||||
|
||||
#[clap(long, default_value_t = 10)]
|
||||
pub active_set_work_factor: u32,
|
||||
|
||||
#[clap(long, default_value_t = 2)]
|
||||
pub interval_pool_emission: u64,
|
||||
|
||||
#[clap(long, default_value_t = 240)]
|
||||
pub rewarded_set_size: u32,
|
||||
|
||||
#[clap(long, default_value_t = 240)]
|
||||
pub active_set_size: u32,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
info!("Starting to generate mixnet contract instantiate msg");
|
||||
|
||||
debug!("Received arguments: {:?}", args);
|
||||
|
||||
let initial_rewarding_params = InitialRewardingParams {
|
||||
initial_reward_pool: Decimal::from_atomics(args.initial_reward_pool, 0)
|
||||
.expect("initial_rewarding_pool can't be converted to Decimal"),
|
||||
initial_staking_supply: Decimal::from_atomics(args.initial_staking_supply, 0)
|
||||
.expect("initial_staking_supply can't be converted to Decimal"),
|
||||
staking_supply_scale_factor: Percent::from_percentage_value(
|
||||
args.staking_supply_scale_factor,
|
||||
)
|
||||
.unwrap(),
|
||||
sybil_resistance: Percent::from_percentage_value(args.sybil_resistance)
|
||||
.expect("sybil_resistance can't be converted to Percent"),
|
||||
active_set_work_factor: Decimal::from_atomics(args.active_set_work_factor, 0)
|
||||
.expect("active_set_work_factor can't be converted to Decimal"),
|
||||
interval_pool_emission: Percent::from_percentage_value(args.interval_pool_emission)
|
||||
.expect("interval_pool_emission can't be converted to Percent"),
|
||||
rewarded_set_size: args.rewarded_set_size,
|
||||
active_set_size: args.active_set_size,
|
||||
};
|
||||
|
||||
debug!("initial_rewarding_params: {:?}", initial_rewarding_params);
|
||||
|
||||
let rewarding_validator_address = args.rewarding_validator_address.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::REWARDING_VALIDATOR_ADDRESS)
|
||||
.expect("Rewarding validator address has to be set")
|
||||
});
|
||||
|
||||
let vesting_contract_address = args.vesting_contract_address.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::VESTING_CONTRACT_ADDRESS)
|
||||
.expect("Vesting contract address has to be set")
|
||||
});
|
||||
|
||||
let rewarding_denom = args.rewarding_denom.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM)
|
||||
.expect("Rewarding (mix) denom has to be set")
|
||||
});
|
||||
|
||||
let instantiate_msg = InstantiateMsg {
|
||||
rewarding_validator_address,
|
||||
vesting_contract_address,
|
||||
rewarding_denom,
|
||||
epochs_in_interval: args.epochs_in_interval,
|
||||
epoch_duration: Duration::from_secs(args.epoch_duration),
|
||||
initial_rewarding_params,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
let res =
|
||||
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
|
||||
|
||||
println!("{}", res)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod coconut_bandwidth;
|
||||
pub mod coconut_dkg;
|
||||
pub mod mixnet;
|
||||
pub mod multisig;
|
||||
pub mod vesting;
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
|
||||
pub struct GenerateMessage {
|
||||
#[clap(subcommand)]
|
||||
pub command: GenerateMessageCommands,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum GenerateMessageCommands {
|
||||
CoconutBandwidth(coconut_bandwidth::Args),
|
||||
CoconutDKG(coconut_dkg::Args),
|
||||
Mixnet(mixnet::Args),
|
||||
Multisig(multisig::Args),
|
||||
Vesting(vesting::Args),
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
|
||||
use cosmwasm_std::Decimal;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
use multisig_contract_common::msg::InstantiateMsg;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub group_addr: String,
|
||||
|
||||
#[clap(long, default_value_t = 67)]
|
||||
pub threshold: u128,
|
||||
|
||||
#[clap(long, default_value_t = 3600)]
|
||||
pub max_voting_period: u64,
|
||||
|
||||
#[clap(long)]
|
||||
pub coconut_bandwidth_contract_address: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub coconut_dkg_contract_address: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
info!("Starting to generate vesting contract instantiate msg");
|
||||
|
||||
debug!("Received arguments: {:?}", args);
|
||||
|
||||
let coconut_bandwidth_contract_address =
|
||||
args.coconut_bandwidth_contract_address.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::COCONUT_BANDWIDTH_CONTRACT_ADDRESS)
|
||||
.expect("Coconut bandwidth contract address has to be set")
|
||||
});
|
||||
|
||||
let coconut_dkg_contract_address = args.coconut_dkg_contract_address.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::COCONUT_DKG_CONTRACT_ADDRESS)
|
||||
.expect("Coconut DKG contract address has to be set")
|
||||
});
|
||||
|
||||
let instantiate_msg = InstantiateMsg {
|
||||
group_addr: args.group_addr,
|
||||
threshold: Threshold::AbsolutePercentage {
|
||||
percentage: Decimal::from_atomics(args.threshold, 2)
|
||||
.expect("threshold can't be converted to Decimal"),
|
||||
},
|
||||
max_voting_period: Duration::Time(args.max_voting_period),
|
||||
coconut_bandwidth_contract_address,
|
||||
coconut_dkg_contract_address,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
let res =
|
||||
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
|
||||
|
||||
println!("{}", res)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use clap::Parser;
|
||||
use log::{debug, info};
|
||||
|
||||
use vesting_contract_common::InitMsg;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Args {
|
||||
#[clap(long)]
|
||||
pub mixnet_contract_address: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub mix_denom: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn generate(args: Args) {
|
||||
info!("Starting to generate vesting contract instantiate msg");
|
||||
|
||||
debug!("Received arguments: {:?}", args);
|
||||
|
||||
let mixnet_contract_address = args.mixnet_contract_address.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::MIXNET_CONTRACT_ADDRESS)
|
||||
.expect("Mixnet contract address has to be set")
|
||||
});
|
||||
|
||||
let mix_denom = args.mix_denom.unwrap_or_else(|| {
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM).expect("Mix denom has to be set")
|
||||
});
|
||||
|
||||
let instantiate_msg = InitMsg {
|
||||
mixnet_contract_address,
|
||||
mix_denom,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
let res =
|
||||
serde_json::to_string(&instantiate_msg).expect("failed to convert instantiate msg to json");
|
||||
|
||||
println!("{}", res)
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
use clap::{Args, Subcommand};
|
||||
|
||||
pub mod execute_contract;
|
||||
pub mod generators;
|
||||
pub mod init_contract;
|
||||
pub mod migrate_contract;
|
||||
pub mod upload_contract;
|
||||
@@ -21,6 +22,8 @@ pub enum CosmwasmCommands {
|
||||
Upload(crate::validator::cosmwasm::upload_contract::Args),
|
||||
/// Init a WASM smart contract
|
||||
Init(crate::validator::cosmwasm::init_contract::Args),
|
||||
/// Generate an instantiate message
|
||||
GenerateInitMessage(generators::GenerateMessage),
|
||||
/// Migrate a WASM smart contract
|
||||
Migrate(crate::validator::cosmwasm::migrate_contract::Args),
|
||||
/// Execute a WASM smart contract method
|
||||
|
||||
@@ -18,6 +18,9 @@ pub struct Args {
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub on_behalf_of: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub amount: u128,
|
||||
}
|
||||
@@ -45,7 +48,7 @@ pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
|
||||
let coin = Coin::new(args.amount, denom);
|
||||
|
||||
let res = client
|
||||
.vesting_delegate_to_mixnode(mix_id, coin.into(), None)
|
||||
.vesting_delegate_to_mixnode(mix_id, coin.into(), args.on_behalf_of, None)
|
||||
.await
|
||||
.expect("failed to delegate to mixnode!");
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ pub struct Args {
|
||||
|
||||
#[clap(long)]
|
||||
pub identity_key: Option<String>,
|
||||
|
||||
#[clap(long)]
|
||||
pub on_behalf_of: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
|
||||
@@ -37,7 +40,7 @@ pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient)
|
||||
};
|
||||
|
||||
let res = client
|
||||
.vesting_undelegate_from_mixnode(mix_id, None)
|
||||
.vesting_undelegate_from_mixnode(mix_id, args.on_behalf_of, None)
|
||||
.await
|
||||
.expect("failed to remove stake from vesting account on mixnode!");
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof};
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, TimeConfiguration};
|
||||
use crate::verification_key::VerificationKeyShare;
|
||||
use cosmwasm_std::Addr;
|
||||
use schemars::JsonSchema;
|
||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct InstantiateMsg {
|
||||
pub group_addr: String,
|
||||
pub multisig_addr: String,
|
||||
pub time_configuration: Option<TimeConfiguration>,
|
||||
pub mix_denom: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
|
||||
pub use contracts_common::dealings::ContractSafeBytes;
|
||||
@@ -12,37 +14,91 @@ pub type EncodedBTEPublicKeyWithProof = String;
|
||||
pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
|
||||
pub type NodeIndex = u64;
|
||||
|
||||
// The time sign-up is open for dealers to join (2 minutes)
|
||||
pub const PUBLIC_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 2;
|
||||
pub const DEALING_EXCHANGE_TIME_SECS: u64 = 60 * 5;
|
||||
pub const VERIFICATION_KEY_SUBMISSION_TIME_SECS: u64 = 60 * 5;
|
||||
pub const VERIFICATION_KEY_VALIDATION_TIME_SECS: u64 = 60;
|
||||
pub const VERIFICATION_KEY_FINALIZATION_TIME_SECS: u64 = 60;
|
||||
// The time an epoch lasts (2 weeks)
|
||||
pub const IN_PROGRESS_TIME_SECS: u64 = 60 * 60 * 24 * 14;
|
||||
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
#[derive(
|
||||
Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd, JsonSchema,
|
||||
)]
|
||||
pub struct TimeConfiguration {
|
||||
// The time sign-up is open for dealers to join
|
||||
pub public_key_submission_time_secs: u64,
|
||||
pub dealing_exchange_time_secs: u64,
|
||||
pub verification_key_submission_time_secs: u64,
|
||||
pub verification_key_validation_time_secs: u64,
|
||||
pub verification_key_finalization_time_secs: u64,
|
||||
// The time an epoch lasts
|
||||
pub in_progress_time_secs: u64,
|
||||
}
|
||||
|
||||
impl FromStr for TimeConfiguration {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let times = s
|
||||
.split(',')
|
||||
.map(|t| t.parse())
|
||||
.collect::<Result<Vec<u64>, _>>()
|
||||
.map_err(|_| String::from("Could not parse string"))?;
|
||||
if times.len() != 6 {
|
||||
Err(String::from("Not enough time specified"))
|
||||
} else {
|
||||
Ok(TimeConfiguration {
|
||||
public_key_submission_time_secs: times[0],
|
||||
dealing_exchange_time_secs: times[1],
|
||||
verification_key_submission_time_secs: times[2],
|
||||
verification_key_validation_time_secs: times[3],
|
||||
verification_key_finalization_time_secs: times[4],
|
||||
in_progress_time_secs: times[5],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TimeConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
public_key_submission_time_secs: 60 * 10, // 10 minutes
|
||||
dealing_exchange_time_secs: 60 * 5, // 5 minutes
|
||||
verification_key_submission_time_secs: 60 * 5, // 5 minutes
|
||||
verification_key_validation_time_secs: 60, // 1 minute
|
||||
verification_key_finalization_time_secs: 60, // 1 minute
|
||||
in_progress_time_secs: 60 * 60 * 24 * 14, // 2 weeks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct Epoch {
|
||||
pub state: EpochState,
|
||||
pub time_configuration: TimeConfiguration,
|
||||
pub finish_timestamp: Timestamp,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
pub fn new(state: EpochState, current_timestamp: Timestamp) -> Self {
|
||||
pub fn new(
|
||||
state: EpochState,
|
||||
time_configuration: TimeConfiguration,
|
||||
current_timestamp: Timestamp,
|
||||
) -> Self {
|
||||
let duration = match state {
|
||||
EpochState::PublicKeySubmission => PUBLIC_KEY_SUBMISSION_TIME_SECS,
|
||||
EpochState::DealingExchange => DEALING_EXCHANGE_TIME_SECS,
|
||||
EpochState::VerificationKeySubmission => VERIFICATION_KEY_SUBMISSION_TIME_SECS,
|
||||
EpochState::VerificationKeyValidation => VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
EpochState::VerificationKeyFinalization => VERIFICATION_KEY_FINALIZATION_TIME_SECS,
|
||||
EpochState::InProgress => IN_PROGRESS_TIME_SECS,
|
||||
EpochState::PublicKeySubmission => time_configuration.public_key_submission_time_secs,
|
||||
EpochState::DealingExchange => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyValidation => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
}
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
}
|
||||
EpochState::InProgress => time_configuration.in_progress_time_secs,
|
||||
};
|
||||
Epoch {
|
||||
state,
|
||||
time_configuration,
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +134,32 @@ pub struct AllDelegationsResponse {
|
||||
pub start_next_after: Option<(u32, MixId, u64)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct AccountVestingCoins {
|
||||
pub account_id: u32,
|
||||
pub owner: Addr,
|
||||
pub still_vesting: Coin,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct VestingCoinsResponse {
|
||||
pub accounts: Vec<AccountVestingCoins>,
|
||||
pub start_next_after: Option<Addr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct BaseVestingAccountInfo {
|
||||
pub account_id: u32,
|
||||
pub owner: Addr,
|
||||
// TODO: should this particular query/response expose anything else?
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)]
|
||||
pub struct AccountsResponse {
|
||||
pub accounts: Vec<BaseVestingAccountInfo>,
|
||||
pub start_next_after: Option<Addr>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use contracts_common::Percent;
|
||||
|
||||
@@ -18,7 +18,8 @@ pub struct InitMsg {
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub v2_mixnet_contract_address: String,
|
||||
// I'm making it explicit so that we wouldn't accidentally forget about it
|
||||
pub manually_verified_no_staking_addresses: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, Default)]
|
||||
@@ -96,9 +97,11 @@ pub enum ExecuteMsg {
|
||||
DelegateToMixnode {
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
on_behalf_of: Option<String>,
|
||||
},
|
||||
UndelegateFromMixnode {
|
||||
mix_id: MixId,
|
||||
on_behalf_of: Option<String>,
|
||||
},
|
||||
CreateAccount {
|
||||
owner_address: String,
|
||||
@@ -188,6 +191,14 @@ impl ExecuteMsg {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum QueryMsg {
|
||||
GetContractVersion {},
|
||||
GetAccountsPaged {
|
||||
start_next_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetAccountsVestingCoinsPaged {
|
||||
start_next_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
LockedCoins {
|
||||
vesting_account_address: String,
|
||||
block_time: Option<Timestamp>,
|
||||
|
||||
@@ -26,5 +26,5 @@ nymsphinx-framing = { path = "../nymsphinx/framing" }
|
||||
nymsphinx-params = { path = "../nymsphinx/params" }
|
||||
nymsphinx-types = { path = "../nymsphinx/types" }
|
||||
task = { path = "../task" }
|
||||
validator-client = { path = "../client-libs/validator-client" }
|
||||
validator-client = { path = "../client-libs/validator-client", features = ["nyxd-client"]}
|
||||
version-checker = { path = "../version-checker" }
|
||||
|
||||
@@ -9,7 +9,8 @@ use futures::StreamExt;
|
||||
use log::*;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
use std::net::SocketAddr;
|
||||
use std::net::ToSocketAddrs;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use task::TaskClient;
|
||||
|
||||
@@ -21,6 +21,8 @@ pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "STATISTICS_SERVICE_DOMAIN_A
|
||||
pub const NYXD: &str = "NYXD";
|
||||
pub const NYM_API: &str = "NYM_API";
|
||||
|
||||
pub const DKG_TIME_CONFIGURATION: &str = "DKG_TIME_CONFIGURATION";
|
||||
|
||||
// we don't want to explicitly tag those with `#[deprecated]` because then our CI would be red and sad : (
|
||||
pub const DEPRECATED_NYMD_VALIDATOR: &str = "NYMD_VALIDATOR";
|
||||
pub const DEPRECATED_API_VALIDATOR: &str = "API_VALIDATOR";
|
||||
|
||||
@@ -5,6 +5,7 @@ use mixnet_contract_common::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
@@ -75,3 +76,37 @@ impl GatewayBond {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct GatewayNodeDetailsResponse {
|
||||
pub identity_key: String,
|
||||
pub sphinx_key: String,
|
||||
pub owner_signature: String,
|
||||
pub announce_address: String,
|
||||
pub bind_address: String,
|
||||
pub version: String,
|
||||
pub mix_port: u16,
|
||||
pub clients_port: u16,
|
||||
pub data_store: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for GatewayNodeDetailsResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Host: {} (bind address: {})",
|
||||
self.announce_address, self.bind_address
|
||||
)?;
|
||||
writeln!(f, "Version: {}", self.version)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Mix Port: {}, Clients port: {}",
|
||||
self.mix_port, self.clients_port
|
||||
)?;
|
||||
|
||||
writeln!(f, "Data store is at: {}", self.data_store)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use mixnet_contract_common::{
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg_attr(feature = "generate-ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(
|
||||
@@ -161,3 +162,42 @@ impl MixNodeCostParams {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MixnodeNodeDetailsResponse {
|
||||
pub identity_key: String,
|
||||
pub sphinx_key: String,
|
||||
pub owner_signature: String,
|
||||
pub announce_address: String,
|
||||
pub bind_address: String,
|
||||
pub version: String,
|
||||
pub mix_port: u16,
|
||||
pub http_api_port: u16,
|
||||
pub verloc_port: u16,
|
||||
pub wallet_address: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for MixnodeNodeDetailsResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let wallet_address = self.wallet_address.clone().unwrap_or_default();
|
||||
writeln!(f, "Identity Key: {}", self.identity_key)?;
|
||||
writeln!(f, "Sphinx Key: {}", self.sphinx_key)?;
|
||||
writeln!(f, "Owner Signature: {}", self.owner_signature)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Host: {} (bind address: {})",
|
||||
self.announce_address, self.bind_address
|
||||
)?;
|
||||
writeln!(f, "Version: {}", self.version)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Mix Port: {}, Verloc port: {}, Http Port: {}\n",
|
||||
self.mix_port, self.verloc_port, self.http_api_port
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"You are bonding to wallet address: {}\n\n",
|
||||
wallet_address
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- vesting-contract: `GetAccountsPaged` and `GetAccountsVestingCoinsPaged` queries for querying multiple accounts simultaneously ([#2791])
|
||||
|
||||
[#2791]: https://github.com/nymtech/nym/pull/2791
|
||||
|
||||
## [nym-contracts-v1.1.2](https://github.com/nymtech/nym/tree/nym-contracts-v1.1.2) (2022-12-07)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -52,7 +52,11 @@ pub fn instantiate(
|
||||
|
||||
CURRENT_EPOCH.save(
|
||||
deps.storage,
|
||||
&Epoch::new(EpochState::default(), env.block.time),
|
||||
&Epoch::new(
|
||||
EpochState::default(),
|
||||
msg.time_configuration.unwrap_or_default(),
|
||||
env.block.time,
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(Response::default())
|
||||
@@ -163,6 +167,7 @@ mod tests {
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: MULTISIG_CONTRACT.to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
app.instantiate_contract(
|
||||
@@ -197,6 +202,7 @@ mod tests {
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: "group_addr".to_string(),
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: "nym".to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -71,7 +71,7 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::helpers;
|
||||
use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS;
|
||||
use coconut_dkg_common::types::TimeConfiguration;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
|
||||
#[test]
|
||||
@@ -83,7 +83,10 @@ pub(crate) mod tests {
|
||||
let bte_key_with_proof = String::from("bte_key_with_proof");
|
||||
let announce_address = String::from("localhost:8000");
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_add_dealer(
|
||||
|
||||
@@ -43,7 +43,7 @@ pub(crate) mod tests {
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::PUBLIC_KEY_SUBMISSION_TIME_SECS;
|
||||
use coconut_dkg_common::types::TimeConfiguration;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
@@ -65,7 +65,10 @@ pub(crate) mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret =
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(crate) fn query_current_epoch_threshold(
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::{EpochState, PUBLIC_KEY_SUBMISSION_TIME_SECS};
|
||||
use coconut_dkg_common::types::{EpochState, TimeConfiguration};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
|
||||
#[test]
|
||||
@@ -35,7 +35,7 @@ pub(crate) mod test {
|
||||
mock_env()
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,11 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
let current_epoch = CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |mut epoch| {
|
||||
// TODO: When defaulting to the first state, some action will probably need to be taken on the
|
||||
// rest of the contract, as we're starting with a new set of signers
|
||||
epoch = Epoch::new(epoch.state.next().unwrap_or_default(), env.block.time);
|
||||
epoch = Epoch::new(
|
||||
epoch.state.next().unwrap_or_default(),
|
||||
epoch.time_configuration,
|
||||
env.block.time,
|
||||
);
|
||||
Ok(epoch)
|
||||
})?;
|
||||
if current_epoch.state == EpochState::DealingExchange {
|
||||
@@ -40,11 +44,7 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::{
|
||||
DealerDetails, EpochState, DEALING_EXCHANGE_TIME_SECS, IN_PROGRESS_TIME_SECS,
|
||||
PUBLIC_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_FINALIZATION_TIME_SECS,
|
||||
VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
};
|
||||
use coconut_dkg_common::types::{DealerDetails, EpochState, TimeConfiguration};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
@@ -57,13 +57,15 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS - 1);
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs - 1);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
@@ -75,10 +77,15 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::DealingExchange);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS)
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS - 2);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs - 2);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
@@ -90,15 +97,19 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS)
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS - 2);
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs
|
||||
- 2,
|
||||
);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(2)
|
||||
@@ -110,15 +121,19 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyValidation);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS)
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS - 3);
|
||||
env.block.time = env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs
|
||||
- 3,
|
||||
);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(3)
|
||||
@@ -130,15 +145,17 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::VerificationKeyFinalization);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS)
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs
|
||||
)
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_FINALIZATION_TIME_SECS - 1);
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_finalization_time_secs - 1);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(1)
|
||||
@@ -150,10 +167,15 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS)
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs)
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(IN_PROGRESS_TIME_SECS - 100);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs - 100);
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
EarlyEpochStateAdvancement(100)
|
||||
@@ -171,7 +193,9 @@ pub(crate) mod tests {
|
||||
assert_eq!(epoch.state, EpochState::PublicKeySubmission);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS)
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -198,7 +222,10 @@ pub(crate) mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
assert_eq!(
|
||||
THRESHOLD.may_load(deps.as_mut().storage).unwrap().unwrap(),
|
||||
|
||||
@@ -25,7 +25,7 @@ pub(crate) fn check_epoch_state(
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use coconut_dkg_common::types::Epoch;
|
||||
use coconut_dkg_common::types::{Epoch, TimeConfiguration};
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
|
||||
#[test]
|
||||
@@ -37,7 +37,7 @@ pub(crate) mod test {
|
||||
CURRENT_EPOCH
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Epoch::new(fixed_state, env.block.time),
|
||||
&Epoch::new(fixed_state, TimeConfiguration::default(), env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
for against_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
|
||||
@@ -17,6 +17,7 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: String::from(GROUP_CONTRACT),
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
};
|
||||
let env = mock_env();
|
||||
|
||||
@@ -77,10 +77,7 @@ mod tests {
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::MULTISIG_CONTRACT;
|
||||
use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::{
|
||||
EpochState, DEALING_EXCHANGE_TIME_SECS, PUBLIC_KEY_SUBMISSION_TIME_SECS,
|
||||
VERIFICATION_KEY_SUBMISSION_TIME_SECS, VERIFICATION_KEY_VALIDATION_TIME_SECS,
|
||||
};
|
||||
use coconut_dkg_common::types::{EpochState, TimeConfiguration};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw_controllers::AdminError;
|
||||
|
||||
@@ -105,9 +102,15 @@ mod tests {
|
||||
expected_state: EpochState::VerificationKeySubmission.to_string()
|
||||
}
|
||||
);
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let ret = try_commit_verification_key_share(
|
||||
deps.as_mut(),
|
||||
@@ -165,19 +168,25 @@ mod tests {
|
||||
}
|
||||
);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS);
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS);
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret =
|
||||
@@ -203,9 +212,15 @@ mod tests {
|
||||
let share = "share".to_string();
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
env.block.time = env.block.time.plus_seconds(PUBLIC_KEY_SUBMISSION_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env.block.time.plus_seconds(DEALING_EXCHANGE_TIME_SECS);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
@@ -223,12 +238,12 @@ mod tests {
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_SUBMISSION_TIME_SECS);
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(VERIFICATION_KEY_VALIDATION_TIME_SECS);
|
||||
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.clone()).unwrap();
|
||||
|
||||
@@ -71,6 +71,7 @@ fn dkg_proposal() {
|
||||
let msg = DkgInstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: multisig_contract_addr.to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
};
|
||||
let coconut_dkg_contract_addr = app
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::errors::ContractError;
|
||||
use crate::queued_migrations::migrate_to_v2_mixnet_contract;
|
||||
use crate::storage::{
|
||||
account_from_address, save_account, BlockTimestampSecs, ADMIN, DELEGATIONS,
|
||||
account_from_address, save_account, BlockTimestampSecs, ACCOUNTS, ADMIN, DELEGATIONS,
|
||||
MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
|
||||
};
|
||||
use crate::traits::{
|
||||
@@ -10,7 +9,7 @@ use crate::traits::{
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use contracts_common::ContractBuildInformation;
|
||||
use cosmwasm_std::{
|
||||
coin, entry_point, to_binary, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
||||
coin, entry_point, to_binary, Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Order,
|
||||
QueryResponse, Response, StdResult, Timestamp, Uint128,
|
||||
};
|
||||
use cw_storage_plus::Bound;
|
||||
@@ -26,8 +25,9 @@ use vesting_contract_common::messages::{
|
||||
ExecuteMsg, InitMsg, MigrateMsg, QueryMsg, VestingSpecification,
|
||||
};
|
||||
use vesting_contract_common::{
|
||||
AllDelegationsResponse, DelegationTimesResponse, OriginalVestingResponse, Period, PledgeCap,
|
||||
PledgeData, VestingDelegation,
|
||||
AccountVestingCoins, AccountsResponse, AllDelegationsResponse, BaseVestingAccountInfo,
|
||||
DelegationTimesResponse, OriginalVestingResponse, Period, PledgeCap, PledgeData,
|
||||
VestingCoinsResponse, VestingDelegation,
|
||||
};
|
||||
|
||||
pub const INITIAL_LOCKED_PLEDGE_CAP: Uint128 = Uint128::new(100_000_000_000);
|
||||
@@ -40,16 +40,28 @@ pub fn instantiate(
|
||||
info: MessageInfo,
|
||||
msg: InitMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
//! ADMIN is set to the address that instantiated the contract
|
||||
ADMIN.save(deps.storage, &info.sender.to_string())?;
|
||||
MIXNET_CONTRACT_ADDRESS.save(deps.storage, &msg.mixnet_contract_address)?;
|
||||
// validate the received mixnet contract address
|
||||
let mixnet_contract_address = deps.api.addr_validate(&msg.mixnet_contract_address)?;
|
||||
|
||||
// ADMIN is set to the address that instantiated the contract
|
||||
ADMIN.save(deps.storage, &info.sender)?;
|
||||
MIXNET_CONTRACT_ADDRESS.save(deps.storage, &mixnet_contract_address)?;
|
||||
MIX_DENOM.save(deps.storage, &msg.mix_denom)?;
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
migrate_to_v2_mixnet_contract(deps, msg)
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
// we can't perform this check inside the migrate function since there are 12k addresses to check
|
||||
// and this invariant MUST hold, otherwise we're gonna have bad time
|
||||
if !msg.manually_verified_no_staking_addresses {
|
||||
return Err(ContractError::Other {
|
||||
message:
|
||||
"the assumption that nobody has set a staking address hasn't been manually verified"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
@@ -94,12 +106,15 @@ pub fn execute(
|
||||
ExecuteMsg::UpdateMixnetAddress { address } => {
|
||||
try_update_mixnet_address(address, info, deps)
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode { mix_id, amount } => {
|
||||
try_delegate_to_mixnode(mix_id, amount, info, env, deps)
|
||||
}
|
||||
ExecuteMsg::UndelegateFromMixnode { mix_id } => {
|
||||
try_undelegate_from_mixnode(mix_id, info, deps)
|
||||
}
|
||||
ExecuteMsg::DelegateToMixnode {
|
||||
mix_id,
|
||||
amount,
|
||||
on_behalf_of,
|
||||
} => try_delegate_to_mixnode(mix_id, amount, on_behalf_of, info, env, deps),
|
||||
ExecuteMsg::UndelegateFromMixnode {
|
||||
mix_id,
|
||||
on_behalf_of,
|
||||
} => try_undelegate_from_mixnode(mix_id, on_behalf_of, info, deps),
|
||||
ExecuteMsg::CreateAccount {
|
||||
owner_address,
|
||||
staking_address,
|
||||
@@ -245,7 +260,9 @@ pub fn try_update_mixnet_address(
|
||||
if info.sender != ADMIN.load(deps.storage)? {
|
||||
return Err(ContractError::NotAdmin(info.sender.as_str().to_string()));
|
||||
}
|
||||
MIXNET_CONTRACT_ADDRESS.save(deps.storage, &address)?;
|
||||
let mixnet_contract_address = deps.api.addr_validate(&address)?;
|
||||
|
||||
MIXNET_CONTRACT_ADDRESS.save(deps.storage, &mixnet_contract_address)?;
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
@@ -313,6 +330,13 @@ fn try_update_staking_address(
|
||||
info: MessageInfo,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
if let Some(ref to_address) = to_address {
|
||||
if account_from_address(to_address, deps.storage, deps.api).is_ok() {
|
||||
// do not allow setting staking address to an existing account's address
|
||||
return Err(ContractError::StakingAccountExists(to_address.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
let address = info.sender.clone();
|
||||
let to_address = to_address.and_then(|x| deps.api.addr_validate(&x).ok());
|
||||
let mut account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
@@ -454,13 +478,23 @@ fn try_track_undelegation(
|
||||
fn try_delegate_to_mixnode(
|
||||
mix_id: MixId,
|
||||
amount: Coin,
|
||||
on_behalf_of: Option<String>,
|
||||
info: MessageInfo,
|
||||
env: Env,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let mix_denom = MIX_DENOM.load(deps.storage)?;
|
||||
let amount = validate_funds(&[amount], mix_denom)?;
|
||||
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
|
||||
let account = match on_behalf_of {
|
||||
Some(account_owner) => {
|
||||
let account = account_from_address(&account_owner, deps.storage, deps.api)?;
|
||||
ensure_staking_permission(&info.sender, &account)?;
|
||||
account
|
||||
}
|
||||
// you're the owner, you can do what you want
|
||||
None => account_from_address(info.sender.as_str(), deps.storage, deps.api)?,
|
||||
};
|
||||
|
||||
account.try_delegate_to_mixnode(mix_id, amount, &env, deps.storage)
|
||||
}
|
||||
@@ -488,10 +522,19 @@ fn try_claim_delegator_reward(
|
||||
/// Undelegates from a mixnode, sends [mixnet_contract_common::ExecuteMsg::UndelegateFromMixnodeOnBehalf] to [crate::storage::MIXNET_CONTRACT_ADDRESS].
|
||||
fn try_undelegate_from_mixnode(
|
||||
mix_id: MixId,
|
||||
on_behalf_of: Option<String>,
|
||||
info: MessageInfo,
|
||||
deps: DepsMut<'_>,
|
||||
) -> Result<Response, ContractError> {
|
||||
let account = account_from_address(info.sender.as_str(), deps.storage, deps.api)?;
|
||||
let account = match on_behalf_of {
|
||||
Some(account_owner) => {
|
||||
let account = account_from_address(&account_owner, deps.storage, deps.api)?;
|
||||
ensure_staking_permission(&info.sender, &account)?;
|
||||
account
|
||||
}
|
||||
// you're the owner, you can do what you want
|
||||
None => account_from_address(info.sender.as_str(), deps.storage, deps.api)?,
|
||||
};
|
||||
|
||||
account.try_undelegate_from_mixnode(mix_id, deps.storage)
|
||||
}
|
||||
@@ -568,6 +611,19 @@ fn try_create_periodic_vesting_account(
|
||||
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let query_res = match msg {
|
||||
QueryMsg::GetContractVersion {} => to_binary(&get_contract_version()),
|
||||
QueryMsg::GetAccountsPaged {
|
||||
start_next_after,
|
||||
limit,
|
||||
} => to_binary(&try_get_all_accounts(deps, start_next_after, limit)?),
|
||||
QueryMsg::GetAccountsVestingCoinsPaged {
|
||||
start_next_after,
|
||||
limit,
|
||||
} => to_binary(&try_get_all_accounts_locked_coins(
|
||||
deps,
|
||||
env,
|
||||
start_next_after,
|
||||
limit,
|
||||
)?),
|
||||
QueryMsg::LockedCoins {
|
||||
vesting_account_address,
|
||||
block_time,
|
||||
@@ -689,6 +745,72 @@ pub fn get_contract_version() -> ContractBuildInformation {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_get_all_accounts(
|
||||
deps: Deps<'_>,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<AccountsResponse, ContractError> {
|
||||
let limit = limit.unwrap_or(150).min(250) as usize;
|
||||
|
||||
let start = start_after
|
||||
.map(|raw| deps.api.addr_validate(&raw).map(Bound::exclusive))
|
||||
.transpose()?;
|
||||
|
||||
let accounts = ACCOUNTS
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|(_, account)| BaseVestingAccountInfo {
|
||||
account_id: account.storage_key(),
|
||||
owner: account.owner_address,
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = accounts.last().map(|acc| acc.owner.clone());
|
||||
|
||||
Ok(AccountsResponse {
|
||||
accounts,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_get_all_accounts_locked_coins(
|
||||
deps: Deps<'_>,
|
||||
env: Env,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<VestingCoinsResponse, ContractError> {
|
||||
let limit = limit.unwrap_or(150).min(250) as usize;
|
||||
|
||||
let start = start_after
|
||||
.map(|raw| deps.api.addr_validate(&raw).map(Bound::exclusive))
|
||||
.transpose()?;
|
||||
|
||||
let accounts = ACCOUNTS
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| {
|
||||
res.map(|(_, account)| {
|
||||
account
|
||||
.get_vesting_coins(None, &env, deps.storage)
|
||||
.map(|still_vesting| AccountVestingCoins {
|
||||
account_id: account.storage_key(),
|
||||
owner: account.owner_address,
|
||||
still_vesting,
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Result<Vec<_>, _>>>()??;
|
||||
|
||||
let start_next_after = accounts.last().map(|acc| acc.owner.clone());
|
||||
|
||||
Ok(VestingCoinsResponse {
|
||||
accounts,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets currently locked coins, see [crate::traits::VestingAccount::locked_coins]
|
||||
pub fn try_get_locked_coins(
|
||||
vesting_account_address: &str,
|
||||
@@ -855,3 +977,15 @@ fn validate_funds(funds: &[Coin], mix_denom: String) -> Result<Coin, ContractErr
|
||||
|
||||
Ok(funds[0].clone())
|
||||
}
|
||||
|
||||
fn ensure_staking_permission(addr: &Addr, account: &Account) -> Result<(), ContractError> {
|
||||
if let Some(staking_address) = account.staking_address() {
|
||||
if staking_address == addr {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(ContractError::InvalidStakingAccount {
|
||||
address: addr.clone(),
|
||||
for_account: account.owner_address(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,50 +6,81 @@ use thiserror::Error;
|
||||
pub enum ContractError {
|
||||
#[error("VESTING ({}): {0}", line!())]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error("VESTING ({}): Account does not exist - {0}", line!())]
|
||||
NoAccountForAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Only admin can perform this action, {0} is not admin", line!())]
|
||||
NotAdmin(String),
|
||||
|
||||
#[error("VESTING ({}): Balance not found for existing account ({0}), this is a bug", line!())]
|
||||
NoBalanceForAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Insufficient balance for address {0} -> {1}", line!())]
|
||||
InsufficientBalance(String, u128),
|
||||
|
||||
#[error("VESTING ({}): Insufficient spendable balance for address {0} -> {1}", line!())]
|
||||
InsufficientSpendable(String, u128),
|
||||
|
||||
#[error(
|
||||
"VESTING ({}):Only delegation owner can perform delegation actions, {0} is not the delegation owner"
|
||||
, line!())]
|
||||
NotDelegate(String),
|
||||
|
||||
#[error("VESTING ({}): Total vesting amount is inprobably low -> {0}, this is likely an error", line!())]
|
||||
ImprobableVestingAmount(u128),
|
||||
|
||||
#[error("VESTING ({}): Address {0} has already bonded a node", line!())]
|
||||
AlreadyBonded(String),
|
||||
|
||||
#[error("VESTING ({}): Received empty funds vector", line!())]
|
||||
EmptyFunds,
|
||||
|
||||
#[error("VESTING ({}): Received wrong denom: {0}, expected {1}", line!())]
|
||||
WrongDenom(String, String),
|
||||
|
||||
#[error("VESTING ({}): Received multiple denoms, expected 1", line!())]
|
||||
MultipleDenoms,
|
||||
|
||||
#[error("VESTING ({}): No delegations found for account {0}, mix_identity {1}", line!())]
|
||||
NoSuchDelegation(Addr, MixId),
|
||||
|
||||
#[error("VESTING ({}): Only mixnet contract can perform this operation, got {0}", line!())]
|
||||
NotMixnetContract(Addr),
|
||||
|
||||
#[error("VESTING ({}): Calculation underflowed", line!())]
|
||||
Underflow,
|
||||
|
||||
#[error("VESTING ({}): No bond found for account {0}", line!())]
|
||||
NoBondFound(String),
|
||||
|
||||
#[error("VESTING ({}): Action can only be executed by account owner -> {0}", line!())]
|
||||
NotOwner(String),
|
||||
|
||||
#[error("VESTING ({}): Invalid address: {0}", line!())]
|
||||
InvalidAddress(String),
|
||||
|
||||
#[error("VESTING ({}): Account already exists: {0}", line!())]
|
||||
AccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({}): Staking account already exists: {0}", line!())]
|
||||
StakingAccountAlreadyExists(String),
|
||||
|
||||
#[error("VESTING ({}): Too few coins sent for vesting account creation, sent {sent}, need at least {need}", line!())]
|
||||
MinVestingFunds { sent: u128, need: u128 },
|
||||
|
||||
#[error("VESTING ({}): Maximum amount of locked coins has already been pledged: {current}, cap is {cap}", line!())]
|
||||
LockedPledgeCapReached { current: Uint128, cap: Uint128 },
|
||||
|
||||
#[error("VESTING: ({}: Account owned by {owner} has unpopulated vesting periods!", line!())]
|
||||
UnpopulatedVestingPeriods { owner: Addr },
|
||||
|
||||
#[error("VESTING: Vesting account associated with {0} already exists, only addresses with not existing vesting accounts can be added as staking addresses")]
|
||||
StakingAccountExists(String),
|
||||
|
||||
#[error("VESTING: {address} is not permitted to perform staking on behalf of {for_account}")]
|
||||
InvalidStakingAccount { address: Addr, for_account: Addr },
|
||||
|
||||
#[error("VESTING: {message}")]
|
||||
Other { message: String },
|
||||
}
|
||||
|
||||
@@ -1,15 +1,2 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::MIXNET_CONTRACT_ADDRESS;
|
||||
use cosmwasm_std::{DepsMut, Response};
|
||||
use vesting_contract_common::MigrateMsg;
|
||||
|
||||
pub fn migrate_to_v2_mixnet_contract(
|
||||
deps: DepsMut<'_>,
|
||||
msg: MigrateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
MIXNET_CONTRACT_ADDRESS.save(deps.storage, &msg.v2_mixnet_contract_address)?;
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
@@ -6,23 +6,52 @@ use mixnet_contract_common::{IdentityKey, MixId};
|
||||
use vesting_contract_common::PledgeData;
|
||||
|
||||
pub(crate) type BlockTimestampSecs = u64;
|
||||
pub(crate) type AccountStorageKey = u32;
|
||||
|
||||
pub const KEY: Item<'_, u32> = Item::new("key");
|
||||
const ACCOUNTS: Map<'_, String, Account> = Map::new("acc");
|
||||
// Holds data related to individual accounts
|
||||
const BALANCES: Map<'_, u32, Uint128> = Map::new("blc");
|
||||
const WITHDRAWNS: Map<'_, u32, Uint128> = Map::new("wthd");
|
||||
const BOND_PLEDGES: Map<'_, u32, PledgeData> = Map::new("bnd");
|
||||
const GATEWAY_PLEDGES: Map<'_, u32, PledgeData> = Map::new("gtw");
|
||||
pub const _OLD_DELEGATIONS: Map<'_, (u32, IdentityKey, BlockTimestampSecs), Uint128> =
|
||||
/// Counter for the unique, monotonically increasing storage key id for the vesting account data.
|
||||
pub const KEY: Item<'_, AccountStorageKey> = Item::new("key");
|
||||
|
||||
/// Storage map containing vesting account information associated with particular owner address.
|
||||
pub const ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc");
|
||||
|
||||
/// Storage map containing information about amount of tokens associated with particular vesting account
|
||||
/// that are currently present in the contract (and have not been withdrawn or staked in the mixnet contract)
|
||||
// note: this assumes I understood the intent behind this correctly
|
||||
const BALANCES: Map<'_, AccountStorageKey, Uint128> = Map::new("blc");
|
||||
|
||||
/// Storage map containing information about amount of tokens withdrawn from the contract by a particular vesting account.
|
||||
const WITHDRAWNS: Map<'_, AccountStorageKey, Uint128> = Map::new("wthd");
|
||||
|
||||
/// Storage map containing information about amount of tokens pledged towards bonding mixnodes
|
||||
/// in the mixnet contract using a particular vesting account.
|
||||
const BOND_PLEDGES: Map<'_, AccountStorageKey, PledgeData> = Map::new("bnd");
|
||||
|
||||
/// Storage map containing information about amount of tokens pledged towards bonding gateways
|
||||
/// in the mixnet contract using a particular vesting account.
|
||||
const GATEWAY_PLEDGES: Map<'_, AccountStorageKey, PledgeData> = Map::new("gtw");
|
||||
|
||||
/// Old, pre-v2 migration, storage map that used to contain information about tokens delegated
|
||||
/// towards particular mixnodes in the mixnet contract with given vesting account.
|
||||
/// It should be completely empty.
|
||||
pub const _OLD_DELEGATIONS: Map<'_, (AccountStorageKey, IdentityKey, BlockTimestampSecs), Uint128> =
|
||||
Map::new("dlg");
|
||||
pub const DELEGATIONS: Map<'_, (u32, MixId, BlockTimestampSecs), Uint128> = Map::new("dlg_v2");
|
||||
pub const ADMIN: Item<'_, String> = Item::new("adm");
|
||||
pub const MIXNET_CONTRACT_ADDRESS: Item<'_, String> = Item::new("mix");
|
||||
|
||||
/// Storage map containing information about tokens delegated towards particular mixnodes
|
||||
/// in the mixnet contract with given vesting account.
|
||||
pub const DELEGATIONS: Map<'_, (AccountStorageKey, MixId, BlockTimestampSecs), Uint128> =
|
||||
Map::new("dlg_v2");
|
||||
|
||||
/// Explicit contract admin that is allowed, among other things, to create new vesting accounts.
|
||||
pub const ADMIN: Item<'_, Addr> = Item::new("adm");
|
||||
|
||||
/// Address of the mixnet contract.
|
||||
pub const MIXNET_CONTRACT_ADDRESS: Item<'_, Addr> = Item::new("mix");
|
||||
|
||||
/// The denomination of coin used for staking.
|
||||
pub const MIX_DENOM: Item<'_, String> = Item::new("den");
|
||||
|
||||
pub fn save_delegation(
|
||||
key: (u32, MixId, BlockTimestampSecs),
|
||||
key: (AccountStorageKey, MixId, BlockTimestampSecs),
|
||||
amount: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -31,26 +60,27 @@ pub fn save_delegation(
|
||||
}
|
||||
|
||||
pub fn remove_delegation(
|
||||
key: (u32, MixId, BlockTimestampSecs),
|
||||
key: (AccountStorageKey, MixId, BlockTimestampSecs),
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
DELEGATIONS.remove(storage, key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_account(address: &Addr, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
ACCOUNTS.remove(storage, address.to_owned().to_string());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_withdrawn(key: u32, storage: &dyn Storage) -> Result<Uint128, ContractError> {
|
||||
pub fn load_withdrawn(
|
||||
key: AccountStorageKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
Ok(WITHDRAWNS
|
||||
.may_load(storage, key)
|
||||
.unwrap_or(None)
|
||||
.unwrap_or_else(Uint128::zero))
|
||||
}
|
||||
|
||||
pub fn load_balance(key: u32, storage: &dyn Storage) -> Result<Uint128, ContractError> {
|
||||
pub fn load_balance(
|
||||
key: AccountStorageKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Uint128, ContractError> {
|
||||
Ok(BALANCES
|
||||
.may_load(storage, key)
|
||||
.unwrap_or(None)
|
||||
@@ -58,7 +88,7 @@ pub fn load_balance(key: u32, storage: &dyn Storage) -> Result<Uint128, Contract
|
||||
}
|
||||
|
||||
pub fn save_balance(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
value: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -67,7 +97,7 @@ pub fn save_balance(
|
||||
}
|
||||
|
||||
pub fn save_withdrawn(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
value: Uint128,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -76,19 +106,22 @@ pub fn save_withdrawn(
|
||||
}
|
||||
|
||||
pub fn load_bond_pledge(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Option<PledgeData>, ContractError> {
|
||||
Ok(BOND_PLEDGES.may_load(storage, key).unwrap_or(None))
|
||||
}
|
||||
|
||||
pub fn remove_bond_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
pub fn remove_bond_pledge(
|
||||
key: AccountStorageKey,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
BOND_PLEDGES.remove(storage, key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_bond_pledge(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
value: &PledgeData,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -97,14 +130,14 @@ pub fn save_bond_pledge(
|
||||
}
|
||||
|
||||
pub fn load_gateway_pledge(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Option<PledgeData>, ContractError> {
|
||||
Ok(GATEWAY_PLEDGES.may_load(storage, key).unwrap_or(None))
|
||||
}
|
||||
|
||||
pub fn save_gateway_pledge(
|
||||
key: u32,
|
||||
key: AccountStorageKey,
|
||||
value: &PledgeData,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
@@ -112,32 +145,34 @@ pub fn save_gateway_pledge(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_gateway_pledge(key: u32, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
pub fn remove_gateway_pledge(
|
||||
key: AccountStorageKey,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
GATEWAY_PLEDGES.remove(storage, key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_account(account: &Account, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
// This is a bit dirty, but its a simple way to allow for both staking account and owner to load it from storage
|
||||
if let Some(staking_address) = account.staking_address() {
|
||||
ACCOUNTS.save(storage, staking_address.to_owned().to_string(), account)?;
|
||||
}
|
||||
ACCOUNTS.save(storage, account.owner_address().to_string(), account)?;
|
||||
ACCOUNTS.save(storage, account.owner_address(), account)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn load_account(
|
||||
address: &Addr,
|
||||
address: Addr,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<Option<Account>, ContractError> {
|
||||
Ok(ACCOUNTS
|
||||
.may_load(storage, address.to_owned().to_string())
|
||||
.unwrap_or(None))
|
||||
Ok(ACCOUNTS.may_load(storage, address).unwrap_or(None))
|
||||
}
|
||||
|
||||
fn validate_account(address: &Addr, storage: &dyn Storage) -> Result<Account, ContractError> {
|
||||
load_account(address, storage)?
|
||||
.ok_or_else(|| ContractError::NoAccountForAddress(address.as_str().to_string()))
|
||||
pub fn delete_account(address: Addr, storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
ACCOUNTS.remove(storage, address);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_account(address: Addr, storage: &dyn Storage) -> Result<Account, ContractError> {
|
||||
load_account(address.clone(), storage)?
|
||||
.ok_or_else(|| ContractError::NoAccountForAddress(address.into_string()))
|
||||
}
|
||||
|
||||
pub fn account_from_address(
|
||||
@@ -145,5 +180,5 @@ pub fn account_from_address(
|
||||
storage: &dyn Storage,
|
||||
api: &dyn Api,
|
||||
) -> Result<Account, ContractError> {
|
||||
validate_account(&api.addr_validate(address)?, storage)
|
||||
validate_account(api.addr_validate(address)?, storage)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::errors::ContractError;
|
||||
use crate::storage::{
|
||||
load_balance, load_bond_pledge, load_gateway_pledge, load_withdrawn, remove_bond_pledge,
|
||||
remove_delegation, remove_gateway_pledge, save_account, save_balance, save_bond_pledge,
|
||||
save_gateway_pledge, save_withdrawn, BlockTimestampSecs, DELEGATIONS, KEY,
|
||||
save_gateway_pledge, save_withdrawn, AccountStorageKey, BlockTimestampSecs, DELEGATIONS, KEY,
|
||||
};
|
||||
use crate::traits::VestingAccount;
|
||||
use cosmwasm_std::{Addr, Coin, Order, Storage, Timestamp, Uint128};
|
||||
@@ -19,7 +19,7 @@ mod mixnode_bonding_account;
|
||||
mod node_families;
|
||||
mod vesting_account;
|
||||
|
||||
fn generate_storage_key(storage: &mut dyn Storage) -> Result<u32, ContractError> {
|
||||
fn generate_storage_key(storage: &mut dyn Storage) -> Result<AccountStorageKey, ContractError> {
|
||||
let key = KEY.may_load(storage)?.unwrap_or(0) + 1;
|
||||
KEY.save(storage, &key)?;
|
||||
Ok(key)
|
||||
@@ -32,7 +32,7 @@ pub struct Account {
|
||||
pub start_time: Timestamp,
|
||||
pub periods: Vec<VestingPeriod>,
|
||||
pub coin: Coin,
|
||||
storage_key: u32,
|
||||
storage_key: AccountStorageKey,
|
||||
#[serde(default)]
|
||||
pub pledge_cap: Option<PledgeCap>,
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ impl VestingAccount for Account {
|
||||
to_address: &Addr,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
delete_account(&self.owner_address(), storage)?;
|
||||
delete_account(self.owner_address(), storage)?;
|
||||
self.owner_address = to_address.to_owned();
|
||||
save_account(self, storage)?;
|
||||
Ok(())
|
||||
@@ -245,7 +245,7 @@ impl VestingAccount for Account {
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<(), ContractError> {
|
||||
if let Some(staking_address) = self.staking_address() {
|
||||
delete_account(staking_address, storage)?;
|
||||
delete_account(staking_address.to_owned(), storage)?;
|
||||
}
|
||||
self.staking_address = to_address;
|
||||
save_account(self, storage)?;
|
||||
|
||||
@@ -37,6 +37,7 @@ pub fn populate_vesting_periods(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::contract::*;
|
||||
use crate::errors::ContractError;
|
||||
use crate::storage::*;
|
||||
use crate::support::tests::helpers::vesting_account_percent_fixture;
|
||||
use crate::support::tests::helpers::{
|
||||
@@ -47,7 +48,7 @@ mod tests {
|
||||
use crate::traits::{GatewayBondingAccount, MixnodeBondingAccount};
|
||||
use crate::vesting::{populate_vesting_periods, Account};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, Addr, Coin, Timestamp, Uint128};
|
||||
use cosmwasm_std::{coin, coins, Addr, Coin, Timestamp, Uint128};
|
||||
use mixnet_contract_common::mixnode::MixNodeCostParams;
|
||||
use mixnet_contract_common::{Gateway, MixNode, Percent};
|
||||
use vesting_contract_common::messages::{ExecuteMsg, VestingSpecification};
|
||||
@@ -71,21 +72,44 @@ mod tests {
|
||||
|
||||
let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM));
|
||||
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
|
||||
let created_account = load_account(&Addr::unchecked("owner"), &deps.storage)
|
||||
let created_account = load_account(Addr::unchecked("owner"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let created_account_test_by_staking =
|
||||
load_account(&Addr::unchecked("staking"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(created_account_test_by_staking, created_account);
|
||||
|
||||
assert_eq!(
|
||||
created_account.load_balance(&deps.storage).unwrap(),
|
||||
// One was liquidated
|
||||
Uint128::new(1_000_000_000_000)
|
||||
);
|
||||
|
||||
// nothing is saved for "staking" account!
|
||||
let created_account_test_by_staking =
|
||||
load_account(Addr::unchecked("staking"), &deps.storage).unwrap();
|
||||
assert!(created_account_test_by_staking.is_none());
|
||||
|
||||
// but we can stake on its behalf!
|
||||
let stake_msg = ExecuteMsg::DelegateToMixnode {
|
||||
on_behalf_of: Some("owner".to_string()),
|
||||
mix_id: 42,
|
||||
amount: coin(500, TEST_COIN_DENOM),
|
||||
};
|
||||
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("staking", &[]),
|
||||
stake_msg,
|
||||
);
|
||||
assert!(response.is_ok());
|
||||
|
||||
assert_eq!(
|
||||
created_account.load_balance(&deps.storage).unwrap(),
|
||||
// One was liquidated
|
||||
Uint128::new(999_999_999_500)
|
||||
);
|
||||
|
||||
// Try create the same account again
|
||||
let _response = execute(deps.as_mut(), env.clone(), info, msg.clone());
|
||||
let response = execute(deps.as_mut(), env.clone(), info, msg);
|
||||
assert!(response.is_err());
|
||||
|
||||
let account_again = vesting_account_new_fixture(&mut deps.storage, &env);
|
||||
@@ -99,11 +123,12 @@ mod tests {
|
||||
let mut env = mock_env();
|
||||
let info = mock_info("owner", &[]);
|
||||
let account = vesting_account_new_fixture(&mut deps.storage, &env);
|
||||
let staker = account.staking_address().unwrap();
|
||||
let msg = ExecuteMsg::TransferOwnership {
|
||||
to_address: "new_owner".to_string(),
|
||||
};
|
||||
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()).unwrap();
|
||||
let new_owner_account = load_account(&Addr::unchecked("new_owner"), &deps.storage)
|
||||
let new_owner_account = load_account(Addr::unchecked("new_owner"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -112,25 +137,45 @@ mod tests {
|
||||
);
|
||||
|
||||
// Check old account is gone
|
||||
let old_owner_account = load_account(&Addr::unchecked("owner"), &deps.storage).unwrap();
|
||||
let old_owner_account = load_account(Addr::unchecked("owner"), &deps.storage).unwrap();
|
||||
assert!(old_owner_account.is_none());
|
||||
|
||||
// Not the owner
|
||||
let response = execute(deps.as_mut(), env.clone(), info.clone(), msg);
|
||||
assert!(response.is_err());
|
||||
|
||||
// can't stake on behalf of the original owner anymore, but we can do it for the new one!
|
||||
let stake_msg = ExecuteMsg::DelegateToMixnode {
|
||||
on_behalf_of: Some("owner".to_string()),
|
||||
mix_id: 42,
|
||||
amount: coin(500, TEST_COIN_DENOM),
|
||||
};
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(staker.as_ref(), &[]),
|
||||
stake_msg,
|
||||
);
|
||||
assert!(response.is_err());
|
||||
|
||||
let new_stake_msg = ExecuteMsg::DelegateToMixnode {
|
||||
on_behalf_of: Some("new_owner".to_string()),
|
||||
mix_id: 42,
|
||||
amount: coin(500, TEST_COIN_DENOM),
|
||||
};
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(staker.as_ref(), &[]),
|
||||
new_stake_msg,
|
||||
);
|
||||
assert!(response.is_ok());
|
||||
|
||||
let info = mock_info("new_owner", &[]);
|
||||
let msg = ExecuteMsg::UpdateStakingAddress {
|
||||
to_address: Some("new_staking".to_string()),
|
||||
};
|
||||
let _response = execute(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
let new_staking_account = load_account(&Addr::unchecked("new_staking"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(new_staking_account.owner_address(), "new_owner".to_string());
|
||||
|
||||
let old_staking_account = load_account(&Addr::unchecked("staking"), &deps.storage).unwrap();
|
||||
assert!(old_staking_account.is_none());
|
||||
|
||||
let msg = ExecuteMsg::WithdrawVestedCoins {
|
||||
amount: Coin {
|
||||
@@ -172,6 +217,117 @@ mod tests {
|
||||
assert!(response.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_staking_address_change() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
let account = vesting_account_new_fixture(&mut deps.storage, &env);
|
||||
let original_staker = account.staking_address().unwrap();
|
||||
|
||||
// can stake on behalf without an issue
|
||||
let stake_msg = ExecuteMsg::DelegateToMixnode {
|
||||
on_behalf_of: Some("owner".to_string()),
|
||||
mix_id: 42,
|
||||
amount: coin(500, TEST_COIN_DENOM),
|
||||
};
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(original_staker.as_ref(), &[]),
|
||||
stake_msg.clone(),
|
||||
);
|
||||
assert!(response.is_ok());
|
||||
|
||||
let info = mock_info("owner", &[]);
|
||||
let msg = ExecuteMsg::UpdateStakingAddress {
|
||||
to_address: Some("new_staking".to_string()),
|
||||
};
|
||||
let _response = execute(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
|
||||
// the old staking account can't do any staking anymore!
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(original_staker.as_ref(), &[]),
|
||||
stake_msg.clone(),
|
||||
);
|
||||
assert!(response.is_err());
|
||||
|
||||
// but the new one can
|
||||
let response = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("new_staking", &[]),
|
||||
stake_msg,
|
||||
);
|
||||
assert!(response.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_staking_account_transfer() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let amount1 = coin(1000000000, "unym");
|
||||
let amount2 = coin(100, "unym");
|
||||
|
||||
// create the accounts
|
||||
let msg1 = ExecuteMsg::CreateAccount {
|
||||
owner_address: "vesting1".to_string(),
|
||||
staking_address: None,
|
||||
vesting_spec: None,
|
||||
cap: None,
|
||||
};
|
||||
let res1 = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("admin", &[amount1.clone()]),
|
||||
msg1,
|
||||
);
|
||||
assert!(res1.is_ok());
|
||||
|
||||
let msg2 = ExecuteMsg::CreateAccount {
|
||||
owner_address: "vesting2".to_string(),
|
||||
staking_address: None,
|
||||
vesting_spec: None,
|
||||
cap: None,
|
||||
};
|
||||
let res2 = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("admin", &[amount2.clone()]),
|
||||
msg2,
|
||||
);
|
||||
assert!(res2.is_ok());
|
||||
|
||||
let vesting1 = try_get_vesting_coins("vesting1", None, env.clone(), deps.as_ref()).unwrap();
|
||||
assert_eq!(vesting1, amount1);
|
||||
|
||||
let vesting2 = try_get_vesting_coins("vesting2", None, env.clone(), deps.as_ref()).unwrap();
|
||||
assert_eq!(vesting2, amount2);
|
||||
|
||||
let staking_address_change = ExecuteMsg::UpdateStakingAddress {
|
||||
to_address: Some("vesting1".to_string()),
|
||||
};
|
||||
let res = execute(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info("vesting2", &[]),
|
||||
staking_address_change,
|
||||
);
|
||||
assert_eq!(
|
||||
Err(ContractError::StakingAccountExists("vesting1".to_string())),
|
||||
res
|
||||
);
|
||||
|
||||
// ensure nothing has changed!
|
||||
let vesting1 = try_get_vesting_coins("vesting1", None, env.clone(), deps.as_ref()).unwrap();
|
||||
assert_eq!(vesting1, amount1);
|
||||
|
||||
let vesting2 = try_get_vesting_coins("vesting2", None, env, deps.as_ref()).unwrap();
|
||||
assert_eq!(vesting2, amount2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_period_logic() {
|
||||
let mut deps = init_contract();
|
||||
@@ -461,7 +617,7 @@ mod tests {
|
||||
let info = mock_info("admin", &coins(1_000_000_000_000, TEST_COIN_DENOM));
|
||||
|
||||
let _response = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone());
|
||||
let account = load_account(&Addr::unchecked("owner"), &deps.storage)
|
||||
let account = load_account(Addr::unchecked("owner"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
@@ -518,7 +674,7 @@ mod tests {
|
||||
let total_delegations = account.total_delegations_for_mix(1, &deps.storage).unwrap();
|
||||
assert_eq!(Uint128::new(90_000_000_000), total_delegations);
|
||||
|
||||
let account = load_account(&Addr::unchecked("owner"), &deps.storage)
|
||||
let account = load_account(Addr::unchecked("owner"), &deps.storage)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
CONFIGURED=true
|
||||
|
||||
RUST_LOG=info
|
||||
RUST_BACKTRACE=1
|
||||
|
||||
BECH32_PREFIX=n
|
||||
MIX_DENOM=unym
|
||||
MIX_DENOM_DISPLAY=nym
|
||||
STAKE_DENOM=unyx
|
||||
STAKE_DENOM_DISPLAY=nyx
|
||||
DENOMS_EXPONENT=6
|
||||
MIXNET_CONTRACT_ADDRESS=n1xsml6tm6pnx8nsuz3a54cznh55g6nuwu9ug9t92ucjkq5ewp5w9syxeqsv
|
||||
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
|
||||
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1lp5zex6685kd22agzskhqsylpnssxnweyuvsz4edr4p4ta92qf3q3dhmsx
|
||||
MULTISIG_CONTRACT_ADDRESS=n1rw8fw2mpcpzzq3jpa4e52ufawnmj5a4u68p35umvgskewuw0nlzsaa5w4m
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n15f5e6ffz33khfzaadaug9axjt5r38xfe84v9jvlhfs0y7mv9sxvs2xt0u6
|
||||
REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
|
||||
NYMD_VALIDATOR="http://127.0.0.1:26657"
|
||||
API_VALIDATOR="http://127.0.0.1:8000"
|
||||
+3
-1
@@ -17,5 +17,7 @@ MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
COCONUT_DKG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
|
||||
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||
nyxd_VALIDATOR="https://rpc.nymtech.net";
|
||||
NYXD_VALIDATOR="https://rpc.nymtech.net";
|
||||
API_VALIDATOR="https://validator.nymtech.net/api/"
|
||||
|
||||
DKG_TIME_CONFIGURATION="259200,300,300,60,60,1209600"
|
||||
|
||||
@@ -19,3 +19,5 @@ REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="https://mainnet-stats.nymte.ch:8090"
|
||||
NYXD_VALIDATOR="https://qwerty-validator.qa.nymte.ch/"
|
||||
API_VALIDATOR="https://qwerty-validator-api.qa.nymte.ch/api"
|
||||
|
||||
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
|
||||
|
||||
@@ -19,3 +19,5 @@ REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
|
||||
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
|
||||
NYXD_VALIDATOR="https://qa-validator.nymtech.net"
|
||||
API_VALIDATOR="https://qa-validator-api.nymtech.net/api"
|
||||
|
||||
DKG_TIME_CONFIGURATION="600,300,300,60,60,1209600"
|
||||
@@ -1,2 +1,2 @@
|
||||
# The path to the geoip database file
|
||||
GEOIP_DB_PATH=./geo_ip/GeoLite2-Country.mmdb
|
||||
GEOIP_DB_PATH=./geo_ip/GeoLite2-City.mmdb
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "explorer-api"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -40,7 +40,7 @@ It should be previously installed thanks to `geoipupdate` service.
|
||||
For example:
|
||||
|
||||
```shell
|
||||
GEOIP_DB_PATH=./geo_ip/GeoLite2-Country.mmdb cargo run
|
||||
GEOIP_DB_PATH=./geo_ip/GeoLite2-City.mmdb cargo run
|
||||
```
|
||||
|
||||
Note: explorer-api binary reads the provided `.env` file.
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::geo_ip::location::Location;
|
||||
use crate::state::ExplorerApiStateContext;
|
||||
use rocket::response::status;
|
||||
use rocket::serde::json::Json;
|
||||
use rocket::{Route, State};
|
||||
use rocket_okapi::okapi::openapi3::OpenApi;
|
||||
use rocket_okapi::settings::OpenApiSettings;
|
||||
|
||||
pub fn nym_terms_make_default_routes(settings: &OpenApiSettings) -> (Vec<Route>, OpenApi) {
|
||||
openapi_get_routes_spec![settings: terms]
|
||||
}
|
||||
|
||||
#[openapi(tag = "terms")]
|
||||
#[get("/")]
|
||||
pub(crate) async fn terms(
|
||||
_state: &State<ExplorerApiStateContext>,
|
||||
location: Location,
|
||||
) -> Result<Json<String>, status::Forbidden<String>> {
|
||||
if location.iso_alpha2 == "US" {
|
||||
return Err(status::Forbidden(Some("US government sucks".to_string())));
|
||||
}
|
||||
Ok(Json("Nym Terms & Conditions: Welcome".to_string()))
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub(crate) mod http;
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
use isocountry::CountryCode;
|
||||
use log::warn;
|
||||
use maxminddb::{geoip2::Country, MaxMindDBError, Reader};
|
||||
use maxminddb::{geoip2::City, MaxMindDBError, Reader};
|
||||
use std::{
|
||||
net::{IpAddr, ToSocketAddrs},
|
||||
str::FromStr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
const DEFAULT_DATABASE_PATH: &str = "./geo_ip/GeoLite2-Country.mmdb";
|
||||
const DEFAULT_DATABASE_PATH: &str = "./geo_ip/GeoLite2-City.mmdb";
|
||||
const FAKE_PORT: u16 = 1234;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -38,6 +38,8 @@ pub(crate) struct Location {
|
||||
pub(crate) iso_alpha3: String,
|
||||
/// English country short name (ISO 3166-1)
|
||||
pub(crate) name: String,
|
||||
pub(crate) latitude: Option<f64>,
|
||||
pub(crate) longitude: Option<f64>,
|
||||
}
|
||||
|
||||
impl GeoIp {
|
||||
@@ -86,7 +88,7 @@ impl GeoIp {
|
||||
error!("No registered GeoIP database");
|
||||
GeoIpError::InternalError
|
||||
})?
|
||||
.lookup::<Country>(ip);
|
||||
.lookup::<City>(ip);
|
||||
match &result {
|
||||
Ok(v) => Ok(Some(
|
||||
Location::try_from(v).map_err(|_| GeoIpError::InternalError)?,
|
||||
@@ -99,11 +101,11 @@ impl GeoIp {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&Country<'a>> for Location {
|
||||
impl<'a> TryFrom<&City<'a>> for Location {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(country: &Country) -> Result<Self, Self::Error> {
|
||||
let data = country.country.as_ref().ok_or_else(|| {
|
||||
fn try_from(city: &City) -> Result<Self, Self::Error> {
|
||||
let data = city.country.as_ref().ok_or_else(|| {
|
||||
warn!("No Country data found");
|
||||
"No Country data found"
|
||||
})?;
|
||||
@@ -119,10 +121,13 @@ impl<'a> TryFrom<&Country<'a>> for Location {
|
||||
warn!("{}", &message);
|
||||
message
|
||||
})?;
|
||||
|
||||
Ok(Location {
|
||||
iso_alpha2,
|
||||
iso_alpha3: String::from(iso_codes.alpha3()),
|
||||
name: String::from(iso_codes.name()),
|
||||
latitude: city.location.as_ref().and_then(|l| l.latitude),
|
||||
longitude: city.location.as_ref().and_then(|l| l.longitude),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use rocket::{Build, Request, Rocket};
|
||||
use rocket_cors::{AllowedHeaders, AllowedOrigins};
|
||||
use rocket_okapi::swagger_ui::make_swagger_ui;
|
||||
|
||||
use crate::buy_terms::http::nym_terms_make_default_routes;
|
||||
use crate::country_statistics::http::country_statistics_make_default_routes;
|
||||
use crate::gateways::http::gateways_make_default_routes;
|
||||
use crate::http::swagger::get_docs;
|
||||
@@ -57,7 +56,6 @@ fn configure_rocket(state: ExplorerApiStateContext) -> Rocket<Build> {
|
||||
"/overview" => overview_make_default_routes(&openapi_settings),
|
||||
"/ping" => ping_make_default_routes(&openapi_settings),
|
||||
"/validators" => validators_make_default_routes(&openapi_settings),
|
||||
"/terms" => nym_terms_make_default_routes(&openapi_settings),
|
||||
};
|
||||
|
||||
building_rocket
|
||||
|
||||
@@ -10,7 +10,6 @@ use logging::setup_logging;
|
||||
use network_defaults::setup_env;
|
||||
use task::{wait_for_signal, TaskManager};
|
||||
|
||||
mod buy_terms;
|
||||
pub(crate) mod cache;
|
||||
mod client;
|
||||
pub(crate) mod commands;
|
||||
|
||||
@@ -36,6 +36,8 @@ pub(crate) struct Location {
|
||||
pub(crate) two_letter_iso_country_code: String,
|
||||
pub(crate) three_letter_iso_country_code: String,
|
||||
pub(crate) country_name: String,
|
||||
pub(crate) latitude: Option<f64>,
|
||||
pub(crate) longitude: Option<f64>,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
@@ -44,6 +46,8 @@ impl Location {
|
||||
country_name: location.name,
|
||||
two_letter_iso_country_code: location.iso_alpha2,
|
||||
three_letter_iso_country_code: location.iso_alpha3,
|
||||
latitude: location.latitude,
|
||||
longitude: location.longitude,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
## UNRELEASED
|
||||
|
||||
## [nym-explorer-v1.0.1](https://github.com/nymtech/nym/tree/nym-explorer-v1.0.1) (2023-01-10)
|
||||
|
||||
- Feat/2161 ne gate version by @gala1234 in https://github.com/nymtech/nym/pull/2743
|
||||
- fix(explorer): set gateway bond 6 decimals by @doums in https://github.com/nymtech/nym/pull/2741
|
||||
- Feat/2130 tables update rebase by @gala1234 in https://github.com/nymtech/nym/pull/2742
|
||||
- fix(explorer,explorer-api): mixnode location by @doums in https://github.com/nymtech/nym-api
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nym/network-explorer",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -55,10 +55,10 @@
|
||||
"@types/topojson-client": "^3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.13.0",
|
||||
"@typescript-eslint/parser": "^5.13.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-loader": "^8.3.0",
|
||||
"babel-plugin-root-import": "^6.6.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"css-loader": "^6.2.0",
|
||||
"css-loader": "^6.7.3",
|
||||
"css-minimizer-webpack-plugin": "^3.0.2",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"eslint": "^8.10.0",
|
||||
@@ -82,13 +82,13 @@
|
||||
"prettier": "2.3.2",
|
||||
"react-refresh": "^0.10.0",
|
||||
"react-refresh-typescript": "^2.0.2",
|
||||
"style-loader": "^3.2.1",
|
||||
"style-loader": "^3.3.1",
|
||||
"ts-jest": "^27.0.5",
|
||||
"ts-loader": "^9.2.5",
|
||||
"ts-loader": "^9.4.2",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.6.2",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.64.3",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^4.8.0",
|
||||
"webpack-dev-server": "^4.5.0",
|
||||
"webpack-favicons": "^1.3.8",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Tooltip } from '@nymproject/react/tooltip/Tooltip';
|
||||
import { CopyToClipboard } from '@nymproject/react/clipboard/CopyToClipboard';
|
||||
import { Box } from '@mui/system';
|
||||
import { cellStyles } from './Universal-DataGrid';
|
||||
import { currencyToString } from '../utils/currency';
|
||||
import { unymToNym } from '../utils/currency';
|
||||
import { GatewayEnrichedRowType } from './Gateways';
|
||||
import { MixnodeRowType } from './MixNodes';
|
||||
|
||||
@@ -38,7 +38,7 @@ function formatCellValues(val: string | number, field: string) {
|
||||
);
|
||||
}
|
||||
if (field === 'bond') {
|
||||
return currencyToString(val.toString());
|
||||
return unymToNym(val, 6);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export type GatewayRowType = {
|
||||
bond: number;
|
||||
host: string;
|
||||
location: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type GatewayEnrichedRowType = GatewayRowType & {
|
||||
@@ -26,6 +27,7 @@ export function gatewayToGridRow(arrayOfGateways: GatewayResponse): GatewayRowTy
|
||||
location: gw?.gateway?.location || '',
|
||||
bond: gw.pledge_amount.amount || 0,
|
||||
host: gw.gateway.host || '',
|
||||
version: gw.gateway.version || '',
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -40,6 +42,7 @@ export function gatewayEnrichedToGridRow(
|
||||
location: gateway?.gateway?.location || '',
|
||||
bond: gateway.pledge_amount.amount || 0,
|
||||
host: gateway.gateway.host || '',
|
||||
version: gateway.gateway.version || '',
|
||||
clientsPort: gateway.gateway.clients_port || 0,
|
||||
mixPort: gateway.gateway.mix_port || 0,
|
||||
routingScore: `${report.most_recent}%`,
|
||||
|
||||
@@ -58,6 +58,12 @@ const columns: ColumnsType[] = [
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
title: 'Version',
|
||||
headerAlign: 'left',
|
||||
flex: 1,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TableToolbar } from '../../components/TableToolbar';
|
||||
import { CustomColumnHeading } from '../../components/CustomColumnHeading';
|
||||
import { Title } from '../../components/Title';
|
||||
import { cellStyles, UniversalDataGrid } from '../../components/Universal-DataGrid';
|
||||
import { currencyToString } from '../../utils/currency';
|
||||
import { unymToNym } from '../../utils/currency';
|
||||
import { Tooltip } from '../../components/Tooltip';
|
||||
import { BIG_DIPPER } from '../../api/constants';
|
||||
import { splice } from '../../utils';
|
||||
@@ -77,7 +77,7 @@ export const PageGateways: React.FC = () => {
|
||||
to={`/network-components/gateway/${params.row.identityKey}`}
|
||||
data-testid="pledge-amount"
|
||||
>
|
||||
{currencyToString(params.value)}
|
||||
{unymToNym(params.value, 6)}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
@@ -128,7 +128,7 @@ export const PageGateways: React.FC = () => {
|
||||
field: 'owner',
|
||||
headerName: 'Owner',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Owner" />,
|
||||
width: 380,
|
||||
width: 180,
|
||||
headerAlign: 'left',
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
@@ -142,6 +142,24 @@ export const PageGateways: React.FC = () => {
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
{
|
||||
field: 'version',
|
||||
headerName: 'Version',
|
||||
renderHeader: () => <CustomColumnHeading headingTitle="Version" />,
|
||||
width: 150,
|
||||
headerAlign: 'left',
|
||||
headerClassName: 'MuiDataGrid-header-override',
|
||||
renderCell: (params: GridRenderCellParams) => (
|
||||
<MuiLink
|
||||
sx={{ ...cellStyles }}
|
||||
href={`${BIG_DIPPER}/account/${params.value}`}
|
||||
target="_blank"
|
||||
data-testid="owner"
|
||||
>
|
||||
{params.value}
|
||||
</MuiLink>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const handlePageSize = (event: SelectChangeEvent<string>) => {
|
||||
|
||||
@@ -164,10 +164,10 @@ const PageMixnodeDetailWithState: React.FC = () => {
|
||||
{mixNode && (
|
||||
<ContentCard title="Location">
|
||||
{mixNode?.error && <ComponentError text="There was a problem retrieving this mixnode location" />}
|
||||
{mixNode.data && mixNode?.data?.location && (
|
||||
{mixNode?.data?.location?.latitude && mixNode?.data?.location?.longitude && (
|
||||
<WorldMap
|
||||
loading={mixNode.isLoading}
|
||||
userLocation={[mixNode?.data?.location?.lng, mixNode?.data?.location?.lat]}
|
||||
userLocation={[mixNode.data.location.longitude, mixNode.data.location.latitude]}
|
||||
/>
|
||||
)}
|
||||
</ContentCard>
|
||||
|
||||
@@ -77,8 +77,8 @@ export interface MixNodeResponseItem {
|
||||
status: MixnodeStatus;
|
||||
location: {
|
||||
country_name: string;
|
||||
lat: number;
|
||||
lng: number;
|
||||
latitude?: number;
|
||||
longitude?: number;
|
||||
three_letter_iso_country_code: string;
|
||||
two_letter_iso_country_code: string;
|
||||
};
|
||||
|
||||
+7
-4
@@ -3,7 +3,7 @@
|
||||
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
@@ -31,7 +31,7 @@ log = "0.4"
|
||||
once_cell = "1.7.2"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.7"
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
@@ -58,7 +58,7 @@ credentials = { path = "../common/credentials" }
|
||||
config = { path = "../common/config" }
|
||||
crypto = { path = "../common/crypto" }
|
||||
completions = { path = "../common/completions" }
|
||||
logging = { path = "../common/logging"}
|
||||
logging = { path = "../common/logging" }
|
||||
gateway-requests = { path = "gateway-requests" }
|
||||
mixnet-client = { path = "../common/client-libs/mixnet-client" }
|
||||
mixnode-common = { path = "../common/mixnode-common" }
|
||||
@@ -71,6 +71,9 @@ validator-client = { path = "../common/client-libs/validator-client", features =
|
||||
"nyxd-client",
|
||||
] }
|
||||
version-checker = { path = "../common/version-checker" }
|
||||
nym-types = { path = "../common/types" }
|
||||
serde_json = "1"
|
||||
atty = "0.2"
|
||||
|
||||
[features]
|
||||
coconut = [
|
||||
@@ -87,4 +90,4 @@ sqlx = { version = "0.5", features = [
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
] }
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
config::{persistence::pathfinder::GatewayPathfinder, Config},
|
||||
OutputFormat,
|
||||
};
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
@@ -101,7 +102,7 @@ impl From<Init> for OverrideConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(args: &Init) {
|
||||
pub async fn execute(args: &Init, output: OutputFormat) {
|
||||
println!("Initialising gateway {}...", args.id);
|
||||
|
||||
let already_init = if Config::default_config_file_path(Some(&args.id)).exists() {
|
||||
@@ -157,7 +158,7 @@ pub async fn execute(args: &Init) {
|
||||
|
||||
crate::node::create_gateway(config)
|
||||
.await
|
||||
.print_node_details();
|
||||
.print_node_details(output);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -66,10 +66,12 @@ pub(crate) struct OverrideConfig {
|
||||
pub(crate) async fn execute(args: Cli) {
|
||||
let bin_name = "nym-gateway";
|
||||
|
||||
let output = args.output();
|
||||
|
||||
match &args.command {
|
||||
Commands::Init(m) => init::execute(m).await,
|
||||
Commands::NodeDetails(m) => node_details::execute(m).await,
|
||||
Commands::Run(m) => run::execute(m).await,
|
||||
Commands::Init(m) => init::execute(m, output.clone()).await,
|
||||
Commands::NodeDetails(m) => node_details::execute(m, output.clone()).await,
|
||||
Commands::Run(m) => run::execute(m, output.clone()).await,
|
||||
Commands::Sign(m) => sign::execute(m),
|
||||
Commands::Upgrade(m) => upgrade::execute(m).await,
|
||||
Commands::Completions(s) => s.generate(&mut crate::Cli::command(), bin_name),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::{config::Config, OutputFormat};
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
use log::error;
|
||||
@@ -13,7 +13,7 @@ pub struct NodeDetails {
|
||||
id: String,
|
||||
}
|
||||
|
||||
pub async fn execute(args: &NodeDetails) {
|
||||
pub async fn execute(args: &NodeDetails, output: OutputFormat) {
|
||||
let config = match Config::load_from_file(Some(&args.id)) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(err) => {
|
||||
@@ -28,5 +28,5 @@ pub async fn execute(args: &NodeDetails) {
|
||||
|
||||
crate::node::create_gateway(config)
|
||||
.await
|
||||
.print_node_details();
|
||||
.print_node_details(output);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
use crate::{
|
||||
commands::{override_config, version_check, OverrideConfig},
|
||||
config::Config,
|
||||
OutputFormat,
|
||||
};
|
||||
use clap::Args;
|
||||
use config::NymConfig;
|
||||
@@ -116,7 +117,7 @@ fn special_addresses() -> Vec<&'static str> {
|
||||
vec!["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]
|
||||
}
|
||||
|
||||
pub async fn execute(args: &Run) {
|
||||
pub async fn execute(args: &Run, output: OutputFormat) {
|
||||
println!("Starting gateway {}...", args.id);
|
||||
|
||||
let mut config = match Config::load_from_file(Some(&args.id)) {
|
||||
@@ -146,7 +147,7 @@ pub async fn execute(args: &Run) {
|
||||
println!(
|
||||
"\nTo bond your gateway you will need to install the Nym wallet, go to https://nymtech.net/get-involved and select the Download button.\n\
|
||||
Select the correct version and install it to your machine. You will need to provide the following: \n ");
|
||||
gateway.print_node_details();
|
||||
gateway.print_node_details(output);
|
||||
|
||||
gateway.run().await;
|
||||
}
|
||||
|
||||
@@ -413,10 +413,11 @@ impl Default for Gateway {
|
||||
private_sphinx_key_file: Default::default(),
|
||||
public_sphinx_key_file: Default::default(),
|
||||
enabled_statistics: false,
|
||||
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS).expect("Invalid default statistics service URL"),
|
||||
statistics_service_url: Url::from_str(STATISTICS_SERVICE_DOMAIN_ADDRESS)
|
||||
.expect("Invalid default statistics service URL"),
|
||||
nym_api_urls: vec![Url::from_str(NYM_API).expect("Invalid default API URL")],
|
||||
nyxd_urls: vec![Url::from_str(NYXD_URL).expect("Invalid default nyxd URL")],
|
||||
cosmos_mnemonic: bip39::Mnemonic::from_str("exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day").unwrap(),
|
||||
cosmos_mnemonic: bip39::Mnemonic::generate(24).unwrap(),
|
||||
nym_root_directory: Config::default_root_directory(),
|
||||
persistent_storage: Default::default(),
|
||||
wallet_address: None,
|
||||
|
||||
+29
-2
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use build_information::BinaryBuildInformation;
|
||||
use clap::{crate_version, Parser};
|
||||
use clap::{crate_version, Parser, ValueEnum};
|
||||
use lazy_static::lazy_static;
|
||||
use logging::setup_logging;
|
||||
use network_defaults::setup_env;
|
||||
@@ -21,6 +21,18 @@ fn pretty_build_info_static() -> &'static str {
|
||||
&PRETTY_BUILD_INFORMATION
|
||||
}
|
||||
|
||||
#[derive(Clone, ValueEnum)]
|
||||
pub enum OutputFormat {
|
||||
Json,
|
||||
Text,
|
||||
}
|
||||
|
||||
impl Default for OutputFormat {
|
||||
fn default() -> Self {
|
||||
OutputFormat::Text
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author = "Nymtech", version, about, long_version = pretty_build_info_static())]
|
||||
struct Cli {
|
||||
@@ -28,14 +40,29 @@ struct Cli {
|
||||
#[clap(short, long)]
|
||||
pub(crate) config_env_file: Option<std::path::PathBuf>,
|
||||
|
||||
#[clap(short, long)]
|
||||
pub(crate) output: Option<OutputFormat>,
|
||||
|
||||
#[clap(subcommand)]
|
||||
command: commands::Commands,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
fn output(&self) -> OutputFormat {
|
||||
if let Some(ref output) = self.output {
|
||||
output.clone()
|
||||
} else {
|
||||
OutputFormat::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
setup_logging();
|
||||
println!("{}", banner());
|
||||
if atty::is(atty::Stream::Stdout) {
|
||||
println!("{}", banner());
|
||||
}
|
||||
|
||||
let args = Cli::parse();
|
||||
setup_env(args.config_env_file.as_ref());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user