Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ca6cea4acf | |||
| 2da6fdc2e8 | |||
| f7574924a8 | |||
| 5d07115706 | |||
| 9e994dfd55 | |||
| 59bc7cb53d | |||
| 655ff9bffb | |||
| a03cb1ef9f | |||
| 60f965ec52 | |||
| 8d26e48a5b | |||
| 94527ab594 | |||
| e312a28aad | |||
| e84a0c4438 | |||
| 6f1a0d987d | |||
| 3caa4c15ca | |||
| 741131f376 | |||
| ae6f161cd6 | |||
| b940c87d64 | |||
| fe6c685ab1 | |||
| c64c36022f | |||
| e52fe65985 | |||
| fea64d4d4f | |||
| 6ff02bc2a1 | |||
| 8b166f12f8 | |||
| ecdbe034fa | |||
| 3e46c8630d | |||
| 93e962524a | |||
| 5b6c1c032c | |||
| 135f1a6e7f | |||
| c61f89144e | |||
| 67fe368e65 | |||
| 522229459b | |||
| e3d8b71ea2 | |||
| 4f98fde362 | |||
| aa75e54419 | |||
| 5190a541a6 | |||
| 3a39fff006 | |||
| 0e302b83ab | |||
| 0d0637fe19 | |||
| 1f0c0090dc | |||
| 4f960330b1 | |||
| a273980aa0 | |||
| 13a55f00d8 | |||
| 4feec780d4 | |||
| 35c044c340 | |||
| ac5539a0fa | |||
| 9c569cbb62 | |||
| 72485cacd3 | |||
| 56d36d2c46 | |||
| 8fb54dd4e7 | |||
| 44d59ff8c2 | |||
| a8caf19f8c | |||
| 53b44fb9c6 | |||
| c0959e853e | |||
| 144df00782 | |||
| be4708cc84 | |||
| d5cddec03c | |||
| 7c26cab4e6 | |||
| f0bcf8c36f | |||
| ac2f0a172e | |||
| 898070bc87 | |||
| cc707660aa | |||
| 31624cf4e4 | |||
| e6a69170a4 | |||
| bc5e19514e | |||
| 5c76b8483e | |||
| a9526c216f | |||
| 903af16a6b | |||
| 0de79b6953 | |||
| fd2fafb52a | |||
| fadb5b4ff9 | |||
| 955583d0f0 | |||
| 3924c53d09 | |||
| c0025ee9c6 | |||
| 7dd0516b63 | |||
| d3cd3c9157 | |||
| 83680473e0 | |||
| 7f9a9f7a0a | |||
| e7ccb38502 | |||
| 1f4c19d396 | |||
| 64842f40d7 | |||
| 2ec18721fc | |||
| 2ef1ac452f | |||
| 6b3700aefe | |||
| e2e06df4e6 | |||
| 835d4f46f6 | |||
| d71ef635e2 | |||
| 6e3773a095 | |||
| 050d370396 | |||
| 29340ed00c | |||
| 2b062b3e5b | |||
| b405adb9e5 | |||
| 5c3c0ac39e | |||
| 1cc06ef349 | |||
| 2bef1603ab | |||
| 11a458a43d | |||
| 1fbf37e0ec | |||
| bc8efda08f | |||
| cecd0b2b0a | |||
| 62fa2ae5e4 | |||
| db2ce8070c | |||
| 70138ff54a | |||
| 30e93c33bb | |||
| ec4955f814 | |||
| e013517348 |
@@ -19,30 +19,27 @@ jobs:
|
||||
override: true
|
||||
components: rustfmt, clippy
|
||||
|
||||
# token credentials (non-coconut) don't work for wasm right now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: build
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown --features=coconut
|
||||
|
||||
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: test
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --manifest-path clients/webassembly/Cargo.toml
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --manifest-path clients/webassembly/Cargo.toml -- --check
|
||||
|
||||
# for some reason this does not seem to work correctly, leave it for later, building is good enough for now
|
||||
# - uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: clippy
|
||||
# args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --manifest-path clients/webassembly/Cargo.toml --target wasm32-unknown-unknown -- -D warnings
|
||||
|
||||
Generated
+622
-184
File diff suppressed because it is too large
Load Diff
+5
-4
@@ -17,7 +17,6 @@ members = [
|
||||
"clients/native/websocket-requests",
|
||||
"clients/socks5",
|
||||
"clients/tauri-client/src-tauri",
|
||||
"clients/webassembly",
|
||||
"common/client-libs/gateway-client",
|
||||
"common/client-libs/mixnet-client",
|
||||
"common/client-libs/validator-client",
|
||||
@@ -26,7 +25,9 @@ members = [
|
||||
"common/credentials",
|
||||
"common/crypto",
|
||||
"common/bandwidth-claim-contract",
|
||||
"common/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/contracts-common",
|
||||
"common/cosmwasm-smart-contracts/mixnet-contract",
|
||||
"common/cosmwasm-smart-contracts/vesting-contract",
|
||||
"common/mixnode-common",
|
||||
"common/network-defaults",
|
||||
"common/nonexhaustive-delayqueue",
|
||||
@@ -52,12 +53,12 @@ members = [
|
||||
"mixnode",
|
||||
"service-providers/network-requester",
|
||||
"validator-api",
|
||||
"validator-api/validator-api-requests",
|
||||
]
|
||||
|
||||
default-members = [
|
||||
"clients/native",
|
||||
"clients/socks5",
|
||||
# "clients/webassembly",
|
||||
"gateway",
|
||||
"service-providers/network-requester",
|
||||
"mixnode",
|
||||
@@ -65,4 +66,4 @@ default-members = [
|
||||
"explorer-api",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "tokenomics-py"]
|
||||
exclude = ["explorer", "contracts", "tokenomics-py", "clients/webassembly"]
|
||||
|
||||
@@ -21,7 +21,8 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr
|
||||
|
||||
### Building
|
||||
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/0.11.0/overview/index/).
|
||||
Platform build instructions are available on [our docs site](https://nymtech.net/docs/stable/run-nym-nodes/build-nym).
|
||||
Wallet build instructions are also available on [our docs site](https://nymtech.net/docs/stable/nym-apps/wallet#for-developers).
|
||||
|
||||
### Developing
|
||||
|
||||
|
||||
@@ -79,9 +79,9 @@ impl KeyManager {
|
||||
))?;
|
||||
|
||||
let gateway_shared_key: SharedKeys =
|
||||
pemstore::load_key(&client_pathfinder.gateway_shared_key().to_owned())?;
|
||||
pemstore::load_key(client_pathfinder.gateway_shared_key())?;
|
||||
|
||||
let ack_key: AckKey = pemstore::load_key(&client_pathfinder.ack_key().to_owned())?;
|
||||
let ack_key: AckKey = pemstore::load_key(client_pathfinder.ack_key())?;
|
||||
|
||||
// TODO: ack key is never stored so it is generated now. But perhaps it should be stored
|
||||
// after all for consistency sake?
|
||||
|
||||
@@ -59,7 +59,7 @@ impl ReplyKeyStorage {
|
||||
) -> Result<(), ReplyKeyStorageError> {
|
||||
let digest = encryption_key.compute_digest();
|
||||
|
||||
let insertion_result = match self.db.insert(digest.to_vec(), encryption_key.to_bytes()) {
|
||||
let insertion_result = match self.db.insert(digest, encryption_key.to_bytes()) {
|
||||
Err(e) => Err(ReplyKeyStorageError::DbWriteError(e)),
|
||||
Ok(existing_key) => {
|
||||
if existing_key.is_some() {
|
||||
@@ -79,7 +79,7 @@ impl ReplyKeyStorage {
|
||||
&self,
|
||||
key_digest: EncryptionKeyDigest,
|
||||
) -> Result<Option<SurbEncryptionKey>, ReplyKeyStorageError> {
|
||||
let removal_result = match self.db.remove(&key_digest.to_vec()) {
|
||||
let removal_result = match self.db.remove(key_digest) {
|
||||
Err(e) => Err(ReplyKeyStorageError::DbReadError(e)),
|
||||
Ok(existing_key) => {
|
||||
Ok(existing_key.map(|existing_key| self.read_encryption_key(existing_key)))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# Nym Desktop Client
|
||||
|
||||
The Nym Desktop Client communicates with the remote, decentralised nodes which make up the Nym system as a whole.
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Nym Desktop Client Code Examples
|
||||
|
||||
This directory contains example code for javascript, python, and rust. Please note that **none of these examples are production-ready**, and are included really just to show how the client interacts with the Nym system.
|
||||
|
||||
> Make sure that you have an instance of the `nym-client` set up and running locally on port 1977 before trying to run any of these examples.
|
||||
|
||||
## Go
|
||||
There are two examples here:
|
||||
* `binarysend`, which (as the name suggests) sends a binary file, and
|
||||
* `textsend` which sends a raw text file.
|
||||
|
||||
Both examples send these files over the Nym mixnet to the address of your running Nym client, logging information in your terminal window.
|
||||
|
||||
## Javascript
|
||||
The example included here starts a websocket server on port 8888, the ui of which can be used to send strings over the Nym mixnet to the address of your running Nym client.
|
||||
|
||||
### Prerequisites
|
||||
* Reasonably up to date `NodeJS` & `npm` (>= ~v12)
|
||||
|
||||
### Running it
|
||||
Run the following commands:
|
||||
|
||||
```
|
||||
# install dependencies
|
||||
npm install
|
||||
# start a webserver on port 8888
|
||||
npm start
|
||||
```
|
||||
|
||||
Then open your browser to `localhost:8888`.
|
||||
|
||||
## Python
|
||||
There are two examples here:
|
||||
* `binarysend`, which (as the name suggests) sends a binary file, and
|
||||
* `textsend` which sends a raw text file.
|
||||
|
||||
Both examples send these files over the Nym mixnet to the address of your running Nym client, logging information in your terminal window.
|
||||
|
||||
Make sure that you have an instance of the `nym-client` set up and running locally on port 1977 before trying to run either of the example scripts.
|
||||
|
||||
## Rust
|
||||
There are two examples here:
|
||||
* `binarysend`, which (as the name suggests) sends a binary file, and
|
||||
* `textsend` which sends a raw text file.
|
||||
|
||||
Both examples send these files over the Nym mixnet to the address of your running Nym client, logging information in your terminal window.
|
||||
|
||||
Make sure that you have an instance of the `nym-client` set up and running locally on port 1977 before trying to run either of the example scripts.
|
||||
Binary file not shown.
@@ -0,0 +1,10 @@
|
||||
### Prerequisites
|
||||
|
||||
* Reasonably up to date Node + `npm`
|
||||
|
||||
### Running it
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start # starts a webserver on port 8888
|
||||
```
|
||||
+6987
-504
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.6.5",
|
||||
"html-webpack-plugin": "^4.2.0",
|
||||
"postcss": "^8.4.5"
|
||||
"html-webpack-plugin": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis efficitur neque. Quisque aliquet vulputate ante, eget vehicula odio feugiat ac. Nulla ut mattis magna. Aenean tincidunt quis nulla eget eleifend. Cras in pretium sem. Nunc lorem metus, blandit sit amet egestas ut, feugiat quis tellus. Aenean tristique, enim a tincidunt condimentum, eros est blandit nunc, id viverra metus erat at nulla. Vivamus at tellus sodales, feugiat odio vel, laoreet neque. Vivamus posuere nulla ac sodales bibendum.
|
||||
|
||||
Vestibulum pulvinar nisi non ultricies egestas. Integer finibus ultrices justo vitae suscipit. Etiam interdum eu justo vel interdum. Morbi sagittis ac nisl quis consequat. Mauris dapibus ut risus ac facilisis. Pellentesque non tortor feugiat, consectetur arcu vel, ullamcorper sapien. Proin sodales purus non orci bibendum, sit amet ultrices justo ullamcorper. Nullam ac risus ac justo ultricies efficitur auctor nec arcu. Etiam sed finibus felis. Suspendisse potenti. Phasellus malesuada velit ac ullamcorper egestas. Sed elementum diam ut est gravida ultricies.
|
||||
|
||||
Pellentesque sed metus massa. Cras imperdiet lacus sit amet dolor aliquam, luctus posuere justo hendrerit. Morbi augue ex, gravida a metus sed, scelerisque euismod lacus. Nam consequat sapien ac pellentesque sagittis. Morbi a ultrices massa, vel aliquet ex. Maecenas ac sem diam. Nunc sed erat et ipsum volutpat auctor. Etiam elit felis, commodo vitae ipsum ac, fermentum lobortis arcu. Aliquam eu tempus enim. Curabitur vulputate imperdiet aliquam. Morbi iaculis rhoncus risus at malesuada. Donec accumsan feugiat ligula ut facilisis. Nunc porttitor sit amet est eget malesuada. Sed sed consectetur augue, non dapibus orci. Mauris aliquam pellentesque quam, sit amet pellentesque velit cursus vitae. Morbi sit amet molestie risus.
|
||||
|
||||
Nam gravida non ligula a egestas. Fusce sodales, purus id rhoncus mattis, purus est vehicula urna, vel finibus augue velit et est. Donec dictum erat eleifend lobortis iaculis. Praesent id venenatis ante. Donec feugiat, ipsum eget porttitor pulvinar, nisl odio posuere lorem, ut placerat elit nulla a ligula. Suspendisse nec nibh tincidunt, sollicitudin mi a, volutpat ligula. In maximus quam lacus, eget semper dolor sagittis sit amet.
|
||||
|
||||
In vitae hendrerit est, quis facilisis dui. In eu ante enim. Nullam hendrerit odio sit amet odio tincidunt eleifend. Aliquam erat volutpat. Curabitur commodo, purus pharetra lobortis rhoncus, tortor massa imperdiet nisl, vel dignissim tortor sem at orci. Aliquam maximus lobortis lacus, eu porttitor purus dapibus ut. Praesent at dapibus felis, efficitur blandit tortor. In hac habitasse platea dictumst. Aenean ultrices, nisl a pretium sagittis, tellus sapien mollis erat, eu consectetur erat mauris sed libero. Duis feugiat dapibus mi, vel ornare velit vehicula mattis. Ut suscipit pharetra leo et sollicitudin.
|
||||
+2
-2
@@ -35,7 +35,7 @@ async fn send_file_with_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
println!("our full address is: {}", recipient);
|
||||
|
||||
let read_data = std::fs::read("examples/dummy_file").unwrap();
|
||||
|
||||
@@ -83,7 +83,7 @@ async fn send_file_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
println!("our full address is: {}", recipient);
|
||||
|
||||
let read_data = std::fs::read("examples/dummy_file").unwrap();
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ async fn send_text_with_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
println!("our full address is: {}", recipient);
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
@@ -76,7 +76,7 @@ async fn send_text_without_reply() {
|
||||
let (mut ws_stream, _) = connect_async(uri).await.unwrap();
|
||||
|
||||
let recipient = get_self_address(&mut ws_stream).await;
|
||||
println!("our full address is: {}", recipient.to_string());
|
||||
println!("our full address is: {}", recipient);
|
||||
|
||||
let send_request = json!({
|
||||
"type" : "send",
|
||||
@@ -180,8 +180,8 @@ export default class ValidatorClient implements INymClient {
|
||||
return this.client.getSybilResistancePercent(this.mixnetContract);
|
||||
}
|
||||
|
||||
public async getEpochRewardPercent(): Promise<number> {
|
||||
return this.client.getEpochRewardPercent(this.mixnetContract);
|
||||
public async getIntervalRewardPercent(): Promise<number> {
|
||||
return this.client.getIntervalRewardPercent(this.mixnetContract);
|
||||
}
|
||||
|
||||
public async getAllNymdMixnodes(): Promise<MixNodeBond[]> {
|
||||
@@ -433,6 +433,14 @@ export default class ValidatorClient implements INymClient {
|
||||
return (this.client as ISigningClient).undelegateFromMixNode(this.mixnetContract, mixIdentity, fee, memo);
|
||||
}
|
||||
|
||||
public async updateMixnodeConfig(
|
||||
mixIdentity: string,
|
||||
fee: StdFee | 'auto' | number,
|
||||
profitPercentage: number,
|
||||
): Promise<ExecuteResult> {
|
||||
return (this.client as ISigningClient).updateMixnodeConfig(this.mixnetContract, mixIdentity, profitPercentage, fee);
|
||||
}
|
||||
|
||||
public async updateContractStateParams(
|
||||
newParams: ContractStateParams,
|
||||
fee?: StdFee | 'auto' | number,
|
||||
@@ -441,4 +449,4 @@ export default class ValidatorClient implements INymClient {
|
||||
this.assertSigning();
|
||||
return (this.client as ISigningClient).updateContractStateParams(this.mixnetContract, newParams, fee, memo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
|
||||
@@ -79,12 +78,6 @@ export default class NymdQuerier implements INymdQuery {
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||
current_rewarding_interval: {},
|
||||
});
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -155,9 +148,9 @@ export default class NymdQuerier implements INymdQuery {
|
||||
});
|
||||
}
|
||||
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.client.queryContractSmart(mixnetContractAddress, {
|
||||
get_epoch_reward_percent: {},
|
||||
get_interval_reward_percent: {},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
import ValidatorApiQuerier, { IValidatorApiQuery } from './validator-api-querier';
|
||||
@@ -63,7 +62,6 @@ export interface INymdQuery {
|
||||
ownsMixNode(mixnetContractAddress: string, address: string): Promise<MixOwnershipResponse>;
|
||||
ownsGateway(mixnetContractAddress: string, address: string): Promise<GatewayOwnershipResponse>;
|
||||
getStateParams(mixnetContractAddress: string): Promise<ContractStateParams>;
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse>;
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
@@ -87,7 +85,7 @@ export interface INymdQuery {
|
||||
getLayerDistribution(mixnetContractAddress: string): Promise<LayerDistribution>;
|
||||
getRewardPool(mixnetContractAddress: string): Promise<string>;
|
||||
getCirculatingSupply(mixnetContractAddress: string): Promise<string>;
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number>;
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number>;
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number>;
|
||||
getRewardingStatus(
|
||||
mixnetContractAddress: string,
|
||||
@@ -138,10 +136,6 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -184,8 +178,8 @@ export default class QueryClient extends CosmWasmClient implements IQueryClient
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
PagedGatewayResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse,
|
||||
RewardingIntervalResponse,
|
||||
RewardingStatus,
|
||||
} from './types';
|
||||
import ValidatorApiQuerier from './validator-api-querier';
|
||||
@@ -178,6 +177,13 @@ export interface ISigningClient extends IQueryClient, ICosmWasmSigning, INymSign
|
||||
memo?: string,
|
||||
): Promise<ExecuteResult>;
|
||||
|
||||
updateMixnodeConfig(
|
||||
mixnetContractAddress: string,
|
||||
mixIdentity: string,
|
||||
profitMarginPercent: number,
|
||||
fee: StdFee | 'auto' | number,
|
||||
): Promise<ExecuteResult>;
|
||||
|
||||
updateContractStateParams(
|
||||
mixnetContractAddress: string,
|
||||
newParams: ContractStateParams,
|
||||
@@ -250,10 +256,6 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
return this.nymdQuerier.getStateParams(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getCurrentRewardingInterval(mixnetContractAddress: string): Promise<RewardingIntervalResponse> {
|
||||
return this.nymdQuerier.getCurrentRewardingInterval(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getAllNetworkDelegationsPaged(
|
||||
mixnetContractAddress: string,
|
||||
limit?: number,
|
||||
@@ -296,8 +298,8 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
return this.nymdQuerier.getCirculatingSupply(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getEpochRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getEpochRewardPercent(mixnetContractAddress);
|
||||
getIntervalRewardPercent(mixnetContractAddress: string): Promise<number> {
|
||||
return this.nymdQuerier.getIntervalRewardPercent(mixnetContractAddress);
|
||||
}
|
||||
|
||||
getSybilResistancePercent(mixnetContractAddress: string): Promise<number> {
|
||||
@@ -448,6 +450,20 @@ export default class SigningClient extends SigningCosmWasmClient implements ISig
|
||||
);
|
||||
}
|
||||
|
||||
updateMixnodeConfig(
|
||||
mixnetContractAddress: string,
|
||||
mixIdentity: string,
|
||||
profitMarginPercent: number,
|
||||
fee: StdFee | 'auto' | number,
|
||||
): Promise<ExecuteResult> {
|
||||
return this.execute(
|
||||
this.clientAddress,
|
||||
mixnetContractAddress,
|
||||
{ update_mixnode_config: { profit_margin_percent: profitMarginPercent, mix_identity: mixIdentity } },
|
||||
fee,
|
||||
);
|
||||
}
|
||||
|
||||
updateContractStateParams(
|
||||
mixnetContractAddress: string,
|
||||
newParams: ContractStateParams,
|
||||
|
||||
@@ -43,12 +43,6 @@ export type ContractStateParams = {
|
||||
mixnode_active_set_size: number;
|
||||
};
|
||||
|
||||
export type RewardingIntervalResponse = {
|
||||
current_rewarding_interval_starting_block: number;
|
||||
current_rewarding_interval_nonce: number;
|
||||
rewarding_in_progress: boolean;
|
||||
};
|
||||
|
||||
export type LayerDistribution = {
|
||||
gateways: number;
|
||||
layer1: number;
|
||||
@@ -135,6 +129,7 @@ export type MixNode = {
|
||||
sphinx_key: string;
|
||||
identity_key: string;
|
||||
version: string;
|
||||
profit_margin_percent: number;
|
||||
};
|
||||
|
||||
export type GatewayBond = {
|
||||
|
||||
@@ -32,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true }
|
||||
crypto = { path = "../../common/crypto" }
|
||||
nymsphinx = { path = "../../common/nymsphinx" }
|
||||
topology = { path = "../../common/topology" }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client" }
|
||||
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
|
||||
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
|
||||
wasm-utils = { path = "../../common/wasm-utils" }
|
||||
|
||||
|
||||
+6569
-12
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@
|
||||
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use gateway_client::GatewayClient;
|
||||
use nymsphinx::acknowledgements::AckKey;
|
||||
use nymsphinx::addressing::clients::Recipient;
|
||||
@@ -111,7 +110,7 @@ impl NymClient {
|
||||
let testnet_mode = self.testnet_mode;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = Some(BandwidthController::new(
|
||||
let bandwidth_controller = Some(gateway_client::bandwidth::BandwidthController::new(
|
||||
vec![self.validator_server.clone()],
|
||||
*self.identity.public_key(),
|
||||
));
|
||||
|
||||
@@ -15,6 +15,8 @@ log = "0.4"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
secp256k1 = "0.20.3"
|
||||
web3 = { version = "0.17.0", default-features = false }
|
||||
|
||||
# internal
|
||||
credentials = { path = "../../credentials" }
|
||||
@@ -36,12 +38,6 @@ features = ["macros", "rt", "net", "sync", "time"]
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
|
||||
version = "0.14"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.secp256k1]
|
||||
version = "0.20.3"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.web3]
|
||||
version = "0.17.0"
|
||||
|
||||
# wasm-only dependencies
|
||||
[target."cfg(target_arch = \"wasm32\")".dependencies.wasm-bindgen]
|
||||
version = "0.2"
|
||||
@@ -70,4 +66,6 @@ features = ["js"]
|
||||
#url = "2.1"
|
||||
|
||||
[features]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface"]
|
||||
coconut = ["gateway-requests/coconut", "coconut-interface"]
|
||||
wasm = ["web3/wasm", "web3/http", "web3/signing"]
|
||||
default = ["web3/default"]
|
||||
@@ -203,7 +203,7 @@ impl BandwidthController {
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0)) == recipt.status {
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Transaction status is 0 (failure): {:?}",
|
||||
|
||||
@@ -9,17 +9,18 @@ rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
mixnet-contract = { path="../../../common/mixnet-contract" }
|
||||
vesting-contract = { path="../../../contracts/vesting" }
|
||||
serde = { version="1", features=["derive"] }
|
||||
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
vesting-contract = { path = "../../../contracts/vesting" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
reqwest = { version="0.11", features=["json"] }
|
||||
reqwest = { version = "0.11", features = ["json"] }
|
||||
thiserror = "1"
|
||||
log = "0.4"
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
|
||||
coconut-interface = { path = "../../coconut-interface" }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
validator-api-requests = { path = "../../../validator-api/validator-api-requests" }
|
||||
|
||||
# required for nymd-client
|
||||
# at some point it might be possible to make it wasm-compatible
|
||||
@@ -27,14 +28,28 @@ network-defaults = { path = "../../network-defaults" }
|
||||
async-trait = { version = "0.1.51", optional = true }
|
||||
bip39 = { version = "1", features = ["rand"], optional = true }
|
||||
config = { path = "../../config", optional = true }
|
||||
#cosmrs = { version = "0.3", features = ["rpc", "bip32", "cosmwasm"], optional = true }
|
||||
cosmrs = { git = "https://github.com/cosmos/cosmos-rust", rev="e5a1872083abb3d88fa62dda966e7f5408deba58", features = ["rpc", "bip32", "cosmwasm"], optional = true }
|
||||
cosmrs = { version = "0.4.1", features = [
|
||||
"rpc",
|
||||
"bip32",
|
||||
"cosmwasm",
|
||||
], optional = true }
|
||||
prost = { version = "0.9", default-features = false, optional = true }
|
||||
flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
itertools = { version = "0.10", optional = true }
|
||||
cosmwasm-std = { version = "1.0.0-beta3", optional = true }
|
||||
ts-rs = {version = "5.1", optional = true}
|
||||
ts-rs = { version = "5.1", optional = true }
|
||||
|
||||
[features]
|
||||
nymd-client = ["async-trait", "bip39", "config", "cosmrs", "prost", "flate2", "sha2", "itertools", "cosmwasm-std"]
|
||||
nymd-client = [
|
||||
"async-trait",
|
||||
"bip39",
|
||||
"config",
|
||||
"cosmrs",
|
||||
"prost",
|
||||
"flate2",
|
||||
"sha2",
|
||||
"itertools",
|
||||
"cosmwasm-std",
|
||||
]
|
||||
typescript-types = ["ts-rs", "validator-api-requests/ts-rs"]
|
||||
|
||||
@@ -6,21 +6,30 @@ use crate::nymd::{
|
||||
error::NymdError, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient,
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use mixnet_contract::ContractStateParams;
|
||||
use mixnet_contract_common::ContractStateParams;
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use mixnet_contract::{
|
||||
Delegation, MixnetContractVersion, MixnodeRewardingStatusResponse, RewardingIntervalResponse,
|
||||
use mixnet_contract_common::{
|
||||
Delegation, Interval, MixnetContractVersion, MixnodeRewardingStatusResponse,
|
||||
};
|
||||
use mixnet_contract_common::{
|
||||
GatewayBond, IdentityKey, IdentityKeyRef, MixNodeBond, RewardedSetNodeStatus,
|
||||
RewardedSetUpdateDetails,
|
||||
};
|
||||
use mixnet_contract::{GatewayBond, MixNodeBond};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, MixnodeStatusResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse,
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[must_use]
|
||||
pub struct Config {
|
||||
api_url: Url,
|
||||
nymd_url: Url,
|
||||
@@ -30,6 +39,7 @@ pub struct Config {
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
rewarded_set_page_limit: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -48,6 +58,7 @@ impl Config {
|
||||
mixnode_page_limit: None,
|
||||
gateway_page_limit: None,
|
||||
mixnode_delegations_page_limit: None,
|
||||
rewarded_set_page_limit: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +76,11 @@ impl Config {
|
||||
self.mixnode_delegations_page_limit = limit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_rewarded_set_page_limit(mut self, limit: Option<u32>) -> Config {
|
||||
self.rewarded_set_page_limit = limit;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -76,6 +92,7 @@ pub struct Client<C> {
|
||||
mixnode_page_limit: Option<u32>,
|
||||
gateway_page_limit: Option<u32>,
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
rewarded_set_page_limit: Option<u32>,
|
||||
|
||||
// ideally they would have been read-only, but unfortunately rust doesn't have such features
|
||||
pub validator_api: validator_api::Client,
|
||||
@@ -104,6 +121,7 @@ impl Client<SigningNymdClient> {
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
rewarded_set_page_limit: None,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -127,14 +145,14 @@ impl Client<QueryNymdClient> {
|
||||
let validator_api_client = validator_api::Client::new(config.api_url.clone());
|
||||
let nymd_client = NymdClient::connect(
|
||||
config.nymd_url.as_str(),
|
||||
config.mixnet_contract_address.clone().unwrap_or_else(|| {
|
||||
Some(config.mixnet_contract_address.clone().unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(network_defaults::DEFAULT_MIXNET_CONTRACT_ADDRESS)
|
||||
.unwrap()
|
||||
}),
|
||||
config.vesting_contract_address.clone().unwrap_or_else(|| {
|
||||
})),
|
||||
Some(config.vesting_contract_address.clone().unwrap_or_else(|| {
|
||||
cosmrs::AccountId::from_str(network_defaults::DEFAULT_VESTING_CONTRACT_ADDRESS)
|
||||
.unwrap()
|
||||
}),
|
||||
})),
|
||||
)?;
|
||||
|
||||
Ok(Client {
|
||||
@@ -144,6 +162,7 @@ impl Client<QueryNymdClient> {
|
||||
mixnode_page_limit: config.mixnode_page_limit,
|
||||
gateway_page_limit: config.gateway_page_limit,
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
rewarded_set_page_limit: config.rewarded_set_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -152,8 +171,8 @@ impl Client<QueryNymdClient> {
|
||||
pub fn change_nymd(&mut self, new_endpoint: Url) -> Result<(), ValidatorClientError> {
|
||||
self.nymd = NymdClient::connect(
|
||||
new_endpoint.as_ref(),
|
||||
self.mixnet_contract_address.clone().unwrap(),
|
||||
self.vesting_contract_address.clone().unwrap(),
|
||||
self.mixnet_contract_address.clone(),
|
||||
self.vesting_contract_address.clone(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -179,6 +198,18 @@ impl<C> Client<C> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_active_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
@@ -197,18 +228,9 @@ impl<C> Client<C> {
|
||||
Ok(self.nymd.get_mixnet_contract_version().await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_rewarding_interval(
|
||||
&self,
|
||||
) -> Result<RewardingIntervalResponse, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_current_rewarding_interval().await?)
|
||||
}
|
||||
|
||||
pub async fn get_rewarding_status(
|
||||
&self,
|
||||
mix_identity: mixnet_contract::IdentityKey,
|
||||
mix_identity: mixnet_contract_common::IdentityKey,
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<MixnodeRewardingStatusResponse, ValidatorClientError>
|
||||
where
|
||||
@@ -227,6 +249,13 @@ impl<C> Client<C> {
|
||||
Ok(self.nymd.get_reward_pool().await?.u128())
|
||||
}
|
||||
|
||||
pub async fn get_current_interval(&self) -> Result<Interval, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_current_interval().await?)
|
||||
}
|
||||
|
||||
pub async fn get_circulating_supply(&self) -> Result<u128, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -241,14 +270,136 @@ impl<C> Client<C> {
|
||||
Ok(self.nymd.get_sybil_resistance_percent().await?)
|
||||
}
|
||||
|
||||
pub async fn get_epoch_reward_percent(&self) -> Result<u8, ValidatorClientError>
|
||||
pub async fn get_active_set_work_factor(&self) -> Result<u8, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_epoch_reward_percent().await?)
|
||||
Ok(self.nymd.get_active_set_work_factor().await?)
|
||||
}
|
||||
|
||||
pub async fn get_interval_reward_percent(&self) -> Result<u8, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.nymd.get_interval_reward_percent().await?)
|
||||
}
|
||||
|
||||
pub async fn get_current_rewarded_set_update_details(
|
||||
&self,
|
||||
) -> Result<RewardedSetUpdateDetails, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self
|
||||
.nymd
|
||||
.query_current_rewarded_set_update_details()
|
||||
.await?)
|
||||
}
|
||||
|
||||
// basically handles paging for us
|
||||
pub async fn get_all_nymd_rewarded_set_mixnode_identities(
|
||||
&self,
|
||||
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let mut identities = Vec::new();
|
||||
let mut start_after = None;
|
||||
let mut height = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_rewarded_set_identities_paged(
|
||||
start_after.take(),
|
||||
self.rewarded_set_page_limit,
|
||||
height,
|
||||
)
|
||||
.await?;
|
||||
identities.append(&mut paged_response.identities);
|
||||
|
||||
if height.is_none() {
|
||||
// keep using the same height (the first query happened at the most recent height)
|
||||
height = Some(paged_response.at_height)
|
||||
}
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(identities)
|
||||
}
|
||||
|
||||
pub async fn get_nymd_rewarded_and_active_sets(
|
||||
&self,
|
||||
) -> Result<Vec<(MixNodeBond, RewardedSetNodeStatus)>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let rewarded_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter_map(|node| {
|
||||
rewarded_set_identities
|
||||
.get(node.identity())
|
||||
.map(|status| (node, *status))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||
pub async fn get_nymd_rewarded_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let rewarded_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|(identity, _status)| identity)
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter(|node| rewarded_set_identities.contains(node.identity()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// If you need both rewarded and the active set, consider using [Self::get_nymd_rewarded_and_active_sets] instead
|
||||
pub async fn get_nymd_active_set(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let all_mixnodes = self.get_all_nymd_mixnodes().await?;
|
||||
let active_set_identities = self
|
||||
.get_all_nymd_rewarded_set_mixnode_identities()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(|(identity, status)| {
|
||||
if status.is_active() {
|
||||
Some(identity)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
Ok(all_mixnodes
|
||||
.into_iter()
|
||||
.filter(|node| active_set_identities.contains(node.identity()))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -297,8 +448,8 @@ impl<C> Client<C> {
|
||||
|
||||
pub async fn get_all_nymd_single_mixnode_delegations(
|
||||
&self,
|
||||
identity: mixnet_contract::IdentityKey,
|
||||
) -> Result<Vec<mixnet_contract::Delegation>, ValidatorClientError>
|
||||
identity: IdentityKey,
|
||||
) -> Result<Vec<Delegation>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
@@ -420,6 +571,12 @@ impl ApiClient {
|
||||
Ok(self.validator_api.get_active_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_rewarded_mixnodes(
|
||||
&self,
|
||||
) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_rewarded_mixnodes().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cached_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnodes().await?)
|
||||
}
|
||||
@@ -428,6 +585,55 @@ impl ApiClient {
|
||||
Ok(self.validator_api.get_gateways().await?)
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_gateway_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_core_status_count(identity, since)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_mixnode_status(identity).await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<RewardEstimationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_reward_estimation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<StakeSaturationResponse, ValidatorClientError> {
|
||||
Ok(self
|
||||
.validator_api
|
||||
.get_mixnode_stake_saturation(identity)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
&self,
|
||||
request_body: &BlindSignRequestBody,
|
||||
|
||||
@@ -9,6 +9,7 @@ pub mod validator_api;
|
||||
|
||||
pub use crate::client::ApiClient;
|
||||
pub use crate::error::ValidatorClientError;
|
||||
pub use validator_api_requests::*;
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub use client::{Client, Config};
|
||||
|
||||
@@ -13,6 +13,7 @@ use cosmrs::proto::cosmos::auth::v1beta1::{
|
||||
};
|
||||
use cosmrs::proto::cosmos::bank::v1beta1::{
|
||||
QueryAllBalancesRequest, QueryAllBalancesResponse, QueryBalanceRequest, QueryBalanceResponse,
|
||||
QueryTotalSupplyRequest, QueryTotalSupplyResponse,
|
||||
};
|
||||
use cosmrs::proto::cosmos::tx::v1beta1::{
|
||||
SimulateRequest, SimulateResponse as ProtoSimulateResponse,
|
||||
@@ -27,6 +28,7 @@ use cosmrs::tendermint::abci::Code as AbciCode;
|
||||
use cosmrs::tendermint::abci::Transaction;
|
||||
use cosmrs::tendermint::{abci, block, chain};
|
||||
use cosmrs::{tx, AccountId, Coin, Denom, Tx};
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use prost::Message;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
@@ -162,6 +164,43 @@ pub trait CosmWasmClient: rpc::Client {
|
||||
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
|
||||
}
|
||||
|
||||
// this is annoyingly and inconsistently returning `Vec<CosmWasmCoin>` rather than
|
||||
// Vec<Coin>, since cosmrs::Coin can't deal with IBC denoms.
|
||||
// Presumably after https://github.com/cosmos/cosmos-rust/issues/173 is resolved,
|
||||
// the code could be adjusted
|
||||
async fn get_total_supply(&self) -> Result<Vec<CosmWasmCoin>, NymdError> {
|
||||
let path = Some("/cosmos.bank.v1beta1.Query/TotalSupply".parse().unwrap());
|
||||
|
||||
let mut supply = Vec::new();
|
||||
let mut pagination = None;
|
||||
|
||||
loop {
|
||||
let req = QueryTotalSupplyRequest { pagination };
|
||||
|
||||
let mut res = self
|
||||
.make_abci_query::<_, QueryTotalSupplyResponse>(path.clone(), req)
|
||||
.await?;
|
||||
|
||||
supply.append(&mut res.supply);
|
||||
if let Some(pagination_info) = res.pagination {
|
||||
pagination = Some(create_pagination(pagination_info.next_key))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
supply
|
||||
.into_iter()
|
||||
.map(|coin| {
|
||||
coin.amount.parse().map(|amount| CosmWasmCoin {
|
||||
denom: coin.denom,
|
||||
amount,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.map_err(|_| NymdError::SerializationError("Coins".to_owned()))
|
||||
}
|
||||
|
||||
async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError> {
|
||||
Ok(self.tx(id, false).await?)
|
||||
}
|
||||
|
||||
@@ -160,10 +160,7 @@ pub trait SigningCosmWasmClient: CosmWasmClient {
|
||||
{
|
||||
let init_msg = cosmwasm::MsgInstantiateContract {
|
||||
sender: sender_address.clone(),
|
||||
admin: options
|
||||
.as_mut()
|
||||
.map(|options| options.admin.take())
|
||||
.flatten(),
|
||||
admin: options.as_mut().and_then(|options| options.admin.take()),
|
||||
code_id,
|
||||
// now this is a weird one. the protobuf files say this field is optional,
|
||||
// but if you omit it, the initialisation will fail CheckTx
|
||||
|
||||
@@ -20,6 +20,7 @@ pub enum Operation {
|
||||
BondMixnodeOnBehalf,
|
||||
UnbondMixnode,
|
||||
UnbondMixnodeOnBehalf,
|
||||
UpdateMixnodeConfig,
|
||||
DelegateToMixnode,
|
||||
DelegateToMixnodeOnBehalf,
|
||||
UndelegateFromMixnode,
|
||||
@@ -40,6 +41,10 @@ pub enum Operation {
|
||||
WithdrawVestedCoins,
|
||||
TrackUndelegation,
|
||||
CreatePeriodicVestingAccount,
|
||||
|
||||
AdvanceCurrentInterval,
|
||||
WriteRewardedSet,
|
||||
ClearRewardedSet,
|
||||
}
|
||||
|
||||
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
|
||||
@@ -57,6 +62,7 @@ impl fmt::Display for Operation {
|
||||
Operation::BondMixnode => f.write_str("BondMixnode"),
|
||||
Operation::BondMixnodeOnBehalf => f.write_str("BondMixnodeOnBehalf"),
|
||||
Operation::UnbondMixnode => f.write_str("UnbondMixnode"),
|
||||
Operation::UpdateMixnodeConfig => f.write_str("UpdateMixnodeConfig"),
|
||||
Operation::UnbondMixnodeOnBehalf => f.write_str("UnbondMixnodeOnBehalf"),
|
||||
Operation::BondGateway => f.write_str("BondGateway"),
|
||||
Operation::BondGatewayOnBehalf => f.write_str("BondGatewayOnBehalf"),
|
||||
@@ -76,6 +82,9 @@ impl fmt::Display for Operation {
|
||||
Operation::WithdrawVestedCoins => f.write_str("WithdrawVestedCoins"),
|
||||
Operation::TrackUndelegation => f.write_str("TrackUndelegation"),
|
||||
Operation::CreatePeriodicVestingAccount => f.write_str("CreatePeriodicVestingAccount"),
|
||||
Operation::AdvanceCurrentInterval => f.write_str("AdvanceCurrentInterval"),
|
||||
Operation::WriteRewardedSet => f.write_str("WriteRewardedSet"),
|
||||
Operation::ClearRewardedSet => f.write_str("ClearRewardedSet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,6 +103,7 @@ impl Operation {
|
||||
Operation::BondMixnodeOnBehalf => 200_000u64.into(),
|
||||
Operation::UnbondMixnode => 175_000u64.into(),
|
||||
Operation::UnbondMixnodeOnBehalf => 175_000u64.into(),
|
||||
Operation::UpdateMixnodeConfig => 175_000u64.into(),
|
||||
Operation::DelegateToMixnode => 175_000u64.into(),
|
||||
Operation::DelegateToMixnodeOnBehalf => 175_000u64.into(),
|
||||
Operation::UndelegateFromMixnode => 175_000u64.into(),
|
||||
@@ -112,6 +122,9 @@ impl Operation {
|
||||
Operation::WithdrawVestedCoins => 175_000u64.into(),
|
||||
Operation::TrackUndelegation => 175_000u64.into(),
|
||||
Operation::CreatePeriodicVestingAccount => 175_000u64.into(),
|
||||
Operation::AdvanceCurrentInterval => 175_000u64.into(),
|
||||
Operation::WriteRewardedSet => 175_000u64.into(),
|
||||
Operation::ClearRewardedSet => 175_000u64.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ use cosmrs::rpc::{Error as TendermintRpcError, HttpClientUrl};
|
||||
use cosmwasm_std::{Coin, Uint128};
|
||||
pub use fee::gas_price::GasPrice;
|
||||
use fee::helpers::Operation;
|
||||
use mixnet_contract::{
|
||||
use mixnet_contract_common::{
|
||||
ContractStateParams, Delegation, ExecuteMsg, Gateway, GatewayBond, GatewayOwnershipResponse,
|
||||
IdentityKey, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse,
|
||||
IdentityKey, Interval, LayerDistribution, MixNode, MixNodeBond, MixOwnershipResponse,
|
||||
MixnetContractVersion, MixnodeRewardingStatusResponse, PagedAllDelegationsResponse,
|
||||
PagedDelegatorDelegationsResponse, PagedGatewayResponse, PagedMixDelegationsResponse,
|
||||
PagedMixnodeResponse, QueryMsg, RewardingIntervalResponse,
|
||||
PagedMixnodeResponse, PagedRewardedSetResponse, QueryMsg, RewardedSetUpdateDetails,
|
||||
};
|
||||
use serde::Serialize;
|
||||
use std::convert::TryInto;
|
||||
@@ -27,9 +27,12 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
pub use crate::nymd::fee::Fee;
|
||||
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
|
||||
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
|
||||
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
|
||||
pub use cosmrs::rpc::Paging;
|
||||
pub use cosmrs::tendermint::block::Height;
|
||||
pub use cosmrs::tendermint::hash;
|
||||
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||
pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::{self, Gas};
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
@@ -57,16 +60,16 @@ pub struct NymdClient<C> {
|
||||
impl NymdClient<QueryNymdClient> {
|
||||
pub fn connect<U>(
|
||||
endpoint: U,
|
||||
mixnet_contract_address: AccountId,
|
||||
vesting_contract_address: AccountId,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
) -> Result<NymdClient<QueryNymdClient>, NymdError>
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
{
|
||||
Ok(NymdClient {
|
||||
client: QueryNymdClient::new(endpoint)?,
|
||||
mixnet_contract_address: Some(mixnet_contract_address),
|
||||
vesting_contract_address: Some(vesting_contract_address),
|
||||
mixnet_contract_address,
|
||||
vesting_contract_address,
|
||||
client_address: None,
|
||||
custom_gas_limits: Default::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
@@ -234,6 +237,17 @@ impl<C> NymdClient<C> {
|
||||
.map(|block| block.block_id.hash)
|
||||
}
|
||||
|
||||
pub async fn get_validators(
|
||||
&self,
|
||||
height: u64,
|
||||
paging: Paging,
|
||||
) -> Result<ValidatorResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
Ok(self.client.validators(height as u32, paging).await?)
|
||||
}
|
||||
|
||||
pub async fn get_balance(
|
||||
&self,
|
||||
address: &AccountId,
|
||||
@@ -255,6 +269,13 @@ impl<C> NymdClient<C> {
|
||||
self.get_balance(address, self.denom()?).await
|
||||
}
|
||||
|
||||
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
self.client.get_total_supply().await
|
||||
}
|
||||
|
||||
pub async fn get_contract_settings(&self) -> Result<ContractStateParams, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -275,35 +296,65 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_current_rewarding_interval(
|
||||
&self,
|
||||
) -> Result<RewardingIntervalResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::CurrentRewardingInterval {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarding_status(
|
||||
&self,
|
||||
mix_identity: mixnet_contract::IdentityKey,
|
||||
rewarding_interval_nonce: u32,
|
||||
mix_identity: mixnet_contract_common::IdentityKey,
|
||||
interval_id: u32,
|
||||
) -> Result<MixnodeRewardingStatusResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetRewardingStatus {
|
||||
mix_identity,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn query_current_rewarded_set_height(&self) -> Result<u64, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetCurrentRewardedSetHeight {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn query_current_rewarded_set_update_details(
|
||||
&self,
|
||||
) -> Result<RewardedSetUpdateDetails, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetRewardedSetUpdateDetails {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_set_identities_paged(
|
||||
&self,
|
||||
start_after: Option<IdentityKey>,
|
||||
page_limit: Option<u32>,
|
||||
height: Option<u64>,
|
||||
) -> Result<PagedRewardedSetResponse, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetRewardedSet {
|
||||
height,
|
||||
start_after,
|
||||
limit: page_limit,
|
||||
};
|
||||
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_layer_distribution(&self) -> Result<LayerDistribution, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -314,6 +365,16 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_current_interval(&self) -> Result<Interval, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetCurrentInterval {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_reward_pool(&self) -> Result<Uint128, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
@@ -344,11 +405,21 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_epoch_reward_percent(&self) -> Result<u8, NymdError>
|
||||
pub async fn get_active_set_work_factor(&self) -> Result<u8, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetEpochRewardPercent {};
|
||||
let request = QueryMsg::GetActiveSetWorkFactor {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_interval_reward_percent(&self) -> Result<u8, NymdError>
|
||||
where
|
||||
C: CosmWasmClient + Sync,
|
||||
{
|
||||
let request = QueryMsg::GetIntervalRewardPercent {};
|
||||
self.client
|
||||
.query_contract_smart(self.mixnet_contract_address()?, &request)
|
||||
.await
|
||||
@@ -773,6 +844,31 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Update the configuration of a mixnode. Right now, only possible for profit margin.
|
||||
pub async fn update_mixnode_config(
|
||||
&self,
|
||||
profit_margin_percent: u8,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = self.operation_fee(Operation::UpdateMixnodeConfig);
|
||||
|
||||
let req = ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"Updating mixnode configuration from rust!",
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Delegates specified amount of stake to particular mixnode.
|
||||
pub async fn delegate_to_mixnode(
|
||||
&self,
|
||||
@@ -1076,41 +1172,38 @@ impl<C> NymdClient<C> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn begin_mixnode_rewarding(
|
||||
&self,
|
||||
rewarding_interval_nonce: u32,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
pub async fn advance_current_interval(&self) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = self.operation_fee(Operation::BeginMixnodeRewarding);
|
||||
let fee = self.operation_fee(Operation::AdvanceCurrentInterval);
|
||||
|
||||
let req = ExecuteMsg::BeginMixnodeRewarding {
|
||||
rewarding_interval_nonce,
|
||||
};
|
||||
let req = ExecuteMsg::AdvanceCurrentInterval {};
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"Beginning mixnode rewarding procedure",
|
||||
"Advancing current interval",
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn finish_mixnode_rewarding(
|
||||
pub async fn write_rewarded_set(
|
||||
&self,
|
||||
rewarding_interval_nonce: u32,
|
||||
rewarded_set: Vec<IdentityKey>,
|
||||
expected_active_set_size: u32,
|
||||
) -> Result<ExecuteResult, NymdError>
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
let fee = self.operation_fee(Operation::FinishMixnodeRewarding);
|
||||
let fee = self.operation_fee(Operation::WriteRewardedSet);
|
||||
|
||||
let req = ExecuteMsg::FinishMixnodeRewarding {
|
||||
rewarding_interval_nonce,
|
||||
let req = ExecuteMsg::WriteRewardedSet {
|
||||
rewarded_set,
|
||||
expected_active_set_size,
|
||||
};
|
||||
self.client
|
||||
.execute(
|
||||
@@ -1118,7 +1211,7 @@ impl<C> NymdClient<C> {
|
||||
self.mixnet_contract_address()?,
|
||||
&req,
|
||||
fee,
|
||||
"Finishing mixnode rewarding procedure",
|
||||
"Writing rewarded set",
|
||||
Vec::new(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::nymd::fee::helpers::Operation;
|
||||
use crate::nymd::{cosmwasm_coin_to_cosmos_coin, NymdClient};
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::Coin;
|
||||
use mixnet_contract::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
|
||||
use mixnet_contract_common::{Gateway, IdentityKey, IdentityKeyRef, MixNode};
|
||||
use vesting_contract::messages::ExecuteMsg as VestingExecuteMsg;
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -133,6 +133,7 @@ impl DirectSecp256k1HdWallet {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct DirectSecp256k1HdWalletBuilder {
|
||||
/// The password to use when deriving a BIP39 seed from a mnemonic.
|
||||
bip39_password: String,
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2021-2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::validator_api::error::ValidatorAPIError;
|
||||
use crate::validator_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use coconut_interface::{BlindSignRequestBody, BlindedSignatureResponse, VerificationKeyResponse};
|
||||
use mixnet_contract::{GatewayBond, MixNodeBond};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixNodeBond};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use url::Url;
|
||||
use validator_api_requests::models::{
|
||||
CoreNodeStatusResponse, InclusionProbabilityResponse, MixnodeStatusResponse,
|
||||
RewardEstimationResponse, StakeSaturationResponse,
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
pub(crate) mod routes;
|
||||
|
||||
type PathSegments<'a> = &'a [&'a str];
|
||||
type Params<'a, K, V> = &'a [(K, V)];
|
||||
|
||||
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
pub struct Client {
|
||||
url: Url,
|
||||
@@ -30,24 +39,33 @@ impl Client {
|
||||
self.url = new_url
|
||||
}
|
||||
|
||||
async fn query_validator_api<T>(&self, path: PathSegments<'_>) -> Result<T, ValidatorAPIError>
|
||||
async fn query_validator_api<T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Result<T, ValidatorAPIError>
|
||||
where
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path);
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self.reqwest_client.get(url).send().await?.json().await?)
|
||||
}
|
||||
|
||||
async fn post_validator_api<B, T>(
|
||||
async fn post_validator_api<B, T, K, V>(
|
||||
&self,
|
||||
path: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
json_body: &B,
|
||||
) -> Result<T, ValidatorAPIError>
|
||||
where
|
||||
B: Serialize + ?Sized,
|
||||
for<'a> T: Deserialize<'a>,
|
||||
K: AsRef<str>,
|
||||
V: AsRef<str>,
|
||||
{
|
||||
let url = create_api_url(&self.url, path);
|
||||
let url = create_api_url(&self.url, path, params);
|
||||
Ok(self
|
||||
.reqwest_client
|
||||
.post(url)
|
||||
@@ -59,18 +77,176 @@ impl Client {
|
||||
}
|
||||
|
||||
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES])
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateways(&self) -> Result<Vec<GatewayBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS])
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::GATEWAYS], NO_PARAMS)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_active_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE])
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::ACTIVE],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_rewarded_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::MIXNODES, routes::REWARDED],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_probs_mixnode_rewarded(
|
||||
&self,
|
||||
mixnode_id: &str,
|
||||
) -> Result<HashMap<String, f32>, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::MIXNODES,
|
||||
routes::REWARDED,
|
||||
routes::INCLUSION_CHANCE,
|
||||
mixnode_id,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_gateway_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::GATEWAY,
|
||||
identity,
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
&[(SINCE_ARG, since.to_string())],
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::GATEWAY,
|
||||
identity,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_core_status_count(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
since: Option<i64>,
|
||||
) -> Result<CoreNodeStatusResponse, ValidatorAPIError> {
|
||||
if let Some(since) = since {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
CORE_STATUS_COUNT,
|
||||
],
|
||||
&[(SINCE_ARG, since.to_string())],
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_status(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<MixnodeStatusResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::STATUS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_reward_estimation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<RewardEstimationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::REWARD_ESTIMATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_stake_saturation(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<StakeSaturationResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::STAKE_SATURATION,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_mixnode_inclusion_probability(
|
||||
&self,
|
||||
identity: IdentityKeyRef<'_>,
|
||||
) -> Result<InclusionProbabilityResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::STATUS_ROUTES,
|
||||
routes::MIXNODE,
|
||||
identity,
|
||||
routes::INCLUSION_CHANCE,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn blind_sign(
|
||||
@@ -79,6 +255,7 @@ impl Client {
|
||||
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
|
||||
self.post_validator_api(
|
||||
&[routes::API_VERSION, routes::COCONUT_BLIND_SIGN],
|
||||
NO_PARAMS,
|
||||
request_body,
|
||||
)
|
||||
.await
|
||||
@@ -87,13 +264,20 @@ impl Client {
|
||||
pub async fn get_coconut_verification_key(
|
||||
&self,
|
||||
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY])
|
||||
.await
|
||||
self.query_validator_api(
|
||||
&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// utility function that should solve the double slash problem in validator API forever.
|
||||
fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
|
||||
fn create_api_url<K: AsRef<str>, V: AsRef<str>>(
|
||||
base: &Url,
|
||||
segments: PathSegments<'_>,
|
||||
params: Params<'_, K, V>,
|
||||
) -> Url {
|
||||
let mut url = base.clone();
|
||||
let mut path_segments = url
|
||||
.path_segments_mut()
|
||||
@@ -108,6 +292,10 @@ fn create_api_url(base: &Url, segments: PathSegments<'_>) -> Url {
|
||||
// and can be dropped
|
||||
drop(path_segments);
|
||||
|
||||
if !params.is_empty() {
|
||||
url.query_pairs_mut().extend_pairs(params);
|
||||
}
|
||||
|
||||
url
|
||||
}
|
||||
|
||||
@@ -122,51 +310,66 @@ mod tests {
|
||||
// works with 1 segment
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo"]).as_str()
|
||||
create_api_url(&base_url, &["foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with 2 segments
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with leading slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo"]).as_str()
|
||||
create_api_url(&base_url, &["/foo"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["/foo", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "/bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "/bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["foo/"]).as_str()
|
||||
create_api_url(&base_url, &["foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo/", "bar"]).as_str()
|
||||
create_api_url(&base_url, &["foo/", "bar"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["foo", "bar/"]).as_str()
|
||||
create_api_url(&base_url, &["foo", "bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// works with both leading and trailing slash
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo",
|
||||
create_api_url(&base_url, &["/foo/"]).as_str()
|
||||
create_api_url(&base_url, &["/foo/"], NO_PARAMS).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar",
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"]).as_str()
|
||||
create_api_url(&base_url, &["/foo/", "/bar/"], NO_PARAMS).as_str()
|
||||
);
|
||||
|
||||
// adds params
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?foomp=baz",
|
||||
create_api_url(&base_url, &["foo", "bar"], &[("foomp", "baz")]).as_str()
|
||||
);
|
||||
assert_eq!(
|
||||
"http://foomp.com/foo/bar?arg1=val1&arg2=val2",
|
||||
create_api_url(
|
||||
&base_url,
|
||||
&["/foo/", "/bar/"],
|
||||
&[("arg1", "val1"), ("arg2", "val2")]
|
||||
)
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,19 @@ pub const MIXNODES: &str = "mixnodes";
|
||||
pub const GATEWAYS: &str = "gateways";
|
||||
|
||||
pub const ACTIVE: &str = "active";
|
||||
pub const REWARDED: &str = "rewarded";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind_sign";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification_key";
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
pub const GATEWAY: &str = "gateway";
|
||||
|
||||
pub const CORE_STATUS_COUNT: &str = "core-status-count";
|
||||
pub const SINCE_ARG: &str = "since";
|
||||
|
||||
pub const STATUS: &str = "status";
|
||||
pub const REWARD_ESTIMATION: &str = "reward-estimation";
|
||||
pub const STAKE_SATURATION: &str = "stake-saturation";
|
||||
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "contracts-common"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0-beta3"
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Event;
|
||||
|
||||
/// Looks up value of particular attribute in the provided event. If it fails to find it,
|
||||
/// the function panics.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `event`: event to search through.
|
||||
/// * `key`: key associated with the particular attribute
|
||||
pub fn must_find_attribute(event: &Event, key: &str) -> String {
|
||||
may_find_attribute(event, key).unwrap()
|
||||
}
|
||||
|
||||
/// Looks up value of particular attribute in the provided event. Returns None if it does not exist.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `event`: event to search through.
|
||||
/// * `key`: key associated with the particular attribute
|
||||
pub fn may_find_attribute(event: &Event, key: &str) -> Option<String> {
|
||||
for attr in &event.attributes {
|
||||
if attr.key == key {
|
||||
return Some(attr.value.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod events;
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "mixnet-contract"
|
||||
name = "mixnet-contract-common"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2018"
|
||||
@@ -14,10 +14,17 @@ serde_repr = "0.1"
|
||||
schemars = "0.8"
|
||||
ts-rs = { version = "5.1", optional = true }
|
||||
thiserror = "1.0"
|
||||
network-defaults = { path = "../network-defaults" }
|
||||
network-defaults = { path = "../../network-defaults" }
|
||||
fixed = { version = "1.1", features = ["serde"] }
|
||||
az = "1.1"
|
||||
log = "0.4.14"
|
||||
time = { version = "0.3.6", features = ["parsing", "formatting"] }
|
||||
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
|
||||
[dev-dependencies]
|
||||
time = { version = "0.3.5", features = ["serde", "macros"] }
|
||||
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@@ -0,0 +1,338 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnode::NodeRewardResult;
|
||||
use crate::{ContractStateParams, Delegation, IdentityKeyRef, Interval, Layer};
|
||||
use cosmwasm_std::{Addr, Coin, Event, Uint128};
|
||||
|
||||
pub use contracts_common::events::*;
|
||||
|
||||
// event types
|
||||
pub const DELEGATION_EVENT_TYPE: &str = "delegation";
|
||||
pub const UNDELEGATION_EVENT_TYPE: &str = "undelegation";
|
||||
pub const GATEWAY_BONDING_EVENT_TYPE: &str = "gateway_bonding";
|
||||
pub const GATEWAY_UNBONDING_EVENT_TYPE: &str = "gateway_unbonding";
|
||||
pub const MIXNODE_BONDING_EVENT_TYPE: &str = "mixnode_bonding";
|
||||
pub const MIXNODE_UNBONDING_EVENT_TYPE: &str = "mixnode_unbonding";
|
||||
pub const SETTINGS_UPDATE_EVENT_TYPE: &str = "settings_update";
|
||||
pub const OPERATOR_REWARDING_EVENT_TYPE: &str = "mix_rewarding";
|
||||
pub const MIX_DELEGATORS_REWARDING_EVENT_TYPE: &str = "mix_delegators_rewarding";
|
||||
pub const CHANGE_REWARDED_SET_EVENT_TYPE: &str = "change_rewarded_set";
|
||||
pub const ADVANCE_INTERVAL_EVENT_TYPE: &str = "advance_interval";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
pub const AMOUNT_KEY: &str = "amount";
|
||||
pub const PROXY_KEY: &str = "proxy";
|
||||
|
||||
// event-specific attributes
|
||||
|
||||
// delegation/undelegation
|
||||
pub const DELEGATOR_KEY: &str = "delegator";
|
||||
pub const DELEGATION_TARGET_KEY: &str = "delegation_target";
|
||||
pub const DELEGATION_HEIGHT_KEY: &str = "delegation_latest_block_height";
|
||||
|
||||
// bonding/unbonding
|
||||
pub const NODE_IDENTITY_KEY: &str = "identity";
|
||||
pub const ASSIGNED_LAYER_KEY: &str = "assigned_layer";
|
||||
|
||||
// settings change
|
||||
pub const OLD_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "old_minimum_mixnode_pledge";
|
||||
pub const OLD_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "old_minimum_gateway_pledge";
|
||||
pub const OLD_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "old_mixnode_rewarded_set_size";
|
||||
pub const OLD_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "old_mixnode_active_set_size";
|
||||
pub const OLD_ACTIVE_SET_WORK_FACTOR_KEY: &str = "old_active_set_work_factor";
|
||||
|
||||
pub const NEW_MINIMUM_MIXNODE_PLEDGE_KEY: &str = "new_minimum_mixnode_pledge";
|
||||
pub const NEW_MINIMUM_GATEWAY_PLEDGE_KEY: &str = "new_minimum_gateway_pledge";
|
||||
pub const NEW_MIXNODE_REWARDED_SET_SIZE_KEY: &str = "new_mixnode_rewarded_set_size";
|
||||
pub const NEW_MIXNODE_ACTIVE_SET_SIZE_KEY: &str = "new_mixnode_active_set_size";
|
||||
|
||||
// rewarding
|
||||
pub const INTERVAL_ID_KEY: &str = "interval_id";
|
||||
pub const TOTAL_MIXNODE_REWARD_KEY: &str = "total_node_reward";
|
||||
pub const OPERATOR_REWARD_KEY: &str = "operator_reward";
|
||||
pub const LAMBDA_KEY: &str = "lambda";
|
||||
pub const SIGMA_KEY: &str = "sigma";
|
||||
pub const DISTRIBUTED_DELEGATION_REWARDS_KEY: &str = "distributed_delegation_rewards";
|
||||
pub const FURTHER_DELEGATIONS_TO_REWARD_KEY: &str = "further_delegations";
|
||||
pub const NO_REWARD_REASON_KEY: &str = "no_reward_reason";
|
||||
pub const BOND_NOT_FOUND_VALUE: &str = "bond_not_found";
|
||||
pub const BOND_TOO_FRESH_VALUE: &str = "bond_too_fresh";
|
||||
pub const ZERO_UPTIME_VALUE: &str = "zero_uptime";
|
||||
|
||||
// rewarded set update
|
||||
pub const ACTIVE_SET_SIZE_KEY: &str = "active_set_size";
|
||||
pub const REWARDED_SET_SIZE_KEY: &str = "rewarded_set_size";
|
||||
pub const NODES_IN_REWARDED_SET_KEY: &str = "nodes_in_rewarded_set";
|
||||
pub const CURRENT_INTERVAL_ID_KEY: &str = "current_interval";
|
||||
|
||||
pub const NEW_CURRENT_INTERVAL_KEY: &str = "new_current_interval";
|
||||
|
||||
pub fn new_delegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
mix_identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
let mut event = Event::new(DELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
}
|
||||
|
||||
pub fn new_undelegation_event(
|
||||
delegator: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
old_delegation: &Delegation,
|
||||
mix_identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
let mut event = Event::new(UNDELEGATION_EVENT_TYPE).add_attribute(DELEGATOR_KEY, delegator);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(AMOUNT_KEY, old_delegation.amount.to_string())
|
||||
.add_attribute(
|
||||
DELEGATION_HEIGHT_KEY,
|
||||
old_delegation.block_height.to_string(),
|
||||
)
|
||||
.add_attribute(DELEGATION_TARGET_KEY, mix_identity)
|
||||
}
|
||||
|
||||
pub fn new_gateway_bonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
let mut event = Event::new(GATEWAY_BONDING_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_gateway_unbonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
let mut event = Event::new(GATEWAY_UNBONDING_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_bonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef,
|
||||
assigned_layer: Layer,
|
||||
) -> Event {
|
||||
let mut event = Event::new(MIXNODE_BONDING_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event
|
||||
.add_attribute(ASSIGNED_LAYER_KEY, assigned_layer)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_mixnode_unbonding_event(
|
||||
owner: &Addr,
|
||||
proxy: &Option<Addr>,
|
||||
amount: &Coin,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
let mut event = Event::new(MIXNODE_UNBONDING_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner)
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity);
|
||||
|
||||
if let Some(proxy) = proxy {
|
||||
event = event.add_attribute(PROXY_KEY, proxy)
|
||||
}
|
||||
|
||||
// coin implements Display trait and we use that implementation here
|
||||
event.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
}
|
||||
|
||||
pub fn new_settings_update_event(
|
||||
old_params: &ContractStateParams,
|
||||
new_params: &ContractStateParams,
|
||||
) -> Event {
|
||||
let mut event = Event::new(SETTINGS_UPDATE_EVENT_TYPE);
|
||||
|
||||
if old_params.minimum_mixnode_pledge != new_params.minimum_mixnode_pledge {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
old_params.minimum_mixnode_pledge,
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_MIXNODE_PLEDGE_KEY,
|
||||
new_params.minimum_mixnode_pledge,
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.minimum_gateway_pledge != new_params.minimum_gateway_pledge {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
old_params.minimum_gateway_pledge,
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MINIMUM_GATEWAY_PLEDGE_KEY,
|
||||
new_params.minimum_gateway_pledge,
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.mixnode_rewarded_set_size != new_params.mixnode_rewarded_set_size {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||
old_params.mixnode_rewarded_set_size.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MIXNODE_REWARDED_SET_SIZE_KEY,
|
||||
new_params.mixnode_rewarded_set_size.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
if old_params.mixnode_active_set_size != new_params.mixnode_active_set_size {
|
||||
event = event
|
||||
.add_attribute(
|
||||
OLD_MIXNODE_ACTIVE_SET_SIZE_KEY,
|
||||
old_params.mixnode_active_set_size.to_string(),
|
||||
)
|
||||
.add_attribute(
|
||||
NEW_MIXNODE_ACTIVE_SET_SIZE_KEY,
|
||||
new_params.mixnode_active_set_size.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
|
||||
pub fn new_not_found_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_NOT_FOUND_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_too_fresh_bond_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(NO_REWARD_REASON_KEY, BOND_TOO_FRESH_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_zero_uptime_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(NO_REWARD_REASON_KEY, ZERO_UPTIME_VALUE)
|
||||
}
|
||||
|
||||
pub fn new_mix_operator_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef,
|
||||
node_reward_result: NodeRewardResult,
|
||||
operator_reward: Uint128,
|
||||
delegation_rewards_distributed: Uint128,
|
||||
further_delegations: bool,
|
||||
) -> Event {
|
||||
Event::new(OPERATOR_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(
|
||||
TOTAL_MIXNODE_REWARD_KEY,
|
||||
node_reward_result.reward().to_string(),
|
||||
)
|
||||
.add_attribute(LAMBDA_KEY, node_reward_result.lambda().to_string())
|
||||
.add_attribute(SIGMA_KEY, node_reward_result.sigma().to_string())
|
||||
.add_attribute(OPERATOR_REWARD_KEY, operator_reward)
|
||||
.add_attribute(
|
||||
DISTRIBUTED_DELEGATION_REWARDS_KEY,
|
||||
delegation_rewards_distributed,
|
||||
)
|
||||
.add_attribute(
|
||||
FURTHER_DELEGATIONS_TO_REWARD_KEY,
|
||||
further_delegations.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new_mix_delegators_rewarding_event(
|
||||
interval_id: u32,
|
||||
identity: IdentityKeyRef,
|
||||
delegation_rewards_distributed: Uint128,
|
||||
further_delegations: bool,
|
||||
) -> Event {
|
||||
Event::new(MIX_DELEGATORS_REWARDING_EVENT_TYPE)
|
||||
.add_attribute(INTERVAL_ID_KEY, interval_id.to_string())
|
||||
.add_attribute(NODE_IDENTITY_KEY, identity)
|
||||
.add_attribute(
|
||||
DISTRIBUTED_DELEGATION_REWARDS_KEY,
|
||||
delegation_rewards_distributed,
|
||||
)
|
||||
.add_attribute(
|
||||
FURTHER_DELEGATIONS_TO_REWARD_KEY,
|
||||
further_delegations.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
// note that when this event is emitted, we'll know the current block height
|
||||
pub fn new_change_rewarded_set_event(
|
||||
active_set_size: u32,
|
||||
rewarded_set_size: u32,
|
||||
nodes_in_rewarded_set: u32,
|
||||
current_interval_id: u32,
|
||||
) -> Event {
|
||||
Event::new(CHANGE_REWARDED_SET_EVENT_TYPE)
|
||||
.add_attribute(ACTIVE_SET_SIZE_KEY, active_set_size.to_string())
|
||||
.add_attribute(REWARDED_SET_SIZE_KEY, rewarded_set_size.to_string())
|
||||
.add_attribute(NODES_IN_REWARDED_SET_KEY, nodes_in_rewarded_set.to_string())
|
||||
.add_attribute(CURRENT_INTERVAL_ID_KEY, current_interval_id.to_string())
|
||||
}
|
||||
|
||||
pub fn new_advance_interval_event(interval: Interval) -> Event {
|
||||
Event::new(ADVANCE_INTERVAL_EVENT_TYPE)
|
||||
.add_attribute(NEW_CURRENT_INTERVAL_KEY, interval.to_string())
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
// internally, since version 0.3.6, time uses deserialize_any for deserialization, which can't be handled
|
||||
// by serde wasm. We could just downgrade to 0.3.5 and call it a day, but then it would break
|
||||
// when we decided to upgrade it at some point in the future. And then it would have been more problematic
|
||||
// to fix it, since the data would have already been stored inside the contract.
|
||||
// Hence, an explicit workaround to use string representation of Rfc3339-formatted datetime.
|
||||
pub(crate) mod string_rfc3339_offset_date_time {
|
||||
use serde::de::Visitor;
|
||||
use serde::ser::Error;
|
||||
use serde::{Deserializer, Serialize, Serializer};
|
||||
use std::fmt::Formatter;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
struct Rfc3339OffsetDateTimeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for Rfc3339OffsetDateTimeVisitor {
|
||||
type Value = OffsetDateTime;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("an rfc3339 `OffsetDateTime`")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
OffsetDateTime::parse(value, &Rfc3339).map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(Rfc3339OffsetDateTimeVisitor)
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
datetime
|
||||
.format(&Rfc3339)
|
||||
.map_err(S::Error::custom)?
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of rewarding interval.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
|
||||
pub struct Interval {
|
||||
id: u32,
|
||||
#[serde(with = "string_rfc3339_offset_date_time")]
|
||||
start: OffsetDateTime,
|
||||
length: Duration,
|
||||
}
|
||||
|
||||
impl Interval {
|
||||
/// Creates new interval instance.
|
||||
pub const fn new(id: u32, start: OffsetDateTime, length: Duration) -> Self {
|
||||
Interval { id, start, length }
|
||||
}
|
||||
|
||||
/// Returns the next interval.
|
||||
#[must_use]
|
||||
pub fn next_interval(&self) -> Self {
|
||||
Interval {
|
||||
id: self.id + 1,
|
||||
start: self.end(),
|
||||
length: self.length,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the last interval.
|
||||
pub fn previous_interval(&self) -> Option<Self> {
|
||||
if self.id > 0 {
|
||||
Some(Interval {
|
||||
id: self.id - 1,
|
||||
start: self.start - self.length,
|
||||
length: self.length,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the provided datetime is contained within the interval
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `datetime`: specified datetime
|
||||
pub fn contains(&self, datetime: OffsetDateTime) -> bool {
|
||||
self.start <= datetime && datetime <= self.end()
|
||||
}
|
||||
|
||||
/// Determines whether the provided unix timestamp is contained within the interval
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `timestamp`: specified timestamp
|
||||
pub fn contains_timestamp(&self, timestamp: i64) -> bool {
|
||||
self.start_unix_timestamp() <= timestamp && timestamp <= self.end_unix_timestamp()
|
||||
}
|
||||
|
||||
/// Returns new instance of [Interval] such that the provided datetime would be within
|
||||
/// its duration.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn current(&self, now: OffsetDateTime) -> Option<Self> {
|
||||
let mut candidate = *self;
|
||||
|
||||
if now > self.start {
|
||||
loop {
|
||||
if candidate.contains(now) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.next_interval();
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if candidate.contains(now) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.previous_interval()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns new instance of [Interval] such that the provided unix timestamp would be within
|
||||
/// its duration.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now_unix`: current unix time
|
||||
pub fn current_with_timestamp(&self, now_unix: i64) -> Option<Self> {
|
||||
let mut candidate = *self;
|
||||
|
||||
if now_unix > self.start_unix_timestamp() {
|
||||
loop {
|
||||
if candidate.contains_timestamp(now_unix) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.next_interval();
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if candidate.contains_timestamp(now_unix) {
|
||||
return Some(candidate);
|
||||
}
|
||||
candidate = candidate.previous_interval()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether this interval has already finished
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn has_elapsed(&self, now: OffsetDateTime) -> bool {
|
||||
self.end() < now
|
||||
}
|
||||
|
||||
/// Returns id of this interval
|
||||
pub const fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Determines amount of time left until this interval finishes.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `now`: current datetime
|
||||
pub fn until_end(&self, now: OffsetDateTime) -> Option<Duration> {
|
||||
let remaining = self.end() - now;
|
||||
if remaining.is_negative() {
|
||||
None
|
||||
} else {
|
||||
remaining.try_into().ok()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the starting datetime of this interval.
|
||||
pub const fn start(&self) -> OffsetDateTime {
|
||||
self.start
|
||||
}
|
||||
|
||||
/// Returns the length of this interval.
|
||||
pub const fn length(&self) -> Duration {
|
||||
self.length
|
||||
}
|
||||
|
||||
/// Returns the ending datetime of this interval.
|
||||
pub fn end(&self) -> OffsetDateTime {
|
||||
self.start + self.length
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the start of this interval.
|
||||
pub const fn start_unix_timestamp(&self) -> i64 {
|
||||
self.start().unix_timestamp()
|
||||
}
|
||||
|
||||
/// Returns the unix timestamp of the end of this interval.
|
||||
pub fn end_unix_timestamp(&self) -> i64 {
|
||||
self.end().unix_timestamp()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Interval {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
let length = self.length().as_secs();
|
||||
let full_hours = length / 3600;
|
||||
let rem = length % 3600;
|
||||
write!(
|
||||
f,
|
||||
"Interval {}: {} - {} ({}h {}s)",
|
||||
self.id,
|
||||
self.start(),
|
||||
self.end(),
|
||||
full_hours,
|
||||
rem
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn previous_interval() {
|
||||
let interval = Interval {
|
||||
id: 1,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
let expected = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-22 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
assert_eq!(expected, interval.previous_interval().unwrap());
|
||||
|
||||
let genesis_interval = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
assert!(genesis_interval.previous_interval().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_interval() {
|
||||
let interval = Interval {
|
||||
id: 0,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
let expected = Interval {
|
||||
id: 1,
|
||||
start: time::macros::datetime!(2021-08-24 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
|
||||
assert_eq!(expected, interval.next_interval())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checking_for_datetime_inclusion() {
|
||||
let interval = Interval {
|
||||
id: 100,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
|
||||
// it must contain its own boundaries
|
||||
assert!(interval.contains(interval.start));
|
||||
assert!(interval.contains(interval.end()));
|
||||
|
||||
let in_the_midle = interval.start + Duration::from_secs(interval.length.as_secs() / 2);
|
||||
assert!(interval.contains(in_the_midle));
|
||||
|
||||
assert!(!interval.contains(interval.next_interval().end()));
|
||||
assert!(!interval.contains(interval.previous_interval().unwrap().start()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn determining_current_interval() {
|
||||
let first_interval = Interval {
|
||||
id: 100,
|
||||
start: time::macros::datetime!(2021-08-23 12:00 UTC),
|
||||
length: Duration::from_secs(24 * 60 * 60),
|
||||
};
|
||||
|
||||
// interval just before
|
||||
let fake_now = first_interval.start - Duration::from_secs(123);
|
||||
assert_eq!(
|
||||
first_interval.previous_interval(),
|
||||
first_interval.current(fake_now)
|
||||
);
|
||||
|
||||
// this interval (start boundary)
|
||||
assert_eq!(
|
||||
first_interval,
|
||||
first_interval.current(first_interval.start).unwrap()
|
||||
);
|
||||
|
||||
// this interval (in the middle)
|
||||
let fake_now = first_interval.start + Duration::from_secs(123);
|
||||
assert_eq!(first_interval, first_interval.current(fake_now).unwrap());
|
||||
|
||||
// this interval (end boundary)
|
||||
assert_eq!(
|
||||
first_interval,
|
||||
first_interval.current(first_interval.end()).unwrap()
|
||||
);
|
||||
|
||||
// next interval
|
||||
let fake_now = first_interval.end() + Duration::from_secs(123);
|
||||
assert_eq!(
|
||||
first_interval.next_interval(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
);
|
||||
|
||||
// few intervals in the past
|
||||
let fake_now = first_interval.start()
|
||||
- first_interval.length
|
||||
- first_interval.length
|
||||
- first_interval.length;
|
||||
assert_eq!(
|
||||
first_interval
|
||||
.previous_interval()
|
||||
.unwrap()
|
||||
.previous_interval()
|
||||
.unwrap()
|
||||
.previous_interval()
|
||||
.unwrap(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
);
|
||||
|
||||
// few intervals in the future
|
||||
let fake_now = first_interval.end()
|
||||
+ first_interval.length
|
||||
+ first_interval.length
|
||||
+ first_interval.length;
|
||||
assert_eq!(
|
||||
first_interval
|
||||
.next_interval()
|
||||
.next_interval()
|
||||
.next_interval(),
|
||||
first_interval.current(fake_now).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -3,7 +3,9 @@
|
||||
|
||||
mod delegation;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
mod gateway;
|
||||
mod interval;
|
||||
pub mod mixnode;
|
||||
mod msg;
|
||||
mod types;
|
||||
@@ -16,6 +18,9 @@ pub use delegation::{
|
||||
PagedMixDelegationsResponse,
|
||||
};
|
||||
pub use gateway::{Gateway, GatewayBond, GatewayOwnershipResponse, PagedGatewayResponse};
|
||||
pub use mixnode::{Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
|
||||
pub use interval::Interval;
|
||||
pub use mixnode::{
|
||||
Layer, MixNode, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse, RewardedSetNodeStatus,
|
||||
};
|
||||
pub use msg::*;
|
||||
pub use types::*;
|
||||
+32
-4
@@ -5,7 +5,7 @@ use crate::{IdentityKey, SphinxKey};
|
||||
use az::CheckedCast;
|
||||
use cosmwasm_std::{coin, Addr, Coin, Uint128};
|
||||
use log::error;
|
||||
use network_defaults::DEFAULT_OPERATOR_EPOCH_COST;
|
||||
use network_defaults::DEFAULT_OPERATOR_INTERVAL_COST;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
@@ -18,6 +18,19 @@ fixed::const_fixed_from_int! {
|
||||
const ONE: U128 = 1;
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub enum RewardedSetNodeStatus {
|
||||
Active,
|
||||
Standby,
|
||||
}
|
||||
|
||||
impl RewardedSetNodeStatus {
|
||||
pub fn is_active(&self) -> bool {
|
||||
matches!(self, RewardedSetNodeStatus::Active)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize, JsonSchema)]
|
||||
pub struct MixNode {
|
||||
@@ -53,6 +66,16 @@ pub enum Layer {
|
||||
Three = 3,
|
||||
}
|
||||
|
||||
impl From<Layer> for String {
|
||||
fn from(layer: Layer) -> Self {
|
||||
if layer == Layer::Gateway {
|
||||
"gateway".to_string()
|
||||
} else {
|
||||
(layer as u8).to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, JsonSchema, PartialEq, Serialize, Deserialize, Copy)]
|
||||
pub struct NodeRewardParams {
|
||||
period_reward_pool: Uint128,
|
||||
@@ -123,7 +146,7 @@ impl NodeRewardParams {
|
||||
}
|
||||
|
||||
pub fn operator_cost(&self) -> U128 {
|
||||
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_EPOCH_COST as u128)
|
||||
U128::from_num(self.uptime.u128() / 100u128 * DEFAULT_OPERATOR_INTERVAL_COST as u128)
|
||||
}
|
||||
|
||||
pub fn set_reward_blockstamp(&mut self, blockstamp: u64) {
|
||||
@@ -243,7 +266,7 @@ impl DelegatorRewardParams {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct NodeRewardResult {
|
||||
reward: U128,
|
||||
lambda: U128,
|
||||
@@ -315,7 +338,7 @@ impl MixNodeBond {
|
||||
&self.mix_node
|
||||
}
|
||||
|
||||
pub fn total_stake(&self) -> Option<u128> {
|
||||
pub fn total_bond(&self) -> Option<u128> {
|
||||
if self.pledge_amount.denom != self.total_delegation.denom {
|
||||
None
|
||||
} else {
|
||||
@@ -327,6 +350,11 @@ impl MixNodeBond {
|
||||
self.total_delegation.clone()
|
||||
}
|
||||
|
||||
pub fn stake_saturation(&self, circulating_supply: u128, rewarded_set_size: u32) -> U128 {
|
||||
self.total_bond_to_circulating_supply(circulating_supply)
|
||||
* U128::from_num(rewarded_set_size)
|
||||
}
|
||||
|
||||
pub fn pledge_to_circulating_supply(&self, circulating_supply: u128) -> U128 {
|
||||
U128::from_num(self.pledge_amount().amount.u128()) / U128::from_num(circulating_supply)
|
||||
}
|
||||
+27
-17
@@ -20,6 +20,9 @@ pub enum ExecuteMsg {
|
||||
owner_signature: String,
|
||||
},
|
||||
UnbondMixnode {},
|
||||
UpdateMixnodeConfig {
|
||||
profit_margin_percent: u8,
|
||||
},
|
||||
BondGateway {
|
||||
gateway: Gateway,
|
||||
owner_signature: String,
|
||||
@@ -35,28 +38,18 @@ pub enum ExecuteMsg {
|
||||
mix_identity: IdentityKey,
|
||||
},
|
||||
|
||||
BeginMixnodeRewarding {
|
||||
// nonce of the current rewarding interval
|
||||
rewarding_interval_nonce: u32,
|
||||
},
|
||||
|
||||
FinishMixnodeRewarding {
|
||||
// nonce of the current rewarding interval
|
||||
rewarding_interval_nonce: u32,
|
||||
},
|
||||
|
||||
RewardMixnode {
|
||||
identity: IdentityKey,
|
||||
// percentage value in range 0-100
|
||||
params: NodeRewardParams,
|
||||
|
||||
// nonce of the current rewarding interval
|
||||
rewarding_interval_nonce: u32,
|
||||
// id of the current rewarding interval
|
||||
interval_id: u32,
|
||||
},
|
||||
RewardNextMixDelegators {
|
||||
mix_identity: IdentityKey,
|
||||
// nonce of the current rewarding interval
|
||||
rewarding_interval_nonce: u32,
|
||||
// id of the current rewarding interval
|
||||
interval_id: u32,
|
||||
},
|
||||
DelegateToMixnodeOnBehalf {
|
||||
mix_identity: IdentityKey,
|
||||
@@ -82,6 +75,11 @@ pub enum ExecuteMsg {
|
||||
UnbondGatewayOnBehalf {
|
||||
owner: String,
|
||||
},
|
||||
WriteRewardedSet {
|
||||
rewarded_set: Vec<IdentityKey>,
|
||||
expected_active_set_size: u32,
|
||||
},
|
||||
AdvanceCurrentInterval {},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
@@ -103,7 +101,6 @@ pub enum QueryMsg {
|
||||
address: String,
|
||||
},
|
||||
StateParams {},
|
||||
CurrentRewardingInterval {},
|
||||
// gets all [paged] delegations in the entire network
|
||||
// TODO: do we even want that?
|
||||
GetAllNetworkDelegations {
|
||||
@@ -134,12 +131,25 @@ pub enum QueryMsg {
|
||||
LayerDistribution {},
|
||||
GetRewardPool {},
|
||||
GetCirculatingSupply {},
|
||||
GetEpochRewardPercent {},
|
||||
GetIntervalRewardPercent {},
|
||||
GetSybilResistancePercent {},
|
||||
GetActiveSetWorkFactor {},
|
||||
GetRewardingStatus {
|
||||
mix_identity: IdentityKey,
|
||||
rewarding_interval_nonce: u32,
|
||||
interval_id: u32,
|
||||
},
|
||||
GetRewardedSet {
|
||||
height: Option<u64>,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
GetRewardedSetHeightsForInterval {
|
||||
interval_id: u32,
|
||||
},
|
||||
GetRewardedSetUpdateDetails {},
|
||||
GetCurrentRewardedSetHeight {},
|
||||
GetCurrentInterval {},
|
||||
GetRewardedSetRefreshBlocks {},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
+24
-17
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mixnode::DelegatorRewardParams;
|
||||
use crate::Layer;
|
||||
use crate::{Layer, RewardedSetNodeStatus};
|
||||
use cosmwasm_std::{Addr, Uint128};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -27,19 +27,12 @@ impl LayerDistribution {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct RewardingIntervalResponse {
|
||||
pub current_rewarding_interval_starting_block: u64,
|
||||
pub current_rewarding_interval_nonce: u32,
|
||||
pub rewarding_in_progress: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
|
||||
pub struct ContractStateParams {
|
||||
// so currently epoch_length is being unused and validator API performs rewarding
|
||||
// based on its own epoch length config value. I guess that's fine for time being
|
||||
// so currently interval_length is being unused and validator API performs rewarding
|
||||
// based on its own interval length config value. I guess that's fine for time being
|
||||
// however, in the future, the contract constant should be controlling it instead.
|
||||
// pub epoch_length: u32, // length of a rewarding epoch/interval, expressed in hours
|
||||
// pub interval_length: u32, // length of a rewarding interval/interval, expressed in hours
|
||||
pub minimum_mixnode_pledge: Uint128, // minimum amount a mixnode must pledge to get into the system
|
||||
pub minimum_gateway_pledge: Uint128, // minimum amount a gateway must pledge to get into the system
|
||||
|
||||
@@ -50,7 +43,6 @@ pub struct ContractStateParams {
|
||||
// subset of rewarded mixnodes that are actively receiving mix traffic
|
||||
// used to handle shorter-term (e.g. hourly) fluctuations of demand
|
||||
pub mixnode_active_set_size: u32,
|
||||
pub active_set_work_factor: u8,
|
||||
}
|
||||
|
||||
impl Display for ContractStateParams {
|
||||
@@ -75,11 +67,6 @@ impl Display for ContractStateParams {
|
||||
f,
|
||||
"mixnode active set size: {}",
|
||||
self.mixnode_active_set_size
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"mixnode active set work factor: {}",
|
||||
self.active_set_work_factor
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -136,3 +123,23 @@ pub struct MixnetContractVersion {
|
||||
pub type IdentityKey = String;
|
||||
pub type IdentityKeyRef<'a> = &'a str;
|
||||
pub type SphinxKey = String;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct PagedRewardedSetResponse {
|
||||
pub identities: Vec<(IdentityKey, RewardedSetNodeStatus)>,
|
||||
pub start_next_after: Option<IdentityKey>,
|
||||
pub at_height: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct RewardedSetUpdateDetails {
|
||||
pub refresh_rate_blocks: u64,
|
||||
pub last_refreshed_block: u64,
|
||||
pub current_height: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize, JsonSchema)]
|
||||
pub struct IntervalRewardedSetHeightsResponse {
|
||||
pub interval_id: u32,
|
||||
pub heights: Vec<u64>,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0-beta3"
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{Addr, Coin, Event, Timestamp};
|
||||
|
||||
// event types
|
||||
pub const WITHDRAW_EVENT_TYPE: &str = "vested_coins_withdraw";
|
||||
pub const OWNERSHIP_TRANSFER_EVENT_TYPE: &str = "ownership_transfer";
|
||||
pub const STAKING_ADDRESS_UPDATE_EVENT_TYPE: &str = "staking_address_update";
|
||||
pub const NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE: &str = "new_periodic_vesting_account";
|
||||
|
||||
pub const VESTING_DELEGATION_EVENT_TYPE: &str = "vesting_delegation";
|
||||
pub const VESTING_UNDELEGATION_EVENT_TYPE: &str = "vesting_undelegation";
|
||||
pub const VESTING_GATEWAY_BONDING_EVENT_TYPE: &str = "vesting_gateway_bonding";
|
||||
pub const VESTING_GATEWAY_UNBONDING_EVENT_TYPE: &str = "vesting_gateway_unbonding";
|
||||
pub const VESTING_MIXNODE_BONDING_EVENT_TYPE: &str = "vesting_mixnode_bonding";
|
||||
pub const VESTING_MIXNODE_UNBONDING_EVENT_TYPE: &str = "vesting_mixnode_unbonding";
|
||||
|
||||
pub const TRACK_MIXNODE_UNBOND_EVENT_TYPE: &str = "track_mixnode_unbond";
|
||||
pub const TRACK_GATEWAY_UNBOND_EVENT_TYPE: &str = "track_gateway_unbond";
|
||||
pub const TRACK_UNDELEGATION_EVENT_TYPE: &str = "track_undelegation";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const OWNER_KEY: &str = "owner";
|
||||
pub const AMOUNT_KEY: &str = "amount";
|
||||
|
||||
// event-specific attributes
|
||||
|
||||
// withdraw
|
||||
pub const REMAINING_SPENDABLE_KEY: &str = "remaining_spendable";
|
||||
|
||||
// ownership transfer
|
||||
pub const FROM_ACCOUNT_KEY: &str = "from";
|
||||
pub const TO_ACCOUNT_KEY: &str = "to";
|
||||
pub const NO_VALUE_VALUE: &str = "none";
|
||||
|
||||
// new vesting account
|
||||
pub const START_TIME_KEY: &str = "start_time";
|
||||
pub const STAKING_ADDRESS_KEY: &str = "staking_address";
|
||||
|
||||
// OPEN QUESTION: would it make sense to also emit amount of vesting/locked coins here?
|
||||
// however, then it would require additional storage reads.
|
||||
pub fn new_vested_coins_withdraw_event(
|
||||
address: &Addr,
|
||||
amount: &Coin,
|
||||
remaining_spendable: &Coin,
|
||||
) -> Event {
|
||||
Event::new(WITHDRAW_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, address)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string())
|
||||
.add_attribute(REMAINING_SPENDABLE_KEY, remaining_spendable.to_string())
|
||||
}
|
||||
|
||||
pub fn new_ownership_transfer_event(from: &Addr, to: &Addr) -> Event {
|
||||
Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE)
|
||||
.add_attribute(FROM_ACCOUNT_KEY, from)
|
||||
.add_attribute(TO_ACCOUNT_KEY, to)
|
||||
}
|
||||
|
||||
pub fn new_staking_address_update_event(from: &Option<Addr>, to: &Option<Addr>) -> Event {
|
||||
let mut event = Event::new(OWNERSHIP_TRANSFER_EVENT_TYPE);
|
||||
|
||||
if let Some(from) = from {
|
||||
event = event.add_attribute(FROM_ACCOUNT_KEY, from)
|
||||
} else {
|
||||
event = event.add_attribute(FROM_ACCOUNT_KEY, NO_VALUE_VALUE);
|
||||
}
|
||||
|
||||
if let Some(to) = to {
|
||||
event = event.add_attribute(TO_ACCOUNT_KEY, to)
|
||||
} else {
|
||||
event = event.add_attribute(TO_ACCOUNT_KEY, NO_VALUE_VALUE);
|
||||
}
|
||||
|
||||
event
|
||||
}
|
||||
|
||||
pub fn new_periodic_vesting_account_event(
|
||||
owner_address: &Addr,
|
||||
amount: &Coin,
|
||||
staking_address: &Option<Addr>,
|
||||
start_time: Timestamp,
|
||||
) -> Event {
|
||||
let mut event = Event::new(NEW_PERIODIC_VESTING_ACCOUNT_EVENT_TYPE)
|
||||
.add_attribute(OWNER_KEY, owner_address)
|
||||
.add_attribute(AMOUNT_KEY, amount.to_string());
|
||||
|
||||
if let Some(staking_address) = staking_address {
|
||||
event = event.add_attribute(STAKING_ADDRESS_KEY, staking_address);
|
||||
}
|
||||
|
||||
event.add_attribute(START_TIME_KEY, start_time.to_string())
|
||||
}
|
||||
|
||||
// In most cases the events are rather barebone as there's no point in attaching
|
||||
// bunch of data to them as it would be redundant. It is because in most cases when the event is emitted
|
||||
// a call to the mixnet contract is made that throws another event with relevant attributes already attached.
|
||||
|
||||
pub fn new_vesting_gateway_bonding_event() -> Event {
|
||||
Event::new(VESTING_GATEWAY_BONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_gateway_unbonding_event() -> Event {
|
||||
Event::new(VESTING_GATEWAY_UNBONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_mixnode_bonding_event() -> Event {
|
||||
Event::new(VESTING_MIXNODE_BONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_mixnode_unbonding_event() -> Event {
|
||||
Event::new(VESTING_MIXNODE_UNBONDING_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_delegation_event() -> Event {
|
||||
Event::new(VESTING_DELEGATION_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_vesting_undelegation_event() -> Event {
|
||||
Event::new(VESTING_UNDELEGATION_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_mixnode_unbond_event() -> Event {
|
||||
Event::new(TRACK_MIXNODE_UNBOND_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_gateway_unbond_event() -> Event {
|
||||
Event::new(TRACK_GATEWAY_UNBOND_EVENT_TYPE)
|
||||
}
|
||||
|
||||
pub fn new_track_undelegation_event() -> Event {
|
||||
Event::new(TRACK_UNDELEGATION_EVENT_TYPE)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod events;
|
||||
@@ -27,7 +27,7 @@ pub struct BandwidthVoucherAttributes {
|
||||
pub binding_number: PrivateAttribute,
|
||||
// the value (e.g., bandwidth) encoded in this voucher
|
||||
pub voucher_value: PublicAttribute,
|
||||
// a field with public information, e.g., type of voucher, epoch etc.
|
||||
// a field with public information, e.g., type of voucher, interval etc.
|
||||
pub voucher_info: PublicAttribute,
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
aes = { version = "0.7.4", features = ["ctr"] }
|
||||
bs58 = "0.4.0"
|
||||
blake3 = { version = "1.0.0", features = ["traits-preview"] }
|
||||
blake3 = { version = "~1.2.0", features = ["traits-preview"] }
|
||||
digest = "0.9.0"
|
||||
generic-array = "0.14"
|
||||
hkdf = "0.11.0"
|
||||
|
||||
@@ -22,7 +22,7 @@ where
|
||||
|
||||
let hkdf = Hkdf::<D>::new(salt, ikm);
|
||||
let mut okm = vec![0u8; okm_length];
|
||||
hkdf.expand(info.unwrap_or_else(|| &[]), &mut okm)?;
|
||||
hkdf.expand(info.unwrap_or(&[]), &mut okm)?;
|
||||
|
||||
Ok(okm)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct ConfigBuilder(Config);
|
||||
|
||||
impl ConfigBuilder {
|
||||
|
||||
@@ -7,7 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
hex-literal = "0.3.3"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
url = "2.2"
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{milhon, qa, sandbox, ValidatorDetails};
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum Network {
|
||||
MILHON,
|
||||
QA,
|
||||
SANDBOX,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct NetworkDetails {
|
||||
bech32_prefix: String,
|
||||
denom: String,
|
||||
mixnet_contract_address: String,
|
||||
vesting_contract_address: String,
|
||||
bandwidth_claim_contract_address: String,
|
||||
rewarding_validator_address: String,
|
||||
validators: Vec<ValidatorDetails>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct SupportedNetworks {
|
||||
networks: HashMap<Network, NetworkDetails>,
|
||||
}
|
||||
|
||||
impl SupportedNetworks {
|
||||
pub fn new(support: Vec<Network>) -> Self {
|
||||
let mut networks = HashMap::new();
|
||||
|
||||
for network in support {
|
||||
match network {
|
||||
Network::MILHON => networks.insert(
|
||||
Network::MILHON,
|
||||
NetworkDetails {
|
||||
bech32_prefix: String::from(milhon::BECH32_PREFIX),
|
||||
denom: String::from(milhon::DENOM),
|
||||
mixnet_contract_address: String::from(milhon::MIXNET_CONTRACT_ADDRESS),
|
||||
vesting_contract_address: String::from(milhon::VESTING_CONTRACT_ADDRESS),
|
||||
bandwidth_claim_contract_address: String::from(
|
||||
milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS,
|
||||
),
|
||||
rewarding_validator_address: String::from(
|
||||
milhon::REWARDING_VALIDATOR_ADDRESS,
|
||||
),
|
||||
validators: milhon::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(),
|
||||
},
|
||||
),
|
||||
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(),
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
SupportedNetworks { networks }
|
||||
}
|
||||
|
||||
pub fn bech32_prefix(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.bech32_prefix.as_str())
|
||||
}
|
||||
|
||||
pub fn denom(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.denom.as_str())
|
||||
}
|
||||
|
||||
pub fn mixnet_contract_address(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.mixnet_contract_address.as_str())
|
||||
}
|
||||
|
||||
pub fn vesting_contract_address(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.vesting_contract_address.as_str())
|
||||
}
|
||||
|
||||
pub fn bandwidth_claim_contract_address(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.bandwidth_claim_contract_address.as_str())
|
||||
}
|
||||
|
||||
pub fn rewarding_validator_address(&self, network: Network) -> Option<&str> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| network_details.rewarding_validator_address.as_str())
|
||||
}
|
||||
|
||||
pub fn validators(&self, network: Network) -> Option<&Vec<ValidatorDetails>> {
|
||||
self.networks
|
||||
.get(&network)
|
||||
.map(|network_details| &network_details.validators)
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,65 @@
|
||||
// Copyright 2020 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
pub mod all;
|
||||
pub mod eth_contract;
|
||||
#[cfg(network = "milhon")]
|
||||
pub mod milhon;
|
||||
#[cfg(network = "sandbox")]
|
||||
pub mod sandbox;
|
||||
mod milhon;
|
||||
mod qa;
|
||||
mod sandbox;
|
||||
|
||||
#[cfg(network = "milhon")]
|
||||
pub use milhon::*;
|
||||
#[cfg(network = "sandbox")]
|
||||
pub use sandbox::*;
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(network = "milhon")] {
|
||||
pub const BECH32_PREFIX: &str = milhon::BECH32_PREFIX;
|
||||
pub const DENOM: &str = milhon::DENOM;
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = milhon::MIXNET_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = milhon::VESTING_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = milhon::BANDWIDTH_CLAIM_CONTRACT_ADDRESS;
|
||||
pub const DEFAULT_REWARDING_VALIDATOR_ADDRESS: &str = milhon::REWARDING_VALIDATOR_ADDRESS;
|
||||
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
milhon::validators()
|
||||
}
|
||||
|
||||
pub fn default_network() -> all::Network {
|
||||
all::Network::MILHON
|
||||
}
|
||||
} else if #[cfg(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 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 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 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ValidatorDetails {
|
||||
@@ -47,25 +92,6 @@ impl ValidatorDetails {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(network = "milhon")]
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
vec![
|
||||
ValidatorDetails::new(
|
||||
"https://testnet-milhon-validator1.nymtech.net",
|
||||
Some("https://testnet-milhon-validator1.nymtech.net/api"),
|
||||
),
|
||||
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(network = "sandbox")]
|
||||
pub fn default_validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://sandbox-validator.nymtech.net",
|
||||
Some("https://sandbox-validator.nymtech.net/api"),
|
||||
)]
|
||||
}
|
||||
|
||||
pub fn default_nymd_endpoints() -> Vec<Url> {
|
||||
default_validators()
|
||||
.iter()
|
||||
@@ -80,6 +106,13 @@ pub fn default_api_endpoints() -> Vec<Url> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
|
||||
// Ethereum constants used for token bridge
|
||||
/// How much bandwidth (in bytes) one token can buy
|
||||
const BYTES_PER_TOKEN: u64 = 1024 * 1024 * 1024;
|
||||
@@ -117,10 +150,9 @@ pub const DEFAULT_VALIDATOR_API_PORT: u16 = 8080;
|
||||
pub const VALIDATOR_API_VERSION: &str = "v1";
|
||||
|
||||
// REWARDING
|
||||
pub const DEFAULT_FIRST_EPOCH_START: OffsetDateTime = time::macros::datetime!(2021-08-23 12:00 UTC);
|
||||
pub const DEFAULT_EPOCH_LENGTH: Duration = Duration::from_secs(24 * 60 * 60 * 30); // 30 days
|
||||
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate epoch costs to Nyms. We'll also assume a cost of 40$ per epoch(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
|
||||
pub const DEFAULT_OPERATOR_EPOCH_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
|
||||
|
||||
/// We'll be assuming a few more things, profit margin and cost function. Since we don't have relialable package measurement, we'll be using uptime. We'll also set the value of 1 Nym to 1 $, to be able to translate interval costs to Nyms. We'll also assume a cost of 40$ per interval(month), converting that to Nym at our 1$ rate translates to 40_000_000 uNyms
|
||||
pub const DEFAULT_OPERATOR_INTERVAL_COST: u64 = 40_000_000; // 40$/(30 days) at 1 Nym == 1$
|
||||
|
||||
// TODO: is there a way to get this from the chain
|
||||
pub const TOTAL_SUPPLY: u128 = 1_000_000_000_000_000;
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const BECH32_PREFIX: &str = "punk";
|
||||
pub const DENOM: &str = "upunk";
|
||||
use crate::ValidatorDetails;
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "";
|
||||
pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
pub(crate) const BECH32_PREFIX: &str = "punk";
|
||||
pub(crate) const DENOM: &str = "upunk";
|
||||
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "punk10pyejy66429refv3g35g2t7am0was7yalwrzen";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"punk1jld76tqw4wnpfenmay2xkv86nr3j0w426eka82";
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfdy7hgk3";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![
|
||||
ValidatorDetails::new(
|
||||
"https://testnet-milhon-validator1.nymtech.net",
|
||||
Some("https://testnet-milhon-validator1.nymtech.net/api"),
|
||||
),
|
||||
ValidatorDetails::new("https://testnet-milhon-validator2.nymtech.net", None),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ValidatorDetails;
|
||||
|
||||
pub(crate) const BECH32_PREFIX: &str = "nymt";
|
||||
pub(crate) const DENOM: &str = "unymt";
|
||||
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt14hj2tavq8fpesdwxxcu44rty3hh90vhuysqrsr";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt1dn52nx8wv9wkqmrvj6tcmdzh4es6jt8tr7f6j9";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://qa-validator.nymtech.net",
|
||||
Some("https://qa-validator.nymtech.net/api"),
|
||||
)]
|
||||
}
|
||||
@@ -1,17 +1,20 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const BECH32_PREFIX: &str = "nymt";
|
||||
pub const DENOM: &str = "unymt";
|
||||
use crate::ValidatorDetails;
|
||||
|
||||
pub const DEFAULT_MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
|
||||
pub const DEFAULT_VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||
pub const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str = "nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
pub const ETH_CONTRACT_ADDRESS: [u8; 20] =
|
||||
hex_literal::hex!("9fEE3e28c17dbB87310A51F13C4fbf4331A6f102");
|
||||
pub(crate) const BECH32_PREFIX: &str = "nymt";
|
||||
pub(crate) const DENOM: &str = "unymt";
|
||||
|
||||
// Name of the event triggered by the eth contract. If the event name is changed,
|
||||
// this would also need to be changed; It is currently tested against the json abi
|
||||
pub const ETH_EVENT_NAME: &str = "Burned";
|
||||
pub const ETH_BURN_FUNCTION_NAME: &str = "burnTokenForAccessCode";
|
||||
pub(crate) const MIXNET_CONTRACT_ADDRESS: &str = "nymt1ghd753shjuwexxywmgs4xz7x2q732vcnstz02j";
|
||||
pub(crate) const VESTING_CONTRACT_ADDRESS: &str = "nymt1nc5tatafv6eyq7llkr2gv50ff9e22mnfp9pc5s";
|
||||
pub(crate) const BANDWIDTH_CLAIM_CONTRACT_ADDRESS: &str =
|
||||
"nymt17p9rzwnnfxcjp32un9ug7yhhzgtkhvl9f8xzkv";
|
||||
pub(crate) const REWARDING_VALIDATOR_ADDRESS: &str = "nymt17zujduc46wvkwvp6f062mm5xhr7jc3fewvqu9e";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(
|
||||
"https://sandbox-validator.nymtech.net",
|
||||
Some("https://sandbox-validator.nymtech.net/api"),
|
||||
)]
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ crypto = { path = "../crypto" }
|
||||
topology = { path = "../topology" }
|
||||
|
||||
[dev-dependencies]
|
||||
mixnet-contract = { path = "../mixnet-contract" }
|
||||
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
|
||||
# do not include this when compiling into wasm as it somehow when combined together with reqwest, it will require
|
||||
# net2 via tokio-util -> tokio -> mio -> net2
|
||||
|
||||
@@ -54,6 +54,7 @@ impl From<NymTopologyError> for PreparationError {
|
||||
/// an optional reply-SURB, padding it to appropriate length, encrypting its content,
|
||||
/// and chunking into appropriate size [`Fragment`]s.
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Clone))]
|
||||
#[must_use]
|
||||
pub struct MessagePreparer<R: CryptoRng + Rng> {
|
||||
/// Instance of a cryptographically secure random number generator.
|
||||
rng: R,
|
||||
@@ -386,13 +387,7 @@ where
|
||||
// (note: surb_ack_bytes contains SURB_ACK_FIRST_HOP || SURB_ACK_DATA )
|
||||
let packet_payload: Vec<_> = surb_ack_bytes
|
||||
.into_iter()
|
||||
.chain(
|
||||
reply_surb
|
||||
.encryption_key()
|
||||
.compute_digest()
|
||||
.to_vec()
|
||||
.into_iter(),
|
||||
)
|
||||
.chain(reply_surb.encryption_key().compute_digest().iter().copied())
|
||||
.chain(reply_content.into_iter())
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ impl MessageReceiver {
|
||||
}
|
||||
|
||||
/// Allows setting non-default number of expected mix hops in the network.
|
||||
#[must_use]
|
||||
pub fn with_mix_hops(mut self, hops: u8) -> Self {
|
||||
self.num_mix_hops = hops;
|
||||
self
|
||||
@@ -186,7 +187,7 @@ impl Default for MessageReceiver {
|
||||
mod message_receiver {
|
||||
use super::*;
|
||||
use crypto::asymmetric::identity;
|
||||
use mixnet_contract::Layer;
|
||||
use mixnet_contract_common::Layer;
|
||||
use nymsphinx_addressing::clients::Recipient;
|
||||
use rand::rngs::OsRng;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -13,7 +13,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
|
||||
## internal
|
||||
crypto = { path = "../crypto" }
|
||||
mixnet-contract = { path = "../mixnet-contract" }
|
||||
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nymsphinx-addressing = { path = "../nymsphinx/addressing" }
|
||||
nymsphinx-types = { path = "../nymsphinx/types" }
|
||||
version-checker = { path = "../version-checker" }
|
||||
|
||||
@@ -9,6 +9,7 @@ pub trait Versioned: Clone {
|
||||
}
|
||||
|
||||
pub trait VersionFilterable<T> {
|
||||
#[must_use]
|
||||
fn filter_by_version(&self, expected_version: &str) -> Self;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::{filter, NetworkAddress};
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use mixnet_contract::GatewayBond;
|
||||
use mixnet_contract_common::GatewayBond;
|
||||
use nymsphinx_addressing::nodes::{NodeIdentity, NymNodeRoutingAddress};
|
||||
use nymsphinx_types::Node as SphinxNode;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::filter::VersionFilterable;
|
||||
use log::warn;
|
||||
use mixnet_contract::{GatewayBond, MixNodeBond};
|
||||
use mixnet_contract_common::{GatewayBond, MixNodeBond};
|
||||
use nymsphinx_addressing::nodes::NodeIdentity;
|
||||
use nymsphinx_types::Node as SphinxNode;
|
||||
use rand::Rng;
|
||||
@@ -206,10 +206,12 @@ impl NymTopology {
|
||||
true
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter_system_version(&self, expected_version: &str) -> Self {
|
||||
self.filter_node_versions(expected_version, expected_version)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn filter_node_versions(
|
||||
&self,
|
||||
expected_mix_version: &str,
|
||||
@@ -272,7 +274,7 @@ mod converting_mixes_to_vec {
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
|
||||
use super::*;
|
||||
use mixnet_contract::Layer;
|
||||
use mixnet_contract_common::Layer;
|
||||
|
||||
#[test]
|
||||
fn returns_a_vec_with_hashmap_values() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::{filter, NetworkAddress};
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use mixnet_contract::{Layer, MixNodeBond};
|
||||
use mixnet_contract_common::{Layer, MixNodeBond};
|
||||
use nymsphinx_addressing::nodes::NymNodeRoutingAddress;
|
||||
use nymsphinx_types::Node as SphinxNode;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
Generated
+126
-132
@@ -17,9 +17,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.49"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a03e93e97a28fbc9f42fbc5ba0886a3c67eb637b476dbee711f80a6ffe8223d"
|
||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -41,9 +41,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.1.2"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d6dff4a1892b54d70af377bf7a17064192e822865791d812957f21e3108c325"
|
||||
checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
|
||||
|
||||
[[package]]
|
||||
name = "bandwidth-claim"
|
||||
@@ -123,7 +123,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -143,9 +143,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.8.0"
|
||||
version = "3.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||
checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
@@ -155,9 +155,9 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.7.2"
|
||||
version = "1.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
|
||||
checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -209,7 +209,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -236,6 +236,13 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
||||
|
||||
[[package]]
|
||||
name = "contracts-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmwasm-crypto"
|
||||
version = "1.0.0-beta3"
|
||||
@@ -320,13 +327,13 @@ dependencies = [
|
||||
"config",
|
||||
"digest 0.9.0",
|
||||
"ed25519-dalek",
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"hkdf",
|
||||
"hmac",
|
||||
"log",
|
||||
"nymsphinx-types",
|
||||
"pemstore",
|
||||
"rand 0.7.3",
|
||||
"rand",
|
||||
"subtle-encoding",
|
||||
"x25519-dalek",
|
||||
]
|
||||
@@ -337,7 +344,7 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"rand_core 0.6.3",
|
||||
"subtle 2.4.1",
|
||||
"zeroize",
|
||||
@@ -359,7 +366,7 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
@@ -387,9 +394,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-storage-plus"
|
||||
version = "0.10.3"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3b8b840947313c1a1cccf056836cd79a60b4526bdcd6582995be37dc97be4ae"
|
||||
checksum = "7d7ee1963302b0ac2a9d42fe0faec826209c17452bfd36fbfd9d002a88929261"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
@@ -398,9 +405,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
|
||||
checksum = "79b71cca7d95d7681a4b3b9cdf63c8dbc3730d0584c2c74e31416d64a90493f4"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
]
|
||||
@@ -420,7 +427,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -458,7 +465,7 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"ed25519",
|
||||
"rand 0.7.3",
|
||||
"rand",
|
||||
"serde",
|
||||
"sha2",
|
||||
"zeroize",
|
||||
@@ -486,7 +493,7 @@ checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b"
|
||||
dependencies = [
|
||||
"crypto-bigint",
|
||||
"ff",
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"group",
|
||||
"pkcs8",
|
||||
"rand_core 0.6.3",
|
||||
@@ -532,9 +539,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "1.10.0"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d333a26ec13a023c6dff4b7584de4d323cfee2e508f5dd2bbee6669e4f7efdf"
|
||||
checksum = "80a9a8cb2e34880a498f09367089339bda5e12d6f871640f947850f7113058c0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bytemuck",
|
||||
@@ -564,9 +571,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@@ -592,17 +599,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getset"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
|
||||
checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
@@ -612,9 +617,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.13.24"
|
||||
version = "0.13.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "845e007a28f1fcac035715988a234e8ec5458fd825b20a20c7dec74237ef341f"
|
||||
checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -715,9 +720,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
@@ -763,15 +768,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.107"
|
||||
version = "0.2.112"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.12.25+1.3.0"
|
||||
version = "0.12.26+1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68169ef08d6519b2fe133ecc637408d933c0174b23b80bb2f79828966fbaab"
|
||||
checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -833,21 +838,6 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
[[package]]
|
||||
name = "mixnet-contract"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mixnet-contracts"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"config",
|
||||
@@ -857,23 +847,41 @@ dependencies = [
|
||||
"crypto",
|
||||
"cw-storage-plus",
|
||||
"fixed",
|
||||
"mixnet-contract",
|
||||
"rand 0.7.3",
|
||||
"rand_chacha 0.2.2",
|
||||
"mixnet-contract-common",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"time 0.3.6",
|
||||
"vergen",
|
||||
"vesting-contract",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mixnet-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"az",
|
||||
"contracts-common",
|
||||
"cosmwasm-std",
|
||||
"fixed",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "network-defaults"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hex-literal",
|
||||
"serde",
|
||||
"time 0.3.5",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -897,6 +905,15 @@ dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71a1eb3a36534514077c1e079ada2fb170ef30c47d203aa6916138cf882ecd52"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nymsphinx-types"
|
||||
version = "0.1.0"
|
||||
@@ -906,9 +923,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
@@ -1001,15 +1018,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.22"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
@@ -1037,9 +1054,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.32"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
@@ -1052,9 +1069,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.10"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1067,21 +1084,9 @@ checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||
dependencies = [
|
||||
"getrandom 0.1.16",
|
||||
"libc",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_chacha",
|
||||
"rand_core 0.5.1",
|
||||
"rand_hc 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha 0.3.1",
|
||||
"rand_core 0.6.3",
|
||||
"rand_hc 0.3.1",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1094,16 +1099,6 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
@@ -1129,7 +1124,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rand 0.7.3",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1141,15 +1136,6 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
@@ -1176,21 +1162,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.5"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
|
||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "271ac0c667b8229adf70f0f957697c96fafd7486ab7481e15dc5e45e3e6a4368"
|
||||
checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3"
|
||||
dependencies = [
|
||||
"dyn-clone",
|
||||
"schemars_derive",
|
||||
@@ -1200,9 +1186,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ebda811090b257411540779860bc09bf321bc587f58d2c5864309d1566214e7"
|
||||
checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1218,27 +1204,27 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.130"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-json-wasm"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449"
|
||||
checksum = "042ac496d97e5885149d34139bad1d617192770d7eb8f1866da2317ff4501853"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.130"
|
||||
version = "1.0.133"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
|
||||
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1258,9 +1244,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.70"
|
||||
version = "1.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3"
|
||||
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -1292,9 +1278,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.8"
|
||||
version = "0.9.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
|
||||
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||
dependencies = [
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
@@ -1330,7 +1316,7 @@ dependencies = [
|
||||
"hmac",
|
||||
"lioness",
|
||||
"log",
|
||||
"rand 0.7.3",
|
||||
"rand",
|
||||
"rand_distr",
|
||||
"sha2",
|
||||
"subtle 2.4.1",
|
||||
@@ -1374,9 +1360,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.81"
|
||||
version = "1.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
|
||||
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1427,11 +1413,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.5"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad"
|
||||
checksum = "c8d54b9298e05179c335de2b9645d061255bcd5155f843b3e328d2cfe0a5b413"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
@@ -1467,9 +1455,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
@@ -1530,9 +1518,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "5.1.18"
|
||||
version = "5.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d48696c0fbbdafd9553e14c4584b4b9583931e9474a3ae506f1872b890d0b47"
|
||||
checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
@@ -1547,9 +1535,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vesting-contract"
|
||||
@@ -1558,12 +1546,18 @@ dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"getrandom 0.2.3",
|
||||
"mixnet-contract",
|
||||
"rand 0.8.4",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"vesting-contract-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+9
-9
@@ -1556,9 +1556,9 @@
|
||||
}
|
||||
},
|
||||
"@openzeppelin/contracts": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.1.tgz",
|
||||
"integrity": "sha512-o+pHCf/yMLSlV5MkDQEzEQL402i6SoRnktru+0rdSxVEFZcTzzGhZCAtZjUFyKGazMSv1TilzMg+RbED1N8XHQ=="
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.4.2.tgz",
|
||||
"integrity": "sha512-NyJV7sJgoGYqbtNUWgzzOGW4T6rR19FmX1IJgXGdapGPWsuMelGJn9h03nos0iqfforCbCB0iYIR0MtIuIFLLw=="
|
||||
},
|
||||
"@openzeppelin/test-helpers": {
|
||||
"version": "0.5.15",
|
||||
@@ -5059,9 +5059,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
|
||||
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==",
|
||||
"version": "1.14.7",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
|
||||
"integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
|
||||
"dev": true
|
||||
},
|
||||
"for-each": {
|
||||
@@ -17880,9 +17880,9 @@
|
||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
|
||||
},
|
||||
"shelljs": {
|
||||
"version": "0.8.4",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz",
|
||||
"integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==",
|
||||
"version": "0.8.5",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
|
||||
"integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.0.0",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@nomiclabs/hardhat-truffle5": "^2.0.2",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/contracts": "^4.4.1",
|
||||
"@openzeppelin/contracts": "^4.4.2",
|
||||
"@openzeppelin/test-helpers": "^0.5.15",
|
||||
"dotenv": "^10.0.0",
|
||||
"find-config": "^1.0.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "mixnet-contracts"
|
||||
name = "mixnet-contract"
|
||||
version = "0.1.0"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
@@ -16,18 +16,19 @@ exclude = [
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
mixnet-contract = { path = "../../common/mixnet-contract" }
|
||||
mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
config = { path = "../../common/config"}
|
||||
vesting-contract = { path = "../vesting" }
|
||||
|
||||
cosmwasm-std = "1.0.0-beta3"
|
||||
cosmwasm-storage = "1.0.0-beta3"
|
||||
cw-storage-plus = "0.10.3"
|
||||
cw-storage-plus = "0.11.1"
|
||||
|
||||
bs58 = "0.4.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { version = "1.0.23" }
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = "1.0.0-beta3"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
extern crate mixnet_contract;
|
||||
|
||||
use cosmwasm_schema::{export_schema, remove_schemas, schema_for};
|
||||
use mixnet_contract::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
|
||||
use mixnet_contract_common::{ExecuteMsg, InstantiateMsg, MixNodeBond, QueryMsg};
|
||||
use std::env::current_dir;
|
||||
use std::fs::create_dir_all;
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
// approximately 1 week (assuming 5s per block)
|
||||
// i.e. approximately quarter of the interval (there are 3600 * 60 * 7 = 604800 seconds in a week, i.e. ~604800 / 5 = 120960 blocks)
|
||||
pub const MINIMUM_BLOCK_AGE_FOR_REWARDING: u64 = 120960;
|
||||
|
||||
pub const INTERVAL_REWARD_PERCENT: u8 = 2; // Used to calculate interval reward pool
|
||||
pub const SYBIL_RESISTANCE_PERCENT: u8 = 30;
|
||||
pub const ACTIVE_SET_WORK_FACTOR: u8 = 10;
|
||||
|
||||
// TODO: this, in theory, represents "epoch" length.
|
||||
// However, since the blocktime is not EXACTLY 5s, we can't really guarantee 720 epochs in interval
|
||||
// and we can't change this easily to `Duration`, because then the entire rewarded set storage
|
||||
// would be messed up... (as we look up stuff "by blocks")
|
||||
pub const REWARDED_SET_REFRESH_BLOCKS: u64 = 720; // with blocktime being approximately 5s, it should be roughly 1h
|
||||
|
||||
pub const REWARDING_INTERVAL_LENGTH: Duration = Duration::from_secs(60 * 60 * 720); // 720h, i.e. 30 days
|
||||
@@ -1,6 +1,10 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::{
|
||||
ACTIVE_SET_WORK_FACTOR, INTERVAL_REWARD_PERCENT, REWARDING_INTERVAL_LENGTH,
|
||||
SYBIL_RESISTANCE_PERCENT,
|
||||
};
|
||||
use crate::delegations::queries::query_all_network_delegations_paged;
|
||||
use crate::delegations::queries::query_delegator_delegations_paged;
|
||||
use crate::delegations::queries::query_mixnode_delegation;
|
||||
@@ -8,8 +12,13 @@ use crate::delegations::queries::query_mixnode_delegations_paged;
|
||||
use crate::error::ContractError;
|
||||
use crate::gateways::queries::query_gateways_paged;
|
||||
use crate::gateways::queries::query_owns_gateway;
|
||||
use crate::interval::queries::{
|
||||
query_current_interval, query_current_rewarded_set_height, query_rewarded_set,
|
||||
query_rewarded_set_heights_for_interval, query_rewarded_set_refresh_minimum_blocks,
|
||||
query_rewarded_set_update_details,
|
||||
};
|
||||
use crate::interval::storage as interval_storage;
|
||||
use crate::mixnet_contract_settings::models::ContractState;
|
||||
use crate::mixnet_contract_settings::queries::query_rewarding_interval;
|
||||
use crate::mixnet_contract_settings::queries::{
|
||||
query_contract_settings_params, query_contract_version,
|
||||
};
|
||||
@@ -17,13 +26,17 @@ use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use crate::mixnodes::bonding_queries as mixnode_queries;
|
||||
use crate::mixnodes::bonding_queries::query_mixnodes_paged;
|
||||
use crate::mixnodes::layer_queries::query_layer_distribution;
|
||||
use crate::rewards::queries::query_reward_pool;
|
||||
use crate::rewards::queries::{query_circulating_supply, query_rewarding_status};
|
||||
use crate::rewards::queries::{
|
||||
query_circulating_supply, query_reward_pool, query_rewarding_status,
|
||||
};
|
||||
use crate::rewards::storage as rewards_storage;
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Addr, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Uint128,
|
||||
};
|
||||
use mixnet_contract::{ContractStateParams, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use mixnet_contract_common::{
|
||||
ContractStateParams, ExecuteMsg, InstantiateMsg, Interval, MigrateMsg, QueryMsg,
|
||||
};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
/// Constant specifying minimum of coin required to bond a gateway
|
||||
pub const INITIAL_GATEWAY_PLEDGE: Uint128 = Uint128::new(100_000_000);
|
||||
@@ -35,15 +48,12 @@ pub const INITIAL_MIXNODE_REWARDED_SET_SIZE: u32 = 200;
|
||||
pub const INITIAL_MIXNODE_ACTIVE_SET_SIZE: u32 = 100;
|
||||
|
||||
pub const INITIAL_REWARD_POOL: u128 = 250_000_000_000_000;
|
||||
pub const EPOCH_REWARD_PERCENT: u8 = 2; // Used to calculate epoch reward pool
|
||||
pub const DEFAULT_SYBIL_RESISTANCE_PERCENT: u8 = 30;
|
||||
pub const DEFAULT_ACTIVE_SET_WORK_FACTOR: u8 = 10;
|
||||
pub const INITIAL_ACTIVE_SET_WORK_FACTOR: u8 = 10;
|
||||
|
||||
fn default_initial_state(
|
||||
owner: Addr,
|
||||
rewarding_validator_address: Addr,
|
||||
env: Env,
|
||||
) -> ContractState {
|
||||
pub const DEFAULT_FIRST_INTERVAL_START: OffsetDateTime =
|
||||
time::macros::datetime!(2022-01-01 12:00 UTC);
|
||||
|
||||
fn default_initial_state(owner: Addr, rewarding_validator_address: Addr) -> ContractState {
|
||||
ContractState {
|
||||
owner,
|
||||
rewarding_validator_address,
|
||||
@@ -52,11 +62,7 @@ fn default_initial_state(
|
||||
minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE,
|
||||
mixnode_rewarded_set_size: INITIAL_MIXNODE_REWARDED_SET_SIZE,
|
||||
mixnode_active_set_size: INITIAL_MIXNODE_ACTIVE_SET_SIZE,
|
||||
active_set_work_factor: DEFAULT_ACTIVE_SET_WORK_FACTOR,
|
||||
},
|
||||
rewarding_interval_starting_block: env.block.height,
|
||||
latest_rewarding_interval_nonce: 0,
|
||||
rewarding_in_progress: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +79,15 @@ pub fn instantiate(
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let rewarding_validator_address = deps.api.addr_validate(&msg.rewarding_validator_address)?;
|
||||
let state = default_initial_state(info.sender, rewarding_validator_address, env);
|
||||
let state = default_initial_state(info.sender, rewarding_validator_address);
|
||||
let rewarding_interval =
|
||||
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
|
||||
|
||||
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &state)?;
|
||||
mixnet_params_storage::LAYERS.save(deps.storage, &Default::default())?;
|
||||
rewards_storage::REWARD_POOL.save(deps.storage, &Uint128::new(INITIAL_REWARD_POOL))?;
|
||||
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
|
||||
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
@@ -104,6 +114,14 @@ pub fn execute(
|
||||
ExecuteMsg::UnbondMixnode {} => {
|
||||
crate::mixnodes::transactions::try_remove_mixnode(deps, info)
|
||||
}
|
||||
ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
} => crate::mixnodes::transactions::try_update_mixnode_config(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
profit_margin_percent,
|
||||
),
|
||||
ExecuteMsg::BondGateway {
|
||||
gateway,
|
||||
owner_signature,
|
||||
@@ -125,14 +143,14 @@ pub fn execute(
|
||||
ExecuteMsg::RewardMixnode {
|
||||
identity,
|
||||
params,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
} => crate::rewards::transactions::try_reward_mixnode(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
identity,
|
||||
params,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
),
|
||||
ExecuteMsg::DelegateToMixnode { mix_identity } => {
|
||||
crate::delegations::transactions::try_delegate_to_mixnode(deps, env, info, mix_identity)
|
||||
@@ -144,29 +162,14 @@ pub fn execute(
|
||||
mix_identity,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::BeginMixnodeRewarding {
|
||||
rewarding_interval_nonce,
|
||||
} => crate::rewards::transactions::try_begin_mixnode_rewarding(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
rewarding_interval_nonce,
|
||||
),
|
||||
ExecuteMsg::FinishMixnodeRewarding {
|
||||
rewarding_interval_nonce,
|
||||
} => crate::rewards::transactions::try_finish_mixnode_rewarding(
|
||||
deps,
|
||||
info,
|
||||
rewarding_interval_nonce,
|
||||
),
|
||||
ExecuteMsg::RewardNextMixDelegators {
|
||||
mix_identity,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
} => crate::rewards::transactions::try_reward_next_mixnode_delegators(
|
||||
deps,
|
||||
info,
|
||||
mix_identity,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
),
|
||||
ExecuteMsg::DelegateToMixnodeOnBehalf {
|
||||
mix_identity,
|
||||
@@ -217,11 +220,24 @@ pub fn execute(
|
||||
ExecuteMsg::UnbondGatewayOnBehalf { owner } => {
|
||||
crate::gateways::transactions::try_remove_gateway_on_behalf(deps, info, owner)
|
||||
}
|
||||
ExecuteMsg::WriteRewardedSet {
|
||||
rewarded_set,
|
||||
expected_active_set_size,
|
||||
} => crate::interval::transactions::try_write_rewarded_set(
|
||||
deps,
|
||||
env,
|
||||
info,
|
||||
rewarded_set,
|
||||
expected_active_set_size,
|
||||
),
|
||||
ExecuteMsg::AdvanceCurrentInterval {} => {
|
||||
crate::interval::transactions::try_advance_interval(env, deps.storage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let query_res = match msg {
|
||||
QueryMsg::GetContractVersion {} => to_binary(&query_contract_version()),
|
||||
QueryMsg::GetMixNodes { start_after, limit } => {
|
||||
@@ -235,7 +251,6 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
|
||||
}
|
||||
QueryMsg::OwnsGateway { address } => to_binary(&query_owns_gateway(deps, address)?),
|
||||
QueryMsg::StateParams {} => to_binary(&query_contract_settings_params(deps)?),
|
||||
QueryMsg::CurrentRewardingInterval {} => to_binary(&query_rewarding_interval(deps)?),
|
||||
QueryMsg::LayerDistribution {} => to_binary(&query_layer_distribution(deps)?),
|
||||
QueryMsg::GetMixnodeDelegations {
|
||||
mix_identity,
|
||||
@@ -266,22 +281,102 @@ pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<QueryResponse, Cont
|
||||
} => to_binary(&query_mixnode_delegation(deps, mix_identity, delegator)?),
|
||||
QueryMsg::GetRewardPool {} => to_binary(&query_reward_pool(deps)?),
|
||||
QueryMsg::GetCirculatingSupply {} => to_binary(&query_circulating_supply(deps)?),
|
||||
QueryMsg::GetEpochRewardPercent {} => to_binary(&EPOCH_REWARD_PERCENT),
|
||||
QueryMsg::GetSybilResistancePercent {} => to_binary(&DEFAULT_SYBIL_RESISTANCE_PERCENT),
|
||||
QueryMsg::GetIntervalRewardPercent {} => to_binary(&INTERVAL_REWARD_PERCENT),
|
||||
QueryMsg::GetSybilResistancePercent {} => to_binary(&SYBIL_RESISTANCE_PERCENT),
|
||||
QueryMsg::GetActiveSetWorkFactor {} => to_binary(&ACTIVE_SET_WORK_FACTOR),
|
||||
QueryMsg::GetRewardingStatus {
|
||||
mix_identity,
|
||||
rewarding_interval_nonce,
|
||||
} => to_binary(&query_rewarding_status(
|
||||
deps,
|
||||
mix_identity,
|
||||
rewarding_interval_nonce,
|
||||
interval_id,
|
||||
} => to_binary(&query_rewarding_status(deps, mix_identity, interval_id)?),
|
||||
QueryMsg::GetRewardedSet {
|
||||
height,
|
||||
start_after,
|
||||
limit,
|
||||
} => to_binary(&query_rewarded_set(
|
||||
deps.storage,
|
||||
height,
|
||||
start_after,
|
||||
limit,
|
||||
)?),
|
||||
QueryMsg::GetRewardedSetHeightsForInterval { interval_id } => to_binary(
|
||||
&query_rewarded_set_heights_for_interval(deps.storage, interval_id)?,
|
||||
),
|
||||
QueryMsg::GetRewardedSetUpdateDetails {} => {
|
||||
to_binary(&query_rewarded_set_update_details(env, deps.storage)?)
|
||||
}
|
||||
QueryMsg::GetCurrentRewardedSetHeight {} => {
|
||||
to_binary(&query_current_rewarded_set_height(deps.storage)?)
|
||||
}
|
||||
QueryMsg::GetCurrentInterval {} => to_binary(&query_current_interval(deps.storage)?),
|
||||
QueryMsg::GetRewardedSetRefreshBlocks {} => {
|
||||
to_binary(&query_rewarded_set_refresh_minimum_blocks())
|
||||
}
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
}
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
pub fn migrate(deps: DepsMut, env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
use cw_storage_plus::Item;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// needed migration:
|
||||
/*
|
||||
1. removal of rewarding_interval_starting_block field from ContractState
|
||||
2. removal of latest_rewarding_interval_nonce field from ContractState
|
||||
3. removal of rewarding_in_progress field from ContractState
|
||||
4. interval_storage::CURRENT_INTERVAL.save(deps.storage, &Interval::default())?;
|
||||
5. interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
|
||||
6. removal of active_set_work_factor fields from ContractStateParams
|
||||
*/
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct OldContractStateParams {
|
||||
pub minimum_mixnode_pledge: Uint128,
|
||||
pub minimum_gateway_pledge: Uint128,
|
||||
pub mixnode_rewarded_set_size: u32,
|
||||
pub mixnode_active_set_size: u32,
|
||||
#[serde(default)]
|
||||
pub active_set_work_factor: u8,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct OldContractState {
|
||||
pub owner: Addr, // only the owner account can update state
|
||||
pub rewarding_validator_address: Addr,
|
||||
pub params: OldContractStateParams,
|
||||
#[serde(default)]
|
||||
pub rewarding_interval_starting_block: u64,
|
||||
#[serde(default)]
|
||||
pub latest_rewarding_interval_nonce: u32,
|
||||
#[serde(default)]
|
||||
pub rewarding_in_progress: bool,
|
||||
}
|
||||
|
||||
let old_contract_state: Item<OldContractState> = Item::new("config");
|
||||
|
||||
let old_state = old_contract_state.load(deps.storage)?;
|
||||
|
||||
let new_params = mixnet_contract_common::ContractStateParams {
|
||||
minimum_mixnode_pledge: old_state.params.minimum_mixnode_pledge,
|
||||
minimum_gateway_pledge: old_state.params.minimum_mixnode_pledge,
|
||||
mixnode_rewarded_set_size: old_state.params.mixnode_rewarded_set_size,
|
||||
mixnode_active_set_size: old_state.params.mixnode_active_set_size,
|
||||
};
|
||||
|
||||
let new_state = crate::mixnet_contract_settings::models::ContractState {
|
||||
owner: old_state.owner,
|
||||
rewarding_validator_address: old_state.rewarding_validator_address,
|
||||
params: new_params,
|
||||
};
|
||||
let rewarding_interval =
|
||||
Interval::new(0, DEFAULT_FIRST_INTERVAL_START, REWARDING_INTERVAL_LENGTH);
|
||||
|
||||
mixnet_params_storage::CONTRACT_STATE.save(deps.storage, &new_state)?;
|
||||
|
||||
interval_storage::CURRENT_INTERVAL.save(deps.storage, &rewarding_interval)?;
|
||||
interval_storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &env.block.height)?;
|
||||
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
@@ -292,14 +387,14 @@ pub mod tests {
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, from_binary};
|
||||
use mixnet_contract::PagedMixnodeResponse;
|
||||
use mixnet_contract_common::PagedMixnodeResponse;
|
||||
|
||||
#[test]
|
||||
fn initialize_contract() {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let msg = InstantiateMsg {
|
||||
rewarding_validator_address: config::defaults::REWARDING_VALIDATOR_ADDRESS.to_string(),
|
||||
rewarding_validator_address: config::defaults::DEFAULT_REWARDING_VALIDATOR.to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ use cosmwasm_std::Deps;
|
||||
use cosmwasm_std::Order;
|
||||
use cosmwasm_std::StdResult;
|
||||
use cw_storage_plus::{Bound, PrimaryKey};
|
||||
use mixnet_contract::PagedAllDelegationsResponse;
|
||||
use mixnet_contract::PagedDelegatorDelegationsResponse;
|
||||
use mixnet_contract::PagedMixDelegationsResponse;
|
||||
use mixnet_contract::{Delegation, IdentityKey};
|
||||
use mixnet_contract_common::{
|
||||
Delegation, IdentityKey, PagedAllDelegationsResponse, PagedDelegatorDelegationsResponse,
|
||||
PagedMixDelegationsResponse,
|
||||
};
|
||||
|
||||
pub(crate) fn query_all_network_delegations_paged(
|
||||
deps: Deps,
|
||||
@@ -140,7 +140,7 @@ pub(crate) mod tests {
|
||||
#[cfg(test)]
|
||||
mod querying_for_mixnode_delegations_paged {
|
||||
use super::*;
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
#[test]
|
||||
fn retrieval_obeys_limits() {
|
||||
@@ -281,7 +281,7 @@ pub(crate) mod tests {
|
||||
mod querying_for_all_mixnode_delegations_paged {
|
||||
use super::*;
|
||||
use crate::support::tests::test_helpers;
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
#[test]
|
||||
fn retrieval_obeys_limits() {
|
||||
@@ -444,7 +444,7 @@ pub(crate) mod tests {
|
||||
|
||||
// add delegation from a different address
|
||||
let delegation = Delegation::new(
|
||||
delegation_owner2.clone(),
|
||||
delegation_owner2,
|
||||
node_identity1.clone(),
|
||||
coin(1234, DENOM),
|
||||
1234,
|
||||
@@ -474,7 +474,7 @@ pub(crate) mod tests {
|
||||
// add delegation for a different node
|
||||
let delegation = Delegation::new(
|
||||
delegation_owner1.clone(),
|
||||
node_identity2.clone(),
|
||||
node_identity2,
|
||||
coin(1234, DENOM),
|
||||
1234,
|
||||
None,
|
||||
@@ -493,11 +493,7 @@ pub(crate) mod tests {
|
||||
identity: node_identity1.clone(),
|
||||
address: Addr::unchecked(delegation_owner1.clone())
|
||||
}),
|
||||
query_mixnode_delegation(
|
||||
deps.as_ref(),
|
||||
node_identity1.clone(),
|
||||
delegation_owner1.to_string()
|
||||
)
|
||||
query_mixnode_delegation(deps.as_ref(), node_identity1, delegation_owner1.to_string())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cw_storage_plus::{Index, IndexList, IndexedMap, MultiIndex};
|
||||
use mixnet_contract::{Addr, Delegation, IdentityKey};
|
||||
use mixnet_contract_common::{Addr, Delegation, IdentityKey};
|
||||
|
||||
// storage prefixes
|
||||
const DELEGATION_PK_NAMESPACE: &str = "dl";
|
||||
@@ -17,9 +17,9 @@ pub(crate) const DELEGATION_PAGE_DEFAULT_LIMIT: u32 = 250;
|
||||
type PrimaryKey = Vec<u8>;
|
||||
|
||||
pub(crate) struct DelegationIndex<'a> {
|
||||
pub(crate) owner: MultiIndex<'a, (Addr, PrimaryKey), Delegation>,
|
||||
pub(crate) owner: MultiIndex<'a, Addr, Delegation>,
|
||||
|
||||
pub(crate) mixnode: MultiIndex<'a, (IdentityKey, PrimaryKey), Delegation>,
|
||||
pub(crate) mixnode: MultiIndex<'a, IdentityKey, Delegation>,
|
||||
}
|
||||
|
||||
impl<'a> IndexList<Delegation> for DelegationIndex<'a> {
|
||||
@@ -46,12 +46,12 @@ impl<'a> IndexList<Delegation> for DelegationIndex<'a> {
|
||||
pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, DelegationIndex<'a>> {
|
||||
let indexes = DelegationIndex {
|
||||
owner: MultiIndex::new(
|
||||
|d, pk| (d.owner.clone(), pk),
|
||||
|d| d.owner.clone(),
|
||||
DELEGATION_PK_NAMESPACE,
|
||||
DELEGATION_OWNER_IDX_NAMESPACE,
|
||||
),
|
||||
mixnode: MultiIndex::new(
|
||||
|d, pk| (d.node_identity.clone(), pk),
|
||||
|d| d.node_identity.clone(),
|
||||
DELEGATION_PK_NAMESPACE,
|
||||
DELEGATION_MIXNODE_IDX_NAMESPACE,
|
||||
),
|
||||
@@ -64,7 +64,7 @@ pub(crate) fn delegations<'a>() -> IndexedMap<'a, PrimaryKey, Delegation, Delega
|
||||
mod tests {
|
||||
use crate::delegations::storage;
|
||||
use cosmwasm_std::Addr;
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
#[cfg(test)]
|
||||
mod reverse_mix_delegations {
|
||||
@@ -74,7 +74,7 @@ mod tests {
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::{coin, Order};
|
||||
use cw_storage_plus::PrimaryKey;
|
||||
use mixnet_contract::Delegation;
|
||||
use mixnet_contract_common::Delegation;
|
||||
|
||||
#[test]
|
||||
fn reverse_mix_delegation_exists() {
|
||||
@@ -94,7 +94,7 @@ mod tests {
|
||||
storage::delegations()
|
||||
.save(
|
||||
&mut deps.storage,
|
||||
(node_identity.clone(), delegation_owner.clone()).joined_key(),
|
||||
(node_identity, delegation_owner.clone()).joined_key(),
|
||||
&dummy_data,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -131,7 +131,7 @@ mod tests {
|
||||
// add delegation for a different node
|
||||
let dummy_data = Delegation::new(
|
||||
delegation_owner1.clone(),
|
||||
node_identity2.clone(),
|
||||
node_identity2,
|
||||
delegation.clone(),
|
||||
mock_env().block.height,
|
||||
None,
|
||||
@@ -156,14 +156,14 @@ mod tests {
|
||||
let dummy_data = Delegation::new(
|
||||
delegation_owner2.clone(),
|
||||
node_identity1.clone(),
|
||||
delegation.clone(),
|
||||
delegation,
|
||||
mock_env().block.height,
|
||||
None,
|
||||
);
|
||||
storage::delegations()
|
||||
.save(
|
||||
&mut deps.storage,
|
||||
(node_identity1.clone(), delegation_owner2.clone()).joined_key(),
|
||||
(node_identity1.clone(), delegation_owner2).joined_key(),
|
||||
&dummy_data,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::support::helpers::generate_storage_key;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response};
|
||||
use cw_storage_plus::PrimaryKey;
|
||||
use mixnet_contract::Delegation;
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract_common::events::{new_delegation_event, new_undelegation_event};
|
||||
use mixnet_contract_common::{Delegation, IdentityKey};
|
||||
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
|
||||
fn validate_delegation_stake(mut delegation: Vec<Coin>) -> Result<Coin, ContractError> {
|
||||
@@ -113,16 +113,21 @@ pub(crate) fn _try_delegate_to_mixnode(
|
||||
}
|
||||
None => Delegation::new(
|
||||
delegate.to_owned(),
|
||||
mix_identity,
|
||||
amount,
|
||||
mix_identity.clone(),
|
||||
amount.clone(),
|
||||
env.block.height,
|
||||
proxy,
|
||||
proxy.clone(),
|
||||
),
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(Response::default())
|
||||
Ok(Response::new().add_event(new_delegation_event(
|
||||
&delegate,
|
||||
&proxy,
|
||||
&amount,
|
||||
&mix_identity,
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn try_remove_delegation_from_mixnode(
|
||||
@@ -204,14 +209,19 @@ pub(crate) fn _try_remove_delegation_from_mixnode(
|
||||
let msg = Some(VestingContractExecuteMsg::TrackUndelegation {
|
||||
owner: delegate.as_str().to_string(),
|
||||
mix_identity: mix_identity.clone(),
|
||||
amount: old_delegation.amount,
|
||||
amount: old_delegation.amount.clone(),
|
||||
});
|
||||
|
||||
let track_undelegation_msg = wasm_execute(proxy, &msg, coins(0, DENOM))?;
|
||||
|
||||
response = response.add_message(track_undelegation_msg);
|
||||
}
|
||||
Ok(response)
|
||||
Ok(response.add_event(new_undelegation_event(
|
||||
&delegate,
|
||||
&proxy,
|
||||
&old_delegation,
|
||||
&mix_identity,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -530,7 +540,7 @@ mod tests {
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
env1,
|
||||
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
|
||||
mock_info(delegation_owner1.as_str(), &[delegation1]),
|
||||
identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -544,7 +554,7 @@ mod tests {
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
env2,
|
||||
mock_info(delegation_owner2.as_str(), &[delegation2.clone()]),
|
||||
mock_info(delegation_owner2.as_str(), &[delegation2]),
|
||||
identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -699,7 +709,7 @@ mod tests {
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(delegation_owner.as_str(), &vec![delegation_amount.clone()]),
|
||||
mock_info(delegation_owner.as_str(), &[delegation_amount.clone()]),
|
||||
identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
@@ -722,6 +732,7 @@ mod tests {
|
||||
|
||||
#[cfg(test)]
|
||||
mod removing_mix_stake_delegation {
|
||||
use crate::delegations::queries::query_mixnode_delegation;
|
||||
use cosmwasm_std::coin;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
@@ -774,11 +785,27 @@ mod tests {
|
||||
identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
Ok(Response::new().add_message(BankMsg::Send {
|
||||
let delegation = query_mixnode_delegation(
|
||||
deps.as_ref(),
|
||||
identity.clone(),
|
||||
delegation_owner.clone().into_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let expected_response = Response::new()
|
||||
.add_message(BankMsg::Send {
|
||||
to_address: delegation_owner.clone().into(),
|
||||
amount: coins(100, DENOM),
|
||||
})),
|
||||
})
|
||||
.add_event(new_undelegation_event(
|
||||
&delegation_owner,
|
||||
&None,
|
||||
&delegation,
|
||||
&identity,
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
Ok(expected_response),
|
||||
try_remove_delegation_from_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_info(delegation_owner.as_str(), &[]),
|
||||
@@ -819,12 +846,27 @@ mod tests {
|
||||
identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
|
||||
assert_eq!(
|
||||
Ok(Response::new().add_message(BankMsg::Send {
|
||||
let delegation = query_mixnode_delegation(
|
||||
deps.as_ref(),
|
||||
identity.clone(),
|
||||
delegation_owner.clone().into_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let expected_response = Response::new()
|
||||
.add_message(BankMsg::Send {
|
||||
to_address: delegation_owner.clone().into(),
|
||||
amount: coins(100, DENOM),
|
||||
})),
|
||||
})
|
||||
.add_event(new_undelegation_event(
|
||||
&delegation_owner,
|
||||
&None,
|
||||
&delegation,
|
||||
&identity,
|
||||
));
|
||||
|
||||
try_remove_mixnode(deps.as_mut(), mock_info(mixnode_owner, &[])).unwrap();
|
||||
assert_eq!(
|
||||
Ok(expected_response),
|
||||
try_remove_delegation_from_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_info(delegation_owner.as_str(), &[]),
|
||||
@@ -853,7 +895,7 @@ mod tests {
|
||||
assert!(try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
mock_env(),
|
||||
mock_info(delegation_owner1.as_str(), &[delegation1.clone()]),
|
||||
mock_info(delegation_owner1.as_str(), &[delegation1]),
|
||||
identity.clone(),
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
@@ -42,7 +42,7 @@ pub enum ContractError {
|
||||
#[error("No coin was sent for the bonding, you must send {}", DENOM)]
|
||||
NoBondFound,
|
||||
|
||||
#[error("Provided active set size is bigger than the demanded set")]
|
||||
#[error("Provided active set size is bigger than the rewarded set")]
|
||||
InvalidActiveSetSize,
|
||||
|
||||
#[error("Provided active set size is zero")]
|
||||
@@ -75,14 +75,8 @@ pub enum ContractError {
|
||||
#[error("We tried to remove more funds then are available in the Reward pool. Wanted to remove {to_remove}, but have only {reward_pool}")]
|
||||
OutOfFunds { to_remove: u128, reward_pool: u128 },
|
||||
|
||||
#[error("Received invalid rewarding interval nonce. Expected {expected}, received {received}")]
|
||||
InvalidRewardingIntervalNonce { received: u32, expected: u32 },
|
||||
|
||||
#[error("Rewarding distribution is currently in progress")]
|
||||
RewardingInProgress,
|
||||
|
||||
#[error("Rewarding distribution is currently not in progress")]
|
||||
RewardingNotInProgress,
|
||||
#[error("Received invalid interval id. Expected {expected}, received {received}")]
|
||||
InvalidIntervalId { received: u32, expected: u32 },
|
||||
|
||||
#[error("Mixnode {identity} has already been rewarded during the current rewarding interval")]
|
||||
MixnodeAlreadyRewarded { identity: IdentityKey },
|
||||
@@ -105,6 +99,29 @@ pub enum ContractError {
|
||||
#[error("Provided ed25519 signature did not verify correctly")]
|
||||
InvalidEd25519Signature,
|
||||
|
||||
#[error("Profit margin percent needs to be an integer in range [0, 100], recieved {0}")]
|
||||
#[error("Profit margin percent needs to be an integer in range [0, 100], received {0}")]
|
||||
InvalidProfitMarginPercent(u8),
|
||||
|
||||
#[error("Rewarded set height not set, was rewarding set determined?")]
|
||||
RewardSetHeightMapEmpty,
|
||||
|
||||
#[error("Received unexpected value for the active set. Got: {received}, expected: {expected}")]
|
||||
UnexpectedActiveSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("Received unexpected value for the rewarded set. Got: {received}, expected at most: {expected}")]
|
||||
UnexpectedRewardedSetSize { received: u32, expected: u32 },
|
||||
|
||||
#[error("There hasn't been sufficient delay since last rewarded set update. It was last updated at height {last_update}. The delay is {minimum_delay}. The current block height is {current_height}")]
|
||||
TooFrequentRewardedSetUpdate {
|
||||
last_update: u64,
|
||||
minimum_delay: u64,
|
||||
current_height: u64,
|
||||
},
|
||||
|
||||
#[error("Can't change to the desired interval as it's not in progress yet. It starts at {interval_start} and finishes at {interval_end}, while the current block time is {current_block_time}")]
|
||||
IntervalNotInProgress {
|
||||
current_block_time: u64,
|
||||
interval_start: i64,
|
||||
interval_end: i64,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@ use super::storage;
|
||||
use crate::mixnodes::storage::{BOND_PAGE_DEFAULT_LIMIT, BOND_PAGE_MAX_LIMIT}; // Keeps gateway and mixnode retrieval in sync by re-using the constant. Could be split into its own constant.
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract::{GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse};
|
||||
use mixnet_contract_common::{
|
||||
GatewayBond, GatewayOwnershipResponse, IdentityKey, PagedGatewayResponse,
|
||||
};
|
||||
|
||||
pub(crate) fn query_gateways_paged(
|
||||
deps: Deps,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
|
||||
use mixnet_contract::{GatewayBond, IdentityKeyRef};
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
|
||||
|
||||
// storage prefixes
|
||||
const GATEWAYS_PK_NAMESPACE: &str = "gt";
|
||||
@@ -41,9 +41,7 @@ mod tests {
|
||||
use cosmwasm_std::StdResult;
|
||||
use cosmwasm_std::Storage;
|
||||
use cosmwasm_std::{coin, Addr, Uint128};
|
||||
use mixnet_contract::GatewayBond;
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract::{Gateway, IdentityKeyRef};
|
||||
use mixnet_contract_common::{Gateway, GatewayBond, IdentityKey, IdentityKeyRef};
|
||||
|
||||
// currently this is only used in tests but may become useful later on
|
||||
pub(crate) fn read_gateway_pledge_amount(
|
||||
@@ -87,7 +85,7 @@ mod tests {
|
||||
|
||||
let gateway_bond = GatewayBond {
|
||||
pledge_amount: coin(pledge_amount, DENOM),
|
||||
owner: node_owner.clone(),
|
||||
owner: node_owner,
|
||||
block_height: 12_345,
|
||||
gateway: Gateway {
|
||||
identity_key: node_identity.clone(),
|
||||
|
||||
@@ -9,7 +9,8 @@ use config::defaults::DENOM;
|
||||
use cosmwasm_std::{
|
||||
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
|
||||
};
|
||||
use mixnet_contract::{Gateway, GatewayBond, Layer};
|
||||
use mixnet_contract_common::events::{new_gateway_bonding_event, new_gateway_unbonding_event};
|
||||
use mixnet_contract_common::{Gateway, GatewayBond, Layer};
|
||||
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
|
||||
pub fn try_add_gateway(
|
||||
@@ -97,12 +98,24 @@ pub(crate) fn _try_add_gateway(
|
||||
&gateway.identity_key,
|
||||
)?;
|
||||
|
||||
let bond = GatewayBond::new(pledge, owner, env.block.height, gateway, proxy);
|
||||
let gateway_identity = gateway.identity_key.clone();
|
||||
let bond = GatewayBond::new(
|
||||
pledge.clone(),
|
||||
owner.clone(),
|
||||
env.block.height,
|
||||
gateway,
|
||||
proxy.clone(),
|
||||
);
|
||||
|
||||
storage::gateways().save(deps.storage, bond.identity(), &bond)?;
|
||||
mixnet_params_storage::increment_layer_count(deps.storage, Layer::Gateway)?;
|
||||
|
||||
Ok(Response::new())
|
||||
Ok(Response::new().add_event(new_gateway_bonding_event(
|
||||
&owner,
|
||||
&proxy,
|
||||
&pledge,
|
||||
&gateway_identity,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn try_remove_gateway_on_behalf(
|
||||
@@ -155,23 +168,24 @@ pub(crate) fn _try_remove_gateway(
|
||||
// decrement layer count
|
||||
mixnet_params_storage::decrement_layer_count(deps.storage, Layer::Gateway)?;
|
||||
|
||||
let mut response = Response::new()
|
||||
.add_message(return_tokens)
|
||||
.add_attribute("action", "unbond")
|
||||
.add_attribute("address", owner.clone())
|
||||
.add_attribute("gateway_bond", gateway_bond.to_string());
|
||||
let mut response = Response::new().add_message(return_tokens);
|
||||
|
||||
if let Some(proxy) = &proxy {
|
||||
let msg = VestingContractExecuteMsg::TrackUnbondGateway {
|
||||
owner: owner.as_str().to_string(),
|
||||
amount: gateway_bond.pledge_amount,
|
||||
amount: gateway_bond.pledge_amount(),
|
||||
};
|
||||
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
|
||||
response = response.add_message(track_unbond_message);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
Ok(response.add_event(new_gateway_unbonding_event(
|
||||
&owner,
|
||||
&proxy,
|
||||
&gateway_bond.pledge_amount,
|
||||
gateway_bond.identity(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn validate_gateway_pledge(
|
||||
@@ -212,12 +226,10 @@ pub mod tests {
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::test_helpers;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::attr;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, BankMsg, Response};
|
||||
use cosmwasm_std::{from_binary, Addr, Uint128};
|
||||
use mixnet_contract::Gateway;
|
||||
use mixnet_contract::{ExecuteMsg, PagedGatewayResponse, QueryMsg};
|
||||
use mixnet_contract_common::{ExecuteMsg, Gateway, PagedGatewayResponse, QueryMsg};
|
||||
|
||||
#[test]
|
||||
fn gateway_add() {
|
||||
@@ -454,19 +466,6 @@ pub mod tests {
|
||||
let msg = ExecuteMsg::UnbondGateway {};
|
||||
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
||||
|
||||
// we should see log messages come back showing an unbond message
|
||||
let expected_attributes = vec![
|
||||
attr("action", "unbond"),
|
||||
attr("address", "fred"),
|
||||
attr(
|
||||
"gateway_bond",
|
||||
format!(
|
||||
"amount: {} {}, owner: fred, identity: {}",
|
||||
INITIAL_GATEWAY_PLEDGE, DENOM, fred_identity
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
// we should see a funds transfer from the contract back to fred
|
||||
let expected_message = BankMsg::Send {
|
||||
to_address: String::from(info.sender),
|
||||
@@ -474,11 +473,17 @@ pub mod tests {
|
||||
};
|
||||
|
||||
// run the executor and check that we got back the correct results
|
||||
let expected = Response::new()
|
||||
.add_attributes(expected_attributes)
|
||||
.add_message(expected_message);
|
||||
let expected_response =
|
||||
Response::new()
|
||||
.add_message(expected_message)
|
||||
.add_event(new_gateway_unbonding_event(
|
||||
&Addr::unchecked("fred"),
|
||||
&None,
|
||||
&tests::fixtures::good_gateway_pledge()[0],
|
||||
&fred_identity,
|
||||
));
|
||||
|
||||
assert_eq!(remove_fred, expected);
|
||||
assert_eq!(expected_response, remove_fred);
|
||||
|
||||
// only 1 node now exists, owned by bob:
|
||||
let gateway_bonds = tests::queries::get_gateways(&mut deps);
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
pub mod transactions;
|
||||
@@ -0,0 +1,425 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{Env, Order, StdResult, Storage};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract_common::{
|
||||
IdentityKey, Interval, IntervalRewardedSetHeightsResponse, PagedRewardedSetResponse,
|
||||
RewardedSetNodeStatus, RewardedSetUpdateDetails,
|
||||
};
|
||||
|
||||
pub fn query_current_interval(storage: &dyn Storage) -> Result<Interval, ContractError> {
|
||||
Ok(storage::CURRENT_INTERVAL.load(storage)?)
|
||||
}
|
||||
|
||||
pub(crate) fn query_rewarded_set_refresh_minimum_blocks() -> u64 {
|
||||
crate::constants::REWARDED_SET_REFRESH_BLOCKS
|
||||
}
|
||||
|
||||
pub fn query_rewarded_set_heights_for_interval(
|
||||
storage: &dyn Storage,
|
||||
interval_id: u32,
|
||||
) -> Result<IntervalRewardedSetHeightsResponse, ContractError> {
|
||||
// I don't think we have to deal with paging here as at most we're going to have 720 values here
|
||||
// and I think the validators are capable of performing 720 storage reads at once if they're only
|
||||
// reading u64 (+ u8) values...
|
||||
let heights = storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
|
||||
.prefix(interval_id)
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.map(|val| val.map(|(height, _)| height))
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
Ok(IntervalRewardedSetHeightsResponse {
|
||||
interval_id,
|
||||
heights,
|
||||
})
|
||||
}
|
||||
|
||||
// note: I have removed the `query_rewarded_set_for_interval`, because I don't think it's appropriate
|
||||
// for the contract to go through so much data (i.e. all "rewarded" sets of particular interval) in one go.
|
||||
// To achieve the same result, the client would have to instead first call `query_rewarded_set_heights_for_interval`
|
||||
// to learn the heights used in given interval and then for each of them `query_rewarded_set` for that particular height.
|
||||
|
||||
pub fn query_current_rewarded_set_height(storage: &dyn Storage) -> Result<u64, ContractError> {
|
||||
Ok(storage::CURRENT_REWARDED_SET_HEIGHT.load(storage)?)
|
||||
}
|
||||
|
||||
fn query_rewarded_set_at_height(
|
||||
storage: &dyn Storage,
|
||||
height: u64,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: u32,
|
||||
) -> Result<Vec<(IdentityKey, RewardedSetNodeStatus)>, ContractError> {
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
|
||||
let rewarded_set = storage::REWARDED_SET
|
||||
.prefix(height)
|
||||
.range(storage, start, None, Order::Ascending)
|
||||
.take(limit as usize)
|
||||
.collect::<StdResult<_>>()?;
|
||||
Ok(rewarded_set)
|
||||
}
|
||||
|
||||
pub fn query_rewarded_set(
|
||||
storage: &dyn Storage,
|
||||
height: Option<u64>,
|
||||
start_after: Option<IdentityKey>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedRewardedSetResponse, ContractError> {
|
||||
let height = match height {
|
||||
Some(height) => height,
|
||||
None => query_current_rewarded_set_height(storage)?,
|
||||
};
|
||||
let limit = limit
|
||||
.unwrap_or(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT)
|
||||
.min(storage::REWARDED_NODE_MAX_PAGE_LIMIT);
|
||||
|
||||
// query for an additional element to determine paging requirements
|
||||
let mut paged_result = query_rewarded_set_at_height(storage, height, start_after, limit + 1)?;
|
||||
|
||||
if paged_result.len() > limit as usize {
|
||||
paged_result.truncate(limit as usize);
|
||||
Ok(PagedRewardedSetResponse {
|
||||
start_next_after: paged_result.last().map(|res| res.0.clone()),
|
||||
identities: paged_result,
|
||||
at_height: height,
|
||||
})
|
||||
} else {
|
||||
Ok(PagedRewardedSetResponse {
|
||||
identities: paged_result,
|
||||
start_next_after: None,
|
||||
at_height: height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// this was all put together into the same query so that all information would be synced together
|
||||
pub fn query_rewarded_set_update_details(
|
||||
env: Env,
|
||||
storage: &dyn Storage,
|
||||
) -> Result<RewardedSetUpdateDetails, ContractError> {
|
||||
Ok(RewardedSetUpdateDetails {
|
||||
refresh_rate_blocks: query_rewarded_set_refresh_minimum_blocks(),
|
||||
last_refreshed_block: query_current_rewarded_set_height(storage)?,
|
||||
current_height: env.block.height,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::interval::storage::REWARDED_NODE_MAX_PAGE_LIMIT;
|
||||
use crate::support::tests::test_helpers;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
|
||||
fn store_rewarded_nodes(
|
||||
storage: &mut dyn Storage,
|
||||
height: u64,
|
||||
active_set: u32,
|
||||
rewarded_set: u32,
|
||||
) -> Vec<IdentityKey> {
|
||||
let identities = (0..rewarded_set)
|
||||
.map(|i| format!("identity{:04}", i))
|
||||
.collect::<Vec<_>>();
|
||||
storage::save_rewarded_set(storage, height, active_set, identities.clone()).unwrap();
|
||||
identities
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_for_rewarded_set_heights_for_interval() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
// no data
|
||||
assert!(
|
||||
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 0)
|
||||
.unwrap()
|
||||
.heights
|
||||
.is_empty()
|
||||
);
|
||||
|
||||
// 100 heights
|
||||
for i in 0..100 {
|
||||
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
|
||||
.save(deps.as_mut().storage, (1, i), &0u8)
|
||||
.unwrap();
|
||||
}
|
||||
let expected = (0..100).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
expected,
|
||||
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 1)
|
||||
.unwrap()
|
||||
.heights
|
||||
);
|
||||
|
||||
// 100 heights for different interval
|
||||
for i in 200..300 {
|
||||
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
|
||||
.save(deps.as_mut().storage, (10, i), &0u8)
|
||||
.unwrap();
|
||||
}
|
||||
let expected = (200..300).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
expected,
|
||||
query_rewarded_set_heights_for_interval(deps.as_ref().storage, 10)
|
||||
.unwrap()
|
||||
.heights
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_for_rewarded_set_at_height() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
// store some nodes
|
||||
let identities1 = store_rewarded_nodes(deps.as_mut().storage, 1, 100, 200);
|
||||
let identities2 = store_rewarded_nodes(deps.as_mut().storage, 2, 50, 200);
|
||||
let identities3 = store_rewarded_nodes(deps.as_mut().storage, 3, 150, 200);
|
||||
let identities4 = store_rewarded_nodes(deps.as_mut().storage, 4, 300, 500);
|
||||
let identities5 = store_rewarded_nodes(deps.as_mut().storage, 5, 500, 500);
|
||||
|
||||
// expected2 and 3 are basically sanity checks to ensure changing active set size (increase or decrease)
|
||||
// doesn't affect the ordering
|
||||
|
||||
let expected1 = identities1
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, identity)| {
|
||||
if i < 100 {
|
||||
(identity, RewardedSetNodeStatus::Active)
|
||||
} else {
|
||||
(identity, RewardedSetNodeStatus::Standby)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
expected1,
|
||||
query_rewarded_set_at_height(deps.as_ref().storage, 1, None, 1000).unwrap()
|
||||
);
|
||||
|
||||
let expected2 = identities2
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, identity)| {
|
||||
if i < 50 {
|
||||
(identity, RewardedSetNodeStatus::Active)
|
||||
} else {
|
||||
(identity, RewardedSetNodeStatus::Standby)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
expected2,
|
||||
query_rewarded_set_at_height(deps.as_ref().storage, 2, None, 1000).unwrap()
|
||||
);
|
||||
|
||||
let expected3 = identities3
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, identity)| {
|
||||
if i < 150 {
|
||||
(identity, RewardedSetNodeStatus::Active)
|
||||
} else {
|
||||
(identity, RewardedSetNodeStatus::Standby)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
expected3,
|
||||
query_rewarded_set_at_height(deps.as_ref().storage, 3, None, 1000).unwrap()
|
||||
);
|
||||
|
||||
// check limit and paging
|
||||
// active: 300, rewarded: 500
|
||||
let first_100 = identities4
|
||||
.iter()
|
||||
.take(100)
|
||||
.map(|identity| (identity.clone(), RewardedSetNodeStatus::Active))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
first_100,
|
||||
query_rewarded_set_at_height(deps.as_ref().storage, 4, None, 100).unwrap()
|
||||
);
|
||||
|
||||
let expected_single1 = vec![("identity0299".to_string(), RewardedSetNodeStatus::Active)];
|
||||
let expected_single2 = vec![("identity0300".to_string(), RewardedSetNodeStatus::Standby)];
|
||||
assert_eq!(
|
||||
expected_single1,
|
||||
query_rewarded_set_at_height(
|
||||
deps.as_ref().storage,
|
||||
4,
|
||||
Some("identity0298".to_string()),
|
||||
1
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
expected_single2,
|
||||
query_rewarded_set_at_height(
|
||||
deps.as_ref().storage,
|
||||
4,
|
||||
Some("identity0299".to_string()),
|
||||
1
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let last_100 = identities4
|
||||
.iter()
|
||||
.skip(400)
|
||||
.map(|identity| (identity.clone(), RewardedSetNodeStatus::Standby))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
last_100,
|
||||
query_rewarded_set_at_height(
|
||||
deps.as_ref().storage,
|
||||
4,
|
||||
Some("identity0399".to_string()),
|
||||
100
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
// all nodes are in the active set
|
||||
let expected5 = identities5
|
||||
.into_iter()
|
||||
.map(|identity| (identity, RewardedSetNodeStatus::Active))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
expected5,
|
||||
query_rewarded_set_at_height(deps.as_ref().storage, 5, None, 1000).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_for_rewarded_set() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
let current_height = 123;
|
||||
let other_height = 456;
|
||||
let different_height = 789;
|
||||
|
||||
storage::CURRENT_REWARDED_SET_HEIGHT
|
||||
.save(deps.as_mut().storage, ¤t_height)
|
||||
.unwrap();
|
||||
|
||||
let identities1 = store_rewarded_nodes(deps.as_mut().storage, current_height, 50, 100);
|
||||
let identities2 = store_rewarded_nodes(deps.as_mut().storage, other_height, 100, 200);
|
||||
let identities3 = store_rewarded_nodes(
|
||||
deps.as_mut().storage,
|
||||
different_height,
|
||||
storage::REWARDED_NODE_MAX_PAGE_LIMIT,
|
||||
storage::REWARDED_NODE_MAX_PAGE_LIMIT * 2,
|
||||
);
|
||||
|
||||
// if height is not set, current height is used, else it's just passed
|
||||
let expected1 = PagedRewardedSetResponse {
|
||||
identities: identities1
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, identity)| {
|
||||
if i < 50 {
|
||||
(identity, RewardedSetNodeStatus::Active)
|
||||
} else {
|
||||
(identity, RewardedSetNodeStatus::Standby)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
start_next_after: None,
|
||||
at_height: current_height,
|
||||
};
|
||||
let expected2 = PagedRewardedSetResponse {
|
||||
identities: identities2
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, identity)| {
|
||||
if i < 100 {
|
||||
(identity, RewardedSetNodeStatus::Active)
|
||||
} else {
|
||||
(identity, RewardedSetNodeStatus::Standby)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
start_next_after: None,
|
||||
at_height: other_height,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
Ok(expected1),
|
||||
query_rewarded_set(deps.as_ref().storage, None, None, None)
|
||||
);
|
||||
assert_eq!(
|
||||
Ok(expected2),
|
||||
query_rewarded_set(deps.as_ref().storage, Some(other_height), None, None)
|
||||
);
|
||||
|
||||
// if limit is not set, a default one is used instead
|
||||
let expected3 = PagedRewardedSetResponse {
|
||||
identities: identities3
|
||||
.iter()
|
||||
.take(storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT as usize)
|
||||
.cloned()
|
||||
.map(|identity| (identity, RewardedSetNodeStatus::Active))
|
||||
.collect::<Vec<_>>(),
|
||||
start_next_after: Some(format!(
|
||||
"identity{:04}",
|
||||
storage::REWARDED_NODE_DEFAULT_PAGE_LIMIT - 1
|
||||
)),
|
||||
at_height: different_height,
|
||||
};
|
||||
assert_eq!(
|
||||
Ok(expected3),
|
||||
query_rewarded_set(deps.as_ref().storage, Some(different_height), None, None)
|
||||
);
|
||||
|
||||
// limit cannot be larger that pre-defined maximum
|
||||
let expected4 = PagedRewardedSetResponse {
|
||||
identities: identities3
|
||||
.iter()
|
||||
.take(storage::REWARDED_NODE_MAX_PAGE_LIMIT as usize)
|
||||
.cloned()
|
||||
.map(|identity| (identity, RewardedSetNodeStatus::Active))
|
||||
.collect::<Vec<_>>(),
|
||||
start_next_after: Some(format!(
|
||||
"identity{:04}",
|
||||
storage::REWARDED_NODE_MAX_PAGE_LIMIT - 1
|
||||
)),
|
||||
at_height: different_height,
|
||||
};
|
||||
assert_eq!(
|
||||
Ok(expected4),
|
||||
query_rewarded_set(
|
||||
deps.as_ref().storage,
|
||||
Some(different_height),
|
||||
None,
|
||||
Some(REWARDED_NODE_MAX_PAGE_LIMIT * 100)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn querying_for_rewarded_set_update_details() {
|
||||
let env = mock_env();
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
let current_height = 123;
|
||||
storage::CURRENT_REWARDED_SET_HEIGHT
|
||||
.save(deps.as_mut().storage, ¤t_height)
|
||||
.unwrap();
|
||||
|
||||
// returns whatever is in the correct environment
|
||||
assert_eq!(
|
||||
RewardedSetUpdateDetails {
|
||||
refresh_rate_blocks: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
|
||||
last_refreshed_block: current_height,
|
||||
current_height: env.block.height
|
||||
},
|
||||
query_rewarded_set_update_details(env, deps.as_ref().storage).unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{StdResult, Storage};
|
||||
use cw_storage_plus::{Item, Map};
|
||||
use mixnet_contract_common::{IdentityKey, Interval, RewardedSetNodeStatus};
|
||||
|
||||
// type aliases for better reasoning for storage keys
|
||||
// (I found it helpful)
|
||||
type BlockHeight = u64;
|
||||
type IntervalId = u32;
|
||||
|
||||
// TODO: those values need to be verified
|
||||
pub(crate) const REWARDED_NODE_DEFAULT_PAGE_LIMIT: u32 = 1000;
|
||||
pub(crate) const REWARDED_NODE_MAX_PAGE_LIMIT: u32 = 1500;
|
||||
|
||||
pub(crate) const CURRENT_INTERVAL: Item<Interval> = Item::new("cep");
|
||||
pub(crate) const CURRENT_REWARDED_SET_HEIGHT: Item<BlockHeight> = Item::new("crh");
|
||||
|
||||
// I've changed the `()` data to an `u8` as after serializing `()` is represented as "null",
|
||||
// taking more space than a single digit u8. If we don't care about what's there, why not go with more efficient approach? : )
|
||||
pub(crate) const REWARDED_SET_HEIGHTS_FOR_INTERVAL: Map<(IntervalId, BlockHeight), u8> =
|
||||
Map::new("rsh");
|
||||
|
||||
// pub(crate) const REWARDED_SET: Map<(u64, IdentityKey), NodeStatus> = Map::new("rs");
|
||||
pub(crate) const REWARDED_SET: Map<(BlockHeight, IdentityKey), RewardedSetNodeStatus> =
|
||||
Map::new("rs");
|
||||
|
||||
pub(crate) fn save_rewarded_set(
|
||||
storage: &mut dyn Storage,
|
||||
height: BlockHeight,
|
||||
active_set_size: u32,
|
||||
entries: Vec<IdentityKey>,
|
||||
) -> StdResult<()> {
|
||||
for (i, identity) in entries.into_iter().enumerate() {
|
||||
// first k nodes are active
|
||||
let set_status = if i < active_set_size as usize {
|
||||
RewardedSetNodeStatus::Active
|
||||
} else {
|
||||
RewardedSetNodeStatus::Standby
|
||||
};
|
||||
|
||||
REWARDED_SET.save(storage, (height, identity), &set_status)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::test_helpers;
|
||||
|
||||
#[test]
|
||||
fn saving_rewarded_set() {
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
let active_set_size = 100;
|
||||
let mut nodes = Vec::new();
|
||||
for i in 0..1000 {
|
||||
nodes.push(format!("identity{:04}", i))
|
||||
}
|
||||
|
||||
save_rewarded_set(deps.as_mut().storage, 1234, active_set_size, nodes).unwrap();
|
||||
|
||||
// first k nodes MUST BE active
|
||||
for i in 0..1000 {
|
||||
let identity = format!("identity{:04}", i);
|
||||
if i < active_set_size {
|
||||
assert_eq!(
|
||||
RewardedSetNodeStatus::Active,
|
||||
REWARDED_SET
|
||||
.load(deps.as_ref().storage, (1234, identity))
|
||||
.unwrap()
|
||||
)
|
||||
} else {
|
||||
assert_eq!(
|
||||
RewardedSetNodeStatus::Standby,
|
||||
REWARDED_SET
|
||||
.load(deps.as_ref().storage, (1234, identity))
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::storage;
|
||||
use crate::error::ContractError;
|
||||
use crate::error::ContractError::IntervalNotInProgress;
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, Storage};
|
||||
use mixnet_contract_common::events::{new_advance_interval_event, new_change_rewarded_set_event};
|
||||
use mixnet_contract_common::IdentityKey;
|
||||
|
||||
pub fn try_write_rewarded_set(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
rewarded_set: Vec<IdentityKey>,
|
||||
active_set_size: u32,
|
||||
) -> Result<Response, ContractError> {
|
||||
let state = mixnet_params_storage::CONTRACT_STATE.load(deps.storage)?;
|
||||
|
||||
// check if this is executed by the permitted validator, if not reject the transaction
|
||||
if info.sender != state.rewarding_validator_address {
|
||||
return Err(ContractError::Unauthorized);
|
||||
}
|
||||
|
||||
// sanity check to make sure the sending validator is in sync with the contract state
|
||||
// (i.e. so that we'd known that top k nodes are actually expected to be active)
|
||||
if active_set_size != state.params.mixnode_active_set_size {
|
||||
return Err(ContractError::UnexpectedActiveSetSize {
|
||||
received: active_set_size,
|
||||
expected: state.params.mixnode_active_set_size,
|
||||
});
|
||||
}
|
||||
|
||||
if rewarded_set.len() as u32 > state.params.mixnode_rewarded_set_size {
|
||||
return Err(ContractError::UnexpectedRewardedSetSize {
|
||||
received: rewarded_set.len() as u32,
|
||||
expected: state.params.mixnode_rewarded_set_size,
|
||||
});
|
||||
}
|
||||
|
||||
let last_update = storage::CURRENT_REWARDED_SET_HEIGHT.load(deps.storage)?;
|
||||
let block_height = env.block.height;
|
||||
|
||||
if last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS > block_height {
|
||||
return Err(ContractError::TooFrequentRewardedSetUpdate {
|
||||
last_update,
|
||||
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
|
||||
current_height: block_height,
|
||||
});
|
||||
}
|
||||
|
||||
let current_interval = storage::CURRENT_INTERVAL.load(deps.storage)?.id();
|
||||
let num_nodes = rewarded_set.len();
|
||||
|
||||
storage::save_rewarded_set(deps.storage, block_height, active_set_size, rewarded_set)?;
|
||||
storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL.save(
|
||||
deps.storage,
|
||||
(current_interval, block_height),
|
||||
&0u8,
|
||||
)?;
|
||||
storage::CURRENT_REWARDED_SET_HEIGHT.save(deps.storage, &block_height)?;
|
||||
|
||||
Ok(Response::new().add_event(new_change_rewarded_set_event(
|
||||
state.params.mixnode_active_set_size,
|
||||
state.params.mixnode_rewarded_set_size,
|
||||
num_nodes as u32,
|
||||
current_interval,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn try_advance_interval(
|
||||
env: Env,
|
||||
storage: &mut dyn Storage,
|
||||
) -> Result<Response, ContractError> {
|
||||
// in theory, we could have just changed the state and relied on its reversal upon failed
|
||||
// execution, but better safe than sorry and do not modify the state at all unless we know
|
||||
// all checks have succeeded.
|
||||
let current_interval = storage::CURRENT_INTERVAL.load(storage)?;
|
||||
let next_interval = current_interval.next_interval();
|
||||
|
||||
if next_interval.start_unix_timestamp() > env.block.time.seconds() as i64 {
|
||||
// the reason for this check is as follows:
|
||||
// nobody, even trusted validators, should be able to continuously keep advancing intervals,
|
||||
// because otherwise it would be possible for them to continuously keep rewarding nodes.
|
||||
//
|
||||
// Therefore, even if "trusted" validator, responsible for rewarding, is malicious,
|
||||
// they can't send rewards more often than every `REWARDED_SET_REFRESH_BLOCKS`
|
||||
// and changing this value requires going through governance and having agreement of
|
||||
// the super-majority of the validators (by stake)
|
||||
return Err(IntervalNotInProgress {
|
||||
current_block_time: env.block.time.seconds(),
|
||||
interval_start: next_interval.start_unix_timestamp(),
|
||||
interval_end: next_interval.end_unix_timestamp(),
|
||||
});
|
||||
}
|
||||
|
||||
storage::CURRENT_INTERVAL.save(storage, &next_interval)?;
|
||||
|
||||
Ok(Response::new().add_event(new_advance_interval_event(next_interval)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::test_helpers;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Timestamp;
|
||||
use mixnet_contract_common::{Interval, RewardedSetNodeStatus};
|
||||
use std::time::Duration;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[test]
|
||||
fn writing_rewarded_set() {
|
||||
let mut env = mock_env();
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let current_state = mixnet_params_storage::CONTRACT_STATE
|
||||
.load(deps.as_mut().storage)
|
||||
.unwrap();
|
||||
let authorised_sender = mock_info(current_state.rewarding_validator_address.as_str(), &[]);
|
||||
let full_rewarded_set = (0..current_state.params.mixnode_rewarded_set_size)
|
||||
.map(|i| format!("identity{:04}", i))
|
||||
.collect::<Vec<_>>();
|
||||
let last_update = 123;
|
||||
storage::CURRENT_REWARDED_SET_HEIGHT
|
||||
.save(deps.as_mut().storage, &last_update)
|
||||
.unwrap();
|
||||
|
||||
// can only be performed by the permitted validator
|
||||
let dummy_sender = mock_info("dummy_sender", &[]);
|
||||
assert_eq!(
|
||||
Err(ContractError::Unauthorized),
|
||||
try_write_rewarded_set(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
dummy_sender,
|
||||
full_rewarded_set.clone(),
|
||||
current_state.params.mixnode_active_set_size
|
||||
)
|
||||
);
|
||||
|
||||
// the sender must use the same active set size as the one defined in the contract
|
||||
assert_eq!(
|
||||
Err(ContractError::UnexpectedActiveSetSize {
|
||||
received: 123,
|
||||
expected: current_state.params.mixnode_active_set_size
|
||||
}),
|
||||
try_write_rewarded_set(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
authorised_sender.clone(),
|
||||
full_rewarded_set.clone(),
|
||||
123
|
||||
)
|
||||
);
|
||||
|
||||
// the sender cannot provide more nodes than the rewarded set size
|
||||
let mut bigger_set = full_rewarded_set.clone();
|
||||
bigger_set.push("another_node".to_string());
|
||||
assert_eq!(
|
||||
Err(ContractError::UnexpectedRewardedSetSize {
|
||||
received: current_state.params.mixnode_rewarded_set_size + 1,
|
||||
expected: current_state.params.mixnode_rewarded_set_size
|
||||
}),
|
||||
try_write_rewarded_set(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
authorised_sender.clone(),
|
||||
bigger_set,
|
||||
current_state.params.mixnode_active_set_size
|
||||
)
|
||||
);
|
||||
|
||||
// cannot be performed too soon after a previous update
|
||||
env.block.height = last_update + 1;
|
||||
assert_eq!(
|
||||
Err(ContractError::TooFrequentRewardedSetUpdate {
|
||||
last_update,
|
||||
minimum_delay: crate::constants::REWARDED_SET_REFRESH_BLOCKS,
|
||||
current_height: last_update + 1,
|
||||
}),
|
||||
try_write_rewarded_set(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
authorised_sender.clone(),
|
||||
full_rewarded_set.clone(),
|
||||
current_state.params.mixnode_active_set_size
|
||||
)
|
||||
);
|
||||
|
||||
// after successful rewarded set write, all internal storage structures are updated appropriately
|
||||
env.block.height = last_update + crate::constants::REWARDED_SET_REFRESH_BLOCKS;
|
||||
let expected_response = Response::new().add_event(new_change_rewarded_set_event(
|
||||
current_state.params.mixnode_active_set_size,
|
||||
current_state.params.mixnode_rewarded_set_size,
|
||||
full_rewarded_set.len() as u32,
|
||||
0,
|
||||
));
|
||||
|
||||
assert_eq!(
|
||||
Ok(expected_response),
|
||||
try_write_rewarded_set(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
authorised_sender,
|
||||
full_rewarded_set.clone(),
|
||||
current_state.params.mixnode_active_set_size
|
||||
)
|
||||
);
|
||||
|
||||
for (i, rewarded_node) in full_rewarded_set.into_iter().enumerate() {
|
||||
if (i as u32) < current_state.params.mixnode_active_set_size {
|
||||
assert_eq!(
|
||||
RewardedSetNodeStatus::Active,
|
||||
storage::REWARDED_SET
|
||||
.load(deps.as_ref().storage, (env.block.height, rewarded_node))
|
||||
.unwrap()
|
||||
)
|
||||
} else {
|
||||
assert_eq!(
|
||||
RewardedSetNodeStatus::Standby,
|
||||
storage::REWARDED_SET
|
||||
.load(deps.as_ref().storage, (env.block.height, rewarded_node))
|
||||
.unwrap()
|
||||
)
|
||||
}
|
||||
}
|
||||
assert!(storage::REWARDED_SET_HEIGHTS_FOR_INTERVAL
|
||||
.has(deps.as_ref().storage, (0, env.block.height)));
|
||||
assert_eq!(
|
||||
env.block.height,
|
||||
storage::CURRENT_REWARDED_SET_HEIGHT
|
||||
.load(deps.as_ref().storage)
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advancing_interval() {
|
||||
let mut env = mock_env();
|
||||
let mut deps = test_helpers::init_contract();
|
||||
|
||||
// 1609459200 = 2021-01-01
|
||||
// 1640995200 = 2022-01-01
|
||||
// 1641081600 = 2022-01-02
|
||||
// 1643673600 = 2022-02-01
|
||||
// 1672531200 = 2023-01-01
|
||||
|
||||
let current_interval = Interval::new(
|
||||
0,
|
||||
OffsetDateTime::from_unix_timestamp(1640995200).unwrap(),
|
||||
Duration::from_secs(60 * 60 * 720),
|
||||
);
|
||||
let next_interval = current_interval.next_interval();
|
||||
storage::CURRENT_INTERVAL
|
||||
.save(deps.as_mut().storage, ¤t_interval)
|
||||
.unwrap();
|
||||
|
||||
// fails if the current interval hasn't finished yet i.e. the new interval hasn't begun
|
||||
env.block.time = Timestamp::from_seconds(1641081600);
|
||||
assert_eq!(
|
||||
Err(ContractError::IntervalNotInProgress {
|
||||
current_block_time: 1641081600,
|
||||
interval_start: next_interval.start_unix_timestamp(),
|
||||
interval_end: next_interval.end_unix_timestamp()
|
||||
}),
|
||||
try_advance_interval(env.clone(), deps.as_mut().storage)
|
||||
);
|
||||
|
||||
// same if the current blocktime is set to BEFORE the first interval has even begun
|
||||
// (say we decided to set the first interval to be some time in the future at initialisation)
|
||||
env.block.time = Timestamp::from_seconds(1609459200);
|
||||
assert_eq!(
|
||||
Err(ContractError::IntervalNotInProgress {
|
||||
current_block_time: 1609459200,
|
||||
interval_start: next_interval.start_unix_timestamp(),
|
||||
interval_end: next_interval.end_unix_timestamp()
|
||||
}),
|
||||
try_advance_interval(env.clone(), deps.as_mut().storage)
|
||||
);
|
||||
|
||||
// works otherwise
|
||||
|
||||
// interval that has just finished
|
||||
env.block.time =
|
||||
Timestamp::from_seconds(next_interval.start_unix_timestamp() as u64 + 10000);
|
||||
let expected_new_interval = current_interval.next_interval();
|
||||
let expected_response =
|
||||
Response::new().add_event(new_advance_interval_event(expected_new_interval));
|
||||
assert_eq!(
|
||||
Ok(expected_response),
|
||||
try_advance_interval(env.clone(), deps.as_mut().storage)
|
||||
);
|
||||
|
||||
// interval way back in the past (i.e. 'somebody' failed to advance it for a long time)
|
||||
env.block.time = Timestamp::from_seconds(1672531200);
|
||||
storage::CURRENT_INTERVAL
|
||||
.save(deps.as_mut().storage, ¤t_interval)
|
||||
.unwrap();
|
||||
let expected_new_interval = current_interval.next_interval();
|
||||
let expected_response =
|
||||
Response::new().add_event(new_advance_interval_event(expected_new_interval));
|
||||
assert_eq!(
|
||||
Ok(expected_response),
|
||||
try_advance_interval(env.clone(), deps.as_mut().storage)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod constants;
|
||||
pub mod contract;
|
||||
mod delegations;
|
||||
mod error;
|
||||
mod gateways;
|
||||
mod interval;
|
||||
mod mixnet_contract_settings;
|
||||
mod mixnodes;
|
||||
mod rewards;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use mixnet_contract::ContractStateParams;
|
||||
use mixnet_contract_common::ContractStateParams;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -11,11 +11,4 @@ pub struct ContractState {
|
||||
pub owner: Addr, // only the owner account can update state
|
||||
pub rewarding_validator_address: Addr,
|
||||
pub params: ContractStateParams,
|
||||
|
||||
// keep track of the changes to the current rewarding interval,
|
||||
// i.e. at which block has the latest rewarding occurred
|
||||
// and whether another run is already in progress
|
||||
pub rewarding_interval_starting_block: u64,
|
||||
pub latest_rewarding_interval_nonce: u32,
|
||||
pub rewarding_in_progress: bool,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use super::storage;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use mixnet_contract::{ContractStateParams, MixnetContractVersion, RewardingIntervalResponse};
|
||||
use mixnet_contract_common::{ContractStateParams, MixnetContractVersion};
|
||||
|
||||
pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult<ContractStateParams> {
|
||||
storage::CONTRACT_STATE
|
||||
@@ -11,16 +11,6 @@ pub(crate) fn query_contract_settings_params(deps: Deps) -> StdResult<ContractSt
|
||||
.map(|settings| settings.params)
|
||||
}
|
||||
|
||||
pub(crate) fn query_rewarding_interval(deps: Deps) -> StdResult<RewardingIntervalResponse> {
|
||||
let state = storage::CONTRACT_STATE.load(deps.storage)?;
|
||||
|
||||
Ok(RewardingIntervalResponse {
|
||||
current_rewarding_interval_starting_block: state.rewarding_interval_starting_block,
|
||||
current_rewarding_interval_nonce: state.latest_rewarding_interval_nonce,
|
||||
rewarding_in_progress: state.rewarding_in_progress,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn query_contract_version() -> MixnetContractVersion {
|
||||
// as per docs
|
||||
// env! macro will expand to the value of the named environment variable at
|
||||
@@ -55,11 +45,7 @@ pub(crate) mod tests {
|
||||
minimum_gateway_pledge: 456u128.into(),
|
||||
mixnode_rewarded_set_size: 1000,
|
||||
mixnode_active_set_size: 500,
|
||||
active_set_work_factor: 10,
|
||||
},
|
||||
rewarding_interval_starting_block: 123,
|
||||
latest_rewarding_interval_nonce: 0,
|
||||
rewarding_in_progress: false,
|
||||
};
|
||||
|
||||
storage::CONTRACT_STATE
|
||||
|
||||
@@ -5,8 +5,7 @@ use crate::mixnet_contract_settings::models::ContractState;
|
||||
use cosmwasm_std::StdResult;
|
||||
use cosmwasm_std::Storage;
|
||||
use cw_storage_plus::Item;
|
||||
use mixnet_contract::Layer;
|
||||
use mixnet_contract::LayerDistribution;
|
||||
use mixnet_contract_common::{Layer, LayerDistribution};
|
||||
|
||||
pub(crate) const CONTRACT_STATE: Item<ContractState> = Item::new("config");
|
||||
pub(crate) const LAYERS: Item<LayerDistribution> = Item::new("layers");
|
||||
|
||||
@@ -6,7 +6,8 @@ use crate::error::ContractError;
|
||||
use cosmwasm_std::DepsMut;
|
||||
use cosmwasm_std::MessageInfo;
|
||||
use cosmwasm_std::Response;
|
||||
use mixnet_contract::ContractStateParams;
|
||||
use mixnet_contract_common::events::new_settings_update_event;
|
||||
use mixnet_contract_common::ContractStateParams;
|
||||
|
||||
pub(crate) fn try_update_contract_settings(
|
||||
deps: DepsMut,
|
||||
@@ -34,10 +35,12 @@ pub(crate) fn try_update_contract_settings(
|
||||
return Err(ContractError::InvalidActiveSetSize);
|
||||
}
|
||||
|
||||
let response = Response::new().add_event(new_settings_update_event(&state.params, ¶ms));
|
||||
|
||||
state.params = params;
|
||||
storage::CONTRACT_STATE.save(deps.storage, &state)?;
|
||||
|
||||
Ok(Response::default())
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -49,7 +52,7 @@ pub mod tests {
|
||||
use crate::support::tests::test_helpers;
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::Response;
|
||||
use mixnet_contract::ContractStateParams;
|
||||
use mixnet_contract_common::ContractStateParams;
|
||||
|
||||
#[test]
|
||||
fn updating_contract_settings() {
|
||||
@@ -60,17 +63,15 @@ pub mod tests {
|
||||
minimum_gateway_pledge: INITIAL_GATEWAY_PLEDGE,
|
||||
mixnode_rewarded_set_size: 100,
|
||||
mixnode_active_set_size: 50,
|
||||
active_set_work_factor: 10,
|
||||
};
|
||||
|
||||
let initial_params = storage::CONTRACT_STATE
|
||||
.load(deps.as_ref().storage)
|
||||
.unwrap()
|
||||
.params;
|
||||
|
||||
// sanity check to ensure new_params are different than the default ones
|
||||
assert_ne!(
|
||||
new_params,
|
||||
storage::CONTRACT_STATE
|
||||
.load(deps.as_ref().storage)
|
||||
.unwrap()
|
||||
.params
|
||||
);
|
||||
assert_ne!(new_params, initial_params);
|
||||
|
||||
// cannot be updated from non-owner account
|
||||
let info = mock_info("not-the-creator", &[]);
|
||||
@@ -80,7 +81,10 @@ pub mod tests {
|
||||
// but works fine from the creator account
|
||||
let info = mock_info("creator", &[]);
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
|
||||
assert_eq!(res, Ok(Response::default()));
|
||||
assert_eq!(
|
||||
res,
|
||||
Ok(Response::new().add_event(new_settings_update_event(&initial_params, &new_params)))
|
||||
);
|
||||
|
||||
// and the state is actually updated
|
||||
let current_state = storage::CONTRACT_STATE.load(deps.as_ref().storage).unwrap();
|
||||
@@ -90,21 +94,21 @@ pub mod tests {
|
||||
let info = mock_info("creator", &[]);
|
||||
let mut new_params = current_state.params.clone();
|
||||
new_params.mixnode_rewarded_set_size = new_params.mixnode_active_set_size - 1;
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
|
||||
assert_eq!(Err(ContractError::InvalidActiveSetSize), res);
|
||||
|
||||
// error is thrown for 0 size rewarded set
|
||||
let info = mock_info("creator", &[]);
|
||||
let mut new_params = current_state.params.clone();
|
||||
new_params.mixnode_rewarded_set_size = 0;
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
|
||||
assert_eq!(Err(ContractError::ZeroRewardedSet), res);
|
||||
|
||||
// error is thrown for 0 size active set
|
||||
let info = mock_info("creator", &[]);
|
||||
let mut new_params = current_state.params.clone();
|
||||
let mut new_params = current_state.params;
|
||||
new_params.mixnode_active_set_size = 0;
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params.clone());
|
||||
let res = try_update_contract_settings(deps.as_mut(), info, new_params);
|
||||
assert_eq!(Err(ContractError::ZeroActiveSet), res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
use super::storage;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use mixnet_contract::{IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse};
|
||||
use mixnet_contract_common::{
|
||||
IdentityKey, MixNodeBond, MixOwnershipResponse, PagedMixnodeResponse,
|
||||
};
|
||||
|
||||
pub fn query_mixnodes_paged(
|
||||
deps: Deps,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use crate::mixnet_contract_settings::storage as mixnet_params_storage;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use mixnet_contract::LayerDistribution;
|
||||
use mixnet_contract_common::LayerDistribution;
|
||||
|
||||
pub(crate) fn query_layer_distribution(deps: Deps) -> StdResult<LayerDistribution> {
|
||||
mixnet_params_storage::LAYERS.load(deps.storage)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{StdResult, Storage, Uint128};
|
||||
use cw_storage_plus::{Index, IndexList, IndexedMap, Map, UniqueIndex};
|
||||
use mixnet_contract::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
|
||||
use mixnet_contract_common::{Addr, Coin, IdentityKeyRef, Layer, MixNode, MixNodeBond};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
@@ -49,7 +49,6 @@ pub(crate) struct StoredMixnodeBond {
|
||||
pub layer: Layer,
|
||||
pub block_height: u64,
|
||||
pub mix_node: MixNode,
|
||||
pub profit_margin_percent: Option<u8>,
|
||||
pub proxy: Option<Addr>,
|
||||
}
|
||||
|
||||
@@ -60,7 +59,6 @@ impl StoredMixnodeBond {
|
||||
layer: Layer,
|
||||
block_height: u64,
|
||||
mix_node: MixNode,
|
||||
profit_margin_percent: Option<u8>,
|
||||
proxy: Option<Addr>,
|
||||
) -> Self {
|
||||
StoredMixnodeBond {
|
||||
@@ -69,7 +67,6 @@ impl StoredMixnodeBond {
|
||||
layer,
|
||||
block_height,
|
||||
mix_node,
|
||||
profit_margin_percent,
|
||||
proxy,
|
||||
}
|
||||
}
|
||||
@@ -141,8 +138,7 @@ mod tests {
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::testing::MockStorage;
|
||||
use cosmwasm_std::{coin, Addr, Uint128};
|
||||
use mixnet_contract::IdentityKey;
|
||||
use mixnet_contract::MixNode;
|
||||
use mixnet_contract_common::{IdentityKey, MixNode};
|
||||
|
||||
#[test]
|
||||
fn mixnode_single_read_retrieval() {
|
||||
@@ -173,14 +169,13 @@ mod tests {
|
||||
|
||||
let mixnode_bond = StoredMixnodeBond {
|
||||
pledge_amount: coin(pledge_value, DENOM),
|
||||
owner: node_owner.clone(),
|
||||
owner: node_owner,
|
||||
layer: Layer::One,
|
||||
block_height: 12_345,
|
||||
mix_node: MixNode {
|
||||
identity_key: node_identity.clone(),
|
||||
..tests::fixtures::mix_node_fixture()
|
||||
},
|
||||
profit_margin_percent: None,
|
||||
proxy: None,
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ use config::defaults::DENOM;
|
||||
use cosmwasm_std::{
|
||||
coins, wasm_execute, Addr, BankMsg, Coin, DepsMut, Env, MessageInfo, Response, Uint128,
|
||||
};
|
||||
use mixnet_contract::MixNode;
|
||||
use mixnet_contract_common::events::{new_mixnode_bonding_event, new_mixnode_unbonding_event};
|
||||
use mixnet_contract_common::MixNode;
|
||||
use vesting_contract::messages::ExecuteMsg as VestingContractExecuteMsg;
|
||||
|
||||
pub fn try_add_mixnode(
|
||||
@@ -109,13 +110,12 @@ fn _try_add_mixnode(
|
||||
let layer = layer_distribution.choose_with_fewest();
|
||||
|
||||
let stored_bond = StoredMixnodeBond::new(
|
||||
pledge_amount,
|
||||
owner,
|
||||
pledge_amount.clone(),
|
||||
owner.clone(),
|
||||
layer,
|
||||
env.block.height,
|
||||
mix_node,
|
||||
None,
|
||||
proxy,
|
||||
proxy.clone(),
|
||||
);
|
||||
|
||||
// technically we don't have to set the total_delegation bucket, but it makes things easier
|
||||
@@ -133,7 +133,13 @@ fn _try_add_mixnode(
|
||||
|
||||
mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?;
|
||||
|
||||
Ok(Response::new())
|
||||
Ok(Response::new().add_event(new_mixnode_bonding_event(
|
||||
&owner,
|
||||
&proxy,
|
||||
&pledge_amount,
|
||||
identity,
|
||||
stored_bond.layer,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn try_remove_mixnode_on_behalf(
|
||||
@@ -186,22 +192,60 @@ pub(crate) fn _try_remove_mixnode(
|
||||
// decrement layer count
|
||||
mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?;
|
||||
|
||||
let mut response = Response::new()
|
||||
.add_message(return_tokens)
|
||||
.add_attribute("action", "unbond")
|
||||
.add_attribute("mixnode_bond", mixnode_bond.to_string());
|
||||
let mut response = Response::new().add_message(return_tokens);
|
||||
|
||||
if let Some(proxy) = &proxy {
|
||||
let msg = VestingContractExecuteMsg::TrackUnbondMixnode {
|
||||
owner: owner.as_str().to_string(),
|
||||
amount: mixnode_bond.pledge_amount,
|
||||
amount: mixnode_bond.pledge_amount(),
|
||||
};
|
||||
|
||||
let track_unbond_message = wasm_execute(proxy, &msg, coins(0, DENOM))?;
|
||||
response = response.add_message(track_unbond_message);
|
||||
}
|
||||
|
||||
Ok(response)
|
||||
Ok(response.add_event(new_mixnode_unbonding_event(
|
||||
&owner,
|
||||
&proxy,
|
||||
&mixnode_bond.pledge_amount,
|
||||
mixnode_bond.identity(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn try_update_mixnode_config(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
profit_margin_percent: u8,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = deps.api.addr_validate(info.sender.as_ref())?;
|
||||
let mix_identity = storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, owner.clone())?
|
||||
.ok_or(ContractError::NoAssociatedMixNodeBond { owner })?
|
||||
.1
|
||||
.identity()
|
||||
.clone();
|
||||
|
||||
// We don't have to check lower bound as its an u8
|
||||
if profit_margin_percent > 100 {
|
||||
return Err(ContractError::InvalidProfitMarginPercent(
|
||||
profit_margin_percent,
|
||||
));
|
||||
}
|
||||
|
||||
storage::mixnodes().update(deps.storage, &mix_identity, |mixnode_bond_opt| {
|
||||
mixnode_bond_opt
|
||||
.map(|mut mixnode_bond| {
|
||||
mixnode_bond.mix_node.profit_margin_percent = profit_margin_percent;
|
||||
mixnode_bond.block_height = env.block.height;
|
||||
mixnode_bond
|
||||
})
|
||||
.ok_or(ContractError::NoBondFound)
|
||||
})?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
fn validate_mixnode_pledge(
|
||||
@@ -242,13 +286,12 @@ pub mod tests {
|
||||
use crate::support::tests;
|
||||
use crate::support::tests::test_helpers;
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::attr;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, BankMsg, Response};
|
||||
use cosmwasm_std::{from_binary, Addr, Uint128};
|
||||
use mixnet_contract::Layer;
|
||||
use mixnet_contract::MixNode;
|
||||
use mixnet_contract::{ExecuteMsg, LayerDistribution, PagedMixnodeResponse, QueryMsg};
|
||||
use mixnet_contract_common::{
|
||||
ExecuteMsg, Layer, LayerDistribution, MixNode, PagedMixnodeResponse, QueryMsg,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn mixnode_add() {
|
||||
@@ -507,18 +550,6 @@ pub mod tests {
|
||||
let msg = ExecuteMsg::UnbondMixnode {};
|
||||
let remove_fred = execute(deps.as_mut(), mock_env(), info.clone(), msg).unwrap();
|
||||
|
||||
// we should see log messages come back showing an unbond message
|
||||
let expected_attributes = vec![
|
||||
attr("action", "unbond"),
|
||||
attr(
|
||||
"mixnode_bond",
|
||||
format!(
|
||||
"amount: {}{}, owner: fred, identity: {}",
|
||||
INITIAL_MIXNODE_PLEDGE, DENOM, fred_identity
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
// we should see a funds transfer from the contract back to fred
|
||||
let expected_message = BankMsg::Send {
|
||||
to_address: String::from(info.sender),
|
||||
@@ -526,11 +557,17 @@ pub mod tests {
|
||||
};
|
||||
|
||||
// run the executor and check that we got back the correct results
|
||||
let expected = Response::new()
|
||||
.add_attributes(expected_attributes)
|
||||
.add_message(expected_message);
|
||||
let expected_response =
|
||||
Response::new()
|
||||
.add_message(expected_message)
|
||||
.add_event(new_mixnode_unbonding_event(
|
||||
&Addr::unchecked("fred"),
|
||||
&None,
|
||||
&tests::fixtures::good_gateway_pledge()[0],
|
||||
&fred_identity,
|
||||
));
|
||||
|
||||
assert_eq!(remove_fred, expected);
|
||||
assert_eq!(expected_response, remove_fred);
|
||||
|
||||
// only 1 node now exists, owned by bob:
|
||||
let mix_node_bonds = tests::queries::get_mix_nodes(&mut deps);
|
||||
@@ -587,6 +624,77 @@ pub mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn updating_mixnode_config() {
|
||||
let sender = "bob";
|
||||
let mut deps = test_helpers::init_contract();
|
||||
let info = mock_info(sender, &[]);
|
||||
|
||||
// try updating a non existing mixnode bond
|
||||
let msg = ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent: 10,
|
||||
};
|
||||
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
|
||||
assert_eq!(
|
||||
ret,
|
||||
Err(ContractError::NoAssociatedMixNodeBond {
|
||||
owner: Addr::unchecked(sender)
|
||||
})
|
||||
);
|
||||
|
||||
test_helpers::add_mixnode(
|
||||
sender,
|
||||
tests::fixtures::good_mixnode_pledge(),
|
||||
deps.as_mut(),
|
||||
);
|
||||
|
||||
// check the initial profit margin is set to the fixture value
|
||||
let fixture_profit_margin = tests::fixtures::mix_node_fixture().profit_margin_percent;
|
||||
assert_eq!(
|
||||
fixture_profit_margin,
|
||||
storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, Addr::unchecked("bob"))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1
|
||||
.mix_node
|
||||
.profit_margin_percent
|
||||
);
|
||||
|
||||
// try updating with an invalid value
|
||||
let profit_margin_percent = 101;
|
||||
let msg = ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
};
|
||||
let ret = execute(deps.as_mut(), mock_env(), info.clone(), msg);
|
||||
assert_eq!(
|
||||
ret,
|
||||
Err(ContractError::InvalidProfitMarginPercent(
|
||||
profit_margin_percent
|
||||
))
|
||||
);
|
||||
|
||||
let profit_margin_percent = fixture_profit_margin + 10;
|
||||
let msg = ExecuteMsg::UpdateMixnodeConfig {
|
||||
profit_margin_percent,
|
||||
};
|
||||
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
assert_eq!(
|
||||
profit_margin_percent,
|
||||
storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, Addr::unchecked("bob"))
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1
|
||||
.mix_node
|
||||
.profit_margin_percent
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validating_mixnode_bond() {
|
||||
// you must send SOME funds
|
||||
@@ -649,6 +757,6 @@ pub mod tests {
|
||||
assert_eq!(alice_node.mix_node.identity_key, alice_identity);
|
||||
assert_eq!(alice_node.layer, Layer::One);
|
||||
assert_eq!(bob_node.mix_node.identity_key, bob_identity);
|
||||
assert_eq!(bob_node.layer, mixnet_contract::Layer::Two);
|
||||
assert_eq!(bob_node.layer, mixnet_contract_common::Layer::Two);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ use super::storage;
|
||||
use crate::error::ContractError;
|
||||
use crate::mixnodes::storage as mixnodes_storage;
|
||||
use cosmwasm_std::{Addr, Storage, Uint128};
|
||||
use mixnet_contract::mixnode::DelegatorRewardParams;
|
||||
use mixnet_contract::{
|
||||
use mixnet_contract_common::mixnode::DelegatorRewardParams;
|
||||
use mixnet_contract_common::{
|
||||
IdentityKey, IdentityKeyRef, PendingDelegatorRewarding, RewardingResult, RewardingStatus,
|
||||
};
|
||||
|
||||
@@ -55,7 +55,7 @@ pub(crate) fn update_post_rewarding_storage(
|
||||
|
||||
pub(crate) fn update_rewarding_status(
|
||||
storage: &mut dyn Storage,
|
||||
rewarding_interval_nonce: u32,
|
||||
interval_id: u32,
|
||||
mix_identity: IdentityKey,
|
||||
rewarding_results: RewardingResult,
|
||||
next_start: Option<Addr>,
|
||||
@@ -64,7 +64,7 @@ pub(crate) fn update_rewarding_status(
|
||||
if let Some(next_start) = next_start {
|
||||
storage::REWARDING_STATUS.save(
|
||||
storage,
|
||||
(rewarding_interval_nonce.into(), mix_identity),
|
||||
(interval_id, mix_identity),
|
||||
&RewardingStatus::PendingNextDelegatorPage(PendingDelegatorRewarding {
|
||||
running_results: rewarding_results,
|
||||
next_start,
|
||||
@@ -74,7 +74,7 @@ pub(crate) fn update_rewarding_status(
|
||||
} else {
|
||||
storage::REWARDING_STATUS.save(
|
||||
storage,
|
||||
(rewarding_interval_nonce.into(), mix_identity),
|
||||
(interval_id, mix_identity),
|
||||
&RewardingStatus::Complete(rewarding_results),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use super::storage;
|
||||
use cosmwasm_std::Uint128;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use mixnet_contract::{IdentityKey, MixnodeRewardingStatusResponse};
|
||||
use mixnet_contract_common::{IdentityKey, MixnodeRewardingStatusResponse};
|
||||
|
||||
pub(crate) fn query_reward_pool(deps: Deps) -> StdResult<Uint128> {
|
||||
storage::REWARD_POOL.load(deps.storage)
|
||||
@@ -17,12 +17,9 @@ pub(crate) fn query_circulating_supply(deps: Deps) -> StdResult<Uint128> {
|
||||
pub(crate) fn query_rewarding_status(
|
||||
deps: Deps,
|
||||
mix_identity: IdentityKey,
|
||||
rewarding_interval_nonce: u32,
|
||||
interval_id: u32,
|
||||
) -> StdResult<MixnodeRewardingStatusResponse> {
|
||||
let status = storage::REWARDING_STATUS.may_load(
|
||||
deps.storage,
|
||||
(rewarding_interval_nonce.into(), mix_identity),
|
||||
)?;
|
||||
let status = storage::REWARDING_STATUS.may_load(deps.storage, (interval_id, mix_identity))?;
|
||||
|
||||
Ok(MixnodeRewardingStatusResponse { status })
|
||||
}
|
||||
@@ -37,16 +34,17 @@ pub(crate) mod tests {
|
||||
|
||||
#[cfg(test)]
|
||||
mod querying_for_rewarding_status {
|
||||
use super::storage;
|
||||
use super::*;
|
||||
use crate::constants;
|
||||
use crate::delegations::transactions::try_delegate_to_mixnode;
|
||||
use crate::rewards::transactions::{
|
||||
try_begin_mixnode_rewarding, try_finish_mixnode_rewarding, try_reward_mixnode,
|
||||
try_reward_next_mixnode_delegators,
|
||||
try_reward_mixnode, try_reward_next_mixnode_delegators,
|
||||
};
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{coin, Addr};
|
||||
use mixnet_contract::{RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT};
|
||||
use mixnet_contract_common::{
|
||||
RewardingResult, RewardingStatus, MIXNODE_DELEGATORS_PAGE_LIMIT,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn returns_empty_status_for_unrewarded_nodes() {
|
||||
@@ -70,19 +68,17 @@ pub(crate) mod tests {
|
||||
.is_none()
|
||||
);
|
||||
|
||||
// node was rewarded but for different epoch
|
||||
// node was rewarded but for different interval
|
||||
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
|
||||
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
|
||||
try_reward_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
env,
|
||||
info,
|
||||
node_identity.clone(),
|
||||
tests::fixtures::node_rewarding_params_fixture(100),
|
||||
1,
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap();
|
||||
|
||||
assert!(query_rewarding_status(deps.as_ref(), node_identity, 2)
|
||||
.unwrap()
|
||||
@@ -107,22 +103,20 @@ pub(crate) mod tests {
|
||||
deps.as_mut(),
|
||||
);
|
||||
|
||||
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
|
||||
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
|
||||
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
|
||||
try_reward_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
info.clone(),
|
||||
info,
|
||||
node_identity.clone(),
|
||||
tests::fixtures::node_rewarding_params_fixture(100),
|
||||
1,
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
try_finish_mixnode_rewarding(deps.as_mut(), info.clone(), 1).unwrap();
|
||||
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap();
|
||||
assert!(matches!(res.status, Some(RewardingStatus::Complete(..))));
|
||||
|
||||
match res.status.unwrap() {
|
||||
@@ -151,40 +145,32 @@ pub(crate) mod tests {
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(
|
||||
&*format!("delegator{:04}", i),
|
||||
&vec![coin(200_000000, DENOM)],
|
||||
),
|
||||
mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]),
|
||||
node_identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
test_helpers::update_env_and_progress_interval(&mut env, deps.as_mut().storage);
|
||||
|
||||
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
|
||||
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 2).unwrap();
|
||||
|
||||
try_reward_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
env,
|
||||
info.clone(),
|
||||
node_identity.clone(),
|
||||
tests::fixtures::node_rewarding_params_fixture(100),
|
||||
2,
|
||||
1,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// rewards all pending
|
||||
try_reward_next_mixnode_delegators(
|
||||
deps.as_mut(),
|
||||
info.clone(),
|
||||
node_identity.to_string(),
|
||||
2,
|
||||
)
|
||||
.unwrap();
|
||||
try_reward_next_mixnode_delegators(deps.as_mut(), info, node_identity.to_string(), 1)
|
||||
.unwrap();
|
||||
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 2).unwrap();
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
|
||||
assert!(matches!(res.status, Some(RewardingStatus::Complete(..))));
|
||||
|
||||
match res.status.unwrap() {
|
||||
@@ -222,31 +208,27 @@ pub(crate) mod tests {
|
||||
try_delegate_to_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
mock_info(
|
||||
&*format!("delegator{:04}", i),
|
||||
&vec![coin(200_000000, DENOM)],
|
||||
),
|
||||
mock_info(&*format!("delegator{:04}", i), &[coin(200_000000, DENOM)]),
|
||||
node_identity.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
env.block.height += storage::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
env.block.height += constants::MINIMUM_BLOCK_AGE_FOR_REWARDING;
|
||||
|
||||
let info = mock_info(rewarding_validator_address.as_ref(), &[]);
|
||||
try_begin_mixnode_rewarding(deps.as_mut(), env.clone(), info.clone(), 1).unwrap();
|
||||
|
||||
try_reward_mixnode(
|
||||
deps.as_mut(),
|
||||
env.clone(),
|
||||
env,
|
||||
info,
|
||||
node_identity.clone(),
|
||||
tests::fixtures::node_rewarding_params_fixture(100),
|
||||
1,
|
||||
0,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 1).unwrap();
|
||||
let res = query_rewarding_status(deps.as_ref(), node_identity, 0).unwrap();
|
||||
assert!(matches!(
|
||||
res.status,
|
||||
Some(RewardingStatus::PendingNextDelegatorPage(..))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user