Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffe9084566 | |||
| 8beb33fe92 | |||
| bf5b8fab85 | |||
| b0960091c1 | |||
| b97a12186f | |||
| 36496a519a | |||
| 87fad25ac3 | |||
| df3d478caa | |||
| 751e3ccd27 | |||
| 74a4546d72 | |||
| 2587d00b9e | |||
| cf25c331c0 | |||
| a24c7e4783 | |||
| 9ea725bf83 | |||
| fe78d4faf0 | |||
| 0df063f9f6 | |||
| ad8fdbdddf | |||
| 2746cabecc | |||
| 666cbcf2cc | |||
| 96ab4325e3 | |||
| 11e2ba33e7 | |||
| 95db26c35b | |||
| aed96b2d44 | |||
| 326d5fcec8 | |||
| afae6fc9a5 | |||
| 23c13a409a | |||
| 1d522143a2 | |||
| 775ce0f95d | |||
| b019786c5a | |||
| 9b9c01fb8f | |||
| 6c857b5daf | |||
| 5ea084d286 |
@@ -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"
|
||||
|
||||
@@ -6,6 +6,8 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
### Added
|
||||
|
||||
- nym-sdk: added initial version of a Rust client sdk
|
||||
|
||||
### Changed
|
||||
|
||||
- renamed all references to validator_api to nym_api
|
||||
|
||||
Generated
+7342
File diff suppressed because it is too large
Load Diff
@@ -73,6 +73,7 @@ members = [
|
||||
"gateway/gateway-requests",
|
||||
"integrations/bity",
|
||||
"mixnode",
|
||||
"sdk/rust/nym-sdk",
|
||||
"service-providers/network-requester",
|
||||
"service-providers/network-statistics",
|
||||
"nym-api",
|
||||
|
||||
@@ -33,12 +33,15 @@ use log::{debug, info};
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
use nymsphinx::addressing::nodes::NodeIdentity;
|
||||
use nymsphinx::receiver::ReconstructedMessage;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tap::TapFallible;
|
||||
use task::{TaskClient, TaskManager};
|
||||
use url::Url;
|
||||
|
||||
use super::received_buffer::ReceivedBufferMessage;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "fs-surb-storage"))]
|
||||
pub mod non_wasm_helpers;
|
||||
|
||||
@@ -48,10 +51,30 @@ pub struct ClientInput {
|
||||
}
|
||||
|
||||
pub struct ClientOutput {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub received_buffer_request_sender: ReceivedBufferRequestSender,
|
||||
}
|
||||
|
||||
impl ClientOutput {
|
||||
pub fn register_receiver(
|
||||
&mut self,
|
||||
) -> Result<mpsc::UnboundedReceiver<Vec<ReconstructedMessage>>, ClientCoreError> {
|
||||
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
|
||||
|
||||
self.received_buffer_request_sender
|
||||
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
|
||||
reconstructed_sender,
|
||||
))
|
||||
.map_err(|_| ClientCoreError::FailedToRegisterReceiver)?;
|
||||
|
||||
Ok(reconstructed_receiver)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ClientState {
|
||||
pub shared_lane_queue_lengths: LaneQueueLengths,
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
pub enum ClientInputStatus {
|
||||
AwaitingProducer { client_input: ClientInput },
|
||||
Connected,
|
||||
@@ -80,6 +103,32 @@ impl ClientOutputStatus {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CredentialsToggle {
|
||||
Enabled,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl CredentialsToggle {
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self == &CredentialsToggle::Enabled
|
||||
}
|
||||
|
||||
pub fn is_disabled(&self) -> bool {
|
||||
self == &CredentialsToggle::Disabled
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for CredentialsToggle {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
CredentialsToggle::Enabled
|
||||
} else {
|
||||
CredentialsToggle::Disabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseClientBuilder<'a, B> {
|
||||
// due to wasm limitations I had to split it like this : (
|
||||
gateway_config: &'a GatewayEndpointConfig,
|
||||
@@ -119,13 +168,13 @@ where
|
||||
key_manager: KeyManager,
|
||||
bandwidth_controller: Option<BandwidthController>,
|
||||
reply_storage_backend: B,
|
||||
disabled_credentials: bool,
|
||||
credentials_toggle: CredentialsToggle,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
) -> BaseClientBuilder<'a, B> {
|
||||
BaseClientBuilder {
|
||||
gateway_config,
|
||||
debug_config,
|
||||
disabled_credentials,
|
||||
disabled_credentials: credentials_toggle.is_disabled(),
|
||||
nym_api_endpoints,
|
||||
reply_storage_backend,
|
||||
bandwidth_controller,
|
||||
@@ -248,7 +297,7 @@ where
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
// disgusting wasm workaround since there's no key persistence there (nor `client init`)
|
||||
let shared_key = if self.key_manager.gateway_key_set() {
|
||||
let shared_key = if self.key_manager.is_gateway_key_set() {
|
||||
Some(self.key_manager.gateway_shared_key())
|
||||
} else {
|
||||
None
|
||||
@@ -475,11 +524,13 @@ where
|
||||
},
|
||||
client_output: ClientOutputStatus::AwaitingConsumer {
|
||||
client_output: ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
},
|
||||
},
|
||||
reply_controller_sender,
|
||||
client_state: ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
},
|
||||
task_manager,
|
||||
})
|
||||
}
|
||||
@@ -488,9 +539,7 @@ where
|
||||
pub struct BaseClient {
|
||||
pub client_input: ClientInputStatus,
|
||||
pub client_output: ClientOutputStatus,
|
||||
|
||||
// it feels very wrong to put this channel here, but I can't think of any other way of passing it to the native client
|
||||
pub reply_controller_sender: ReplyControllerSender,
|
||||
pub client_state: ClientState,
|
||||
|
||||
pub task_manager: TaskManager,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::client::replies::reply_storage::{
|
||||
fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
self, fs_backend, CombinedReplyStorage, ReplyStorageBackend,
|
||||
};
|
||||
use crate::config::DebugConfig;
|
||||
use crate::error::ClientCoreError;
|
||||
@@ -85,3 +85,10 @@ pub async fn setup_fs_reply_surb_backend<P: AsRef<Path>>(
|
||||
setup_fresh_backend(db_path, debug_config).await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_empty_reply_surb_backend(debug_config: &DebugConfig) -> reply_storage::Empty {
|
||||
reply_storage::Empty {
|
||||
min_surb_threshold: debug_config.minimum_reply_surb_storage_threshold,
|
||||
max_surb_threshold: debug_config.maximum_reply_surb_storage_threshold,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use std::sync::Arc;
|
||||
// use the old key after new one was issued.
|
||||
|
||||
// Remember that Arc<T> has Deref implementation for T
|
||||
#[derive(Clone)]
|
||||
pub struct KeyManager {
|
||||
/// identity key associated with the client instance.
|
||||
identity_keypair: Arc<identity::KeyPair>,
|
||||
@@ -57,16 +58,22 @@ impl KeyManager {
|
||||
}
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
self.gateway_shared_key = Some(gateway_shared_key)
|
||||
pub fn from_keys(
|
||||
id_keypair: identity::KeyPair,
|
||||
enc_keypair: encryption::KeyPair,
|
||||
gateway_shared_key: SharedKeys,
|
||||
ack_key: AckKey,
|
||||
) -> Self {
|
||||
Self {
|
||||
identity_keypair: Arc::new(id_keypair),
|
||||
encryption_keypair: Arc::new(enc_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
ack_key: Arc::new(ack_key),
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
/// Loads previously stored client keys from the disk.
|
||||
fn load_client_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let identity_keypair: identity::KeyPair =
|
||||
pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
client_pathfinder.private_identity_key().to_owned(),
|
||||
@@ -78,21 +85,51 @@ impl KeyManager {
|
||||
client_pathfinder.public_encryption_key().to_owned(),
|
||||
))?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
|
||||
|
||||
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||
// after all for consistency sake?
|
||||
Ok(KeyManager {
|
||||
identity_keypair: Arc::new(identity_keypair),
|
||||
encryption_keypair: Arc::new(encryption_keypair),
|
||||
gateway_shared_key: Some(Arc::new(gateway_shared_key)),
|
||||
gateway_shared_key: None,
|
||||
ack_key: Arc::new(ack_key),
|
||||
})
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk. Fails if not all, including the shared gateway
|
||||
/// key, is available.
|
||||
pub fn load_keys(client_pathfinder: &ClientKeyPathfinder) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
key_manager.gateway_shared_key = Some(Arc::new(gateway_shared_key));
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
/// Loads previously stored keys from the disk. Fails if client keys are not availabe, but the
|
||||
/// shared gateway key is optional.
|
||||
pub fn load_keys_but_gateway_is_optional(
|
||||
client_pathfinder: &ClientKeyPathfinder,
|
||||
) -> io::Result<Self> {
|
||||
let mut key_manager = Self::load_client_keys(client_pathfinder)?;
|
||||
|
||||
let gateway_shared_key: Result<SharedKeys, io::Error> =
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key());
|
||||
|
||||
// It's ok if the gateway key was not found
|
||||
let gateway_shared_key = match gateway_shared_key {
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
Ok(key) => Ok(Some(key)),
|
||||
}?;
|
||||
|
||||
key_manager.gateway_shared_key = gateway_shared_key.map(Arc::new);
|
||||
|
||||
Ok(key_manager)
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
@@ -119,7 +156,7 @@ impl KeyManager {
|
||||
pemstore::store_key(self.ack_key.as_ref(), client_pathfinder.ack_key())?;
|
||||
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => warn!("No gateway shared key available to store!"),
|
||||
None => debug!("No gateway shared key available to store!"),
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
@@ -128,16 +165,60 @@ impl KeyManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn store_gateway_key(&self, client_pathfinder: &ClientKeyPathfinder) -> io::Result<()> {
|
||||
match self.gateway_shared_key.as_ref() {
|
||||
None => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"trying to store a non-existing key",
|
||||
))
|
||||
}
|
||||
Some(gate_key) => {
|
||||
pemstore::store_key(gate_key.as_ref(), client_pathfinder.gateway_shared_key())?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Overwrite the existing identity keypair
|
||||
pub fn set_identity_keypair(&mut self, id_keypair: identity::KeyPair) {
|
||||
self.identity_keypair = Arc::new(id_keypair);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`identity::KeyPair`].
|
||||
pub fn identity_keypair(&self) -> Arc<identity::KeyPair> {
|
||||
Arc::clone(&self.identity_keypair)
|
||||
}
|
||||
|
||||
/// Overwrite the existing encryption keypair
|
||||
pub fn set_encryption_keypair(&mut self, enc_keypair: encryption::KeyPair) {
|
||||
self.encryption_keypair = Arc::new(enc_keypair);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`encryption::KeyPair`].
|
||||
pub fn encryption_keypair(&self) -> Arc<encryption::KeyPair> {
|
||||
Arc::clone(&self.encryption_keypair)
|
||||
}
|
||||
|
||||
/// Overwrite the existing ack key
|
||||
pub fn set_ack_key(&mut self, ack_key: AckKey) {
|
||||
self.ack_key = Arc::new(ack_key);
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`AckKey`].
|
||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
||||
Arc::clone(&self.ack_key)
|
||||
}
|
||||
|
||||
// this is actually **NOT** dead code
|
||||
// I have absolutely no idea why the compiler insists it's unused. The call happens during client::init::execute
|
||||
#[allow(dead_code)]
|
||||
/// After shared key with the gateway is derived, puts its ownership to this instance of a [`KeyManager`].
|
||||
pub fn insert_gateway_shared_key(&mut self, gateway_shared_key: Arc<SharedKeys>) {
|
||||
self.gateway_shared_key = Some(gateway_shared_key)
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`SharedKey`].
|
||||
// since this function is not fully public, it is not expected to be used externally and
|
||||
// hence it's up to us to ensure it's called in correct context
|
||||
@@ -149,12 +230,7 @@ impl KeyManager {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn gateway_key_set(&self) -> bool {
|
||||
pub fn is_gateway_key_set(&self) -> bool {
|
||||
self.gateway_shared_key.is_some()
|
||||
}
|
||||
|
||||
/// Gets an atomically reference counted pointer to [`AckKey`].
|
||||
pub fn ack_key(&self) -> Arc<AckKey> {
|
||||
Arc::clone(&self.ack_key)
|
||||
}
|
||||
}
|
||||
|
||||
+16
-8
@@ -213,7 +213,11 @@ impl ActionController {
|
||||
}
|
||||
|
||||
// note: when the entry expires it's automatically removed from pending_acks_timers
|
||||
fn handle_expired_ack_timer(&mut self, expired_ack: Expired<FragmentIdentifier>) {
|
||||
fn handle_expired_ack_timer(
|
||||
&mut self,
|
||||
expired_ack: Expired<FragmentIdentifier>,
|
||||
task_client: &mut task::TaskClient,
|
||||
) {
|
||||
// I'm honestly not sure how to handle it, because getting it means other things in our
|
||||
// system are already misbehaving. If we ever see this panic, then I guess we should worry
|
||||
// about it. Perhaps just reschedule it at later point?
|
||||
@@ -231,9 +235,16 @@ impl ActionController {
|
||||
// downgrading an arc and then upgrading vs cloning is difference of 30ns vs 15ns
|
||||
// so it's literally a NO difference while it might prevent us from unnecessarily
|
||||
// resending data (in maybe 1 in 1 million cases, but it's something)
|
||||
self.retransmission_sender
|
||||
if self
|
||||
.retransmission_sender
|
||||
.unbounded_send(Arc::downgrade(pending_ack_data))
|
||||
.unwrap()
|
||||
.is_err()
|
||||
{
|
||||
assert!(
|
||||
task_client.is_shutdown_poll(),
|
||||
"Failed to send pending ack for retransmission"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// this shouldn't cause any issues but shouldn't have happened to begin with!
|
||||
error!("An already removed pending ack has expired")
|
||||
@@ -264,7 +275,7 @@ impl ActionController {
|
||||
}
|
||||
},
|
||||
expired_ack = self.pending_acks_timers.next() => match expired_ack {
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack),
|
||||
Some(expired_ack) => self.handle_expired_ack_timer(expired_ack, &mut shutdown),
|
||||
None => {
|
||||
log::trace!("ActionController: Stopping since ack channel closed");
|
||||
break;
|
||||
@@ -275,10 +286,7 @@ impl ActionController {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
shutdown.recv_timeout().await;
|
||||
log::debug!("ActionController: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,9 +535,7 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
tokio::time::timeout(Duration::from_secs(5), shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
shutdown.recv_timeout().await;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
||||
@@ -99,6 +99,24 @@ impl ReplyControllerSender {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReplyQueueLengths {
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
}
|
||||
|
||||
impl ReplyQueueLengths {
|
||||
pub fn new(reply_controller_sender: ReplyControllerSender) -> Self {
|
||||
Self {
|
||||
reply_controller_sender,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_lane_queue_length(&self, connection_id: ConnectionId) -> usize {
|
||||
self.reply_controller_sender
|
||||
.get_lane_queue_length(connection_id)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ReplyControllerReceiver = mpsc::UnboundedReceiver<ReplyControllerMessage>;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -7,6 +7,7 @@ use async_trait::async_trait;
|
||||
|
||||
// well, right now we don't have the browser storage : (
|
||||
// so we keep everything in memory
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
empty: Empty,
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ pub mod fs_backend;
|
||||
#[error("no information provided")]
|
||||
pub struct UndefinedError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Empty {
|
||||
// we need to keep 'basic' metadata here to "load" the CombinedReplyStorage
|
||||
min_surb_threshold: usize,
|
||||
max_surb_threshold: usize,
|
||||
pub min_surb_threshold: usize,
|
||||
pub max_surb_threshold: usize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use config::defaults::NymNetworkDetails;
|
||||
use config::{NymConfig, OptionalSet, DB_FILE_NAME};
|
||||
use nymsphinx::params::PacketSize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -499,13 +500,29 @@ pub struct Client<T> {
|
||||
|
||||
impl<T: NymConfig> Default for Client<T> {
|
||||
fn default() -> Self {
|
||||
let network = NymNetworkDetails::new_mainnet();
|
||||
let nyxd_urls = network
|
||||
.endpoints
|
||||
.iter()
|
||||
.map(|validator| validator.nyxd_url())
|
||||
.collect();
|
||||
let nym_api_urls = network
|
||||
.endpoints
|
||||
.iter()
|
||||
.filter_map(|validator| validator.api_url())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if nym_api_urls.is_empty() {
|
||||
panic!("we do not have any default nym-api urls available!")
|
||||
}
|
||||
|
||||
// there must be explicit checks for whether id is not empty later
|
||||
Client {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
disabled_credentials_mode: true,
|
||||
nyxd_urls: vec![],
|
||||
nym_api_urls: vec![],
|
||||
nyxd_urls,
|
||||
nym_api_urls,
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
private_encryption_key_file: Default::default(),
|
||||
|
||||
@@ -7,12 +7,12 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClientKeyPathfinder {
|
||||
identity_private_key: PathBuf,
|
||||
identity_public_key: PathBuf,
|
||||
encryption_private_key: PathBuf,
|
||||
encryption_public_key: PathBuf,
|
||||
gateway_shared_key: PathBuf,
|
||||
ack_key: PathBuf,
|
||||
pub identity_private_key: PathBuf,
|
||||
pub identity_public_key: PathBuf,
|
||||
pub encryption_private_key: PathBuf,
|
||||
pub encryption_public_key: PathBuf,
|
||||
pub gateway_shared_key: PathBuf,
|
||||
pub ack_key: PathBuf,
|
||||
}
|
||||
|
||||
impl ClientKeyPathfinder {
|
||||
@@ -22,8 +22,8 @@ impl ClientKeyPathfinder {
|
||||
ClientKeyPathfinder {
|
||||
identity_private_key: config_dir.join("private_identity.pem"),
|
||||
identity_public_key: config_dir.join("public_identity.pem"),
|
||||
encryption_private_key: config_dir.join("public_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_private_key: config_dir.join("private_encryption.pem"),
|
||||
encryption_public_key: config_dir.join("public_encryption.pem"),
|
||||
gateway_shared_key: config_dir.join("gateway_shared.pem"),
|
||||
ack_key: config_dir.join("ack_key.pem"),
|
||||
}
|
||||
@@ -40,6 +40,28 @@ impl ClientKeyPathfinder {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn any_file_exists(&self) -> bool {
|
||||
matches!(self.identity_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.identity_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_public_key.try_exists(), Ok(true))
|
||||
|| matches!(self.encryption_private_key.try_exists(), Ok(true))
|
||||
|| matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
|| matches!(self.ack_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn any_file_exists_and_return(&self) -> Option<PathBuf> {
|
||||
file_exists(&self.identity_public_key)
|
||||
.or_else(|| file_exists(&self.identity_private_key))
|
||||
.or_else(|| file_exists(&self.encryption_public_key))
|
||||
.or_else(|| file_exists(&self.encryption_private_key))
|
||||
.or_else(|| file_exists(&self.gateway_shared_key))
|
||||
.or_else(|| file_exists(&self.ack_key))
|
||||
}
|
||||
|
||||
pub fn gateway_key_file_exists(&self) -> bool {
|
||||
matches!(self.gateway_shared_key.try_exists(), Ok(true))
|
||||
}
|
||||
|
||||
pub fn private_identity_key(&self) -> &Path {
|
||||
&self.identity_private_key
|
||||
}
|
||||
@@ -64,3 +86,10 @@ impl ClientKeyPathfinder {
|
||||
&self.ack_key
|
||||
}
|
||||
}
|
||||
|
||||
fn file_exists(path: &Path) -> Option<PathBuf> {
|
||||
if matches!(path.try_exists(), Ok(true)) {
|
||||
return Some(path.to_path_buf());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -55,6 +55,9 @@ pub enum ClientCoreError {
|
||||
#[error("The address of the gateway is unknown - did you run init?")]
|
||||
GatwayAddressUnknown,
|
||||
|
||||
#[error("failed to register receiver for reconstructed mixnet messages")]
|
||||
FailedToRegisterReceiver,
|
||||
|
||||
#[error("Unexpected exit")]
|
||||
UnexpectedExit,
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use config::NymConfig;
|
||||
use crypto::asymmetric::identity;
|
||||
use gateway_client::GatewayClient;
|
||||
use gateway_requests::registration::handshake::SharedKeys;
|
||||
use rand::{rngs::OsRng, seq::SliceRandom, thread_rng};
|
||||
use rand::{seq::SliceRandom, thread_rng};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use tap::TapFallible;
|
||||
use topology::{filter::VersionFilterable, gateway};
|
||||
@@ -51,7 +51,7 @@ pub(super) async fn query_gateway_details(
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_with_gateway(
|
||||
pub(super) async fn register_with_gateway(
|
||||
gateway: &gateway::Node,
|
||||
our_identity: Arc<identity::KeyPair>,
|
||||
) -> Result<Arc<SharedKeys>, ClientCoreError> {
|
||||
@@ -74,20 +74,13 @@ async fn register_with_gateway(
|
||||
Ok(shared_keys)
|
||||
}
|
||||
|
||||
pub(super) async fn register_with_gateway_and_store_keys<T>(
|
||||
gateway_details: gateway::Node,
|
||||
pub(super) fn store_keys<T>(
|
||||
key_manager: &KeyManager,
|
||||
config: &Config<T>,
|
||||
) -> Result<(), ClientCoreError>
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let mut key_manager = KeyManager::new(&mut rng);
|
||||
|
||||
let shared_keys =
|
||||
register_with_gateway(&gateway_details, key_manager.identity_keypair()).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
let pathfinder = ClientKeyPathfinder::new_from_config(config);
|
||||
Ok(key_manager
|
||||
.store_keys(&pathfinder)
|
||||
|
||||
@@ -6,23 +6,26 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use nymsphinx::addressing::{clients::Recipient, nodes::NodeIdentity};
|
||||
use rand::rngs::OsRng;
|
||||
use serde::Serialize;
|
||||
use tap::TapFallible;
|
||||
|
||||
use config::NymConfig;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use url::Url;
|
||||
|
||||
use crate::client::key_manager::KeyManager;
|
||||
use crate::{
|
||||
config::{
|
||||
persistence::key_pathfinder::ClientKeyPathfinder, ClientCoreConfigTrait, Config,
|
||||
GatewayEndpointConfig,
|
||||
},
|
||||
error::ClientCoreError,
|
||||
init::helpers::{query_gateway_details, register_with_gateway_and_store_keys},
|
||||
};
|
||||
|
||||
mod helpers;
|
||||
|
||||
/// Struct describing the results of the client initialization procedure.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct InitResults {
|
||||
version: String,
|
||||
@@ -60,6 +63,12 @@ impl Display for InitResults {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new set of client keys.
|
||||
pub fn new_client_keys() -> KeyManager {
|
||||
let mut rng = OsRng;
|
||||
KeyManager::new(&mut rng)
|
||||
}
|
||||
|
||||
/// Convenience function for setting up the gateway for a client. Depending on the arguments given
|
||||
/// it will do the sensible thing.
|
||||
pub async fn setup_gateway<C, T>(
|
||||
@@ -74,7 +83,7 @@ where
|
||||
{
|
||||
let id = config.get_id();
|
||||
if register_gateway {
|
||||
register_with_gateway(user_chosen_gateway_id, config).await
|
||||
register_with_gateway_and_store(user_chosen_gateway_id, config).await
|
||||
} else if let Some(user_chosen_gateway_id) = user_chosen_gateway_id {
|
||||
config_gateway_with_existing_keys(user_chosen_gateway_id, config).await
|
||||
} else {
|
||||
@@ -82,27 +91,51 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
/// the chosen one if it's among the available ones.
|
||||
pub async fn register_with_gateway(
|
||||
key_manager: &mut KeyManager,
|
||||
nym_api_endpoints: Vec<Url>,
|
||||
chosen_gateway_id: Option<String>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError> {
|
||||
// Our identity is derived from our key
|
||||
let our_identity = key_manager.identity_keypair();
|
||||
|
||||
// Get the gateway details of the gateway we will use
|
||||
let gateway = helpers::query_gateway_details(nym_api_endpoints, chosen_gateway_id).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
|
||||
// Establish connection, authenticate and generate keys for talking with the gateway
|
||||
let shared_keys = helpers::register_with_gateway(&gateway, our_identity).await?;
|
||||
key_manager.insert_gateway_shared_key(shared_keys);
|
||||
|
||||
Ok(gateway.into())
|
||||
}
|
||||
|
||||
/// Get the gateway details by querying the validator-api. Either pick one at random or use
|
||||
/// the chosen one if it's among the available ones.
|
||||
/// Saves keys to disk, specified by the paths in `config`.
|
||||
pub async fn register_with_gateway<T>(
|
||||
user_chosen_gateway_id: Option<String>,
|
||||
pub async fn register_with_gateway_and_store<T>(
|
||||
chosen_gateway_id: Option<String>,
|
||||
config: &Config<T>,
|
||||
) -> Result<GatewayEndpointConfig, ClientCoreError>
|
||||
where
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Configuring gateway");
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), user_chosen_gateway_id).await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
let mut key_manager = new_client_keys();
|
||||
|
||||
// Registering with gateway by setting up and writing shared keys to disk
|
||||
log::trace!("Registering gateway");
|
||||
register_with_gateway_and_store_keys(gateway.clone(), config).await?;
|
||||
let gateway = register_with_gateway(
|
||||
&mut key_manager,
|
||||
config.get_nym_api_endpoints(),
|
||||
chosen_gateway_id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
helpers::store_keys(&key_manager, config)?;
|
||||
println!("Saved all generated keys");
|
||||
|
||||
Ok(gateway.into())
|
||||
Ok(gateway)
|
||||
}
|
||||
|
||||
/// Set the gateway using the usual procedue of querying the validator-api, but don't register or
|
||||
@@ -117,8 +150,11 @@ where
|
||||
T: NymConfig,
|
||||
{
|
||||
println!("Using gateway provided by user, keeping existing keys");
|
||||
let gateway =
|
||||
query_gateway_details(config.get_nym_api_endpoints(), Some(user_chosen_gateway_id)).await?;
|
||||
let gateway = helpers::query_gateway_details(
|
||||
config.get_nym_api_endpoints(),
|
||||
Some(user_chosen_gateway_id),
|
||||
)
|
||||
.await?;
|
||||
log::debug!("Querying gateway gives: {}", gateway);
|
||||
Ok(gateway.into())
|
||||
}
|
||||
@@ -143,6 +179,20 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the full client address from the client keys and the gateway identity
|
||||
pub fn get_client_address(
|
||||
key_manager: &KeyManager,
|
||||
gateway_config: &GatewayEndpointConfig,
|
||||
) -> Recipient {
|
||||
Recipient::new(
|
||||
*key_manager.identity_keypair().public_key(),
|
||||
*key_manager.encryption_keypair().public_key(),
|
||||
// TODO: below only works under assumption that gateway address == gateway id
|
||||
// (which currently is true)
|
||||
NodeIdentity::from_base58_string(&gateway_config.gateway_id).unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the client address by loading the keys from stored files.
|
||||
pub fn get_client_address_from_stored_keys<T>(
|
||||
config: &Config<T>,
|
||||
|
||||
@@ -8,12 +8,11 @@ use crate::error::ClientError;
|
||||
use crate::websocket;
|
||||
use client_connections::TransmissionLane;
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::inbound_messages::InputMessage;
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
|
||||
use client_core::client::replies::reply_controller::requests::ReplyControllerSender;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
@@ -87,8 +86,8 @@ impl SocketClient {
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_state: ClientState,
|
||||
self_address: &Recipient,
|
||||
reply_controller_sender: ReplyControllerSender,
|
||||
shutdown: task::TaskClient,
|
||||
) {
|
||||
info!("Starting websocket listener...");
|
||||
@@ -99,10 +98,14 @@ impl SocketClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender,
|
||||
} = client_state;
|
||||
|
||||
let websocket_handler = websocket::HandlerBuilder::new(
|
||||
input_sender,
|
||||
connection_command_sender,
|
||||
@@ -151,13 +154,14 @@ impl SocketClient {
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
|
||||
Self::start_websocket_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
&self_address,
|
||||
started_client.reply_controller_sender,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) struct Init {
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
disable_socket: Option<bool>,
|
||||
|
||||
/// Port for the socket (if applicable) to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
@@ -60,7 +60,7 @@ pub(crate) struct Init {
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
#[clap(long)]
|
||||
|
||||
@@ -54,7 +54,7 @@ pub(crate) enum Commands {
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
disable_socket: bool,
|
||||
disable_socket: Option<bool>,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
no_cover: bool,
|
||||
@@ -62,7 +62,7 @@ pub(crate) struct OverrideConfig {
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -80,7 +80,7 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Syn
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
config = config
|
||||
.with_disabled_socket(args.disable_socket)
|
||||
.with_optional(Config::with_disabled_socket, args.disable_socket)
|
||||
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
@@ -100,9 +100,9 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
network_defaults::var_names::NYXD,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_base(
|
||||
.with_optional_ext(
|
||||
BaseConfig::with_disabled_credentials,
|
||||
!args.enabled_credentials_mode,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ pub(crate) struct Run {
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
disable_socket: bool,
|
||||
disable_socket: Option<bool>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
@@ -57,7 +57,7 @@ pub(crate) struct Run {
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::socks::{
|
||||
server::SphinxSocksServer,
|
||||
};
|
||||
use client_core::client::base_client::{
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput,
|
||||
non_wasm_helpers, BaseClientBuilder, ClientInput, ClientOutput, ClientState,
|
||||
};
|
||||
use client_core::client::key_manager::KeyManager;
|
||||
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
|
||||
@@ -95,6 +95,7 @@ impl NymClient {
|
||||
config: &Config,
|
||||
client_input: ClientInput,
|
||||
client_output: ClientOutput,
|
||||
client_status: ClientState,
|
||||
self_address: Recipient,
|
||||
shutdown: TaskClient,
|
||||
) {
|
||||
@@ -108,10 +109,14 @@ impl NymClient {
|
||||
} = client_input;
|
||||
|
||||
let ClientOutput {
|
||||
shared_lane_queue_lengths,
|
||||
received_buffer_request_sender,
|
||||
} = client_output;
|
||||
|
||||
let ClientState {
|
||||
shared_lane_queue_lengths,
|
||||
reply_controller_sender: _,
|
||||
} = client_status;
|
||||
|
||||
let authenticator = Authenticator::new(auth_methods, allowed_users);
|
||||
let mut sphinx_socks = SphinxSocksServer::new(
|
||||
config.get_listening_port(),
|
||||
@@ -218,11 +223,13 @@ impl NymClient {
|
||||
let mut started_client = base_builder.start_base().await?;
|
||||
let client_input = started_client.client_input.register_producer();
|
||||
let client_output = started_client.client_output.register_consumer();
|
||||
let client_state = started_client.client_state;
|
||||
|
||||
Self::start_socks5_listener(
|
||||
&self.config,
|
||||
client_input,
|
||||
client_output,
|
||||
client_state,
|
||||
self_address,
|
||||
started_client.task_manager.subscribe(),
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) struct Init {
|
||||
/// Note that some service providers might not support this.
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "use_anonymous_sender_tag")]
|
||||
use_reply_surbs: bool,
|
||||
use_reply_surbs: Option<bool>,
|
||||
|
||||
/// Id of the gateway we are going to connect to.
|
||||
#[clap(long)]
|
||||
@@ -69,7 +69,7 @@ pub(crate) struct Init {
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Save a summary of the initialization to a json file
|
||||
#[clap(long)]
|
||||
|
||||
@@ -57,14 +57,14 @@ pub(crate) enum Commands {
|
||||
pub(crate) struct OverrideConfig {
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
port: Option<u16>,
|
||||
use_anonymous_replies: bool,
|
||||
use_anonymous_replies: Option<bool>,
|
||||
fastmode: bool,
|
||||
no_cover: bool,
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
@@ -84,7 +84,7 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config
|
||||
.with_base(BaseConfig::with_high_default_traffic_volume, args.fastmode)
|
||||
.with_base(BaseConfig::with_disabled_cover_traffic, args.no_cover)
|
||||
.with_anonymous_replies(args.use_anonymous_replies)
|
||||
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
|
||||
.with_optional(Config::with_port, args.port)
|
||||
.with_optional_custom_env_ext(
|
||||
BaseConfig::with_custom_nym_apis,
|
||||
@@ -102,9 +102,9 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
network_defaults::var_names::NYXD,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_base(
|
||||
.with_optional_ext(
|
||||
BaseConfig::with_disabled_credentials,
|
||||
!args.enabled_credentials_mode,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ pub(crate) struct Run {
|
||||
/// Note that some service providers might not support this.
|
||||
// the alias here is included for backwards compatibility (1.1.4 and before)
|
||||
#[clap(long, alias = "use_anonymous_sender_tag")]
|
||||
use_anonymous_replies: bool,
|
||||
use_anonymous_replies: Option<bool>,
|
||||
|
||||
/// Address of the socks5 provider to send messages to.
|
||||
#[clap(long)]
|
||||
@@ -68,7 +68,7 @@ pub(crate) struct Run {
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
@@ -116,10 +114,7 @@ impl MixnetResponseListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(Duration::from_secs(5), self.shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
self.shutdown.recv_timeout().await;
|
||||
log::debug!("MixnetResponseListener: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -5,7 +5,9 @@ use self::config::Config;
|
||||
use crate::client::helpers::InputSender;
|
||||
use crate::client::response_pusher::ResponsePusher;
|
||||
use client_connections::TransmissionLane;
|
||||
use client_core::client::base_client::{BaseClientBuilder, ClientInput, ClientOutput};
|
||||
use client_core::client::base_client::{
|
||||
BaseClientBuilder, ClientInput, ClientOutput, CredentialsToggle,
|
||||
};
|
||||
use client_core::client::replies::reply_storage::browser_backend;
|
||||
use client_core::client::{inbound_messages::InputMessage, key_manager::KeyManager};
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
@@ -92,13 +94,19 @@ impl NymClientBuilder {
|
||||
future_to_promise(async move {
|
||||
console_log!("Starting the wasm client");
|
||||
|
||||
let disabled_credentials = if self.disabled_credentials {
|
||||
CredentialsToggle::Disabled
|
||||
} else {
|
||||
CredentialsToggle::Enabled
|
||||
};
|
||||
|
||||
let base_builder = BaseClientBuilder::new(
|
||||
&self.config.gateway_endpoint,
|
||||
&self.config.debug,
|
||||
self.key_manager,
|
||||
self.bandwidth_controller,
|
||||
self.reply_surb_storage_backend,
|
||||
self.disabled_credentials,
|
||||
disabled_credentials,
|
||||
vec![self.config.nym_api_url.clone()],
|
||||
);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
|
||||
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 {
|
||||
@@ -14,6 +16,24 @@ pub struct Args {
|
||||
#[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>,
|
||||
}
|
||||
@@ -32,9 +52,42 @@ pub async fn generate(args: Args) {
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -19,6 +19,7 @@ url = "2.2"
|
||||
thiserror = "1.0.37"
|
||||
|
||||
crypto = { path = "../crypto" }
|
||||
network-defaults = { path = "../network-defaults" }
|
||||
nymsphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
|
||||
nymsphinx-addressing = { path = "../nymsphinx/addressing" }
|
||||
nymsphinx-forwarding = { path = "../nymsphinx/forwarding" }
|
||||
|
||||
@@ -7,6 +7,7 @@ use crypto::asymmetric::identity;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use futures::StreamExt;
|
||||
use log::*;
|
||||
use network_defaults::mainnet::NYM_API;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
use std::net::SocketAddr;
|
||||
@@ -160,7 +161,7 @@ impl Default for ConfigBuilder {
|
||||
tested_nodes_batch_size: DEFAULT_BATCH_SIZE,
|
||||
testing_interval: DEFAULT_TESTING_INTERVAL,
|
||||
retry_timeout: DEFAULT_RETRY_TIMEOUT,
|
||||
nym_api_urls: vec![],
|
||||
nym_api_urls: vec![NYM_API.parse().expect("Invalid default API URL")],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -7,17 +7,18 @@ use std::borrow::Borrow;
|
||||
use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use digest::Digest;
|
||||
use digest::generic_array::typenum::Unsigned;
|
||||
use group::GroupEncoding;
|
||||
use itertools::izip;
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::Attribute;
|
||||
use crate::error::{CoconutError, Result};
|
||||
use crate::scheme::issuance::compute_commitment_hash;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::VerificationKey;
|
||||
use crate::utils::{hash_g1, try_deserialize_scalar, try_deserialize_scalar_vec};
|
||||
use crate::Attribute;
|
||||
|
||||
// as per the reference python implementation
|
||||
type ChallengeDigest = Sha256;
|
||||
@@ -38,10 +39,10 @@ pub struct ProofCmCs {
|
||||
// and as per the bls12-381 library all elements are using big-endian form
|
||||
/// Generates a Scalar [or Fp] challenge by hashing a number of elliptic curve points.
|
||||
fn compute_challenge<D, I, B>(iter: I) -> Scalar
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item = B>,
|
||||
B: AsRef<[u8]>,
|
||||
where
|
||||
D: Digest,
|
||||
I: Iterator<Item=B>,
|
||||
B: AsRef<[u8]>,
|
||||
{
|
||||
let mut h = D::new();
|
||||
for point_representation in iter {
|
||||
@@ -69,8 +70,8 @@ fn produce_response(witness: &Scalar, challenge: &Scalar, secret: &Scalar) -> Sc
|
||||
|
||||
// note: it's caller's responsibility to ensure witnesses.len() = secrets.len()
|
||||
fn produce_responses<S>(witnesses: &[Scalar], challenge: &Scalar, secrets: &[S]) -> Vec<Scalar>
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
where
|
||||
S: Borrow<Scalar>,
|
||||
{
|
||||
debug_assert_eq!(witnesses.len(), secrets.len());
|
||||
|
||||
@@ -91,6 +92,7 @@ impl ProofCmCs {
|
||||
commitments: &[G1Projective],
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
) -> Self {
|
||||
// note: this is only called from `prepare_blind_sign` that already checks
|
||||
// whether private attributes are non-empty and whether we don't have too many
|
||||
@@ -104,7 +106,7 @@ impl ProofCmCs {
|
||||
let witness_attributes = params.n_random_scalars(private_attributes.len());
|
||||
|
||||
// recompute h
|
||||
let h = hash_g1(commitment.to_bytes());
|
||||
let h = compute_commitment_hash(*commitment, public_attributes);
|
||||
let hs_bytes = params
|
||||
.gen_hs()
|
||||
.iter()
|
||||
@@ -119,10 +121,10 @@ impl ProofCmCs {
|
||||
// Ccm = (wr * g1) + (wm[0] * hs[0]) + ... + (wm[i] * hs[i])
|
||||
let commitment_attributes = g1 * witness_commitment_opening
|
||||
+ witness_attributes
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter())
|
||||
.map(|(wm_i, hs_i)| hs_i * wm_i)
|
||||
.sum::<G1Projective>();
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter())
|
||||
.map(|(wm_i, hs_i)| hs_i * wm_i)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
// zkp commitments for the individual attributes
|
||||
let commitments_attributes = witness_pedersen_commitments_openings
|
||||
@@ -186,7 +188,7 @@ impl ProofCmCs {
|
||||
}
|
||||
|
||||
// recompute h
|
||||
let h = hash_g1(commitment.to_bytes());
|
||||
let h = compute_commitment_hash(*commitment, public_attributes);
|
||||
let g1 = params.gen1();
|
||||
|
||||
let hs_bytes = params
|
||||
@@ -199,26 +201,26 @@ impl ProofCmCs {
|
||||
// Cw = (cm * c) + (rr * g1) + (rm[0] * hs[0]) + ... + (rm[n] * hs[n])
|
||||
let commitment_attributes = (commitment
|
||||
- public_attributes
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter().skip(self.response_attributes.len()))
|
||||
.map(|(pub_attr, hs)| hs * pub_attr)
|
||||
.sum::<G1Projective>())
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter().skip(self.response_attributes.len()))
|
||||
.map(|(pub_attr, hs)| hs * pub_attr)
|
||||
.sum::<G1Projective>())
|
||||
* self.challenge
|
||||
+ g1 * self.response_opening
|
||||
+ self
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter())
|
||||
.map(|(res_attr, hs)| hs * res_attr)
|
||||
.sum::<G1Projective>();
|
||||
.response_attributes
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter())
|
||||
.map(|(res_attr, hs)| hs * res_attr)
|
||||
.sum::<G1Projective>();
|
||||
|
||||
let commitments_attributes = izip!(
|
||||
commitments.iter(),
|
||||
self.response_openings.iter(),
|
||||
self.response_attributes.iter()
|
||||
)
|
||||
.map(|(cm_j, r_o_j, r_m_j)| cm_j * self.challenge + g1 * r_o_j + h * r_m_j)
|
||||
.collect::<Vec<_>>();
|
||||
.map(|(cm_j, r_o_j, r_m_j)| cm_j * self.challenge + g1 * r_o_j + h * r_m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let commitments_bytes = commitments
|
||||
.iter()
|
||||
@@ -365,10 +367,10 @@ impl ProofKappaZeta {
|
||||
let commitment_kappa = params.gen2() * witness_blinder
|
||||
+ verification_key.alpha
|
||||
+ witness_attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(wm_i, beta_i)| beta_i * wm_i)
|
||||
.sum::<G2Projective>();
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(wm_i, beta_i)| beta_i * wm_i)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
// zeta is the public value associated with the serial number
|
||||
let commitment_zeta = params.gen2() * witness_serial_number;
|
||||
@@ -422,10 +424,10 @@ impl ProofKappaZeta {
|
||||
+ params.gen2() * self.response_blinder
|
||||
+ verification_key.alpha * (Scalar::one() - self.challenge)
|
||||
+ response_attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.sum::<G2Projective>();
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
// zeta is the public value associated with the serial number
|
||||
let commitment_zeta = zeta * self.challenge + params.gen2() * self.response_serial_number;
|
||||
@@ -529,9 +531,18 @@ mod tests {
|
||||
let cms: [G1Projective; 1] = [G1Projective::random(&mut rng)];
|
||||
let rs = params.n_random_scalars(1);
|
||||
let private_attributes = params.n_random_scalars(1);
|
||||
let public_attributes = params.n_random_scalars(1);
|
||||
|
||||
// 0 public 1 private
|
||||
let pi_s = ProofCmCs::construct(¶ms, &cm, &r, &cms, &rs, &private_attributes);
|
||||
let pi_s = ProofCmCs::construct(
|
||||
¶ms,
|
||||
&cm,
|
||||
&r,
|
||||
&cms,
|
||||
&rs,
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
);
|
||||
|
||||
let bytes = pi_s.to_bytes();
|
||||
assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s);
|
||||
@@ -547,7 +558,15 @@ mod tests {
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
|
||||
// 0 public 2 privates
|
||||
let pi_s = ProofCmCs::construct(¶ms, &cm, &r, &cms, &rs, &private_attributes);
|
||||
let pi_s = ProofCmCs::construct(
|
||||
¶ms,
|
||||
&cm,
|
||||
&r,
|
||||
&cms,
|
||||
&rs,
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
);
|
||||
|
||||
let bytes = pi_s.to_bytes();
|
||||
assert_eq!(ProofCmCs::from_bytes(&bytes).unwrap(), pi_s);
|
||||
|
||||
@@ -201,8 +201,16 @@ pub fn compute_pedersen_commitments_for_private_attributes(
|
||||
(commitments_openings, pedersen_commitments)
|
||||
}
|
||||
|
||||
pub fn compute_commitment_hash(commitment: G1Projective) -> G1Projective {
|
||||
hash_g1(commitment.to_bytes())
|
||||
pub fn compute_commitment_hash(
|
||||
commitment: G1Projective,
|
||||
public_attributes: &[Attribute],
|
||||
) -> G1Projective {
|
||||
let mut msg_bytes = Vec::with_capacity(public_attributes.len() * 32);
|
||||
msg_bytes.extend_from_slice(&commitment.to_affine().to_compressed());
|
||||
for attr in public_attributes {
|
||||
msg_bytes.extend_from_slice(&attr.to_bytes());
|
||||
}
|
||||
hash_g1(msg_bytes)
|
||||
}
|
||||
|
||||
/// Builds cryptographic material required for blind sign.
|
||||
@@ -230,7 +238,7 @@ pub fn prepare_blind_sign(
|
||||
compute_attributes_commitment(params, private_attributes, public_attributes, hs);
|
||||
|
||||
// Compute the challenge as the commitment hash
|
||||
let commitment_hash = compute_commitment_hash(commitment);
|
||||
let commitment_hash = compute_commitment_hash(commitment, public_attributes);
|
||||
|
||||
let (pedersen_commitments_openings, pedersen_commitments) =
|
||||
compute_pedersen_commitments_for_private_attributes(
|
||||
@@ -246,6 +254,7 @@ pub fn prepare_blind_sign(
|
||||
&pedersen_commitments,
|
||||
&pedersen_commitments_openings,
|
||||
private_attributes,
|
||||
public_attributes,
|
||||
);
|
||||
|
||||
Ok((
|
||||
@@ -276,7 +285,7 @@ pub fn blind_sign(
|
||||
}
|
||||
|
||||
// Verify the commitment hash
|
||||
let h = hash_g1(blind_sign_request.commitment.to_bytes());
|
||||
let h = compute_commitment_hash(blind_sign_request.commitment, public_attributes);
|
||||
if !(h == blind_sign_request.commitment_hash) {
|
||||
return Err(CoconutError::Issuance(
|
||||
"Failed to verify the commitment hash".to_string(),
|
||||
|
||||
@@ -254,11 +254,7 @@ impl Controller {
|
||||
},
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(Duration::from_secs(5), self.shutdown.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
assert!(self.shutdown.is_shutdown_poll());
|
||||
self.shutdown.recv_timeout().await;
|
||||
log::debug!("SOCKS5 Controller: Exiting");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ pub struct TaskClient {
|
||||
|
||||
impl TaskClient {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT: Duration = Duration::from_secs(5);
|
||||
|
||||
fn new(
|
||||
notify: watch::Receiver<()>,
|
||||
@@ -231,7 +231,6 @@ impl TaskClient {
|
||||
let (_notify_tx, notify_rx) = watch::channel(());
|
||||
let (task_halt_tx, _task_halt_rx) = mpsc::unbounded_channel();
|
||||
let (task_drop_tx, _task_drop_rx) = mpsc::unbounded_channel();
|
||||
//let (task_status_tx, _task_status_rx) = futures::channel::mpsc::unbounded();
|
||||
let (task_status_tx, _task_status_rx) = futures::channel::mpsc::channel(128);
|
||||
TaskClient {
|
||||
shutdown: false,
|
||||
@@ -280,9 +279,12 @@ impl TaskClient {
|
||||
return pending().await;
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::timeout(Self::SHUTDOWN_TIMEOUT, self.recv())
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
tokio::time::timeout(
|
||||
Self::SHUTDOWN_TIMEOUT_WAITING_FOR_SIGNAL_ON_EXIT,
|
||||
self.recv(),
|
||||
)
|
||||
.await
|
||||
.expect("Task stopped without shutdown called");
|
||||
}
|
||||
|
||||
pub fn is_shutdown_poll(&mut self) -> bool {
|
||||
@@ -300,8 +302,8 @@ impl TaskClient {
|
||||
has_changed
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Polling shutdown failed: {err}");
|
||||
log::debug!("Assuming this means we should shutdown...");
|
||||
log::error!("Polling shutdown failed: {err}");
|
||||
log::error!("Assuming this means we should shutdown...");
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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",
|
||||
|
||||
@@ -68,11 +68,11 @@ pub struct Init {
|
||||
/// bypass bandwidth credential requirement
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
only_coconut_credentials: bool,
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: bool,
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
@@ -182,11 +182,11 @@ mod tests {
|
||||
nym_apis: None,
|
||||
mnemonic: None,
|
||||
statistics_service_url: None,
|
||||
enabled_statistics: false,
|
||||
enabled_statistics: None,
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: None,
|
||||
#[cfg(feature = "coconut")]
|
||||
only_coconut_credentials: false,
|
||||
only_coconut_credentials: None,
|
||||
};
|
||||
std::env::set_var(BECH32_PREFIX, "n");
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ pub(crate) struct OverrideConfig {
|
||||
clients_port: Option<u16>,
|
||||
datastore: Option<PathBuf>,
|
||||
announce_host: Option<String>,
|
||||
enabled_statistics: bool,
|
||||
enabled_statistics: Option<bool>,
|
||||
statistics_service_url: Option<url::Url>,
|
||||
nym_apis: Option<Vec<url::Url>>,
|
||||
mnemonic: Option<bip39::Mnemonic>,
|
||||
@@ -60,7 +60,7 @@ pub(crate) struct OverrideConfig {
|
||||
#[cfg(feature = "coconut")]
|
||||
nyxd_urls: Option<Vec<url::Url>>,
|
||||
#[cfg(feature = "coconut")]
|
||||
only_coconut_credentials: bool,
|
||||
only_coconut_credentials: Option<bool>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) {
|
||||
@@ -103,7 +103,7 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
NYM_API,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_enabled_statistics(args.enabled_statistics)
|
||||
.with_optional(Config::with_enabled_statistics, args.enabled_statistics)
|
||||
.with_optional_env(
|
||||
Config::with_custom_statistics_service_url,
|
||||
args.statistics_service_url,
|
||||
@@ -130,7 +130,10 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
NYXD,
|
||||
config::parse_urls,
|
||||
)
|
||||
.with_only_coconut_credentials(args.only_coconut_credentials);
|
||||
.with_optional(
|
||||
Config::with_only_coconut_credentials,
|
||||
args.only_coconut_credentials,
|
||||
);
|
||||
}
|
||||
|
||||
config
|
||||
|
||||
@@ -68,11 +68,11 @@ pub struct Run {
|
||||
/// bypass bandwidth credential requirement
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
only_coconut_credentials: bool,
|
||||
only_coconut_credentials: Option<bool>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: bool,
|
||||
enabled_statistics: Option<bool>,
|
||||
|
||||
/// URL where a statistics aggregator is running. The default value is a Nym aggregator server
|
||||
#[clap(long)]
|
||||
|
||||
+11
-8
@@ -94,11 +94,11 @@ struct ApiArgs {
|
||||
|
||||
/// Specifies whether network monitoring is enabled on this API
|
||||
#[clap(short = 'm', long)]
|
||||
enable_monitor: bool,
|
||||
enable_monitor: Option<bool>,
|
||||
|
||||
/// Specifies whether network rewarding is enabled on this API
|
||||
#[clap(short = 'r', long, requires = "enable_monitor", requires = "mnemonic")]
|
||||
enable_rewarding: bool,
|
||||
enable_rewarding: Option<bool>,
|
||||
|
||||
/// Endpoint to nyxd instance from which the monitor will grab nodes to test
|
||||
#[clap(long)]
|
||||
@@ -132,7 +132,7 @@ struct ApiArgs {
|
||||
|
||||
/// Set this nym api to work in a enabled credentials that would attempt to use gateway with the bandwidth credential requirement
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// Announced address where coconut clients will connect.
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -142,7 +142,7 @@ struct ApiArgs {
|
||||
/// Flag to indicate whether coconut signer authority is enabled on this API
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long, requires = "mnemonic", requires = "announce-address")]
|
||||
enable_coconut: bool,
|
||||
enable_coconut: Option<bool>,
|
||||
}
|
||||
|
||||
async fn wait_for_interrupt(mut shutdown: TaskManager) {
|
||||
@@ -186,15 +186,18 @@ fn override_config(mut config: Config, args: ApiArgs) -> Config {
|
||||
Config::with_min_gateway_reliability,
|
||||
args.min_gateway_reliability,
|
||||
)
|
||||
.with_network_monitor_enabled(args.enable_monitor)
|
||||
.with_rewarding_enabled(args.enable_rewarding)
|
||||
.with_disabled_credentials_mode(!args.enabled_credentials_mode);
|
||||
.with_optional(Config::with_network_monitor_enabled, args.enable_monitor)
|
||||
.with_optional(Config::with_rewarding_enabled, args.enable_rewarding)
|
||||
.with_optional(
|
||||
Config::with_disabled_credentials_mode,
|
||||
args.enabled_credentials_mode.map(|b| !b),
|
||||
);
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
config = config
|
||||
.with_optional(Config::with_announce_address, args.announce_address)
|
||||
.with_coconut_signer_enabled(args.enable_coconut);
|
||||
.with_optional(Config::with_coconut_signer_enabled, args.enable_coconut);
|
||||
}
|
||||
|
||||
if args.save_config {
|
||||
|
||||
@@ -68,10 +68,10 @@
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@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",
|
||||
@@ -97,14 +97,14 @@
|
||||
"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",
|
||||
"thread-loader": "^3.0.4",
|
||||
"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",
|
||||
|
||||
Generated
+1
-1
@@ -2932,7 +2932,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nym_wallet"
|
||||
version = "1.1.5"
|
||||
version = "1.1.6"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2 0.3.4",
|
||||
|
||||
@@ -78,10 +78,10 @@
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@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",
|
||||
@@ -107,14 +107,14 @@
|
||||
"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",
|
||||
"thread-loader": "^3.0.4",
|
||||
"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",
|
||||
|
||||
@@ -151,7 +151,11 @@ pub async fn simulate_vesting_delegate_to_mixnode(
|
||||
let amount = guard.attempt_convert_to_base_coin(amount)?.into();
|
||||
|
||||
simulate_vesting_operation(
|
||||
ExecuteMsg::DelegateToMixnode { mix_id, amount },
|
||||
ExecuteMsg::DelegateToMixnode {
|
||||
on_behalf_of: None,
|
||||
mix_id,
|
||||
amount,
|
||||
},
|
||||
None,
|
||||
&state,
|
||||
)
|
||||
@@ -163,7 +167,15 @@ pub async fn simulate_vesting_undelegate_from_mixnode(
|
||||
mix_id: MixId,
|
||||
state: tauri::State<'_, WalletState>,
|
||||
) -> Result<FeeDetails, BackendError> {
|
||||
simulate_vesting_operation(ExecuteMsg::UndelegateFromMixnode { mix_id }, None, &state).await
|
||||
simulate_vesting_operation(
|
||||
ExecuteMsg::UndelegateFromMixnode {
|
||||
on_behalf_of: None,
|
||||
mix_id,
|
||||
},
|
||||
None,
|
||||
&state,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
||||
@@ -29,7 +29,7 @@ pub async fn vesting_delegate_to_mixnode(
|
||||
let res = guard
|
||||
.current_client()?
|
||||
.nyxd
|
||||
.vesting_delegate_to_mixnode(mix_id, delegation, fee)
|
||||
.vesting_delegate_to_mixnode(mix_id, delegation, None, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
@@ -54,7 +54,7 @@ pub async fn vesting_undelegate_from_mixnode(
|
||||
let res = guard
|
||||
.current_client()?
|
||||
.nyxd
|
||||
.vesting_undelegate_from_mixnode(mix_id, fee)
|
||||
.vesting_undelegate_from_mixnode(mix_id, None, fee)
|
||||
.await?;
|
||||
log::info!("<<< tx hash = {}", res.transaction_hash);
|
||||
log::trace!("<<< {:?}", res);
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
[package]
|
||||
name = "nym-sdk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
client-connections = { path = "../../../common/client-connections" }
|
||||
client-core = { path = "../../../clients/client-core", features = ["fs-surb-storage"]}
|
||||
crypto = { path = "../../../common/crypto" }
|
||||
gateway-client = { path = "../../../common/client-libs/gateway-client" }
|
||||
gateway-requests = { path = "../../../gateway/gateway-requests" }
|
||||
network-defaults = { path = "../../../common/network-defaults" }
|
||||
nymsphinx = { path = "../../../common/nymsphinx" }
|
||||
task = { path = "../../../common/task" }
|
||||
|
||||
futures = "0.3"
|
||||
log = "0.4"
|
||||
rand = { version = "0.7.3" }
|
||||
tap = "1.0.1"
|
||||
thiserror = "1.0.38"
|
||||
url = "2.2"
|
||||
toml = "0.5.10"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_env_logger = "0.4.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
logging = { path = "../../../common/logging" }
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user