Compare commits

..

15 Commits

Author SHA1 Message Date
Tommy Verrall a6a661b4c5 change location 2022-03-03 16:59:02 +00:00
Tommy Verrall fad6381e27 continue rework 2022-03-03 16:54:35 +00:00
Tommy Verrall 67af1ea504 add base mocks for nymd client 2022-03-03 11:47:30 +00:00
Tommy Verrall 043c4c8d86 update a few things - will start to mock shortly. 2022-03-03 10:28:26 +00:00
Tommy Verrall c40375a6b0 merge develop 2022-03-02 10:55:28 +00:00
Tommy Verrall 459506c03e merge develop 2022-02-03 09:39:26 +00:00
Tommy Verrall e0fb0dba7f remove cast 2022-01-19 17:07:26 +00:00
Tommy Verrall 02cf16ea8c add example 2022-01-19 16:52:02 +00:00
Tommy Verrall f8171f3beb update integration test for when run in gh actions
- removing and cleaning
- next step is to start mocking out a few of the other basic interactions with the client
2022-01-19 16:49:46 +00:00
Tommy Verrall 7d39996f7e commit two new methods 2022-01-19 12:28:12 +00:00
Tommy Verrall 0c3e40ce5b merge develop 2022-01-19 12:25:57 +00:00
Tommy Verrall f3ea81c97d Add .env example.
added .env.example
2022-01-14 16:34:09 +00:00
Tommy Verrall aadeac332e adding some actions
- this is just for debugging purposes currently
2022-01-14 16:29:46 +00:00
Tommy Verrall 77140342d9 merge develop 2022-01-14 15:46:09 +00:00
Tommy Verrall f95e9a7d37 Implement base line for tests on the validator ts client
- This will need to be separated and configured accordingly 
- This was a quick spin up, using jest as a library to implement some coverage
- Further things to be refined - mocks, more coverage, better configuration, clean up methods, improve env vars
2022-01-06 10:01:08 +00:00
123 changed files with 28246 additions and 5577 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --all --all-features
args: --all
- name: Check formatting
uses: actions-rs/cargo@v1
+6 -7
View File
@@ -56,12 +56,6 @@ jobs:
command: fmt
args: --all -- --check
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
- uses: actions-rs/clippy-check@v1
name: Clippy checks
with:
@@ -75,8 +69,13 @@ jobs:
command: clippy
args: -- -D warnings
# COCONUT stuff
- name: Reclaim some disk space (because Windows is being annoying)
uses: actions-rs/cargo@v1
if: ${{ matrix.os == 'windows-latest' }}
with:
command: clean
# COCONUT stuff
- name: Build all binaries with coconut enabled
uses: actions-rs/cargo@v1
with:
Generated
+2571 -733
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -17,7 +17,7 @@ members = [
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
# "clients/tauri-client/src-tauri",
"clients/tauri-client/src-tauri",
"common/client-libs/gateway-client",
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
+2 -2
View File
@@ -3,7 +3,7 @@ happy: fmt clippy-happy test
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet
cargo-test: test-main test-contracts test-wallet
build: build-contracts build-wallet build-main
build: build-main build-contracts build-wallet
fmt: fmt-main fmt-contracts fmt-wallet
clippy-happy-main:
@@ -25,7 +25,7 @@ clippy-all-wallet:
cargo clippy --manifest-path nym-wallet/Cargo.toml --all-features -- -D warnings
test-main:
cargo test --all-features --all
cargo test --all-features
test-contracts:
cargo test --manifest-path contracts/Cargo.toml --all-features
@@ -1,10 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::generic_array::typenum::Unsigned;
use log::*;
use nymsphinx::anonymous_replies::{
encryption_key::EncryptionKeyDigest, SurbEncryptionKey, SurbEncryptionKeySize,
encryption_key::EncryptionKeyDigest, encryption_key::Unsigned, SurbEncryptionKey,
SurbEncryptionKeySize,
};
use std::path::Path;
@@ -43,7 +43,7 @@ impl ReplyKeyStorage {
// if this fails it means we have some database corruption and we
// absolutely can't continue
if key_bytes_ref.len() != SurbEncryptionKeySize::USIZE {
if key_bytes_ref.len() != SurbEncryptionKeySize::to_usize() {
error!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH");
panic!("REPLY KEY STORAGE DATA CORRUPTION - ENCRYPTION KEY HAS INVALID LENGTH");
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -26,7 +26,7 @@
"@rollup/plugin-node-resolve": "^8.0.0",
"@rollup/plugin-replace": "^2.4.0",
"@rollup/plugin-url": "^5.0.0",
"@tauri-apps/cli": "^1.0.0-rc.5",
"@tauri-apps/cli": "^1.0.0-beta.5",
"rollup": "^2.3.4",
"rollup-plugin-svelte": "^7.0.0",
"rollup-plugin-terser": "^7.0.0",
+2 -6
View File
@@ -12,19 +12,15 @@ build = "src/build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "=1.0.0-rc.2" }
tauri-build = { version = "1.0.0-beta.2" }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "=1.0.0-rc.2", features = [] }
tauri = { version = "1.0.0-beta.4", features = [] }
tokio = "1.4"
url = "2.2"
tauri-codegen = "=1.0.0-rc.1"
tauri-macros = "=1.0.0-rc.1"
core-graphics = "=0.22.2"
coconut-interface = { path = "../../../common/coconut-interface" }
credentials = { path = "../../../common/credentials" }
validator-client = {path = "../../../common/client-libs/validator-client"}
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
# CLIENT INIT
NYMD_URL=https://sandbox-validator.nymtech.net
VALIDATOR_API=https://sandbox-validator.nymtech.net/api
MIXNET_CONTRACT=nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j
VESTING_CONTRACT=nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s
CURRENCY_PREFIX=nymt
CHAIN_ID=nym-sandbox
# USER DETAILS
USER_MNEMONIC=
USER_WALLET_ADDRESS=
# MIXNODE DETAILS
MIXNODE_IDENTITY=
MIXNODE_SPHINX_KEY=
MIXNODE_SIGNATURE=
MIXNODE_HOST="1.1.1.1"
MIXNODE_VERSION="0.12.1"
# GATEWAY DETAILS
GATEWAY_IDENTITY=
GATEWAY_SPHINX=
GATEAWAY_LOCATION=
GATEWAY_HOST="1.1.1.1"
GATEWAY_VERSION="0.12.1"
+6 -12
View File
@@ -3,26 +3,20 @@ Nym Validator Client
A TypeScript client for interacting with CosmWasm smart contracts in Nym validators.
Running examples
-----------------
With the code checked out, `cd examples`. This folder contains runnable example code that will set up a blockchain and allow you to interact with it through the client.
Running tests
-------------
The tests will be separated into three categories: unit, integration and mock.
Currently the command to run all tests:
```
npm test
```
You can also trigger test execution with a test watcher. I don't have the centuries of life left to me that are needed to fight through the arcana of wiring up a working TypeScript mocha triggered execution setup, so for now my Cargo-based hack is:
The tests require `.env.example` being renamed to `.env`. The variables and their values for these tests are currently pointing to the `nym-sandbox` environment.
```
cargo watch -s "cd clients/validator && npm test"
```
It's ugly but works fine if you have Cargo installed. TypeScript setup help happily accepted here.
`Tests are still in development` - the test libary is `jest` and the test script will execute currently with: `--coverage --verbosity false`
Generating Documentation
------------------------
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ["dotenv/config"],
testTimeout: 20000
};
+8 -14
View File
@@ -6,9 +6,7 @@
"main": "./dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "ts-mocha tests/**/*.test.ts",
"coverage": "nyc npm test",
"test": "jest --verbose false",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"docs": "typedoc --out docs src/index.ts"
@@ -21,27 +19,21 @@
],
"license": "Apache-2.0",
"devDependencies": {
"@types/chai": "^4.2.15",
"@types/expect": "^24.3.0",
"@types/mocha": "^8.2.1",
"@types/jest": "27.4.0",
"@typescript-eslint/eslint-plugin": "^5.7.0",
"@typescript-eslint/parser": "^5.7.0",
"chai": "^4.2.0",
"eslint": "^7.18.0",
"eslint-config-airbnb": "^19.0.2",
"eslint-config-airbnb-typescript": "^16.1.0",
"eslint-config-prettier": "^8.3.0",
"eslint-import-resolver-root-import": "^1.0.4",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-mocha": "^10.0.3",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^8.2.1",
"moq.ts": "^7.2.0",
"nyc": "^15.1.0",
"jest": "^27.4.5",
"prettier": "^2.5.1",
"ts-mocha": "^8.0.0",
"ts-jest": "^27.1.2",
"typedoc": "^0.20.27",
"typescript": "^4.1.3"
"typescript": "^4.5.4"
},
"dependencies": {
"@cosmjs/cosmwasm-stargate": "^0.27.0-rc2",
@@ -51,6 +43,8 @@
"@cosmjs/stargate": "^0.27.0-rc2",
"@cosmjs/tendermint-rpc": "^0.27.0-rc2",
"axios": "^0.21.1",
"cosmjs-types": "^0.4.0"
"cosmjs-types": "^0.4.0",
"dotenv": "^10.0.0",
"moq.ts": "^7.3.4"
}
}
+2 -10
View File
@@ -64,23 +64,15 @@ export default class ValidatorClient implements INymClient {
readonly vestingContract: string;
readonly mainnetDenom = "unym";
readonly mainnetPrefix = "n";
private constructor(
client: SigningClient | QueryClient,
prefix: string,
mixnetContract: string,
vestingContract: string
vestingContract: string,
) {
this.client = client;
this.prefix = prefix;
if (prefix == this.mainnetPrefix) {
this.denom = this.mainnetDenom;
} else {
this.denom = `u${prefix}`;
}
this.denom = `u${prefix}`;
this.mixnetContract = mixnetContract;
this.vestingContract = vestingContract;
@@ -0,0 +1,201 @@
import validator from "../../src/index";
import { ExecuteResult } from "@cosmjs/cosmwasm-stargate";
import { config } from "../test-utils/config";
import {buildCoin, buildWallet, profitPercentage} from "../test-utils/utils"
import {
Gateway,
GatewayOwnershipResponse,
MixNode,
MixOwnershipResponse,
} from "../../src/types";
let response: ExecuteResult;
let validatorClient: validator;
let ownsMixNode: MixOwnershipResponse;
let ownsGateway: GatewayOwnershipResponse;
beforeEach(async () => {
validatorClient = await validator.connect(
config.USER_MNEMONIC,
config.NYMD_URL,
config.VALIDATOR_API,
config.NETWORK_BECH,
config.MIXNET_CONTRACT,
config.VESTING_CONTRACT
);
});
describe("long running e2e tests", () => {
test.skip("token transfer", async () => {
try {
//make sure there's enough balance in the wallet
let coin = buildCoin("50000", "nymt");
let userAddress = await buildWallet();
let send = await validatorClient.send(
userAddress,
Array(coin),
"auto",
"send-tokens"
);
let jsonParse = JSON.parse(send.rawLog as string);
//check successful network broadcast - via events
//1 - get key attributes values for sender an assert them
//2 - get key attributes for receiver assert they match
//3 - transaction hash present in response
// { array of events -> attribute -> event information }
expect(jsonParse[0].events[1].attributes[1].value).toStrictEqual(
config.USER_WALLET_ADDRESS
);
expect(jsonParse[0].events[1].attributes[0].value).toStrictEqual(
userAddress
);
expect(jsonParse[0].events[1].type).toStrictEqual(
"transfer"
);
expect(send.transactionHash).toStrictEqual(expect.any(String));
} catch (error) {
throw error;
}
});
test.skip("update mixnode profit percentage", async () => {
const nodeIdentity = config.MIXNODE_IDENTITY;
const profitPercent = profitPercentage();
try {
//use auto fees - simulated gas
response = await validatorClient.updateMixnodeConfig(nodeIdentity, 'auto', profitPercent);
}
catch (error) {
throw error;
}
try {
ownsMixNode = await validatorClient.client.ownsMixNode(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
}
catch (error) {
throw error;
}
expect(ownsMixNode.mixnode?.mix_node.profit_margin_percent).toStrictEqual(profitPercent);
});
test.skip("unbond and bond mixnode", async () => {
try {
await validatorClient.unbondMixNode("auto", "unbond-mixnode");
}
catch (error) {
throw error;
}
const profitPercent = profitPercentage();
const mixnodeDetails = <MixNode>{
host: config.MIXNODE_HOST,
mix_port: 1789,
verloc_port: 1790,
http_api_port: 8080,
identity_key: config.MIXNODE_IDENTITY,
sphinx_key: config.MIXNODE_SPHINX_KEY,
version: config.MIXNODE_VERSION,
profit_margin_percent: profitPercent
};
const bond = buildCoin("100000000", config.CURRENCY_DENOM)
try {
response = await validatorClient.bondMixNode(
mixnodeDetails,
config.MIXNODE_SIGNATURE,
bond,
"auto"
);
}
catch (error) {
throw error;
}
ownsMixNode = await validatorClient.client.ownsMixNode(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
expect(ownsMixNode.mixnode?.mix_node.profit_margin_percent).toStrictEqual(profitPercent);
});
test.skip("unbond and bond gateway", async () => {
//gateway requires different user wallet
//init inside test
//todo
try {
await validatorClient.unbondGateway("auto", "unbonding gateway");
}
catch (error) {
throw error;
}
const gateway = <Gateway>{
host: config.GATEWAY_HOST,
mix_port: 1789,
clients_port: 9000,
version: config.GATEWAY_VERSION,
sphinx_key: config.GATEWAY_SPHINX,
identity_key: config.GATEWAY_IDENTITY,
location: "earth"
};
const bond = buildCoin("100000000", config.CURRENCY_DENOM)
try {
response = await validatorClient.bondGateway(
gateway,
config.GATEWAY_SIGNATURE,
bond,
"auto"
);
}
catch (error) {
throw error;
}
ownsGateway = await validatorClient.client.ownsGateway(config.MIXNET_CONTRACT, config.USER_WALLET_ADDRESS);
expect(ownsGateway.gateway?.bond_amount).toStrictEqual(bond.amount);
expect(ownsGateway.address).toStrictEqual(config.USER_WALLET_ADDRESS);
});
test.skip("delegate to mixnode, then undelegate", async () => {
const pledge = buildCoin("100000000", config.CURRENCY_DENOM)
try {
response = await validatorClient.delegateToMixNode(
config.MIXNODE_IDENTITY,
pledge,
"auto"
);
//todo - we can assert the events for responses
response.logs.forEach((log) => {
console.log(log.events);
console.log(log.log);
console.log(log.msg_index);
})
}
catch (error) {
throw error;
}
try {
const unbond = await validatorClient.undelegateFromMixNode(
config.MIXNODE_IDENTITY,
"auto"
);
//todo - we can assert the events for responses
unbond.logs.forEach((logs) => {
logs.events.forEach((events) => {
console.log(events.type);
console.log(events.attributes);
})
});
} catch (error) {
throw error;
}
});
});
@@ -0,0 +1,46 @@
import { Mock, Times } from "moq.ts";
import { Block, BlockHeader } from "@cosmjs/stargate";
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
describe("implement cosmwasm client test", () => {
test.only("get height of a block then search for it", async () => {
let height = Promise.resolve(200);
let blockHeader = <BlockHeader>{
version: {
block: "200",
app: "testing",
},
height: 200,
chainId: "nym",
time: "today",
};
let block = Promise.resolve(<Block>{
header: blockHeader,
id: "test",
txs: [],
});
const getheight = new Mock<CosmWasmClient>()
.setup((nym) => nym.getHeight())
.returns(height);
const getblock = new Mock<CosmWasmClient>()
.setup((nym) => nym.getBlock(200))
.returns(block);
let heightC = getheight.object();
let blockC = getblock.object();
let executeHeight = await heightC.getHeight();
let executeBlock = await blockC.getBlock(200);
getheight.verify((nym) => nym.getHeight(), Times.Exactly(1));
getblock.verify((nym) => nym.getBlock(200), Times.Exactly(1));
expect(executeHeight).toStrictEqual(await height);
expect(executeBlock.header.height).toStrictEqual(await height);
expect(executeBlock.header.chainId).toStrictEqual("nym");
});
});
@@ -0,0 +1,25 @@
import { Mock, Times } from "moq.ts";
import { INymdQuery } from "../../src/query-client";
describe("nym-client mocks", () => {
test.only("gets interval rewarding percent", async () => {
let contract = "mixnet_contract";
let response = Promise.resolve(Number(2));
const client = new Mock<INymdQuery>()
.setup((nym) => nym.getIntervalRewardPercent(contract))
.returns(response);
const obj = client.object();
let execute = await obj.getIntervalRewardPercent(contract);
client.verify(
(nym) => nym.getIntervalRewardPercent(contract),
Times.Exactly(1)
);
expect(execute).toStrictEqual(await response);
});
});
@@ -0,0 +1,176 @@
import { Coin } from "@cosmjs/proto-signing";
import { Mock, Times } from "moq.ts";
import ValidatorClient from "../../src/index";
import { Gateway, MixNode } from "../../src/types";
import { config } from "../test-utils/config";
import { buildWallet, buildCoin, profitPercentage } from "../test-utils/utils";
import { promiseExecuteResult } from "../test-utils/expectedResults";
import { promiseTxResult } from "../test-utils/expectedResults"
describe("mock validator client tests", () => {
test.skip("token transfer", async () => {
//arrange
//todo -- add more here
let recipientAddress = "nymt14ev4p8qaa7ayr06cg3z7y2u2kxc9a8f4h9gkch";
let sender = "nymt1cv59jumgvz2chn7ffst8tzvnapqzp282m5vat2";
const coin = buildCoin("50000", "nymt");
let transaction = promiseTxResult();
let mockClient = new Mock<ValidatorClient>()
.setup((nym) => nym.send(recipientAddress, [coin], "auto", "test")).returns(transaction);
let token = mockClient.object();
//act
let response = await token.send(recipientAddress, [coin], "auto", "test");
//assert
mockClient.verify(cl => cl.send(recipientAddress, [coin], "auto"), Times.Exactly(1));
});
test.only("bond mixnode test", async () => {
//arrange
let ownerSignature = "ownersignature";
let coin = buildCoin("50000", "nymt");
let expectedResult = promiseExecuteResult();
const profitPercent = profitPercentage();
const mixnode = <MixNode>{
host: "1.1.1.1",
mix_port: 1789,
verloc_port: 1790,
http_api_port: 8080,
identity_key: "identity",
sphinx_key: "identity",
version: "0.12.1",
profit_margin_percent: profitPercent,
};
let client = new Mock<ValidatorClient>()
.setup((client) =>
client.bondMixNode(mixnode, ownerSignature, coin, "auto")
)
.returns(expectedResult);
let mixnodeBond = client.object();
//act
let response = await mixnodeBond.bondMixNode(
mixnode,
ownerSignature,
coin,
"auto"
);
client.verify((cl) =>
cl.bondMixNode(mixnode, ownerSignature, coin, "auto")
);
//assert
expect(response.logs[0].log).toStrictEqual("test");
expect(response.transactionHash).toStrictEqual(
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
);
});
test.only("un-bond mixnode", async () => {
//arrange
let expectedResult = promiseExecuteResult();
let client = new Mock<ValidatorClient>()
.setup((client) => client.unbondMixNode("auto"))
.returns(expectedResult);
let unbondNode = client.object();
//act
let response = await unbondNode.unbondMixNode("auto");
client.verify((cl) => cl.unbondMixNode("auto"));
//assert
expect(response.logs[0].log).toStrictEqual("test");
expect(response.transactionHash).toStrictEqual(
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
);
});
test.only("bond gateway", async () => {
//arrange
let expectedResult = promiseExecuteResult();
let ownerSignature = "ownersigntature";
let coin = buildCoin("50000", "nymt");
const gateway = <Gateway>{
host: '1.2.3.4',
mix_port: 1789,
clients_port: 9000,
version: "0.12.1",
sphinx_key: "sphinx_key",
identity_key: "identity_key",
location: "earth"
};
let client = new Mock<ValidatorClient>()
.setup((client) => client.bondGateway(gateway, ownerSignature, coin, "auto", "memo"))
.returns(expectedResult);
let mock = client.object();
//act
let response = await mock.bondGateway(gateway, ownerSignature, coin, "auto", "memo");
client.verify((cl) => cl.bondGateway(gateway, ownerSignature, coin, "auto", "memo"));
//assert
expect(response.logs[0].log).toStrictEqual("test");
expect(response.transactionHash).toStrictEqual(
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
);
});
test.only("unbond gateway", async () => {
//arrange
let expectedResult = promiseExecuteResult();
let client = new Mock<ValidatorClient>()
.setup((client) => client.unbondGateway())
.returns(expectedResult);
let mock = client.object();
//act
let response = await mock.unbondGateway();
client.verify((cl) => cl.unbondGateway());
//assert
expect(response.logs[0].log).toStrictEqual("test");
expect(response.transactionHash).toStrictEqual(
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E"
);
});
test.only("retrieve a newly created account and the balance should be empty", async () => {
let nymWallet = await buildWallet();
let coin = Promise.resolve(<Coin>{
denom: `${config.CURRENCY_DENOM}`,
amount: "0",
});
let client = new Mock<ValidatorClient>()
.setup((nym) => nym.getBalance(nymWallet))
.returns(coin);
let obj = client.object();
let execute = await obj.getBalance(nymWallet);
client.verify((nym) => nym.getBalance(nymWallet), Times.Exactly(1));
expect(execute).toStrictEqual(await coin);
});
});
@@ -0,0 +1,23 @@
export const config = {
NYMD_URL: process.env.NYMD_URL as string,
VALIDATOR_API: process.env.VALIDATOR_API as string,
MIXNET_CONTRACT: process.env.MIXNET_CONTRACT as string,
VESTING_CONTRACT: process.env.VESTING_CONTRACT as string,
USER_MNEMONIC: process.env.USER_MNEMONIC as string,
USER_WALLET_ADDRESS: process.env.USER_WALLET_ADDRESS as string,
CURRENCY_DENOM: process.env.CURRENCY_DENOM as string,
CHAIN_ID: process.env.CHAIN_ID as string,
MIXNODE_IDENTITY: process.env.MIXNODE_IDENTITY as string,
MIXNODE_SPHINX_KEY: process.env.MIXNODE_SPHINX_KEY as string,
MIXNODE_SIGNATURE: process.env.MIXNODE_SIGNATURE as string,
MIXNODE_HOST: process.env.MIXNODE_HOST as string,
MIXNODE_VERSION: process.env.MIXNODE_VERSION as string,
GATEWAY_IDENTITY: process.env.GATEWAY_IDENTITY as string,
GATEWAY_SIGNATURE: process.env.GATEWAY_SIGNATURE as string,
GATEWAY_SPHINX: process.env.GATEWAY_SPHINX as string,
GATEWAY_LOCATION: process.env.GATEWAY_LOCATION as string,
GATEWAY_HOST: process.env.GATEWAY_HOST as string,
GATEWAY_VERSION: process.env.GATEWAY_VERSION as string,
NETWORK_BECH: process.env.NETWORK_BECH as string,
};
@@ -0,0 +1,27 @@
import { ExecuteResult } from "@cosmjs/cosmwasm-stargate";
import { DeliverTxResponse, logs } from "@cosmjs/stargate";
export const promiseExecuteResult = (): Promise<ExecuteResult> => {
let log = <logs.Log>{
msg_index: 0,
log: "test",
events: [],
};
return Promise.resolve(<ExecuteResult>{
logs: [log],
transactionHash:
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E",
});
};
export const promiseTxResult = (): Promise<DeliverTxResponse> => {
return Promise.resolve(<DeliverTxResponse>{
code: 0,
height: 1208302,
rawLog: "[]",
transactionHash:
"9C7BF465AB5CAB0D62446CBB251CF89CD173A640C5DE8DBC14A4BB950916114E",
gasUsed: 65042,
gasWanted: 67977,
});
};
@@ -0,0 +1,46 @@
// timer actions
import ValidatorClient, { Coin } from "../../src";
import { config } from "./config";
// Store current time as `start`
export const now = (eventName = null) => {
if (eventName) {
console.log(`Started ${eventName}..`);
}
return new Date().getTime();
};
//takes arg of start time
export const elapsed = (beginning: number, log = false) => {
const duration = new Date().getTime() - beginning;
if (log) {
console.log(`${duration / 1000}s`);
}
return duration;
};
export const profitPercentage = (): number => {
return Math.floor(Math.random() * 100) + 1;
};
export const buildCoin = (amount: string, denomination: string): Coin => {
return {
denom: `u${denomination}`,
amount: amount,
};
};
export const buildWallet = async (): Promise<string> => {
let mnemonic = ValidatorClient.randomMnemonic();
const randomAddress = await ValidatorClient.buildWallet(
mnemonic,
config.NETWORK_BECH
);
let accountdetails = await randomAddress.getAccounts();
let nymWallet = accountdetails[0].address;
return nymWallet;
};
@@ -0,0 +1,10 @@
const currency = require("../../src/currency");
describe("currency module tests", () => {
test.skip("convert to native balance", () => {
const decimal = "12.0346";
const value = currency.printableBalanceToNative(decimal);
expect(value).toStrictEqual("12034600");
});
});
@@ -0,0 +1,19 @@
const stargate = require("../../src/stargate-helper");
import { config } from "../test-utils/config";
describe("test the stargate functions within the project", () => {
test.skip("gas price is returned correctly", () => {
const nymCurrency = config.CURRENCY_DENOM;
const getGasPrice = stargate.nymGasPrice(nymCurrency);
expect(getGasPrice.denom).toBe(`u${nymCurrency}`);
});
test.skip("provide invalid type returns an error message", () => {
//pass invalid type
expect(() => {
const nymCurrency = 13;
stargate.nymGasPrice(nymCurrency);
}).toThrow("13 is not of type string");
});
});
@@ -0,0 +1,10 @@
import validator from "../../src/index";
describe("validator build network mnemonic", () => {
test.skip("get mnemonic", async () => {
const mnemonic = validator.randomMnemonic();
const mnemonicCount = mnemonic.split(" ").length;
expect(mnemonicCount).toStrictEqual(24);
});
});
+10 -2
View File
@@ -5,7 +5,11 @@
"esModuleInterop": true,
"strict": true,
"declaration": true,
"outDir": "./dist"
"outDir": "./dist",
"types": [
"jest",
"node",
]
},
"typedocOptions": {
"entryPoints": [
@@ -16,7 +20,11 @@
"exclude": [
"dist",
"examples",
"tests",
"node_modules"
],
"include": [
"tests",
"./tests/*/*.tsx",
"./tests/*/*.ts"
]
}
+3654 -2959
View File
File diff suppressed because it is too large Load Diff
@@ -39,9 +39,8 @@ flate2 = { version = "1.0.20", optional = true }
sha2 = { version = "0.9.5", optional = true }
itertools = { version = "0.10", optional = true }
cosmwasm-std = { version = "1.0.0-beta3", optional = true }
[dev-dependencies]
ts-rs = "6.1.2"
# Leaving it as * so that it inherits whatever the wallet is using
ts-rs = { version = "*", optional = true }
[features]
nymd-client = [
@@ -55,3 +54,4 @@ nymd-client = [
"itertools",
"cosmwasm-std",
]
typescript-types = ["ts-rs", "validator-api-requests/ts-rs"]
@@ -4,7 +4,6 @@
use crate::{validator_api, ValidatorClientError};
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
use network_defaults::DEFAULT_NETWORK;
use url::Url;
use validator_api_requests::models::{
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
@@ -30,7 +29,6 @@ use std::str::FromStr;
#[cfg(feature = "nymd-client")]
#[must_use]
#[derive(Debug)]
pub struct Config {
network: network_defaults::all::Network,
api_url: Url,
@@ -161,10 +159,12 @@ impl Client<QueryNymdClient> {
let nymd_client = NymdClient::connect(
config.nymd_url.as_str(),
Some(config.mixnet_contract_address.clone().unwrap_or_else(|| {
cosmrs::AccountId::from_str(DEFAULT_NETWORK.mixnet_contract_address()).unwrap()
cosmrs::AccountId::from_str(network_defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS)
.unwrap()
})),
Some(config.vesting_contract_address.clone().unwrap_or_else(|| {
cosmrs::AccountId::from_str(DEFAULT_NETWORK.vesting_contract_address()).unwrap()
cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS)
.unwrap()
})),
Some(
config
@@ -172,7 +172,7 @@ impl Client<QueryNymdClient> {
.clone()
.unwrap_or_else(|| {
cosmrs::AccountId::from_str(
DEFAULT_NETWORK.bandwidth_claim_contract_address(),
network_defaults::DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
)
.unwrap()
}),
@@ -72,7 +72,7 @@ impl FromStr for GasPrice {
}
impl GasPrice {
pub fn new_with_default_price(denom: &str) -> Result<Self, NymdError> {
pub fn new_with_default_price(denom: String) -> Result<Self, NymdError> {
format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse()
}
}
@@ -83,8 +83,8 @@ mod tests {
#[test]
fn default_gas_price_is_valid() {
let denom = "unym";
GasPrice::new_with_default_price(denom).unwrap();
let denom = "unym".parse().unwrap();
let _ = GasPrice::new_with_default_price(denom);
}
#[test]
@@ -7,11 +7,7 @@ use cosmrs::Coin;
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../../../nym-wallet/src/types/rust/operation.ts")
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum Operation {
Upload,
@@ -57,16 +57,16 @@ pub struct DirectSecp256k1HdWallet {
}
impl DirectSecp256k1HdWallet {
pub fn builder(prefix: &str) -> DirectSecp256k1HdWalletBuilder {
pub fn builder(prefix: String) -> DirectSecp256k1HdWalletBuilder {
DirectSecp256k1HdWalletBuilder::new(prefix)
}
/// Restores a wallet from the given BIP39 mnemonic using default options.
pub fn from_mnemonic(prefix: &str, mnemonic: bip39::Mnemonic) -> Result<Self, NymdError> {
pub fn from_mnemonic(prefix: String, mnemonic: bip39::Mnemonic) -> Result<Self, NymdError> {
DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic)
}
pub fn generate(prefix: &str, word_count: usize) -> Result<Self, NymdError> {
pub fn generate(prefix: String, word_count: usize) -> Result<Self, NymdError> {
let mneomonic = bip39::Mnemonic::generate(word_count)?;
Self::from_mnemonic(prefix, mneomonic)
}
@@ -146,11 +146,11 @@ pub struct DirectSecp256k1HdWalletBuilder {
}
impl DirectSecp256k1HdWalletBuilder {
pub fn new(prefix: &str) -> Self {
pub fn new(prefix: String) -> Self {
DirectSecp256k1HdWalletBuilder {
bip39_password: String::new(),
hd_paths: vec![defaults::COSMOS_DERIVATION_PATH.parse().unwrap()],
prefix: prefix.into(),
prefix,
}
}
@@ -197,11 +197,11 @@ impl DirectSecp256k1HdWalletBuilder {
#[cfg(test)]
mod tests {
use super::*;
use network_defaults::DEFAULT_NETWORK;
use network_defaults::{default_network, BECH32_PREFIX};
#[test]
fn generating_account_addresses() {
let (addr1, addr2, addr3) = match DEFAULT_NETWORK.bech32_prefix() {
let (addr1, addr2, addr3) = match BECH32_PREFIX {
"punk" => (
"punk1jw6mp7d5xqc7w6xm79lha27glmd0vdt32a3fj2",
"punk1h5hgn94nsq4kh99rjj794hr5h5q6yfm22mcqqn",
@@ -222,7 +222,7 @@ mod tests {
];
for (mnemonic, address) in mnemonic_address.into_iter() {
let prefix = DEFAULT_NETWORK.bech32_prefix();
let prefix = default_network().bech32_prefix();
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.parse().unwrap()).unwrap();
assert_eq!(
@@ -12,6 +12,8 @@ cosmwasm-std = "1.0.0-beta3"
serde = { version = "1.0", features = ["derive"] }
serde_repr = "0.1"
schemars = "0.8"
# Leaving it as * so that it inherits whatever the wallet is using
ts-rs = { version = "*", optional = true }
thiserror = "1.0"
network-defaults = { path = "../../network-defaults" }
fixed = { version = "1.1", features = ["serde"] }
@@ -23,7 +25,7 @@ contracts-common = { path = "../contracts-common" }
[dev-dependencies]
time = { version = "0.3.5", features = ["serde", "macros"] }
ts-rs = "6.1.2"
[features]
default = []
@@ -8,11 +8,7 @@ use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Display;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../../../nym-wallet/src/types/rust/gateway.ts")
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub struct Gateway {
pub host: String,
@@ -18,14 +18,7 @@ fixed::const_fixed_from_int! {
const ONE: U128 = 1;
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(
export,
export_to = "../../../nym-wallet/src/types/rust/rewardedsetnodestatus.ts"
)
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub enum RewardedSetNodeStatus {
Active,
@@ -38,11 +31,7 @@ impl RewardedSetNodeStatus {
}
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../../../nym-wallet/src/types/rust/mixnode.ts")
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
pub struct MixNode {
pub host: String,
@@ -12,6 +12,4 @@ serde = { version = "1.0", features = ["derive"] }
schemars = "0.8"
cw-storage-plus = "0.11.1"
config = { path = "../../config" }
[dev-dependencies]
ts-rs = "6.1.2"
ts-rs = { version = "*", optional = true }
@@ -12,11 +12,7 @@ pub fn one_ucoin() -> Coin {
Coin::new(1, DENOM)
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../../../nym-wallet/src/types/rust/period.ts")
)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, JsonSchema)]
pub enum Period {
Before,
+1 -1
View File
@@ -11,6 +11,6 @@ url = "2.2"
# I guess temporarily until we get serde support in coconut up and running
coconut-interface = { path = "../coconut-interface" }
crypto = { path = "../crypto", features = ["asymmetric"] }
crypto = { path = "../crypto" }
network-defaults = { path = "../network-defaults" }
validator-client = { path = "../client-libs/validator-client" }
+16 -22
View File
@@ -3,8 +3,8 @@
use coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign,
prove_bandwidth_credential, Attribute, BlindSignRequest, BlindSignRequestBody, Credential,
ElGamalKeyPair, Parameters, Signature, SignatureShare, VerificationKey,
prove_bandwidth_credential, Attribute, BlindSignRequestBody, Credential, Parameters, Signature,
SignatureShare, VerificationKey,
};
use url::Url;
@@ -47,13 +47,13 @@ pub async fn obtain_aggregate_verification_key(
let mut client = validator_client::ApiClient::new(validators[0].clone());
let response = client.get_coconut_verification_key().await?;
indices.push(1);
indices.push(0);
shares.push(response.key);
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone());
let response = client.get_coconut_verification_key().await?;
indices.push((id + 1) as u64);
indices.push(id as u64);
shares.push(response.key);
}
@@ -66,11 +66,17 @@ async fn obtain_partial_credential(
private_attributes: &[Attribute],
client: &validator_client::ApiClient,
validator_vk: &VerificationKey,
blind_sign_request: &BlindSignRequest,
elgamal_keypair: &ElGamalKeyPair,
) -> Result<Signature, Error> {
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
let blind_sign_request = prepare_blind_sign(
params,
&elgamal_keypair,
private_attributes,
public_attributes,
)?;
let blind_sign_request_body = BlindSignRequestBody::new(
blind_sign_request,
&blind_sign_request,
elgamal_keypair.public_key(),
public_attributes,
(public_attributes.len() + private_attributes.len()) as u32,
@@ -110,25 +116,15 @@ pub async fn obtain_aggregate_signature(
let validator_partial_vk = client.get_coconut_verification_key().await?;
validators_partial_vks.push(validator_partial_vk.key.clone());
let elgamal_keypair = coconut_interface::elgamal_keygen(params);
let blind_sign_request = prepare_blind_sign(
params,
&elgamal_keypair,
private_attributes,
public_attributes,
)?;
let first = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&client,
&validator_partial_vk.key,
&blind_sign_request,
&elgamal_keypair,
)
.await?;
shares.push(SignatureShare::new(first, 1));
shares.push(SignatureShare::new(first, 0));
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone());
@@ -140,11 +136,9 @@ pub async fn obtain_aggregate_signature(
private_attributes,
&client,
&validator_partial_vk.key,
&blind_sign_request,
&elgamal_keypair,
)
.await?;
let share = SignatureShare::new(signature, (id + 1) as u64);
let share = SignatureShare::new(signature, id as u64);
shares.push(share)
}
@@ -154,7 +148,7 @@ pub async fn obtain_aggregate_signature(
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
for i in 0..validators_partial_vks.len() {
indices.push((i + 1) as u64);
indices.push(i as u64);
}
let verification_key =
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
+11 -19
View File
@@ -7,29 +7,21 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
aes = { version = "0.8.1", optional = true }
aes = { version = "0.7.4", features = ["ctr"] }
bs58 = "0.4.0"
blake3 = { version = "1.3.1", features = ["traits-preview"], optional = true }
ctr = { version = "0.9.1", optional = true }
digest = { version = "0.10.3", optional = true }
generic-array = { version = "0.14", optional = true }
hkdf = { version = "0.12.3", optional = true }
hmac = { version = "0.12.1", optional = true }
cipher = { version = "0.4.3", optional = true }
x25519-dalek = { version = "1.1", optional = true }
ed25519-dalek = { version = "1.0", optional = true }
rand = { version = "0.7.3", features = ["wasm-bindgen"], optional = true }
blake3 = { version = "~1.2.0", features = ["traits-preview"] }
digest = "0.9.0"
generic-array = "0.14"
hkdf = "0.11.0"
hmac = "0.11.0"
cipher = "0.3.0"
x25519-dalek = "1.1"
ed25519-dalek = "1.0"
log = "0.4"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
# internal
nymsphinx-types = { path = "../nymsphinx/types" }
pemstore = { path = "../../common/pemstore" }
config = { path="../../common/config" }
[dev-dependencies]
rand_chacha = "0.2"
[features]
asymmetric = ["x25519-dalek", "ed25519-dalek"]
hashing = ["blake3", "digest", "hkdf", "hmac", "generic-array"]
symmetric = ["aes", "ctr", "cipher", "generic-array"]
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter};
@@ -47,7 +46,6 @@ pub struct KeyPair {
}
impl KeyPair {
#[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let private_key = x25519_dalek::StaticSecret::new(rng);
let public_key = (&private_key).into();
@@ -6,7 +6,6 @@ pub use ed25519_dalek::SignatureError;
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
use nymsphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
#[cfg(feature = "rand")]
use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter};
@@ -46,7 +45,6 @@ pub struct KeyPair {
}
impl KeyPair {
#[cfg(feature = "rand")]
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
let ed25519_keypair = ed25519_dalek::Keypair::generate(rng);
@@ -1,4 +1,4 @@
use config::defaults::DEFAULT_NETWORK;
use config::defaults;
use subtle_encoding::bech32;
#[derive(Debug, Clone, PartialEq)]
@@ -18,12 +18,12 @@ pub fn try_bech32_decode(address: &str) -> Result<String, Bech32Error> {
pub fn validate_bech32_prefix(address: &str) -> Result<(), Bech32Error> {
let prefix = try_bech32_decode(address)?;
if prefix == DEFAULT_NETWORK.bech32_prefix() {
if prefix == defaults::BECH32_PREFIX {
Ok(())
} else {
Err(Bech32Error::WrongPrefix(format!(
"your bech32 address prefix should be {}, not {}",
DEFAULT_NETWORK.bech32_prefix(),
defaults::BECH32_PREFIX,
prefix
)))
}
+6 -3
View File
@@ -1,11 +1,14 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use digest::{Digest, Output};
use digest::{BlockInput, Digest, FixedOutput, Reset, Update};
use generic_array::{ArrayLength, GenericArray};
pub fn compute_digest<D>(data: &[u8]) -> Output<D>
pub fn compute_digest<D>(data: &[u8]) -> GenericArray<u8, <D as Digest>::OutputSize>
where
D: Digest,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
D::digest(data)
}
+7 -9
View File
@@ -1,13 +1,9 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use hkdf::{
hmac::{
digest::{crypto_common::BlockSizeUser, Digest},
SimpleHmac,
},
Hkdf,
};
use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::ArrayLength;
use hkdf::Hkdf;
/// Perform HKDF `extract` then `expand` as a single step.
pub fn extract_then_expand<D>(
@@ -17,12 +13,14 @@ pub fn extract_then_expand<D>(
okm_length: usize,
) -> Result<Vec<u8>, hkdf::InvalidLength>
where
D: Digest + BlockSizeUser + Clone,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
// TODO: this would need to change if we ever needed the generated pseudorandom key, but
// realistically I don't see any reasons why we might need it
let hkdf = Hkdf::<D, SimpleHmac<D>>::new(salt, ikm);
let hkdf = Hkdf::<D>::new(salt, ikm);
let mut okm = vec![0u8; okm_length];
hkdf.expand(info.unwrap_or(&[]), &mut okm)?;
+24 -19
View File
@@ -1,23 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use hmac::{
digest::{crypto_common::BlockSizeUser, CtOutput, Digest, Output},
Mac, SimpleHmac,
};
use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::{typenum::Unsigned, ArrayLength, GenericArray};
use hmac::{crypto_mac, Hmac, Mac, NewMac};
pub use hmac;
// TODO: We should probably change it to use some sealed trait to allow for both `Hmac` and `SimpleHmac`
pub type HmacOutput<D> = CtOutput<SimpleHmac<D>>;
// Type alias for ease of use so that it would not require explicit import of crypto_mac or Hmac
pub type HmacOutput<D> = crypto_mac::Output<Hmac<D>>;
/// Compute keyed hmac
pub fn compute_keyed_hmac<D>(key: &[u8], data: &[u8]) -> HmacOutput<D>
where
D: Digest + BlockSizeUser,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
let mut hmac = SimpleHmac::<D>::new_from_slice(key)
.expect("HMAC was instantiated with a key of an invalid size!");
let mut hmac =
Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
hmac.update(data);
hmac.finalize()
}
@@ -25,28 +26,32 @@ where
/// Compute keyed hmac and performs constant time equality check with the provided tag value.
pub fn recompute_keyed_hmac_and_verify_tag<D>(key: &[u8], data: &[u8], tag: &[u8]) -> bool
where
D: Digest + BlockSizeUser,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
let mut hmac = SimpleHmac::<D>::new_from_slice(key)
.expect("HMAC was instantiated with a key of an invalid size!");
let mut hmac =
Hmac::<D>::new_from_slice(key).expect("HMAC should be able to take key of any size!");
hmac.update(data);
let tag_arr = Output::<D>::from_slice(tag);
// note, under the hood ct_eq is called
hmac.verify(tag_arr).is_ok()
hmac.verify(tag).is_ok()
}
/// Verifies tag of an hmac output.
pub fn verify_tag<D>(tag: &[u8], out: HmacOutput<D>) -> bool
where
D: Digest + BlockSizeUser,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
if tag.len() != <D as Digest>::output_size() {
if tag.len() != D::OutputSize::to_usize() {
return false;
}
let tag_arr = Output::<D>::from_slice(tag);
out == tag_arr.into()
let tag_bytes = GenericArray::clone_from_slice(tag);
let tag_out = HmacOutput::new(tag_bytes);
// note, under the hood ct_eq is called
out == tag_out
}
#[cfg(test)]
+1 -13
View File
@@ -1,33 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "asymmetric")]
pub mod asymmetric;
pub mod bech32_address_validation;
#[cfg(feature = "hashing")]
pub mod crypto_hash;
#[cfg(feature = "hashing")]
pub mod hkdf;
#[cfg(feature = "hashing")]
pub mod hmac;
#[cfg(all(feature = "asymmetric", feature = "hashing", feature = "symmetric"))]
pub mod shared_key;
#[cfg(feature = "symmetric")]
pub mod symmetric;
#[cfg(feature = "hashing")]
pub use digest::{Digest, OutputSizeUser};
#[cfg(any(feature = "hashing", feature = "symmetric"))]
pub use digest::Digest;
pub use generic_array;
// with the below my idea was to try to introduce having a single place of importing all hashing, encryption,
// etc. algorithms and import them elsewhere as needed via common/crypto
#[cfg(feature = "symmetric")]
pub use aes;
#[cfg(feature = "hashing")]
pub use blake3;
#[cfg(feature = "symmetric")]
pub use ctr;
// TODO: this function uses all three modules: asymmetric crypto, symmetric crypto and derives key...,
// so I don't know where to put it...
+17 -15
View File
@@ -3,22 +3,22 @@
use crate::asymmetric::encryption;
use crate::hkdf;
use cipher::{Key, KeyIvInit, StreamCipher};
use digest::crypto_common::BlockSizeUser;
use digest::Digest;
#[cfg(feature = "rand")]
use cipher::{CipherKey, NewCipher, StreamCipher};
use digest::{BlockInput, FixedOutput, Reset, Update};
use generic_array::{typenum::Unsigned, ArrayLength};
use rand::{CryptoRng, RngCore};
/// Generate an ephemeral encryption keypair and perform diffie-hellman to establish
/// shared key with the remote.
#[cfg(feature = "rand")]
pub fn new_ephemeral_shared_key<C, D, R>(
rng: &mut R,
remote_key: &encryption::PublicKey,
) -> (encryption::KeyPair, Key<C>)
) -> (encryption::KeyPair, CipherKey<C>)
where
C: StreamCipher + KeyIvInit,
D: Digest + BlockSizeUser + Clone,
C: StreamCipher + NewCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
R: RngCore + CryptoRng,
{
let ephemeral_keypair = encryption::KeyPair::new(rng);
@@ -27,11 +27,11 @@ where
let dh_result = ephemeral_keypair.private_key().diffie_hellman(remote_key);
// there is no reason for this to fail as our okm is expected to be only C::KeySize bytes
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::key_size())
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::KeySize::to_usize())
.expect("somehow too long okm was provided");
let derived_shared_key =
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!");
CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!");
(ephemeral_keypair, derived_shared_key)
}
@@ -40,16 +40,18 @@ where
pub fn recompute_shared_key<C, D>(
remote_key: &encryption::PublicKey,
local_key: &encryption::PrivateKey,
) -> Key<C>
) -> CipherKey<C>
where
C: StreamCipher + KeyIvInit,
D: Digest + BlockSizeUser + Clone,
C: StreamCipher + NewCipher,
D: Update + BlockInput + FixedOutput + Reset + Default + Clone,
D::BlockSize: ArrayLength<u8>,
D::OutputSize: ArrayLength<u8>,
{
let dh_result = local_key.diffie_hellman(remote_key);
// there is no reason for this to fail as our okm is expected to be only C::KeySize bytes
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::key_size())
let okm = hkdf::extract_then_expand::<D>(None, &dh_result, None, C::KeySize::to_usize())
.expect("somehow too long okm was provided");
Key::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!")
CipherKey::<C>::from_exact_iter(okm).expect("okm was expanded to incorrect length!")
}
+22 -27
View File
@@ -1,13 +1,12 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cipher::{Iv, StreamCipher};
pub use cipher::{IvSizeUser, KeyIvInit, KeySizeUser};
#[cfg(feature = "rand")]
use cipher::{Nonce, StreamCipher};
use generic_array::{typenum::Unsigned, GenericArray};
use rand::{CryptoRng, RngCore};
// re-export this for ease of use
pub use cipher::Key as CipherKey;
pub use cipher::{CipherKey, NewCipher};
// SECURITY:
// TODO: note that this is not the most secure approach here
@@ -20,51 +19,49 @@ pub use cipher::Key as CipherKey;
// I think 'IV' looks better than 'Iv', feel free to change that.
#[allow(clippy::upper_case_acronyms)]
pub type IV<C> = Iv<C>;
pub type IV<C> = Nonce<C>;
#[cfg(feature = "rand")]
pub fn generate_key<C, R>(rng: &mut R) -> CipherKey<C>
where
C: KeyIvInit,
C: NewCipher,
R: RngCore + CryptoRng,
{
let mut key = CipherKey::<C>::default();
let mut key = GenericArray::default();
rng.fill_bytes(&mut key);
key
}
#[cfg(feature = "rand")]
pub fn random_iv<C, R>(rng: &mut R) -> IV<C>
where
C: KeyIvInit,
C: NewCipher,
R: RngCore + CryptoRng,
{
let mut iv = IV::<C>::default();
let mut iv = GenericArray::default();
rng.fill_bytes(&mut iv);
iv
}
pub fn zero_iv<C>() -> IV<C>
where
C: KeyIvInit,
C: NewCipher,
{
Iv::<C>::default()
GenericArray::default()
}
pub fn iv_from_slice<C>(b: &[u8]) -> &IV<C>
where
C: KeyIvInit,
C: NewCipher,
{
if b.len() != C::iv_size() {
if b.len() != C::NonceSize::to_usize() {
// `from_slice` would have caused a panic about this issue anyway.
// Now we at least have slightly more information
panic!(
"Tried to convert {} bytes to IV. Expected {}",
b.len(),
C::iv_size()
C::NonceSize::to_usize()
)
}
IV::<C>::from_slice(b)
GenericArray::from_slice(b)
}
// TODO: there's really no way to use more parts of the keystream if it was required at some point.
@@ -73,7 +70,7 @@ where
#[inline]
pub fn encrypt<C>(key: &CipherKey<C>, iv: &IV<C>, data: &[u8]) -> Vec<u8>
where
C: StreamCipher + KeyIvInit,
C: StreamCipher + NewCipher,
{
let mut ciphertext = data.to_vec();
encrypt_in_place::<C>(key, iv, &mut ciphertext);
@@ -83,7 +80,7 @@ where
#[inline]
pub fn encrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
where
C: StreamCipher + KeyIvInit,
C: StreamCipher + NewCipher,
{
let mut cipher = C::new(key, iv);
cipher.apply_keystream(data)
@@ -92,7 +89,7 @@ where
#[inline]
pub fn decrypt<C>(key: &CipherKey<C>, iv: &IV<C>, ciphertext: &[u8]) -> Vec<u8>
where
C: StreamCipher + KeyIvInit,
C: StreamCipher + NewCipher,
{
let mut data = ciphertext.to_vec();
decrypt_in_place::<C>(key, iv, &mut data);
@@ -102,7 +99,7 @@ where
#[inline]
pub fn decrypt_in_place<C>(key: &CipherKey<C>, iv: &IV<C>, data: &mut [u8])
where
C: StreamCipher + KeyIvInit,
C: StreamCipher + NewCipher,
{
let mut cipher = C::new(key, iv);
cipher.apply_keystream(data)
@@ -111,12 +108,12 @@ where
#[cfg(test)]
mod tests {
use super::*;
use rand_chacha::rand_core::SeedableRng;
use rand::rngs::OsRng;
#[cfg(test)]
mod aes_ctr128 {
use super::*;
type Aes128Ctr = ctr::Ctr64LE<aes::Aes128>;
use aes::Aes128Ctr;
#[test]
fn zero_iv_is_actually_zero() {
@@ -128,8 +125,7 @@ mod tests {
#[test]
fn decryption_is_reciprocal_to_encryption() {
let dummy_seed = [1u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let mut rng = OsRng;
let arr_input = [42; 200];
let vec_input = vec![123, 200];
@@ -152,8 +148,7 @@ mod tests {
#[test]
fn in_place_variants_work_same_way() {
let dummy_seed = [1u8; 32];
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
let mut rng = OsRng;
let mut data = [42; 200];
let original_data = data;
-1
View File
@@ -9,7 +9,6 @@ edition = "2021"
[dependencies]
cfg-if = "1.0.0"
hex-literal = "0.3.3"
once_cell = "1.7.2"
serde = {version = "1.0", features = ["derive"]}
thiserror = "1.0"
url = "2.2"
+61 -57
View File
@@ -2,11 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt, ops::Deref, str::FromStr};
use std::{collections::HashMap, fmt, str::FromStr};
use crate::{
DefaultNetworkDetails, ValidatorDetails, MAINNET_DEFAULTS, QA_DEFAULTS, SANDBOX_DEFAULTS,
};
use crate::{mainnet, qa, sandbox, ValidatorDetails};
use thiserror::Error;
@@ -24,40 +22,20 @@ pub enum Network {
}
impl Network {
fn details(&self) -> &DefaultNetworkDetails<'_> {
pub fn bech32_prefix(&self) -> String {
match self {
Self::QA => &QA_DEFAULTS,
Self::SANDBOX => &SANDBOX_DEFAULTS,
Self::MAINNET => &MAINNET_DEFAULTS,
Self::QA => String::from(qa::BECH32_PREFIX),
Self::SANDBOX => String::from(sandbox::BECH32_PREFIX),
Self::MAINNET => String::from(mainnet::BECH32_PREFIX),
}
}
pub fn bech32_prefix(&self) -> &str {
self.details().bech32_prefix
}
pub fn denom(&self) -> &str {
self.details().denom
}
pub fn mixnet_contract_address(&self) -> &str {
self.details().mixnet_contract_address
}
pub fn vesting_contract_address(&self) -> &str {
self.details().vesting_contract_address
}
pub fn bandwidth_claim_contract_address(&self) -> &str {
self.details().bandwidth_claim_contract_address
}
pub fn rewarding_validator_address(&self) -> &str {
self.details().rewarding_validator_address
}
pub fn validators(&self) -> impl Iterator<Item = &ValidatorDetails> {
self.details().validators.iter()
pub fn denom(&self) -> String {
match self {
Self::QA => String::from(qa::DENOM),
Self::SANDBOX => String::from(sandbox::DENOM),
Self::MAINNET => String::from(mainnet::DENOM),
}
}
}
@@ -86,7 +64,7 @@ impl fmt::Display for Network {
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct NetworkDetails {
bech32_prefix: String,
denom: String,
@@ -97,21 +75,7 @@ pub struct NetworkDetails {
validators: Vec<ValidatorDetails>,
}
impl From<&DefaultNetworkDetails<'_>> for NetworkDetails {
fn from(details: &DefaultNetworkDetails) -> Self {
NetworkDetails {
bech32_prefix: details.bech32_prefix.into(),
denom: details.denom.into(),
mixnet_contract_address: details.mixnet_contract_address.into(),
vesting_contract_address: details.vesting_contract_address.into(),
bandwidth_claim_contract_address: details.bandwidth_claim_contract_address.into(),
rewarding_validator_address: details.rewarding_validator_address.into(),
validators: details.validators.clone(),
}
}
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SupportedNetworks {
networks: HashMap<Network, NetworkDetails>,
}
@@ -122,13 +86,53 @@ impl SupportedNetworks {
for network in support {
match network {
Network::MAINNET => {
networks.insert(Network::MAINNET, MAINNET_DEFAULTS.deref().into())
}
Network::SANDBOX => {
networks.insert(Network::SANDBOX, SANDBOX_DEFAULTS.deref().into())
}
Network::QA => networks.insert(Network::QA, QA_DEFAULTS.deref().into()),
Network::MAINNET => networks.insert(
Network::MAINNET,
NetworkDetails {
bech32_prefix: String::from(mainnet::BECH32_PREFIX),
denom: String::from(mainnet::DENOM),
mixnet_contract_address: String::from(mainnet::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(mainnet::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(
mainnet::REWARDING_VALIDATOR_ADDRESS,
),
validators: mainnet::validators(),
},
),
Network::SANDBOX => networks.insert(
Network::SANDBOX,
NetworkDetails {
bech32_prefix: String::from(sandbox::BECH32_PREFIX),
denom: String::from(sandbox::DENOM),
mixnet_contract_address: String::from(sandbox::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(sandbox::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(
sandbox::REWARDING_VALIDATOR_ADDRESS,
),
validators: sandbox::validators(),
},
),
Network::QA => networks.insert(
Network::QA,
NetworkDetails {
bech32_prefix: String::from(qa::BECH32_PREFIX),
denom: String::from(qa::DENOM),
mixnet_contract_address: String::from(qa::MIXNET_CONTRACT_ADDRESS),
vesting_contract_address: String::from(qa::VESTING_CONTRACT_ADDRESS),
bandwidth_claim_contract_address: String::from(
qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
),
rewarding_validator_address: String::from(qa::REWARDING_VALIDATOR_ADDRESS),
validators: qa::validators(),
},
),
};
}
SupportedNetworks { networks }
+42 -59
View File
@@ -1,7 +1,5 @@
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use url::Url;
@@ -11,80 +9,65 @@ pub mod mainnet;
pub mod qa;
pub mod sandbox;
// The set of defaults that are decided at compile time. Ideally we want to reduce these to a
// minimum.
// Keep DENOM around mostly for use in contracts. (TODO: consider moving it there, or renaming?)
cfg_if::cfg_if! {
if #[cfg(network = "mainnet")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::MAINNET;
pub const BECH32_PREFIX: &str = mainnet::BECH32_PREFIX;
pub const DENOM: &str = mainnet::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = mainnet::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = mainnet::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = mainnet::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = mainnet::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
mainnet::validators()
}
pub fn default_network() -> all::Network {
all::Network::MAINNET
}
} else if #[cfg(network = "qa")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::QA;
pub const BECH32_PREFIX: &str = qa::BECH32_PREFIX;
pub const DENOM: &str = qa::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = qa::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = qa::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = qa::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = qa::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
qa::validators()
}
pub fn default_network() -> all::Network {
all::Network::QA
}
} else if #[cfg(network = "sandbox")] {
pub const DEFAULT_NETWORK: all::Network = all::Network::SANDBOX;
pub const BECH32_PREFIX: &str = sandbox::BECH32_PREFIX;
pub const DENOM: &str = sandbox::DENOM;
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = sandbox::MIXNET_CONTRACT_ADDRESS;
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = sandbox::VESTING_CONTRACT_ADDRESS;
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
pub const ETH_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_CONTRACT_ADDRESS;
pub const ETH_ERC20_CONTRACT_ADDRESS: [u8; 20] = sandbox::_ETH_ERC20_CONTRACT_ADDRESS;
pub const DEFAULT_REWARDING_VALIDATOR: &str = sandbox::REWARDING_VALIDATOR_ADDRESS;
pub fn default_validators() -> Vec<ValidatorDetails> {
sandbox::validators()
}
pub fn default_network() -> all::Network {
all::Network::SANDBOX
}
}
}
// Since these are lazily constructed, we can afford to switch some of them to stronger types in the
// future. If we do this, and also get rid of the references we could potentially unify with
// `NetworkDetails`.
#[derive(Debug)]
pub struct DefaultNetworkDetails<'a> {
bech32_prefix: &'a str,
denom: &'a str,
mixnet_contract_address: &'a str,
vesting_contract_address: &'a str,
bandwidth_claim_contract_address: &'a str,
rewarding_validator_address: &'a str,
validators: Vec<ValidatorDetails>,
}
static MAINNET_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: mainnet::BECH32_PREFIX,
denom: mainnet::DENOM,
mixnet_contract_address: mainnet::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: mainnet::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: mainnet::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
rewarding_validator_address: mainnet::REWARDING_VALIDATOR_ADDRESS,
validators: mainnet::validators(),
});
static SANDBOX_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> =
Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: sandbox::BECH32_PREFIX,
denom: sandbox::DENOM,
mixnet_contract_address: sandbox::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: sandbox::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: sandbox::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
rewarding_validator_address: sandbox::REWARDING_VALIDATOR_ADDRESS,
validators: sandbox::validators(),
});
static QA_DEFAULTS: Lazy<DefaultNetworkDetails<'static>> = Lazy::new(|| DefaultNetworkDetails {
bech32_prefix: qa::BECH32_PREFIX,
denom: qa::DENOM,
mixnet_contract_address: qa::MIXNET_CONTRACT_ADDRESS,
vesting_contract_address: qa::VESTING_CONTRACT_ADDRESS,
bandwidth_claim_contract_address: qa::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
rewarding_validator_address: qa::REWARDING_VALIDATOR_ADDRESS,
validators: qa::validators(),
});
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ValidatorDetails {
// it is assumed those values are always valid since they're being provided in our defaults file
pub nymd_url: String,
@@ -116,15 +99,15 @@ impl ValidatorDetails {
}
pub fn default_nymd_endpoints() -> Vec<Url> {
DEFAULT_NETWORK
.validators()
default_validators()
.iter()
.map(|validator| validator.nymd_url())
.collect()
}
pub fn default_api_endpoints() -> Vec<Url> {
DEFAULT_NETWORK
.validators()
default_validators()
.iter()
.filter_map(|validator| validator.api_url())
.collect()
}
+2 -2
View File
@@ -7,9 +7,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
rand = {version = "0.7.3", features = ["wasm-bindgen"]}
crypto = { path = "../../crypto", features = ["symmetric", "rand"] }
crypto = { path = "../../crypto" }
nymsphinx-addressing = { path = "../addressing" }
nymsphinx-params = { path = "../params" }
nymsphinx-types = { path = "../types" }
@@ -2,7 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
use crate::AckKey;
use crypto::symmetric::stream_cipher::{self, encrypt, iv_from_slice, random_iv, IvSizeUser};
use crypto::generic_array::typenum::Unsigned;
use crypto::symmetric::stream_cipher::{self, encrypt, iv_from_slice, random_iv, NewCipher};
use nymsphinx_params::{
packet_sizes::PacketSize, AckEncryptionAlgorithm, SerializedFragmentIdentifier, FRAG_ID_LEN,
};
@@ -32,7 +33,7 @@ pub fn recover_identifier(
return None;
}
let iv_size = AckEncryptionAlgorithm::iv_size();
let iv_size = <AckEncryptionAlgorithm as NewCipher>::NonceSize::to_usize();
let iv = iv_from_slice::<AckEncryptionAlgorithm>(&iv_id_ciphertext[..iv_size]);
let id = stream_cipher::decrypt::<AckEncryptionAlgorithm>(
+4 -6
View File
@@ -1,7 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::symmetric::stream_cipher::{generate_key, CipherKey, KeySizeUser};
use crypto::generic_array::{typenum::Unsigned, GenericArray};
use crypto::symmetric::stream_cipher::{generate_key, CipherKey, NewCipher};
use nymsphinx_params::AckEncryptionAlgorithm;
use pemstore::traits::PemStorableKey;
use rand::{CryptoRng, RngCore};
@@ -32,14 +33,11 @@ impl AckKey {
}
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, AckKeyConversionError> {
if bytes.len() != AckEncryptionAlgorithm::key_size() {
if bytes.len() != <AckEncryptionAlgorithm as NewCipher>::KeySize::to_usize() {
return Err(AckKeyConversionError::BytesOfInvalidLengthError);
}
// Ok(AckKey(GenericArray::clone_from_slice(bytes)))
Ok(AckKey(
CipherKey::<AckEncryptionAlgorithm>::clone_from_slice(bytes),
))
Ok(AckKey(GenericArray::clone_from_slice(bytes)))
}
pub fn to_bytes(&self) -> Vec<u8> {
+1 -1
View File
@@ -7,7 +7,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crypto = { path = "../../crypto", features = ["asymmetric"] } # all addresses are expressed in terms on their crypto keys
crypto = { path = "../../crypto" } # all addresses are expressed in terms on their crypto keys
nymsphinx-types = { path = "../types" } # we need to be able to refer to some types defined inside sphinx crate
serde = "1.0" # implementing serialization/deserialization for some types, like `Recipient`
@@ -1,20 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub use crypto::generic_array::typenum::Unsigned;
use crypto::{
crypto_hash,
generic_array::{typenum::Unsigned, GenericArray},
symmetric::stream_cipher::{generate_key, CipherKey, KeySizeUser},
OutputSizeUser,
generic_array::GenericArray,
symmetric::stream_cipher::{generate_key, CipherKey, NewCipher},
Digest,
};
use nymsphinx_params::{ReplySurbEncryptionAlgorithm, ReplySurbKeyDigestAlgorithm};
use rand::{CryptoRng, RngCore};
use std::fmt::{self, Display, Formatter};
pub type EncryptionKeyDigest =
GenericArray<u8, <ReplySurbKeyDigestAlgorithm as OutputSizeUser>::OutputSize>;
GenericArray<u8, <ReplySurbKeyDigestAlgorithm as Digest>::OutputSize>;
pub type SurbEncryptionKeySize = <ReplySurbEncryptionAlgorithm as KeySizeUser>::KeySize;
pub type SurbEncryptionKeySize = <ReplySurbEncryptionAlgorithm as NewCipher>::KeySize;
#[derive(Clone, Debug)]
pub struct SurbEncryptionKey(CipherKey<ReplySurbEncryptionAlgorithm>);
@@ -44,7 +45,7 @@ impl SurbEncryptionKey {
}
pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, SurbEncryptionKeyError> {
if bytes.len() != SurbEncryptionKeySize::USIZE {
if bytes.len() != SurbEncryptionKeySize::to_usize() {
return Err(SurbEncryptionKeyError::BytesOfInvalidLengthError);
}
@@ -1,6 +1,5 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod encryption_key;
pub mod reply_surb;
@@ -1,8 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::encryption_key::{SurbEncryptionKey, SurbEncryptionKeyError, SurbEncryptionKeySize};
use crypto::{generic_array::typenum::Unsigned, Digest};
use crate::encryption_key::{
SurbEncryptionKey, SurbEncryptionKeyError, SurbEncryptionKeySize, Unsigned,
};
use crypto::Digest;
use nymsphinx_addressing::clients::Recipient;
use nymsphinx_addressing::nodes::{NymNodeRoutingAddress, MAX_NODE_ADDRESS_UNPADDED_LEN};
use nymsphinx_params::packet_sizes::PacketSize;
@@ -63,7 +65,7 @@ pub struct ReplySurb {
// Serialize + Deserialize is not really used anymore (it was for a CBOR experiment)
// however, if we decided we needed it again, it's already here
impl Serialize for ReplySurb {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
@@ -137,7 +139,7 @@ impl ReplySurb {
// the SURB itself consists of SURB_header, first hop address and set of payload keys
// (note extra 1 for the gateway)
SurbEncryptionKeySize::USIZE
SurbEncryptionKeySize::to_usize()
+ HEADER_SIZE
+ NODE_ADDRESS_LENGTH
+ (1 + mix_hops as usize) * PAYLOAD_KEY_SIZE
@@ -158,9 +160,9 @@ impl ReplySurb {
pub fn from_bytes(bytes: &[u8]) -> Result<Self, ReplySurbError> {
let encryption_key =
SurbEncryptionKey::try_from_bytes(&bytes[..SurbEncryptionKeySize::USIZE])?;
SurbEncryptionKey::try_from_bytes(&bytes[..SurbEncryptionKeySize::to_usize()])?;
let surb = match SURB::from_bytes(&bytes[SurbEncryptionKeySize::USIZE..]) {
let surb = match SURB::from_bytes(&bytes[SurbEncryptionKeySize::to_usize()..]) {
Err(err) => return Err(ReplySurbError::RecoveryError(err)),
Ok(surb) => surb,
};
+1 -1
View File
@@ -7,5 +7,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crypto = { path = "../../crypto", features = ["hashing", "symmetric"] }
crypto = { path = "../../crypto" }
nymsphinx-types = { path = "../types" }
+1 -4
View File
@@ -1,11 +1,8 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::aes::Aes128;
use crypto::aes::Aes128Ctr;
use crypto::blake3;
use crypto::ctr;
type Aes128Ctr = ctr::Ctr64LE<Aes128>;
// Re-export for ease of use
pub use packet_modes::PacketMode;
+39 -5
View File
@@ -27,6 +27,12 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "autocfg"
version = "1.0.1"
@@ -84,6 +90,21 @@ dependencies = [
"opaque-debug 0.2.3",
]
[[package]]
name = "blake3"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "526c210b4520e416420759af363083471656e819a75e831b8d2c9d5a584f2413"
dependencies = [
"arrayref",
"arrayvec",
"cc",
"cfg-if",
"constant_time_eq",
"crypto-mac 0.11.1",
"digest 0.9.0",
]
[[package]]
name = "block-buffer"
version = "0.7.3"
@@ -209,6 +230,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d6f2aa4d0537bcc1c74df8755072bd31c1ef1a3a1b85a68e8404a8c353b7b8b"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "contracts-common"
version = "0.1.0"
@@ -293,9 +320,17 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
name = "crypto"
version = "0.1.0"
dependencies = [
"aes",
"blake3",
"bs58",
"cipher",
"config",
"digest 0.9.0",
"ed25519-dalek",
"generic-array 0.14.5",
"hkdf",
"hmac",
"log",
"nymsphinx-types",
"pemstore",
"rand",
@@ -415,9 +450,9 @@ dependencies = [
[[package]]
name = "ed25519"
version = "1.4.0"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed12bbf7b5312f8da1c2722bc06d8c6b12c2d86a7fb35a194c7f3e6fc2bbe39"
checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
dependencies = [
"signature",
]
@@ -846,7 +881,6 @@ version = "0.1.0"
dependencies = [
"cfg-if",
"hex-literal",
"once_cell",
"serde",
"thiserror",
"url",
@@ -1642,9 +1676,9 @@ dependencies = [
[[package]]
name = "zeroize_derive"
version = "1.3.2"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17"
checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73"
dependencies = [
"proc-macro2",
"quote",
+1 -1
View File
@@ -35,7 +35,7 @@ cosmwasm-schema = "1.0.0-beta3"
fixed = "1.1"
rand_chacha = "0.2"
rand = "0.7"
crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
crypto = { path = "../../common/crypto" }
[build-dependencies]
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc"] }
+2 -2
View File
@@ -335,7 +335,7 @@ pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Respon
pub mod tests {
use super::*;
use crate::support::tests;
use config::defaults::{DEFAULT_NETWORK, DENOM};
use config::defaults::DENOM;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
use mixnet_contract_common::PagedMixnodeResponse;
@@ -345,7 +345,7 @@ pub mod tests {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(),
rewarding_validator_address: config::defaults::DEFAULT_REWARDING_VALIDATOR.to_string(),
};
let info = mock_info("creator", &[]);
+2 -2
View File
@@ -17,7 +17,7 @@ pub mod test_helpers {
use crate::mixnodes::storage as mixnodes_storage;
use crate::mixnodes::transactions::try_add_mixnode;
use crate::support::tests;
use config::defaults::{DEFAULT_NETWORK, DENOM};
use config::defaults::DENOM;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::testing::mock_env;
use cosmwasm_std::testing::mock_info;
@@ -83,7 +83,7 @@ pub mod test_helpers {
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
rewarding_validator_address: DEFAULT_NETWORK.rewarding_validator_address().to_string(),
rewarding_validator_address: config::defaults::DEFAULT_REWARDING_VALIDATOR.to_string(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
+5 -3
View File
@@ -1,9 +1,11 @@
use network_defaults::{default_api_endpoints, default_nymd_endpoints, DEFAULT_NETWORK};
use network_defaults::{
default_api_endpoints, default_network, default_nymd_endpoints, DEFAULT_MIXNET_CONTRACT_ADDRESS,
};
use validator_client::nymd::QueryNymdClient;
pub(crate) fn new_nymd_client() -> validator_client::Client<QueryNymdClient> {
let network = DEFAULT_NETWORK;
let mixnet_contract = network.mixnet_contract_address().to_string();
let network = default_network();
let mixnet_contract = DEFAULT_MIXNET_CONTRACT_ADDRESS.to_string();
let nymd_url = default_nymd_endpoints()[0].clone();
let api_url = default_api_endpoints()[0].clone();
+2 -2
View File
@@ -2,12 +2,12 @@
// SPDX-License-Identifier: Apache-2.0
use crypto::generic_array::{typenum::Unsigned, GenericArray};
use crypto::symmetric::stream_cipher::{random_iv, IvSizeUser, IV as CryptoIV};
use crypto::symmetric::stream_cipher::{random_iv, NewCipher, IV as CryptoIV};
use nymsphinx::params::GatewayEncryptionAlgorithm;
use rand::{CryptoRng, RngCore};
use thiserror::Error;
type NonceSize = <GatewayEncryptionAlgorithm as IvSizeUser>::IvSize;
type NonceSize = <GatewayEncryptionAlgorithm as NewCipher>::NonceSize;
// I think 'IV' looks better than 'Iv', feel free to change that.
#[allow(clippy::upper_case_acronyms)]
+2 -3
View File
@@ -2,8 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
pub use crypto::generic_array;
use crypto::hmac::HmacOutput;
use crypto::OutputSizeUser;
use crypto::hmac::{hmac::Mac, HmacOutput};
use nymsphinx::params::GatewayIntegrityHmacAlgorithm;
pub use types::*;
@@ -16,4 +15,4 @@ pub type GatewayMac = HmacOutput<GatewayIntegrityHmacAlgorithm>;
// TODO: could using `Mac` trait here for OutputSize backfire?
// Should hmac itself be exposed, imported and used instead?
pub type GatewayMacSize = <GatewayIntegrityHmacAlgorithm as OutputSizeUser>::OutputSize;
pub type GatewayMacSize = <GatewayIntegrityHmacAlgorithm as Mac>::OutputSize;
@@ -7,7 +7,7 @@ use crypto::generic_array::{
GenericArray,
};
use crypto::hmac::{compute_keyed_hmac, recompute_keyed_hmac_and_verify_tag};
use crypto::symmetric::stream_cipher::{self, CipherKey, KeySizeUser, IV};
use crypto::symmetric::stream_cipher::{self, CipherKey, NewCipher, IV};
use nymsphinx::params::{GatewayEncryptionAlgorithm, GatewayIntegrityHmacAlgorithm};
use pemstore::traits::PemStorableKey;
use std::fmt::{self, Display, Formatter};
@@ -17,7 +17,7 @@ pub type SharedKeySize = Sum<EncryptionKeySize, MacKeySize>;
// we're using 16 byte long key in sphinx, so let's use the same one here
type MacKeySize = U16;
type EncryptionKeySize = <GatewayEncryptionAlgorithm as KeySizeUser>::KeySize;
type EncryptionKeySize = <GatewayEncryptionAlgorithm as NewCipher>::KeySize;
/// Shared key used when computing MAC for messages exchanged between client and its gateway.
pub type MacKey = GenericArray<u8, MacKeySize>;
@@ -3,7 +3,6 @@
use bip39::core::str::FromStr;
use bip39::Mnemonic;
use config::defaults::DEFAULT_NETWORK;
use rand::seq::SliceRandom;
use rand::thread_rng;
use url::Url;
@@ -20,7 +19,10 @@ use bandwidth_claim_contract::payment::LinkPaymentData;
use credentials::token::bandwidth::TokenCredential;
use crypto::asymmetric::identity::{PublicKey, Signature, SIGNATURE_LENGTH};
use gateway_client::bandwidth::eth_contract;
use network_defaults::{ETH_EVENT_NAME, ETH_MIN_BLOCK_DEPTH};
use network_defaults::{
DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS, DEFAULT_MIXNET_CONTRACT_ADDRESS, ETH_EVENT_NAME,
ETH_MIN_BLOCK_DEPTH,
};
use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient};
pub(crate) struct ERC20Bridge {
@@ -40,11 +42,11 @@ impl ERC20Bridge {
let mnemonic =
Mnemonic::from_str(&cosmos_mnemonic).expect("Invalid Cosmos mnemonic provided");
let nymd_client = NymdClient::connect_with_mnemonic(
DEFAULT_NETWORK,
config::defaults::default_network(),
nymd_url.as_ref(),
AccountId::from_str(DEFAULT_NETWORK.mixnet_contract_address()).ok(),
AccountId::from_str(DEFAULT_MIXNET_CONTRACT_ADDRESS).ok(),
None,
AccountId::from_str(DEFAULT_NETWORK.bandwidth_claim_contract_address()).ok(),
AccountId::from_str(DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS).ok(),
mnemonic,
None,
)
+1025 -885
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -22,7 +22,6 @@
"@types/react-dom": "^17.0.9",
"bs58": "^4.0.1",
"clsx": "^1.1.1",
"date-fns": "^2.28.0",
"notistack": "^2.0.3",
"qrcode.react": "^1.0.1",
"react": "^17.0.2",
@@ -39,8 +38,8 @@
"@babel/preset-env": "^7.15.0",
"@babel/preset-react": "^7.14.5",
"@svgr/webpack": "^6.1.1",
"@tauri-apps/api": "^1.0.0-rc.1",
"@tauri-apps/cli": "^1.0.0-rc.5",
"@tauri-apps/api": "^1.0.0-beta.6",
"@tauri-apps/cli": "^1.0.0-beta.9",
"@types/bs58": "^4.0.1",
"@types/qrcode.react": "^1.0.2",
"@types/react-router-dom": "^5.1.8",
+24 -19
View File
@@ -8,34 +8,26 @@ repository = ""
default-run = "nym_wallet"
edition = "2021"
build = "src/build.rs"
rust-version = "1.58"
rust-version = "1.56"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "=1.0.0-rc.2", features = [] }
tauri-codegen = "=1.0.0-rc.1"
tauri-macros = "=1.0.0-rc.1"
tauri-build = { version = "1.0.0-beta.4" }
[dependencies]
bip39 = "1.0"
dirs = "4.0"
eyre = "0.6.5"
rand = "0.6.5"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
strum = { version = "0.23", features = ["derive"] }
tauri = { version = "=1.0.0-rc.2", features = [
"clipboard-all",
"shell-open",
"window-maximize",
] }
tendermint-rpc = "0.23.0"
thiserror = "1.0"
tauri = { version = "1.0.0-beta.8", features = ["shell-open"] }
tokio = { version = "1.10", features = ["sync"] }
toml = "0.5.8"
dirs = "4.0"
bip39 = "1.0"
thiserror = "1.0"
tendermint-rpc = "0.23.0"
url = "2.2"
rand = "0.6.5"
eyre = "0.6.5"
aes-gcm = "0.9.4"
argon2 = { version = "0.3.2", features = ["std"] }
@@ -53,13 +45,26 @@ vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vestin
config = { path = "../../common/config" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
# Used for Type conversion, can be extracted but its a lot of work
vesting-contract = { path = "../../contracts/vesting" }
[dev-dependencies]
ts-rs = "6.1.2"
ts-rs = "5.1"
tempfile = "3.3.0"
[dev-dependencies.mixnet-contract-common]
path = "../../common/cosmwasm-smart-contracts/mixnet-contract"
features = ["ts-rs"]
[dev-dependencies.validator-client]
path = "../../common/client-libs/validator-client"
features = ["typescript-types"]
[dev-dependencies.vesting-contract-common]
path = "../../common/cosmwasm-smart-contracts/vesting-contract"
features = ["ts-rs"]
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
-2
View File
@@ -14,7 +14,6 @@ use strum::IntoEnumIterator;
use validator_client::nymd::{CosmosCoin, GasPrice};
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export, export_to = "../src/types/rust/denom.ts"))]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Denom {
Major,
@@ -49,7 +48,6 @@ impl TryFrom<CosmosDenom> for Denom {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/coin.ts"))]
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub struct Coin {
amount: String,
+17 -104
View File
@@ -1,37 +1,34 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::network::Network as WalletNetwork;
use config::defaults::{all::SupportedNetworks, ValidatorDetails};
use crate::network::Network;
use config::defaults::all::SupportedNetworks;
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::{fs, io, path::PathBuf};
use std::path::PathBuf;
use strum::IntoEnumIterator;
use url::Url;
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
mod template;
use template::config_template;
#[derive(Debug, Default, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct Config {
// Base configuration is not part of the configuration file as it's not intended to be changed.
#[serde(skip)]
base: Base,
// Network level configuration
network: Network,
}
#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(deny_unknown_fields)]
struct Base {
pub struct Base {
/// Information on all the networks that the wallet connects to.
networks: SupportedNetworks,
}
impl Default for Base {
fn default() -> Self {
let networks = WalletNetwork::iter()
.map(|network| network.into())
.collect();
let networks = Network::iter().map(|network| network.into()).collect();
Base {
networks: SupportedNetworks::new(networks),
}
@@ -40,8 +37,7 @@ impl Default for Base {
impl NymConfig for Config {
fn template() -> &'static str {
// For now we're not using a template
unimplemented!();
config_template()
}
fn default_root_directory() -> PathBuf {
@@ -62,32 +58,10 @@ impl NymConfig for Config {
fn data_directory(&self) -> PathBuf {
self.root_directory().join("data")
}
fn save_to_file(&self, custom_location: Option<PathBuf>) -> io::Result<()> {
let config_toml = toml::to_string_pretty(&self)
.map_err(|toml_err| io::Error::new(io::ErrorKind::Other, toml_err))?;
// Make sure the whole directory structure actually exists
match custom_location.clone() {
Some(loc) => {
if let Some(parent_dir) = loc.parent() {
fs::create_dir_all(parent_dir)
} else {
Ok(())
}
}
None => fs::create_dir_all(self.config_directory()),
}?;
fs::write(
custom_location.unwrap_or_else(|| self.config_directory().join(Self::config_file_name())),
config_toml,
)
}
}
impl Config {
pub fn get_nymd_validator_url(&self, network: WalletNetwork) -> Url {
pub fn get_nymd_validator_url(&self, network: Network) -> Url {
// TODO make this a random choice
if let Some(Some(validator_details)) = self
.base
@@ -101,7 +75,7 @@ impl Config {
}
}
pub fn get_validator_api_url(&self, network: WalletNetwork) -> Url {
pub fn get_validator_api_url(&self, network: Network) -> Url {
// TODO make this a random choice
if let Some(Some(validator_details)) = self
.base
@@ -115,7 +89,7 @@ impl Config {
}
}
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option<cosmrs::AccountId> {
pub fn get_mixnet_contract_address(&self, network: Network) -> Option<cosmrs::AccountId> {
self
.base
.networks
@@ -125,7 +99,7 @@ impl Config {
.ok()
}
pub fn get_vesting_contract_address(&self, network: WalletNetwork) -> Option<cosmrs::AccountId> {
pub fn get_vesting_contract_address(&self, network: Network) -> Option<cosmrs::AccountId> {
self
.base
.networks
@@ -137,7 +111,7 @@ impl Config {
pub fn get_bandwidth_claim_contract_address(
&self,
network: WalletNetwork,
network: Network,
) -> Option<cosmrs::AccountId> {
self
.base
@@ -148,64 +122,3 @@ impl Config {
.ok()
}
}
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
struct Network {
// User supplied additional validator urls in addition to the hardcoded ones.
// NOTE: these are separate fields, rather than a map, to force the serialization order.
mainnet: Option<Vec<ValidatorDetails>>,
sandbox: Option<Vec<ValidatorDetails>>,
qa: Option<Vec<ValidatorDetails>>,
}
#[cfg(test)]
mod tests {
use super::*;
use config::defaults::all::Network as NetworkConfig;
fn test_config() -> Config {
Config {
base: Base::default(),
network: Network {
mainnet: Some(vec![
// Add the default one, although the hardcoded default isn't intended to be included in
// the config file.
NetworkConfig::MAINNET.validators().next().unwrap().clone(),
// An additional one
ValidatorDetails {
nymd_url: "https://42".to_string(),
api_url: None,
},
]),
sandbox: Some(NetworkConfig::SANDBOX.validators().cloned().collect()),
qa: None,
},
}
}
#[test]
fn serialize_to_toml() {
assert_eq!(
toml::to_string_pretty(&test_config()).unwrap(),
r#"[[network.mainnet]]
nymd_url = 'https://rpc.nyx.nodes.guru/'
api_url = 'https://api.nyx.nodes.guru/'
[[network.mainnet]]
nymd_url = 'https://42'
[[network.sandbox]]
nymd_url = 'https://sandbox-validator.nymtech.net'
api_url = 'https://sandbox-validator.nymtech.net/api'
"#
);
}
#[test]
fn serialize_and_deserialize_to_toml() {
let config = test_config();
let config_str = toml::to_string_pretty(&config).unwrap();
let config_from_toml = toml::from_str(&config_str).unwrap();
assert_eq!(config, config_from_toml);
}
}
@@ -0,0 +1,17 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub(crate) fn config_template() -> &'static str {
r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base tauri-wallet config options #####
[base]
# Validator server to which the API will be getting information about the network.
validator_url = '{{ base.validator_url }}'
"#
}
+30
View File
@@ -86,3 +86,33 @@ fn main() {
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[cfg(test)]
mod test {
ts_rs::export! {
mixnet_contract_common::MixNode => "../src/types/rust/mixnode.ts",
crate::coin::Coin => "../src/types/rust/coin.ts",
crate::network::Network => "../src/types/rust/network.ts",
crate::mixnet::account::Balance => "../src/types/rust/balance.ts",
mixnet_contract_common::Gateway => "../src/types/rust/gateway.ts",
crate::mixnet::send::TauriTxResult => "../src/types/rust/tauritxresult.ts",
crate::mixnet::send::TransactionDetails => "../src/types/rust/transactiondetails.ts",
validator_client::nymd::fee::helpers::Operation => "../src/types/rust/operation.ts",
crate::coin::Denom => "../src/types/rust/denom.ts",
crate::utils::DelegationResult => "../src/types/rust/delegationresult.ts",
crate::mixnet::account::Account => "../src/types/rust/account.ts",
crate::mixnet::account::CreatedAccount => "../src/types/rust/createdaccount.ts",
crate::mixnet::admin::TauriContractStateParams => "../src/types/rust/stateparams.ts",
validator_client::models::CoreNodeStatusResponse => "../src/types/corenodestatusresponse.ts",
validator_client::models::MixnodeStatus => "../src/types/rust/mixnodestatus.ts",
validator_client::models::MixnodeStatusResponse => "../src/types/rust/mixnodestatusresponse.ts",
validator_client::models::RewardEstimationResponse => "../src/types/rust/rewardestimationresponse.ts",
validator_client::models::StakeSaturationResponse => "../src/types/rust/stakesaturaionresponse.ts",
validator_client::models::InclusionProbabilityResponse => "../src/types/rust/inclusionprobabilityresponse.ts",
vesting_contract_common::Period => "../src/types/rust/period.ts",
crate::vesting::PledgeData => "../src/types/rust/pledgedata.ts",
crate::vesting::OriginalVestingResponse => "../src/types/rust/originalvestingresponse.ts",
crate::vesting::VestingAccountInfo => "../src/types/rust/vestingaccountinfo.ts",
crate::vesting::VestingPeriod => "../src/types/rust/vestingperiod.ts",
}
}
-1
View File
@@ -13,7 +13,6 @@ use config::defaults::{mainnet, qa, sandbox};
#[allow(clippy::upper_case_acronyms)]
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/network.ts"))]
#[derive(Copy, Clone, Debug, Deserialize, EnumIter, Eq, Hash, PartialEq, Serialize)]
pub enum Network {
QA,
@@ -13,7 +13,6 @@ use strum::IntoEnumIterator;
use tokio::sync::RwLock;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/account.ts"))]
#[derive(Serialize, Deserialize)]
pub struct Account {
contract_address: String,
@@ -32,7 +31,6 @@ impl Account {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/createdaccount.ts"))]
#[derive(Serialize, Deserialize)]
pub struct CreatedAccount {
account: Account,
@@ -40,7 +38,6 @@ pub struct CreatedAccount {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/balance.ts"))]
#[derive(Serialize, Deserialize)]
pub struct Balance {
coin: Coin,
@@ -132,7 +129,7 @@ async fn _connect_with_mnemonic(
mnemonic: Mnemonic,
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, BackendError> {
let default_network = Network::try_from(config::defaults::DEFAULT_NETWORK)?;
let default_network = Network::try_from(config::defaults::default_network())?;
let mut default_account = None;
for network in Network::iter() {
let client = {
@@ -165,7 +162,6 @@ async fn _connect_with_mnemonic(
w_state.add_client(network, client);
}
default_account.ok_or(BackendError::NetworkNotSupported(
config::defaults::DEFAULT_NETWORK,
))
default_account
.ok_or_else(|| BackendError::NetworkNotSupported(config::defaults::default_network()))
}
@@ -9,7 +9,6 @@ use std::sync::Arc;
use tokio::sync::RwLock;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/stateparams.ts"))]
#[derive(Serialize, Deserialize)]
pub struct TauriContractStateParams {
minimum_mixnode_pledge: String,
@@ -10,7 +10,6 @@ use tokio::sync::RwLock;
use validator_client::nymd::{AccountId, CosmosCoin};
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/tauritxresult.ts"))]
#[derive(Deserialize, Serialize)]
pub struct TauriTxResult {
block_height: u64,
@@ -22,10 +21,6 @@ pub struct TauriTxResult {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../src/types/rust/transactiondetails.ts")
)]
#[derive(Deserialize, Serialize)]
pub struct TransactionDetails {
amount: Coin,
@@ -10,7 +10,6 @@ pub mod delegate;
pub mod queries;
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/pledgedata.ts"))]
#[derive(Serialize, Deserialize, Debug)]
pub struct PledgeData {
pub amount: Coin,
@@ -33,10 +32,6 @@ impl PledgeData {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../src/types/rust/originalvestingresponse.ts")
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct OriginalVestingResponse {
amount: Coin,
@@ -55,10 +50,6 @@ impl From<VestingOriginalVestingResponse> for OriginalVestingResponse {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(
test,
ts(export, export_to = "../src/types/rust/vestingaccountinfo.ts")
)]
#[derive(Serialize, Deserialize, Debug)]
pub struct VestingAccountInfo {
owner_address: String,
@@ -85,7 +76,6 @@ impl From<VestingAccount> for VestingAccountInfo {
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/vestingperiod.ts"))]
#[derive(Serialize, Deserialize, Debug)]
pub struct VestingPeriod {
start_time: u64,
-1
View File
@@ -59,7 +59,6 @@ pub async fn outdated_get_approximate_fee(
}
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/delegationresult.ts"))]
#[derive(Serialize, Deserialize)]
pub struct DelegationResult {
source_address: String,
-6
View File
@@ -49,12 +49,6 @@
"active": false
},
"allowlist": {
"window": {
"maximize": true
},
"clipboard": {
"all": true
},
"shell": {
"open": true
}
@@ -1,18 +0,0 @@
import { ListItem, ListItemText, Select } from '@mui/material'
import React, { useState } from 'react'
type TPool = 'balance' | 'locked'
export const TokenPoolSelector: React.FC<{ onSelect: (pool: TPool) => void }> = ({ onSelect }) => {
const [value, setValue] = useState<TPool>()
return (
<>
<Select label="Token Pool" value={value}>
<ListItem>
<ListItemText primary="Balance" secondary="123 nymt" />
</ListItem>
</Select>
</>
)
}
+9 -23
View File
@@ -8,9 +8,7 @@ import {
getSpendableCoins,
getOriginalVesting,
getCurrentVestingPeriod,
getVestingAccountInfo,
} from '../requests'
import { VestingAccountInfo } from 'src/types/rust/vestingaccountinfo'
type TTokenAllocation = {
[key in 'vesting' | 'vested' | 'locked' | 'spendable']: Coin['amount']
@@ -22,7 +20,6 @@ export type TUseuserBalance = {
tokenAllocation?: TTokenAllocation
originalVesting?: OriginalVestingResponse
currentVestingPeriod?: Period
vestingAccountInfo?: VestingAccountInfo
isLoading: boolean
fetchBalance: () => void
clearBalance: () => void
@@ -36,7 +33,6 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
const [tokenAllocation, setTokenAllocation] = useState<TTokenAllocation>()
const [originalVesting, setOriginalVesting] = useState<OriginalVestingResponse>()
const [currentVestingPeriod, setCurrentVestingPeriod] = useState<Period>()
const [vestingAccountInfo, setVestingAccountInfo] = useState<VestingAccountInfo>()
const [isLoading, setIsLoading] = useState(false)
const fetchBalance = useCallback(async () => {
@@ -58,23 +54,15 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
setIsLoading(true)
if (address) {
try {
const [
originalVestingValue,
vestingCoins,
vestedCoins,
lockedCoins,
spendableCoins,
currentVestingPeriod,
vestingAccountInfo,
] = await Promise.all([
getOriginalVesting(address),
getVestingCoins(address),
getVestedCoins(address),
getLockedCoins(address),
getSpendableCoins(address),
getCurrentVestingPeriod(address),
getVestingAccountInfo(address),
])
const [originalVestingValue, vestingCoins, vestedCoins, lockedCoins, spendableCoins, currentVestingPeriod] =
await Promise.all([
getOriginalVesting(address),
getVestingCoins(address),
getVestedCoins(address),
getLockedCoins(address),
getSpendableCoins(address),
getCurrentVestingPeriod(address),
])
setOriginalVesting(originalVestingValue)
setCurrentVestingPeriod(currentVestingPeriod)
setTokenAllocation({
@@ -83,7 +71,6 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
locked: lockedCoins.amount,
spendable: spendableCoins.amount,
})
setVestingAccountInfo(vestingAccountInfo)
} catch (e) {
clearTokenAllocation()
clearOriginalVesting()
@@ -123,7 +110,6 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
tokenAllocation,
originalVesting,
currentVestingPeriod,
vestingAccountInfo,
fetchBalance,
clearBalance,
clearAll,
+1 -1
View File
@@ -23,7 +23,7 @@ export const BalanceCard = () => {
{!userBalance.error && (
<Typography
data-testid="refresh-success"
sx={{ color: 'nym.background.dark', textTransform: 'uppercase' }}
sx={{ color: 'nym.background.dark' }}
variant="h5"
fontWeight="700"
>
@@ -1,54 +0,0 @@
import React, { useContext } from 'react'
import { Box, Tooltip, Typography } from '@mui/material'
import { format } from 'date-fns'
import { ClientContext } from '../../../context/main'
const calculateMarkerPosition = (arrLength: number, index: number) => (1 / arrLength) * 100 * index
export const VestingTimeline: React.FC<{ percentageComplete: number }> = ({ percentageComplete }) => {
const {
userBalance: { currentVestingPeriod, vestingAccountInfo },
} = useContext(ClientContext)
const nextPeriod =
typeof currentVestingPeriod === 'object' && !!vestingAccountInfo?.periods
? Number(vestingAccountInfo?.periods[currentVestingPeriod.In + 1]?.start_time)
: undefined
return (
<Box display="flex" flexDirection="column" gap={1} position="relative" width="100%">
<svg width="100%" height="12">
<rect y="2" width="100%" height="6" rx="0" fill="#E6E6E6" />
<rect y="2" width={`${percentageComplete}%`} height="6" rx="0" fill="#121726" />
{vestingAccountInfo?.periods.map((period, i, arr) => (
<Marker
position={`${calculateMarkerPosition(arr.length, i)}%`}
color={+percentageComplete.toFixed(2) >= calculateMarkerPosition(arr.length, i) ? '#121726' : '#B9B9B9'}
tooltipText={format(new Date(Number(period.start_time) * 1000), 'HH:mm do MMM yyyy')}
key={i}
/>
))}
<Marker
position="calc(100% - 4px)"
color={percentageComplete === 100 ? '#121726' : '#B9B9B9'}
tooltipText="End of vesting schedule"
/>
</svg>
{nextPeriod && (
<Typography variant="caption" sx={{ color: 'grey.500', position: 'absolute', top: 15, left: 0 }}>
Next vesting period: {format(new Date(nextPeriod * 1000), 'HH:mm do MMM yyyy')}
</Typography>
)}
</Box>
)
}
const Marker: React.FC<{ tooltipText: string; color: string; position: string }> = ({
tooltipText,
color,
position,
}) => (
<Tooltip title={tooltipText}>
<rect x={position} width="4" height="12" rx="1" fill={color} style={{ cursor: 'pointer' }} />
</Tooltip>
)
+11 -8
View File
@@ -20,7 +20,6 @@ import { NymCard, InfoTooltip, Title, Fee } from '../../components'
import { ClientContext } from '../../context/main'
import { withdrawVestedCoins } from '../../requests'
import { Period } from '../../types'
import { VestingTimeline } from './components/vesting-timeline'
export const VestingCard = () => {
const { userBalance } = useContext(ClientContext)
@@ -56,7 +55,7 @@ export const VestingCard = () => {
<VestingSchedule />
<TokenTransfer />
<Box display="flex" justifyContent="space-between" alignItems="center">
{userBalance.tokenAllocation?.spendable !== '0' ? <Fee feeType="Send" /> : <div />}
<Fee feeType="Send" />
<Button
size="large"
variant="contained"
@@ -80,7 +79,7 @@ export const VestingCard = () => {
}
}}
endIcon={isLoading && <CircularProgress size={16} color="inherit" />}
disabled={isLoading || userBalance.tokenAllocation?.spendable === '0'}
disabled={isLoading}
disableElevation
>
Transfer
@@ -104,9 +103,8 @@ const VestingSchedule = () => {
const calculatePercentage = () => {
const { tokenAllocation, originalVesting } = userBalance
if (tokenAllocation?.vesting && tokenAllocation.vested && tokenAllocation.vested !== '0' && originalVesting) {
const percentage = (+tokenAllocation.vested / +originalVesting?.amount.amount) * 100
const rounded = percentage.toFixed(2)
setVestedPercentage(+rounded)
const percentage = Math.round((+tokenAllocation.vested / +originalVesting?.amount.amount) * 100)
setVestedPercentage(percentage)
} else {
setVestedPercentage(0)
}
@@ -137,8 +135,13 @@ const VestingSchedule = () => {
</TableCell>
<TableCell sx={{ borderBottom: 'none' }}>
<Box display="flex" alignItems="center" gap={1}>
<Typography variant="body2">{`${vestedPercentage}%`}</Typography>
<VestingTimeline percentageComplete={vestedPercentage} />
<Typography variant="caption">{`${vestedPercentage}%`}</Typography>
<LinearProgress
sx={{ flexBasis: '99%' }}
variant="determinate"
value={vestedPercentage}
color="inherit"
/>
</Box>
</TableCell>
<TableCell sx={{ borderBottom: 'none' }} align="right">
+4 -20
View File
@@ -1,7 +1,6 @@
import { invoke } from '@tauri-apps/api'
import { VestingAccountInfo } from 'src/types/rust/vestingaccountinfo'
import { majorToMinor, minorToMajor } from '.'
import { Coin, DelegationResult, OriginalVestingResponse, Period } from '../types'
import { Coin, OriginalVestingResponse, Period } from '../types'
export const getLockedCoins = async (address: string): Promise<Coin> => {
const res: Coin = await invoke('locked_coins', { address })
@@ -24,8 +23,8 @@ export const getVestedCoins = async (vestingAccountAddress: string): Promise<Coi
export const getOriginalVesting = async (vestingAccountAddress: string): Promise<OriginalVestingResponse> => {
const res: OriginalVestingResponse = await invoke('original_vesting', { vestingAccountAddress })
const majorValue = await minorToMajor(res.amount.amount)
return { ...res, amount: majorValue }
const majorValue = await minorToMajor(res.amount.amount)
return {...res, amount: majorValue}
}
export const withdrawVestedCoins = async (amount: string) => {
@@ -33,19 +32,4 @@ export const withdrawVestedCoins = async (amount: string) => {
await invoke('withdraw_vested_coins', { amount: { amount: minor.amount, denom: 'Minor' } })
}
export const getCurrentVestingPeriod = async (address: string): Promise<Period> =>
await invoke('get_current_vesting_period', { address })
export const vestingDelegateToMixnode = async ({
identity,
amount,
}: {
identity: string
amount: Coin
}): Promise<DelegationResult> => await invoke('vesting_delegate_to_mixnode', { identity, amount })
export const vestingUnelegateFromMixnode = async (identity: string): Promise<DelegationResult> =>
await invoke('vesting_delegate_to_mixnode', { identity })
export const getVestingAccountInfo = async (address: string): Promise<VestingAccountInfo> =>
await invoke('get_account_info', { address })
export const getCurrentVestingPeriod = async (address: string): Promise<Period> => await invoke('get_current_vesting_period', {address})
+6 -2
View File
@@ -1,3 +1,7 @@
import type { Denom } from "./denom";
import { Denom } from "./denom";
export interface Account { contract_address: string, client_address: string, denom: Denom, }
export interface Account {
contract_address: string;
client_address: string;
denom: Denom;
}
+5 -2
View File
@@ -1,3 +1,6 @@
import type { Coin } from "./coin";
import { Coin } from "./coin";
export interface Balance { coin: Coin, printable_balance: string, }
export interface Balance {
coin: Coin;
printable_balance: string;
}
+5 -2
View File
@@ -1,3 +1,6 @@
import type { Denom } from "./denom";
import { Denom } from "./denom";
export interface Coin { amount: string, denom: Denom, }
export interface Coin {
amount: string;
denom: Denom;
}
@@ -1,2 +1,4 @@
export interface CoreNodeStatusResponse { identity: string, count: number, }
export interface CoreNodeStatusResponse {
identity: string;
count: number;
}
+5 -2
View File
@@ -1,3 +1,6 @@
import type { Account } from "./account";
import { Account } from "./account";
export interface CreatedAccount { account: Account, mnemonic: string, }
export interface CreatedAccount {
account: Account;
mnemonic: string;
}
@@ -1,3 +1,7 @@
import type { Coin } from "./coin";
import { Coin } from "./coin";
export interface DelegationResult { source_address: string, target_address: string, amount: Coin | null, }
export interface DelegationResult {
source_address: string;
target_address: string;
amount: Coin | null;
}
-1
View File
@@ -1,2 +1 @@
export type Denom = "Major" | "Minor";
+9 -2
View File
@@ -1,2 +1,9 @@
export interface Gateway { host: string, mix_port: number, clients_port: number, location: string, sphinx_key: string, identity_key: string, version: string, }
export interface Gateway {
host: string;
mix_port: number;
clients_port: number;
location: string;
sphinx_key: string;
identity_key: string;
version: string;
}
@@ -1,2 +1,4 @@
export interface InclusionProbabilityResponse { in_active: number, in_reserve: number, }
export interface InclusionProbabilityResponse {
in_active: number;
in_reserve: number;
}
+9 -12
View File
@@ -4,20 +4,17 @@ export * from './coin'
export * from './delegationresult'
export * from './denom'
export * from './gateway'
export * from './inclusionprobabilityresponse'
export * from './mixnode'
export * from './mixnodestatus'
export * from './mixnodestatus'
export * from './mixnodestatusresponse'
export * from './mixnodestatusresponse'
export * from './network'
export * from './operation'
export * from './originalvestingresponse'
export * from './rewardedsetnodestatus'
export * from './rewardestimationresponse'
export * from './stakesaturationresponse'
export * from './stateparams'
export * from './tauritxresult'
export * from './transactiondetails'
export * from './vestingaccountinfo'
export * from './vestingperiod'
export * from './mixnodestatus'
export * from './mixnodestatusresponse'
export * from './stakesaturaionresponse'
export * from './rewardestimationresponse'
export * from './mixnodestatus'
export * from './mixnodestatusresponse'
export * from './inclusionprobabilityresponse'
export * from './network'
export * from './originalvestingresponse'

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