Feature/dkg publish vk (#1747)
* Save to disk coconut keypair * Check verification keys of the other signers * Post verification key to chain * Add multisig propose/vote for vks * Execute the proposal * Parse announce address argument * Gateway uses chain data * Network requester uses chain data * Native&socks5 clients use chain data * Credential client signature uses chain data * Remove redundant api endpoints * Undo debugging logging * Fix some tests * Fix clippy * Fix wasm client and contract test * More contract clippy * Update CHANGELOG * Use a bigger expiry period then the testing one
This commit is contained in:
committed by
GitHub
parent
f1deebc0f1
commit
2db1bc8efa
@@ -8,13 +8,20 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
|
||||
|
||||
- binaries: add `-c` shortform for `--config-env-file`
|
||||
- websocket-requests: add server response signalling current packet queue length in the client
|
||||
- contracts: DKG contract that handles coconut key generation ([#1678][#1708][#1747])
|
||||
- validator-api: generate coconut keys interactively, using DKG and multisig contracts ([#1678][#1708][#1747])
|
||||
|
||||
### Changed
|
||||
|
||||
- clients: add concept of transmission lanes to better handle multiple data streams ([#1720])
|
||||
- clients,validator-api: take coconut signers from the chain instead of specifying them via CLI ([#1747])
|
||||
- multisig contract: add DKG contract to the list of addresses that can create proposals ([#1747])
|
||||
- socks5-client: wait closing inbound connection until data is sent, and throttle incoming data in general ([#1783])
|
||||
|
||||
[#1678]: https://github.com/nymtech/nym/pull/1678
|
||||
[#1708]: https://github.com/nymtech/nym/pull/1708
|
||||
[#1720]: https://github.com/nymtech/nym/pull/1720
|
||||
[#1747]: https://github.com/nymtech/nym/pull/1747
|
||||
[#1783]: https://github.com/nymtech/nym/pull/1783
|
||||
|
||||
|
||||
|
||||
Generated
+40
-9
@@ -642,6 +642,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"contracts-common",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -963,7 +965,6 @@ dependencies = [
|
||||
"crypto",
|
||||
"rand 0.7.3",
|
||||
"thiserror",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"validator-client",
|
||||
]
|
||||
@@ -1242,9 +1243,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-utils"
|
||||
version = "0.13.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "babd2c090f39d07ce5bf2556962305e795daa048ce20a93709eb591476e4a29e"
|
||||
checksum = "9dbaecb78c8e8abfd6b4258c7f4fbeb5c49a5e45ee4d910d3240ee8e1d714e1b"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"schemars",
|
||||
@@ -1253,10 +1254,22 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.13.2"
|
||||
name = "cw2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f871854338a54c7bb094d16ffe17212b93b146d9659dbce4c9402a9b77e240ef"
|
||||
checksum = "04cf4639517490dd36b333bbd6c4fbd92e325fd0acf4683b41753bc5eb63bfc1"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw3"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe19462a7f644ba60c19d3443cb90d00c50d9b6b3b0a3a7fca93df8261af979b"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
@@ -1265,10 +1278,26 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.13.2"
|
||||
name = "cw3-fixed-multisig"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4476d6a7c13c46ed9ff260bd0e1cf648dc37b13f483822e1ff2a431f0f6ee52"
|
||||
checksum = "df54aa54c13f405ec4ab36b6217538bc957d439eee58f89312db05a79caf6706"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw3",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cw4"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0acc3549d5ce11c6901b3a676f2e2628684722197054d97cd0101ea174ed5cbd"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
@@ -2952,6 +2981,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -3485,6 +3515,7 @@ dependencies = [
|
||||
"getrandom 0.2.6",
|
||||
"group 0.11.0",
|
||||
"itertools",
|
||||
"pemstore",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
|
||||
@@ -138,7 +138,7 @@ impl TopologyRefresherConfig {
|
||||
}
|
||||
|
||||
pub struct TopologyRefresher {
|
||||
validator_client: validator_client::ApiClient,
|
||||
validator_client: validator_client::client::ApiClient,
|
||||
client_version: String,
|
||||
|
||||
validator_api_urls: Vec<Url>,
|
||||
@@ -154,7 +154,9 @@ impl TopologyRefresher {
|
||||
cfg.validator_api_urls.shuffle(&mut thread_rng());
|
||||
|
||||
TopologyRefresher {
|
||||
validator_client: validator_client::ApiClient::new(cfg.validator_api_urls[0].clone()),
|
||||
validator_client: validator_client::client::ApiClient::new(
|
||||
cfg.validator_api_urls[0].clone(),
|
||||
),
|
||||
client_version: cfg.client_version,
|
||||
validator_api_urls: cfg.validator_api_urls,
|
||||
topology_accessor,
|
||||
|
||||
@@ -125,6 +125,10 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
pub fn set_custom_validators(&mut self, validator_urls: Vec<Url>) {
|
||||
self.client.validator_urls = validator_urls;
|
||||
}
|
||||
|
||||
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
||||
self.client.validator_api_urls = validator_api_urls;
|
||||
}
|
||||
@@ -179,6 +183,10 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.ack_key_file.clone()
|
||||
}
|
||||
|
||||
pub fn get_validator_endpoints(&self) -> Vec<Url> {
|
||||
self.client.validator_urls.clone()
|
||||
}
|
||||
|
||||
pub fn get_validator_api_endpoints(&self) -> Vec<Url> {
|
||||
self.client.validator_api_urls.clone()
|
||||
}
|
||||
@@ -306,6 +314,9 @@ pub struct Client<T> {
|
||||
#[serde(default)]
|
||||
disabled_credentials_mode: bool,
|
||||
|
||||
/// Addresses to nymd validators via which the client can communicate with the chain.
|
||||
validator_urls: Vec<Url>,
|
||||
|
||||
/// Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls: Vec<Url>,
|
||||
|
||||
@@ -354,6 +365,7 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
id: "".to_string(),
|
||||
disabled_credentials_mode: true,
|
||||
validator_urls: vec![],
|
||||
validator_api_urls: vec![],
|
||||
private_identity_key_file: Default::default(),
|
||||
public_identity_key_file: Default::default(),
|
||||
|
||||
@@ -31,7 +31,7 @@ pub async fn query_gateway_details(
|
||||
let validator_api = validator_servers
|
||||
.choose(&mut thread_rng())
|
||||
.ok_or(ClientCoreError::ListOfValidatorApisIsEmpty)?;
|
||||
let validator_client = validator_client::ApiClient::new(validator_api.clone());
|
||||
let validator_client = validator_client::client::ApiClient::new(validator_api.clone());
|
||||
|
||||
log::trace!("Fetching list of gateways from: {}", validator_api);
|
||||
let gateways = validator_client.get_cached_gateways().await?;
|
||||
|
||||
@@ -14,8 +14,9 @@ use credential_storage::PersistentStorage;
|
||||
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use network_defaults::VOUCHER_INFO;
|
||||
use network_defaults::{NymNetworkDetails, VOUCHER_INFO};
|
||||
use validator_client::nymd::tx::Hash;
|
||||
use validator_client::{CoconutApiClient, Config};
|
||||
|
||||
use crate::client::Client;
|
||||
use crate::error::{CredentialClientError, Result};
|
||||
@@ -107,10 +108,9 @@ pub(crate) struct GetCredential {
|
||||
/// The hash of a successful deposit transaction
|
||||
#[clap(long)]
|
||||
tx_hash: String,
|
||||
/// The URLs to the validator-api endpoints the are run as coconut signer authorities, separated
|
||||
/// by comma (,)
|
||||
/// The nymd URL that should be used
|
||||
#[clap(long)]
|
||||
signer_authorities: String,
|
||||
nymd_url: String,
|
||||
/// If we want to get the signature without attaching a blind sign request; it is expected that
|
||||
/// there is already a signature stored on the signer
|
||||
#[clap(long, parse(from_flag))]
|
||||
@@ -124,7 +124,10 @@ impl Execute for GetCredential {
|
||||
.get::<State>(&self.tx_hash)
|
||||
.ok_or(CredentialClientError::NoDeposit)?;
|
||||
|
||||
let urls = config::parse_validators(&self.signer_authorities);
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let config = Config::try_from_nym_network_details(&network_details)?;
|
||||
let client = validator_client::Client::new_query(config)?;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?;
|
||||
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let bandwidth_credential_attributes = if self.__no_request {
|
||||
@@ -178,8 +181,12 @@ impl Execute for GetCredential {
|
||||
)?);
|
||||
db.set(&self.tx_hash, &state).unwrap();
|
||||
|
||||
let signature =
|
||||
obtain_aggregate_signature(¶ms, &bandwidth_credential_attributes, &urls).await?;
|
||||
let signature = obtain_aggregate_signature(
|
||||
¶ms,
|
||||
&bandwidth_credential_attributes,
|
||||
&coconut_api_clients,
|
||||
)
|
||||
.await?;
|
||||
shared_storage
|
||||
.insert_coconut_credential(
|
||||
state.amount.to_string(),
|
||||
|
||||
@@ -8,6 +8,7 @@ use credentials::error::Error as CredentialError;
|
||||
use crypto::asymmetric::encryption::KeyRecoveryError;
|
||||
use crypto::asymmetric::identity::Ed25519RecoveryError;
|
||||
use validator_client::nymd::error::NymdError;
|
||||
use validator_client::ValidatorClientError;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, CredentialClientError>;
|
||||
|
||||
@@ -16,6 +17,9 @@ pub enum CredentialClientError {
|
||||
#[error("Nymd error: {0}")]
|
||||
Nymd(#[from] NymdError),
|
||||
|
||||
#[error("Validator client error: {0}")]
|
||||
ValidatorClientError(#[from] ValidatorClientError),
|
||||
|
||||
#[error("Credential error: {0}")]
|
||||
Credential(#[from] CredentialError),
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ id = '{{ client.id }}'
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
|
||||
|
||||
# Addresses to nymd validators via which the client can communicate with the chain.
|
||||
validator_urls = [
|
||||
{{#each client.validator_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls = [
|
||||
{{#each client.validator_api_urls }}
|
||||
|
||||
@@ -201,11 +201,22 @@ impl NymClient {
|
||||
.expect("provided gateway id is invalid!");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
);
|
||||
let bandwidth_controller = {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let client = validator_client::Client::new_query(client_config)
|
||||
.expect("Could not construct query client");
|
||||
let coconut_api_clients =
|
||||
validator_client::CoconutApiClient::all_coconut_api_clients(&client)
|
||||
.await
|
||||
.expect("Could not query api clients");
|
||||
BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
coconut_api_clients,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
|
||||
@@ -25,9 +25,13 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
|
||||
/// Whether to not start the websocket
|
||||
#[clap(long)]
|
||||
@@ -52,7 +56,8 @@ pub(crate) struct Init {
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
nymd_validators: init_config.nymd_validators,
|
||||
api_validators: init_config.api_validators,
|
||||
disable_socket: init_config.disable_socket,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
@@ -75,7 +75,8 @@ pub(crate) enum Commands {
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
api_validators: Option<String>,
|
||||
disable_socket: bool,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
@@ -98,7 +99,18 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), ClientError> {
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
if let Some(raw_validators) = args.nymd_validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validators(config::parse_validators(&raw_validators));
|
||||
} else if std::env::var(network_defaults::var_names::CONFIGURED).is_ok() {
|
||||
let raw_validators = std::env::var(network_defaults::var_names::NYMD_VALIDATOR)
|
||||
.expect("nymd validator not set");
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validators(config::parse_validators(&raw_validators));
|
||||
}
|
||||
if let Some(raw_validators) = args.api_validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(config::parse_validators(&raw_validators));
|
||||
|
||||
@@ -18,9 +18,13 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
id: String,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
|
||||
/// Id of the gateway we want to connect to. If overridden, it is user's responsibility to
|
||||
/// ensure prior registration happened
|
||||
@@ -45,7 +49,8 @@ pub(crate) struct Run {
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
nymd_validators: run_config.nymd_validators,
|
||||
api_validators: run_config.api_validators,
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
@@ -23,6 +23,13 @@ id = '{{ client.id }}'
|
||||
# to claim bandwidth without presenting bandwidth credentials.
|
||||
disabled_credentials_mode = {{ client.disabled_credentials_mode }}
|
||||
|
||||
# Addresses to nymd validators via which the client can communicate with the chain.
|
||||
validator_urls = [
|
||||
{{#each client.validator_urls }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
# Addresses to APIs running on validator from which the client gets the view of the network.
|
||||
validator_api_urls = [
|
||||
{{#each client.validator_api_urls }}
|
||||
|
||||
@@ -199,11 +199,22 @@ impl NymClient {
|
||||
.expect("provided gateway id is invalid!");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_validator_api_endpoints(),
|
||||
);
|
||||
let bandwidth_controller = {
|
||||
let details = network_defaults::NymNetworkDetails::new_from_env();
|
||||
let client_config = validator_client::Config::try_from_nym_network_details(&details)
|
||||
.expect("failed to construct validator client config");
|
||||
let client = validator_client::Client::new_query(client_config)
|
||||
.expect("Could not construct query client");
|
||||
let coconut_api_clients =
|
||||
validator_client::CoconutApiClient::all_coconut_api_clients(&client)
|
||||
.await
|
||||
.expect("Could not query api clients");
|
||||
BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
coconut_api_clients,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
|
||||
@@ -29,9 +29,13 @@ pub(crate) struct Init {
|
||||
#[clap(long)]
|
||||
force_register_gateway: bool,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
|
||||
/// Port for the socket to listen on in all subsequent runs
|
||||
#[clap(short, long)]
|
||||
@@ -52,7 +56,8 @@ pub(crate) struct Init {
|
||||
impl From<Init> for OverrideConfig {
|
||||
fn from(init_config: Init) -> Self {
|
||||
OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
nymd_validators: init_config.nymd_validators,
|
||||
api_validators: init_config.api_validators,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
#[cfg(feature = "coconut")]
|
||||
|
||||
@@ -78,7 +78,8 @@ pub(crate) enum Commands {
|
||||
|
||||
// Configuration that can be overridden.
|
||||
pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
api_validators: Option<String>,
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
@@ -100,7 +101,16 @@ pub(crate) async fn execute(args: &Cli) -> Result<(), Socks5ClientError> {
|
||||
}
|
||||
|
||||
pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Config {
|
||||
if let Some(raw_validators) = args.validators {
|
||||
if let Some(raw_validators) = args.nymd_validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validators(parse_validators(&raw_validators));
|
||||
} else if let Ok(raw_validators) = std::env::var(network_defaults::var_names::NYMD_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validators(parse_validators(&raw_validators));
|
||||
}
|
||||
if let Some(raw_validators) = args.api_validators {
|
||||
config
|
||||
.get_base_mut()
|
||||
.set_custom_validator_apis(parse_validators(&raw_validators));
|
||||
|
||||
@@ -31,9 +31,13 @@ pub(crate) struct Run {
|
||||
#[clap(long)]
|
||||
gateway: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the validators
|
||||
/// Comma separated list of rest endpoints of the nymd validators
|
||||
#[clap(long)]
|
||||
validators: Option<String>,
|
||||
nymd_validators: Option<String>,
|
||||
|
||||
/// Comma separated list of rest endpoints of the API validators
|
||||
#[clap(long)]
|
||||
api_validators: Option<String>,
|
||||
|
||||
/// Port for the socket to listen on
|
||||
#[clap(short, long)]
|
||||
@@ -49,7 +53,8 @@ pub(crate) struct Run {
|
||||
impl From<Run> for OverrideConfig {
|
||||
fn from(run_config: Run) -> Self {
|
||||
OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
nymd_validators: run_config.nymd_validators,
|
||||
api_validators: run_config.api_validators,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn get_gateway(api_server: String, preferred: Option<String>) -> GatewayEndpoint {
|
||||
let validator_client = validator_client::ApiClient::new(api_server.parse().unwrap());
|
||||
let validator_client = validator_client::client::ApiClient::new(api_server.parse().unwrap());
|
||||
|
||||
let gateways = match validator_client.get_cached_gateways().await {
|
||||
Err(err) => panic!("failed to obtain list of all gateways - {}", err),
|
||||
|
||||
@@ -17,6 +17,8 @@ use credential_storage::error::StorageError;
|
||||
#[cfg(feature = "coconut")]
|
||||
use std::str::FromStr;
|
||||
#[cfg(feature = "coconut")]
|
||||
use validator_client::client::CoconutApiClient;
|
||||
#[cfg(feature = "coconut")]
|
||||
use {
|
||||
coconut_interface::Base58,
|
||||
credentials::coconut::{
|
||||
@@ -29,7 +31,7 @@ pub struct BandwidthController<St: Storage> {
|
||||
#[allow(dead_code)]
|
||||
storage: St,
|
||||
#[cfg(feature = "coconut")]
|
||||
validator_endpoints: Vec<url::Url>,
|
||||
coconut_api_clients: Vec<CoconutApiClient>,
|
||||
}
|
||||
|
||||
impl<St> BandwidthController<St>
|
||||
@@ -37,10 +39,10 @@ where
|
||||
St: Storage + Clone + 'static,
|
||||
{
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn new(storage: St, validator_endpoints: Vec<url::Url>) -> Self {
|
||||
pub fn new(storage: St, coconut_api_clients: Vec<CoconutApiClient>) -> Self {
|
||||
BandwidthController {
|
||||
storage,
|
||||
validator_endpoints,
|
||||
coconut_api_clients,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +55,7 @@ where
|
||||
pub async fn prepare_coconut_credential(
|
||||
&self,
|
||||
) -> Result<coconut_interface::Credential, GatewayClientError> {
|
||||
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
|
||||
let verification_key = obtain_aggregate_verification_key(&self.coconut_api_clients).await?;
|
||||
let bandwidth_credential = self.storage.get_next_coconut_credential().await?;
|
||||
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
|
||||
.map_err(|_| StorageError::InconsistentData)?;
|
||||
|
||||
@@ -10,7 +10,6 @@ rust-version = "1.56"
|
||||
[dependencies]
|
||||
base64 = "0.13"
|
||||
colored = "2.0"
|
||||
cw3 = "0.13.1"
|
||||
|
||||
coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
|
||||
contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
|
||||
@@ -39,6 +38,7 @@ async-trait = { version = "0.1.51", optional = true }
|
||||
bip39 = { version = "1", features = ["rand"], optional = true }
|
||||
config = { path = "../../config", optional = true }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", features = ["rpc", "bip32", "cosmwasm"], optional = true}
|
||||
cw3 = { version = "0.13.4", optional = true }
|
||||
prost = { version = "0.10", default-features = false, optional = true }
|
||||
flate2 = { version = "1.0.20", optional = true }
|
||||
sha2 = { version = "0.9.5", optional = true }
|
||||
@@ -55,6 +55,7 @@ nymd-client = [
|
||||
"bip39",
|
||||
"config",
|
||||
"cosmrs",
|
||||
"cw3",
|
||||
"prost",
|
||||
"flate2",
|
||||
"sha2",
|
||||
|
||||
@@ -2,15 +2,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::{validator_api, ValidatorClientError};
|
||||
use coconut_dkg_common::dealer::ContractDealing;
|
||||
use coconut_dkg_common::types::DealerDetails;
|
||||
use coconut_dkg_common::types::NodeIndex;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use coconut_dkg_common::{
|
||||
dealer::ContractDealing, types::DealerDetails, verification_key::ContractVKShare,
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use coconut_interface::Base58;
|
||||
use coconut_interface::VerificationKey;
|
||||
use mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use mixnet_contract_common::MixId;
|
||||
use mixnet_contract_common::{GatewayBond, IdentityKeyRef};
|
||||
use url::Url;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use std::str::FromStr;
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_api_requests::models::{
|
||||
GatewayCoreStatusResponse, MixnodeCoreStatusResponse, MixnodeStatusResponse,
|
||||
@@ -18,10 +24,12 @@ use validator_api_requests::models::{
|
||||
};
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use crate::nymd::traits::{DkgQueryClient, MixnetQueryClient};
|
||||
use crate::nymd::traits::{DkgQueryClient, MixnetQueryClient, MultisigQueryClient};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use crate::nymd::{self, CosmWasmClient, NymdClient, QueryNymdClient, SigningNymdClient};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use cw3::ProposalResponse;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use mixnet_contract_common::{
|
||||
mixnode::MixNodeBond,
|
||||
pending_events::{PendingEpochEvent, PendingIntervalEvent},
|
||||
@@ -29,6 +37,7 @@ use mixnet_contract_common::{
|
||||
};
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use network_defaults::NymNetworkDetails;
|
||||
use url::Url;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
use validator_api_requests::models::MixNodeBondAnnotated;
|
||||
|
||||
@@ -46,6 +55,8 @@ pub struct Config {
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
rewarded_set_page_limit: Option<u32>,
|
||||
dealers_page_limit: Option<u32>,
|
||||
verification_key_page_limit: Option<u32>,
|
||||
proposals_page_limit: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
@@ -76,6 +87,8 @@ impl Config {
|
||||
mixnode_delegations_page_limit: None,
|
||||
rewarded_set_page_limit: None,
|
||||
dealers_page_limit: None,
|
||||
verification_key_page_limit: None,
|
||||
proposals_page_limit: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -124,6 +137,8 @@ pub struct Client<C> {
|
||||
mixnode_delegations_page_limit: Option<u32>,
|
||||
rewarded_set_page_limit: Option<u32>,
|
||||
dealers_page_limit: Option<u32>,
|
||||
verification_key_page_limit: Option<u32>,
|
||||
proposals_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,
|
||||
@@ -151,6 +166,8 @@ impl Client<SigningNymdClient> {
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
rewarded_set_page_limit: config.rewarded_set_page_limit,
|
||||
dealers_page_limit: config.dealers_page_limit,
|
||||
verification_key_page_limit: config.verification_key_page_limit,
|
||||
proposals_page_limit: config.proposals_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -185,6 +202,8 @@ impl Client<QueryNymdClient> {
|
||||
mixnode_delegations_page_limit: config.mixnode_delegations_page_limit,
|
||||
rewarded_set_page_limit: config.rewarded_set_page_limit,
|
||||
dealers_page_limit: config.dealers_page_limit,
|
||||
verification_key_page_limit: config.verification_key_page_limit,
|
||||
proposals_page_limit: config.proposals_page_limit,
|
||||
validator_api: validator_api_client,
|
||||
nymd: nymd_client,
|
||||
})
|
||||
@@ -602,6 +621,59 @@ impl<C> Client<C> {
|
||||
|
||||
Ok(dealings)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_verification_key_shares(
|
||||
&self,
|
||||
) -> Result<Vec<ContractVKShare>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut shares = Vec::new();
|
||||
let mut start_after = None;
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.get_vk_shares_paged(start_after.take(), self.verification_key_page_limit)
|
||||
.await?;
|
||||
shares.append(&mut paged_response.shares);
|
||||
|
||||
if let Some(start_after_res) = paged_response.start_next_after {
|
||||
start_after = Some(start_after_res.into_string())
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(shares)
|
||||
}
|
||||
|
||||
pub async fn get_all_nymd_proposals(
|
||||
&self,
|
||||
) -> Result<Vec<ProposalResponse>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
let mut proposals = Vec::new();
|
||||
let mut start_after = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self
|
||||
.nymd
|
||||
.list_proposals(start_after.take(), self.proposals_page_limit)
|
||||
.await?;
|
||||
|
||||
let last_id = paged_response.proposals.last().map(|prop| prop.id);
|
||||
proposals.append(&mut paged_response.proposals);
|
||||
|
||||
if let Some(start_after_res) = last_id {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(proposals)
|
||||
}
|
||||
}
|
||||
|
||||
// validator-api wrappers
|
||||
@@ -655,14 +727,53 @@ impl<C> Client<C> {
|
||||
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.blind_sign(request_body).await?)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_coconut_verification_key(
|
||||
&self,
|
||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
#[derive(Clone)]
|
||||
pub struct CoconutApiClient {
|
||||
pub api_client: ApiClient,
|
||||
pub verification_key: VerificationKey,
|
||||
pub node_id: NodeIndex,
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub cosmos_address: cosmrs::AccountId,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
impl CoconutApiClient {
|
||||
pub async fn all_coconut_api_clients<C>(
|
||||
nymd_client: &Client<C>,
|
||||
) -> Result<Vec<Self>, ValidatorClientError>
|
||||
where
|
||||
C: CosmWasmClient + Sync + Send,
|
||||
{
|
||||
Ok(nymd_client
|
||||
.get_all_nymd_verification_key_shares()
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter_map(Self::try_from)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn try_from(share: ContractVKShare) -> Option<Self> {
|
||||
if share.verified {
|
||||
if let Ok(url_address) = Url::parse(&share.announce_address) {
|
||||
if let Ok(verification_key) = VerificationKey::try_from_bs58(&share.share) {
|
||||
if let Ok(cosmos_address) = cosmrs::AccountId::from_str(share.owner.as_str()) {
|
||||
return Some(CoconutApiClient {
|
||||
api_client: ApiClient::new(url_address),
|
||||
verification_key,
|
||||
node_id: share.node_index,
|
||||
cosmos_address,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ApiClient {
|
||||
pub validator_api: validator_api::Client,
|
||||
// TODO: perhaps if we really need it at some (currently I don't see any reasons for it)
|
||||
@@ -768,16 +879,6 @@ impl ApiClient {
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_coconut_verification_key(
|
||||
&self,
|
||||
) -> Result<VerificationKeyResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_coconut_verification_key().await?)
|
||||
}
|
||||
|
||||
pub async fn get_cosmos_address(&self) -> Result<CosmosAddressResponse, ValidatorClientError> {
|
||||
Ok(self.validator_api.get_cosmos_address().await?)
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -9,7 +9,8 @@ mod error;
|
||||
pub mod nymd;
|
||||
pub mod validator_api;
|
||||
|
||||
pub use crate::client::ApiClient;
|
||||
#[cfg(feature = "nymd-client")]
|
||||
pub use crate::client::{ApiClient, CoconutApiClient};
|
||||
pub use crate::error::ValidatorClientError;
|
||||
pub use validator_api_requests::*;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use coconut_dkg_common::dealer::{
|
||||
};
|
||||
use coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use coconut_dkg_common::types::{EpochState, MinimumDepositResponse};
|
||||
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use cosmrs::AccountId;
|
||||
|
||||
#[async_trait]
|
||||
@@ -36,6 +37,11 @@ pub trait DkgQueryClient {
|
||||
start_after: Option<String>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedDealingsResponse, NymdError>;
|
||||
async fn get_vk_shares_paged(
|
||||
&self,
|
||||
start_after: Option<String>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedVKSharesResponse, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -111,4 +117,18 @@ where
|
||||
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_vk_shares_paged(
|
||||
&self,
|
||||
start_after: Option<String>,
|
||||
page_limit: Option<u32>,
|
||||
) -> Result<PagedVKSharesResponse, NymdError> {
|
||||
let request = DkgQueryMsg::GetVerificationKeys {
|
||||
limit: page_limit,
|
||||
start_after,
|
||||
};
|
||||
self.client
|
||||
.query_contract_smart(self.coconut_dkg_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::nymd::{Fee, NymdClient, SigningCosmWasmClient};
|
||||
use async_trait::async_trait;
|
||||
use coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
|
||||
use coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
|
||||
use coconut_dkg_common::verification_key::VerificationKeyShare;
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
|
||||
#[async_trait]
|
||||
@@ -15,6 +16,7 @@ pub trait DkgSigningClient {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
@@ -23,6 +25,12 @@ pub trait DkgSigningClient {
|
||||
commitment: ContractSafeBytes,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -33,10 +41,12 @@ where
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let req = DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof: bte_key,
|
||||
announce_address,
|
||||
};
|
||||
let deposit = self.get_deposit_amount().await?;
|
||||
|
||||
@@ -70,4 +80,23 @@ where
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NymdError> {
|
||||
let req = DkgExecuteMsg::CommitVerificationKeyShare { share };
|
||||
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
self.coconut_dkg_contract_address(),
|
||||
&req,
|
||||
fee.unwrap_or_default(),
|
||||
"verification key share commitment",
|
||||
vec![],
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{CosmWasmClient, NymdClient};
|
||||
|
||||
use multisig_contract_common::msg::{ProposalResponse, QueryMsg};
|
||||
use cw3::{ProposalListResponse, ProposalResponse};
|
||||
use multisig_contract_common::msg::QueryMsg;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MultisigQueryClient {
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse, NymdError>;
|
||||
async fn list_proposals(
|
||||
&self,
|
||||
start_after: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<ProposalListResponse, NymdError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -21,4 +27,15 @@ impl<C: CosmWasmClient + Sync + Send> MultisigQueryClient for NymdClient<C> {
|
||||
.query_contract_smart(self.multisig_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn list_proposals(
|
||||
&self,
|
||||
start_after: Option<u64>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<ProposalListResponse, NymdError> {
|
||||
let request = QueryMsg::ListProposals { start_after, limit };
|
||||
self.client
|
||||
.query_contract_smart(self.multisig_contract_address(), &request)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,11 +7,11 @@ use crate::nymd::error::NymdError;
|
||||
use crate::nymd::{Fee, NymdClient};
|
||||
|
||||
use coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
|
||||
use cw3::Vote;
|
||||
use multisig_contract_common::msg::ExecuteMsg;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cosmwasm_std::{to_binary, Coin, CosmosMsg, WasmMsg};
|
||||
use cw3::Vote;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MultisigSigningClient {
|
||||
|
||||
@@ -9,8 +9,7 @@ use reqwest::Response;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use url::Url;
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_api_requests::models::{
|
||||
GatewayCoreStatusResponse, GatewayStatusReportResponse, GatewayUptimeHistoryResponse,
|
||||
@@ -27,6 +26,7 @@ type Params<'a, K, V> = &'a [(K, V)];
|
||||
|
||||
const NO_PARAMS: Params<'_, &'_ str, &'_ str> = &[];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Client {
|
||||
url: Url,
|
||||
reqwest_client: reqwest::Client,
|
||||
@@ -443,34 +443,6 @@ impl Client {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_coconut_verification_key(
|
||||
&self,
|
||||
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_VERIFICATION_KEY,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_cosmos_address(&self) -> Result<CosmosAddressResponse, ValidatorAPIError> {
|
||||
self.query_validator_api(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_COSMOS_ADDRESS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn verify_bandwidth_credential(
|
||||
&self,
|
||||
request_body: &VerifyCredentialBody,
|
||||
|
||||
@@ -15,8 +15,6 @@ pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
|
||||
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
|
||||
pub const COCONUT_COSMOS_ADDRESS: &str = "cosmos-address";
|
||||
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const PROPOSAL_ID: &str = "proposal_id";
|
||||
pub const BANDWIDTH_PROPOSAL_ID: &str = "proposal_id";
|
||||
|
||||
@@ -7,7 +7,9 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
cw-utils = "0.13.4"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
multisig-contract-common = { path = "../multisig-contract" }
|
||||
@@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
||||
pub struct DealerDetails {
|
||||
pub address: Addr,
|
||||
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
pub announce_address: String,
|
||||
pub assigned_index: NodeIndex,
|
||||
pub deposit: Coin,
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub const NODE_INDEX: &str = "node_index";
|
||||
pub const DKG_PROPOSAL_ID: &str = "proposal_id";
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod dealer;
|
||||
pub mod event_attributes;
|
||||
pub mod msg;
|
||||
pub mod types;
|
||||
pub mod verification_key;
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof};
|
||||
use crate::verification_key::VerificationKeyShare;
|
||||
use cosmwasm_std::Addr;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
pub struct InstantiateMsg {
|
||||
pub group_addr: String,
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: String,
|
||||
pub admin: String,
|
||||
pub mix_denom: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)]
|
||||
@@ -17,12 +20,21 @@ pub struct InstantiateMsg {
|
||||
pub enum ExecuteMsg {
|
||||
RegisterDealer {
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
},
|
||||
|
||||
CommitDealing {
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
},
|
||||
|
||||
CommitVerificationKeyShare {
|
||||
share: VerificationKeyShare,
|
||||
},
|
||||
|
||||
VerifyVerificationKeyShare {
|
||||
owner: Addr,
|
||||
},
|
||||
|
||||
AdvanceEpochState {},
|
||||
|
||||
// DEBUG ONLY TXs. THEY SHALL BE REMOVED BEFORE FINALISING THE CODE
|
||||
@@ -53,6 +65,10 @@ pub enum QueryMsg {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
},
|
||||
GetVerificationKeys {
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||
|
||||
@@ -33,6 +33,8 @@ pub enum EpochState {
|
||||
PublicKeySubmission,
|
||||
DealingExchange,
|
||||
VerificationKeySubmission,
|
||||
VerificationKeyValidation,
|
||||
VerificationKeyFinalization,
|
||||
InProgress,
|
||||
}
|
||||
|
||||
@@ -48,6 +50,8 @@ impl Display for EpochState {
|
||||
EpochState::PublicKeySubmission => write!(f, "PublicKeySubmission"),
|
||||
EpochState::DealingExchange => write!(f, "DealingExchange"),
|
||||
EpochState::VerificationKeySubmission => write!(f, "VerificationKeySubmission"),
|
||||
EpochState::VerificationKeyValidation => write!(f, "VerificationKeyValidation"),
|
||||
EpochState::VerificationKeyFinalization => write!(f, "VerificationKeyFinalization"),
|
||||
EpochState::InProgress => write!(f, "InProgress"),
|
||||
}
|
||||
}
|
||||
@@ -58,7 +62,9 @@ impl EpochState {
|
||||
match self {
|
||||
EpochState::PublicKeySubmission => Some(EpochState::DealingExchange),
|
||||
EpochState::DealingExchange => Some(EpochState::VerificationKeySubmission),
|
||||
EpochState::VerificationKeySubmission => Some(EpochState::InProgress),
|
||||
EpochState::VerificationKeySubmission => Some(EpochState::VerificationKeyValidation),
|
||||
EpochState::VerificationKeyValidation => Some(EpochState::VerificationKeyFinalization),
|
||||
EpochState::VerificationKeyFinalization => Some(EpochState::InProgress),
|
||||
EpochState::InProgress => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::msg::ExecuteMsg;
|
||||
use crate::types::NodeIndex;
|
||||
use cosmwasm_std::{from_binary, to_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg};
|
||||
use cw_utils::Expiration;
|
||||
use multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub type VerificationKeyShare = String;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct ContractVKShare {
|
||||
pub share: VerificationKeyShare,
|
||||
pub announce_address: String,
|
||||
pub node_index: NodeIndex,
|
||||
pub owner: Addr,
|
||||
pub verified: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct PagedVKSharesResponse {
|
||||
pub shares: Vec<ContractVKShare>,
|
||||
pub per_page: usize,
|
||||
pub start_next_after: Option<Addr>,
|
||||
}
|
||||
|
||||
pub fn to_cosmos_msg(
|
||||
owner: Addr,
|
||||
coconut_dkg_addr: String,
|
||||
multisig_addr: String,
|
||||
expiration_time: Timestamp,
|
||||
) -> StdResult<CosmosMsg> {
|
||||
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner };
|
||||
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: coconut_dkg_addr,
|
||||
msg: to_binary(&verify_vk_share_req)?,
|
||||
funds: vec![],
|
||||
});
|
||||
let req = MultisigExecuteMsg::Propose {
|
||||
title: String::from("Verify VK share, as ordered by Coconut DKG Contract"),
|
||||
description: String::new(),
|
||||
msgs: vec![verify_vk_share_msg],
|
||||
latest: Some(Expiration::AtTime(expiration_time)),
|
||||
};
|
||||
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: multisig_addr,
|
||||
msg: to_binary(&req)?,
|
||||
funds: vec![],
|
||||
});
|
||||
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
|
||||
if let Some(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: _,
|
||||
msg,
|
||||
funds: _,
|
||||
})) = msgs.get(0)
|
||||
{
|
||||
if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner }) = from_binary::<ExecuteMsg>(msg)
|
||||
{
|
||||
return Some(owner);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -6,9 +6,10 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.1" }
|
||||
cw3 = { version = "0.13.1" }
|
||||
cw4 = { version = "0.13.1" }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cosmwasm-std = "1.0.0"
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -5,7 +5,6 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use cosmwasm_std::{CosmosMsg, Empty};
|
||||
pub use cw3::ProposalResponse;
|
||||
use cw3::Vote;
|
||||
use cw4::MemberChangedHookMsg;
|
||||
use cw_utils::{Duration, Expiration, Threshold};
|
||||
@@ -15,6 +14,7 @@ pub struct InstantiateMsg {
|
||||
// this is the group contract that contains the member list
|
||||
pub group_addr: String,
|
||||
pub coconut_bandwidth_contract_address: String,
|
||||
pub coconut_dkg_contract_address: String,
|
||||
pub threshold: Threshold,
|
||||
pub max_voting_period: Duration,
|
||||
}
|
||||
@@ -82,4 +82,5 @@ pub enum QueryMsg {
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub coconut_bandwidth_address: String,
|
||||
pub coconut_dkg_address: String,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ edition = "2021"
|
||||
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support", optional = true }
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
|
||||
# I guess temporarily until we get serde support in coconut up and running
|
||||
coconut-interface = { path = "../coconut-interface" }
|
||||
|
||||
@@ -8,8 +8,8 @@ use coconut_interface::{
|
||||
use crypto::asymmetric::encryption::PublicKey;
|
||||
use crypto::shared_key::recompute_shared_key;
|
||||
use crypto::symmetric::stream_cipher;
|
||||
use url::Url;
|
||||
use validator_api_requests::coconut::BlindSignRequestBody;
|
||||
use validator_client::client::CoconutApiClient;
|
||||
|
||||
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
|
||||
use crate::coconut::params::{
|
||||
@@ -17,46 +17,21 @@ use crate::coconut::params::{
|
||||
};
|
||||
use crate::error::Error;
|
||||
|
||||
/// Contacts all provided validators and then aggregate their verification keys.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `validators`: list of validators to obtain verification keys from.
|
||||
///
|
||||
/// Note: list of validators must be correctly ordered by the polynomial coordinates used
|
||||
/// during key generation and it is responsibility of the caller to ensure that correct
|
||||
/// number of them is provided
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use url::{Url, ParseError};
|
||||
/// use credentials::obtain_aggregate_verification_key;
|
||||
///
|
||||
/// async fn example() -> Result<(), ParseError> {
|
||||
/// let validators = vec!["https://sandbox-validator1.nymtech.net/api".parse()?, "https://sandbox-validator2.nymtech.net/api".parse()?];
|
||||
/// let aggregated_key = obtain_aggregate_verification_key(&validators).await;
|
||||
/// // deal with the obtained Result
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn obtain_aggregate_verification_key(
|
||||
validators: &[Url],
|
||||
api_clients: &[CoconutApiClient],
|
||||
) -> Result<VerificationKey, Error> {
|
||||
if validators.is_empty() {
|
||||
if api_clients.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
}
|
||||
|
||||
let mut indices = Vec::with_capacity(validators.len());
|
||||
let mut shares = Vec::with_capacity(validators.len());
|
||||
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
for (id, validator_url) in validators.iter().enumerate() {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let response = client.get_coconut_verification_key().await?;
|
||||
indices.push((id + 1) as u64);
|
||||
shares.push(response.key);
|
||||
}
|
||||
let indices: Vec<_> = api_clients
|
||||
.iter()
|
||||
.map(|api_client| api_client.node_id)
|
||||
.collect();
|
||||
let shares: Vec<_> = api_clients
|
||||
.iter()
|
||||
.map(|api_client| api_client.verification_key.clone())
|
||||
.collect();
|
||||
|
||||
Ok(aggregate_verification_keys(&shares, Some(&indices))?)
|
||||
}
|
||||
@@ -64,7 +39,7 @@ pub async fn obtain_aggregate_verification_key(
|
||||
async fn obtain_partial_credential(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucher,
|
||||
client: &validator_client::ApiClient,
|
||||
client: &validator_client::client::ApiClient,
|
||||
validator_vk: &VerificationKey,
|
||||
) -> Result<Signature, Error> {
|
||||
let public_attributes = attributes.get_public_attributes();
|
||||
@@ -118,26 +93,33 @@ async fn obtain_partial_credential(
|
||||
pub async fn obtain_aggregate_signature(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucher,
|
||||
validators: &[Url],
|
||||
coconut_api_clients: &[CoconutApiClient],
|
||||
) -> Result<Signature, Error> {
|
||||
if validators.is_empty() {
|
||||
if coconut_api_clients.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
}
|
||||
let public_attributes = attributes.get_public_attributes();
|
||||
let private_attributes = attributes.get_private_attributes();
|
||||
|
||||
let mut shares = Vec::with_capacity(validators.len());
|
||||
let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len());
|
||||
let mut shares = Vec::with_capacity(coconut_api_clients.len());
|
||||
let validators_partial_vks: Vec<_> = coconut_api_clients
|
||||
.iter()
|
||||
.map(|api_client| api_client.verification_key.clone())
|
||||
.collect();
|
||||
let indices: Vec<_> = coconut_api_clients
|
||||
.iter()
|
||||
.map(|api_client| api_client.node_id)
|
||||
.collect();
|
||||
|
||||
let mut client = validator_client::ApiClient::new(validators[0].clone());
|
||||
for (id, validator_url) in validators.iter().enumerate() {
|
||||
client.change_validator_api(validator_url.clone());
|
||||
let validator_partial_vk = client.get_coconut_verification_key().await?;
|
||||
validators_partial_vks.push(validator_partial_vk.key.clone());
|
||||
let signature =
|
||||
obtain_partial_credential(params, attributes, &client, &validator_partial_vk.key)
|
||||
.await?;
|
||||
let share = SignatureShare::new(signature, (id + 1) as u64);
|
||||
for coconut_api_client in coconut_api_clients.iter() {
|
||||
let signature = obtain_partial_credential(
|
||||
params,
|
||||
attributes,
|
||||
&coconut_api_client.api_client,
|
||||
&coconut_api_client.verification_key,
|
||||
)
|
||||
.await?;
|
||||
let share = SignatureShare::new(signature, coconut_api_client.node_id);
|
||||
shares.push(share)
|
||||
}
|
||||
|
||||
@@ -145,10 +127,6 @@ pub async fn obtain_aggregate_signature(
|
||||
attributes.extend_from_slice(&private_attributes);
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
|
||||
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
|
||||
for i in 0..validators_partial_vks.len() {
|
||||
indices.push((i + 1) as u64);
|
||||
}
|
||||
let verification_key =
|
||||
aggregate_verification_keys(&validators_partial_vks, Some(indices.as_ref()))?;
|
||||
|
||||
|
||||
@@ -15,6 +15,13 @@ use rand_core::RngCore;
|
||||
use std::collections::BTreeMap;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct RecoveredVerificationKeys {
|
||||
pub recovered_master: G2Projective,
|
||||
pub recovered_partials: Vec<G2Projective>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
pub struct Dealing {
|
||||
@@ -245,7 +252,7 @@ pub fn try_recover_verification_keys(
|
||||
dealings: &[Dealing],
|
||||
threshold: Threshold,
|
||||
receivers: &BTreeMap<NodeIndex, PublicKey>,
|
||||
) -> Result<(G2Projective, Vec<G2Projective>), DkgError> {
|
||||
) -> Result<RecoveredVerificationKeys, DkgError> {
|
||||
if dealings.is_empty() {
|
||||
return Err(DkgError::NoDealingsAvailable);
|
||||
}
|
||||
@@ -292,7 +299,10 @@ pub fn try_recover_verification_keys(
|
||||
.map(|index| interpolated_coefficients.evaluate_at(&Scalar::from(*index)))
|
||||
.collect();
|
||||
|
||||
Ok((master_verification_key, verification_key_shares))
|
||||
Ok(RecoveredVerificationKeys {
|
||||
recovered_master: master_verification_key,
|
||||
recovered_partials: verification_key_shares,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_verification_keys(
|
||||
@@ -389,8 +399,10 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// END OF SETUP
|
||||
let (recovered_master, recovered_partials) =
|
||||
try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master,
|
||||
recovered_partials,
|
||||
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let g2 = G2Projective::generator();
|
||||
assert_eq!(g2 * master_secret, recovered_master);
|
||||
@@ -425,8 +437,10 @@ mod tests {
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let (recovered_master, recovered_partials) =
|
||||
try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master,
|
||||
recovered_partials,
|
||||
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
|
||||
assert!(verify_verification_keys(
|
||||
&recovered_master,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use bls12_381::{G2Projective, Scalar};
|
||||
use dkg::bte::{decrypt_share, keygen, setup};
|
||||
use dkg::dealing::RecoveredVerificationKeys;
|
||||
use dkg::interpolation::perform_lagrangian_interpolation_at_origin;
|
||||
use dkg::{combine_shares, try_recover_verification_keys, Dealing};
|
||||
use rand_core::SeedableRng;
|
||||
@@ -100,8 +101,10 @@ fn full_threshold_secret_sharing() {
|
||||
}
|
||||
|
||||
// recover verification keys
|
||||
let (recovered_master, recovered_partials) =
|
||||
try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master,
|
||||
recovered_partials,
|
||||
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let g2 = G2Projective::generator();
|
||||
|
||||
@@ -171,8 +174,10 @@ fn full_threshold_secret_resharing() {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// recover verification keys
|
||||
let (public_original_master, recovered_partials) =
|
||||
try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master: public_original_master,
|
||||
recovered_partials,
|
||||
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
@@ -217,8 +222,10 @@ fn full_threshold_secret_resharing() {
|
||||
}
|
||||
|
||||
// recover verification keys
|
||||
let (public_reshared_master, reshared_partials) =
|
||||
try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master: public_reshared_master,
|
||||
recovered_partials: reshared_partials,
|
||||
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut reshared_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
|
||||
@@ -17,6 +17,9 @@ serde_derive = "1.0"
|
||||
bs58 = "0.4.0"
|
||||
sha2 = "0.9"
|
||||
|
||||
dkg = { path = "../crypto/dkg" }
|
||||
pemstore = { path = "../pemstore" }
|
||||
|
||||
[dependencies.ff]
|
||||
version = "0.11"
|
||||
default-features = false
|
||||
@@ -28,7 +31,6 @@ default-features = false
|
||||
[dev-dependencies]
|
||||
criterion = { version="0.3", features=["html_reports"] }
|
||||
doc-comment = "0.3"
|
||||
dkg = { path = "../crypto/dkg" }
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[dev-dependencies.bincode]
|
||||
|
||||
@@ -21,6 +21,7 @@ pub use scheme::keygen::SecretKey;
|
||||
pub use scheme::keygen::VerificationKey;
|
||||
pub use scheme::setup::setup;
|
||||
pub use scheme::setup::Parameters;
|
||||
pub use scheme::verification::check_vk_pairing;
|
||||
pub use scheme::verification::prove_bandwidth_credential;
|
||||
pub use scheme::verification::verify_credential;
|
||||
pub use scheme::verification::Theta;
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::convert::TryInto;
|
||||
|
||||
use bls12_381::{G1Projective, G2Projective, Scalar};
|
||||
use group::Curve;
|
||||
use pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::{CoconutError, Result};
|
||||
@@ -29,6 +30,22 @@ pub struct SecretKey {
|
||||
pub(crate) ys: Vec<Scalar>,
|
||||
}
|
||||
|
||||
impl PemStorableKey for SecretKey {
|
||||
type Error = CoconutError;
|
||||
|
||||
fn pem_type() -> &'static str {
|
||||
"COCONUT SECRET KEY"
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.to_bytes()
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for SecretKey {
|
||||
type Error = CoconutError;
|
||||
|
||||
@@ -128,6 +145,22 @@ pub struct VerificationKey {
|
||||
pub(crate) beta_g2: Vec<G2Projective>,
|
||||
}
|
||||
|
||||
impl PemStorableKey for VerificationKey {
|
||||
type Error = CoconutError;
|
||||
|
||||
fn pem_type() -> &'static str {
|
||||
"COCONUT VERIFICATION KEY"
|
||||
}
|
||||
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
self.to_bytes()
|
||||
}
|
||||
|
||||
fn from_bytes(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
|
||||
Self::from_bytes(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for VerificationKey {
|
||||
type Error = CoconutError;
|
||||
|
||||
@@ -366,6 +399,23 @@ pub struct KeyPair {
|
||||
pub index: Option<SignerIndex>,
|
||||
}
|
||||
|
||||
impl PemStorableKeyPair for KeyPair {
|
||||
type PrivatePemKey = SecretKey;
|
||||
type PublicPemKey = VerificationKey;
|
||||
|
||||
fn private_key(&self) -> &Self::PrivatePemKey {
|
||||
&self.secret_key
|
||||
}
|
||||
|
||||
fn public_key(&self) -> &Self::PublicPemKey {
|
||||
&self.verification_key
|
||||
}
|
||||
|
||||
fn from_keys(secret_key: Self::PrivatePemKey, verification_key: Self::PublicPemKey) -> Self {
|
||||
Self::from_keys(secret_key, verification_key)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
const MARKER_BYTES: &'static [u8] = b"coconutkeypair";
|
||||
|
||||
|
||||
@@ -212,6 +212,39 @@ pub fn check_bilinear_pairing(p: &G1Affine, q: &G2Prepared, r: &G1Affine, s: &G2
|
||||
multi_miller.final_exponentiation().is_identity().into()
|
||||
}
|
||||
|
||||
pub fn check_vk_pairing(
|
||||
params: &Parameters,
|
||||
dkg_values: &[G2Projective],
|
||||
vk: &VerificationKey,
|
||||
) -> bool {
|
||||
let values_len = dkg_values.len();
|
||||
if values_len == 0 || values_len - 1 != vk.beta_g1.len() || values_len - 1 != vk.beta_g2.len() {
|
||||
return false;
|
||||
}
|
||||
if vk.alpha != *dkg_values.last().unwrap() {
|
||||
return false;
|
||||
}
|
||||
if dkg_values
|
||||
.iter()
|
||||
.zip(vk.beta_g2.iter())
|
||||
.any(|(dkg_beta, vk_beta)| dkg_beta != vk_beta)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if vk.beta_g1.iter().zip(vk.beta_g2.iter()).any(|(g1, g2)| {
|
||||
!check_bilinear_pairing(
|
||||
params.gen1(),
|
||||
&G2Prepared::from(g2.to_affine()),
|
||||
&g1.to_affine(),
|
||||
params.prepared_miller_g2(),
|
||||
)
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn verify_credential(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
@@ -284,6 +317,15 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn vk_pairing() {
|
||||
let params = setup(2).unwrap();
|
||||
let vk = keygen(¶ms).verification_key();
|
||||
let mut dkg_values = vk.beta_g2.clone();
|
||||
dkg_values.push(vk.alpha);
|
||||
assert!(check_vk_pairing(¶ms, &dkg_values, &vk));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn theta_bytes_roundtrip() {
|
||||
let params = setup(2).unwrap();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::*;
|
||||
use itertools::izip;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub fn theta_from_keys_and_attributes(
|
||||
params: &Parameters,
|
||||
@@ -87,13 +88,26 @@ pub fn theta_from_keys_and_attributes(
|
||||
Ok(theta)
|
||||
}
|
||||
|
||||
pub fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
|
||||
let len = matrix[0].len();
|
||||
let mut iters: Vec<_> = matrix.into_iter().map(|d| d.into_iter()).collect();
|
||||
(0..len)
|
||||
.map(|_| {
|
||||
iters
|
||||
.iter_mut()
|
||||
.map(|it| it.next().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::{KeyPair, Parameters, SecretKey};
|
||||
use bls12_381::Scalar;
|
||||
use dkg::{bte::decrypt_share, combine_shares, Dealing, NodeIndex};
|
||||
use rand_chacha::rand_core::SeedableRng;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub fn generate_dkg_secrets(node_indices: &[NodeIndex]) -> Vec<Scalar> {
|
||||
let dummy_seed = [42u8; 32];
|
||||
@@ -148,19 +162,4 @@ pub mod tests {
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
|
||||
let len = matrix[0].len();
|
||||
let mut iters: Vec<_> = matrix.into_iter().map(|d| d.into_iter()).collect();
|
||||
(0..len)
|
||||
.map(|_| {
|
||||
iters
|
||||
.iter_mut()
|
||||
.map(|it| it.next().unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.try_into()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+13
-7
@@ -210,6 +210,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"contracts-common",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"multisig-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
@@ -221,6 +223,8 @@ dependencies = [
|
||||
"bandwidth-claim-contract",
|
||||
"coconut-bandwidth",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-dkg",
|
||||
"coconut-dkg-common",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-controllers",
|
||||
@@ -228,6 +232,7 @@ dependencies = [
|
||||
"cw-storage-plus",
|
||||
"cw-utils",
|
||||
"cw3-flex-multisig",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"multisig-contract-common",
|
||||
"schemars",
|
||||
@@ -409,9 +414,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw-multi-test"
|
||||
version = "0.13.2"
|
||||
version = "0.13.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbea57e5be4a682268a5eca1a57efece57a54ff216bfd87603d5e864aad40e12"
|
||||
checksum = "a3f9a8ab7c3c29ec93cb7a39ce4b14a05e053153b4a17ef7cf2246af1b7c087e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmwasm-std",
|
||||
@@ -523,7 +528,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cw4-group"
|
||||
version = "0.13.1"
|
||||
version = "0.13.4"
|
||||
dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
@@ -998,6 +1003,7 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw3",
|
||||
"cw3-fixed-multisig",
|
||||
"cw4",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -1746,9 +1752,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "x25519-dalek"
|
||||
version = "1.2.0"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077"
|
||||
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"rand_core 0.5.1",
|
||||
@@ -1757,9 +1763,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.3.0"
|
||||
version = "1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
@@ -5,3 +5,5 @@ use cosmwasm_std::Uint128;
|
||||
|
||||
// to be determined whether those should be constants or exist as contract state
|
||||
pub(crate) const MINIMUM_DEPOSIT: Uint128 = Uint128::new(1_000_000_000);
|
||||
// Wait time for the verification to take place (currently 24h)
|
||||
pub(crate) const BLOCK_TIME_FOR_VERIFICATION_SECS: u64 = 86400;
|
||||
|
||||
@@ -65,6 +65,7 @@ pub fn try_add_dealer(
|
||||
mut deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::PublicKeySubmission)?;
|
||||
let state = STATE.load(deps.storage)?;
|
||||
@@ -94,6 +95,7 @@ pub fn try_add_dealer(
|
||||
let dealer_details = DealerDetails {
|
||||
address: info.sender.clone(),
|
||||
bte_public_key_with_proof: bte_key_with_proof,
|
||||
announce_address,
|
||||
assigned_index: node_index,
|
||||
deposit,
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ pub fn try_commit_dealings(
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange)?;
|
||||
// ensure the sender is a dealer for the current epoch
|
||||
// ensure the sender is a dealer
|
||||
if dealers_storage::current_dealers()
|
||||
.may_load(deps.storage, &info.sender)?
|
||||
.is_none()
|
||||
@@ -31,5 +31,7 @@ pub fn try_commit_dealings(
|
||||
}
|
||||
}
|
||||
|
||||
Err(ContractError::AlreadyCommitted)
|
||||
Err(ContractError::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ pub enum ContractError {
|
||||
#[error("This sender is not a dealer for the current epoch")]
|
||||
NotADealer,
|
||||
|
||||
#[error("This dealer has already commited dealing for this epoch")]
|
||||
AlreadyCommitted,
|
||||
#[error("This dealer has already committed {commitment}")]
|
||||
AlreadyCommitted { commitment: String },
|
||||
|
||||
#[error("No verification key committed for owner {owner}")]
|
||||
NoCommitForOwner { owner: String },
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ use crate::dealers::queries::{
|
||||
use crate::dealings::queries::query_dealings_paged;
|
||||
use crate::epoch_state::queries::query_current_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, ADMIN, STATE};
|
||||
use crate::state::{State, ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use coconut_dkg_common::types::{EpochState, MinimumDepositResponse};
|
||||
use cosmwasm_std::{
|
||||
@@ -23,6 +24,7 @@ mod dealings;
|
||||
mod epoch_state;
|
||||
mod error;
|
||||
mod state;
|
||||
mod verification_key_shares;
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
@@ -37,7 +39,9 @@ pub fn instantiate(
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let admin_addr = deps.api.addr_validate(&msg.admin)?;
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
ADMIN.set(deps.branch(), Some(admin_addr))?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
ContractError::InvalidGroup {
|
||||
@@ -47,6 +51,7 @@ pub fn instantiate(
|
||||
|
||||
let state = State {
|
||||
group_addr,
|
||||
multisig_addr,
|
||||
mix_denom: msg.mix_denom,
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
@@ -65,12 +70,25 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::RegisterDealer { bte_key_with_proof } => {
|
||||
dealers::transactions::try_add_dealer(deps, info, bte_key_with_proof)
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
announce_address,
|
||||
} => {
|
||||
dealers::transactions::try_add_dealer(deps, info, bte_key_with_proof, announce_address)
|
||||
}
|
||||
ExecuteMsg::CommitDealing { dealing_bytes } => {
|
||||
dealings::transactions::try_commit_dealings(deps, info, dealing_bytes)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share } => {
|
||||
verification_key_shares::transactions::try_commit_verification_key_share(
|
||||
deps, env, info, share,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::VerifyVerificationKeyShare { owner } => {
|
||||
verification_key_shares::transactions::try_verify_verification_key_share(
|
||||
deps, info, owner,
|
||||
)
|
||||
}
|
||||
ExecuteMsg::DebugUnsafeResetAll { init_msg } => {
|
||||
reset_contract_state(deps, env, info, init_msg)
|
||||
}
|
||||
@@ -140,6 +158,9 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
limit,
|
||||
start_after,
|
||||
} => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?,
|
||||
QueryMsg::GetVerificationKeys { limit, start_after } => {
|
||||
to_binary(&query_vk_shares_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
@@ -161,8 +182,9 @@ mod tests {
|
||||
let env = mock_env();
|
||||
let msg = InstantiateMsg {
|
||||
group_addr: "group_addr".to_string(),
|
||||
mix_denom: "nym".to_string(),
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
admin: "admin".to_string(),
|
||||
mix_denom: "nym".to_string(),
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::Cw4Contract;
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
@@ -10,9 +11,11 @@ use serde::{Deserialize, Serialize};
|
||||
// unique items
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
pub const ADMIN: Admin = Admin::new("admin");
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)]
|
||||
pub struct State {
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: Addr,
|
||||
pub group_addr: Cw4Contract,
|
||||
}
|
||||
|
||||
@@ -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,38 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verification_key_shares::storage;
|
||||
use crate::verification_key_shares::storage::VK_SHARES;
|
||||
use coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
|
||||
pub fn query_vk_shares_paged(
|
||||
deps: Deps<'_>,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedVKSharesResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(storage::VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT)
|
||||
.min(storage::VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT) as usize;
|
||||
|
||||
let addr = start_after
|
||||
.map(|addr| deps.api.addr_validate(&addr))
|
||||
.transpose()?;
|
||||
|
||||
let start = addr.as_ref().map(Bound::exclusive);
|
||||
|
||||
let shares = VK_SHARES
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|(_, share)| share))
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = shares.last().map(|share| share.owner.clone());
|
||||
|
||||
Ok(PagedVKSharesResponse {
|
||||
shares,
|
||||
per_page: limit,
|
||||
start_next_after,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use coconut_dkg_common::verification_key::ContractVKShare;
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_storage_plus::Map;
|
||||
|
||||
pub(crate) const VERIFICATION_KEY_SHARES_PAGE_MAX_LIMIT: u32 = 75;
|
||||
pub(crate) const VERIFICATION_KEY_SHARES_PAGE_DEFAULT_LIMIT: u32 = 50;
|
||||
|
||||
type VKShareKey<'a> = &'a Addr;
|
||||
|
||||
pub(crate) const VK_SHARES: Map<'_, VKShareKey<'_>, ContractVKShare> = Map::new("vks");
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::BLOCK_TIME_FOR_VERIFICATION_SECS;
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{MULTISIG, STATE};
|
||||
use crate::verification_key_shares::storage::VK_SHARES;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use coconut_dkg_common::verification_key::{to_cosmos_msg, ContractVKShare, VerificationKeyShare};
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response};
|
||||
|
||||
pub fn try_commit_verification_key_share(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
share: VerificationKeyShare,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::VerificationKeySubmission)?;
|
||||
// ensure the sender is a dealer
|
||||
let details = dealers_storage::current_dealers()
|
||||
.load(deps.storage, &info.sender)
|
||||
.map_err(|_| ContractError::NotADealer)?;
|
||||
if VK_SHARES.may_load(deps.storage, &info.sender)?.is_some() {
|
||||
return Err(ContractError::AlreadyCommitted {
|
||||
commitment: String::from("verification key share"),
|
||||
});
|
||||
}
|
||||
|
||||
let data = ContractVKShare {
|
||||
share,
|
||||
node_index: details.assigned_index,
|
||||
announce_address: details.announce_address,
|
||||
owner: info.sender.clone(),
|
||||
verified: false,
|
||||
};
|
||||
VK_SHARES.save(deps.storage, &info.sender, &data)?;
|
||||
|
||||
let msg = to_cosmos_msg(
|
||||
info.sender,
|
||||
env.contract.address.to_string(),
|
||||
STATE.load(deps.storage)?.multisig_addr.to_string(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(BLOCK_TIME_FOR_VERIFICATION_SECS),
|
||||
)?;
|
||||
|
||||
Ok(Response::new().add_message(msg))
|
||||
}
|
||||
|
||||
pub fn try_verify_verification_key_share(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
owner: Addr,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::VerificationKeyFinalization)?;
|
||||
MULTISIG.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
VK_SHARES.update(deps.storage, &owner, |vk_share| {
|
||||
vk_share
|
||||
.map(|mut share| {
|
||||
share.verified = true;
|
||||
share
|
||||
})
|
||||
.ok_or(ContractError::NoCommitForOwner {
|
||||
owner: owner.to_string(),
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
@@ -8,10 +8,12 @@ edition = "2021"
|
||||
[dependencies]
|
||||
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
|
||||
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
|
||||
coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
|
||||
multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
cw4 = "0.13.4"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw-controllers = "0.13.4"
|
||||
cw-utils = "0.13.4"
|
||||
@@ -21,7 +23,8 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
|
||||
coconut-bandwidth = { path = "../coconut-bandwidth" }
|
||||
cw-multi-test = { version = "0.13.2" }
|
||||
coconut-dkg = { path = "../coconut-dkg" }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const OWNER: &str = "admin0001";
|
||||
pub const MEMBER1: &str = "member1";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
pub const POOL_CONTRACT: &str = "mix pool contract address";
|
||||
pub const RANDOM_ADDRESS: &str = "random address";
|
||||
@@ -16,6 +17,7 @@ pub const RANDOM_ADDRESS: &str = "random address";
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct MigrateMsg {
|
||||
pub coconut_bandwidth_address: String,
|
||||
pub coconut_dkg_address: String,
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
@@ -32,8 +34,20 @@ pub fn mock_app(init_funds: &[Coin]) -> App {
|
||||
.bank
|
||||
.init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec())
|
||||
.unwrap();
|
||||
router
|
||||
.bank
|
||||
.init_balance(storage, &Addr::unchecked(MEMBER1), init_funds.to_vec())
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
pub fn contract_dkg() -> Box<dyn Contract<Empty>> {
|
||||
let contract = ContractWrapper::new(
|
||||
coconut_dkg::execute,
|
||||
coconut_dkg::instantiate,
|
||||
coconut_dkg::query,
|
||||
);
|
||||
Box::new(contract)
|
||||
}
|
||||
|
||||
pub fn contract_bandwidth() -> Box<dyn Contract<Empty>> {
|
||||
let contract = ContractWrapper::new(
|
||||
|
||||
@@ -15,6 +15,7 @@ use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
|
||||
pub const TEST_COIN_DENOM: &str = "unym";
|
||||
pub const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
pub const TEST_COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
|
||||
#[test]
|
||||
fn spend_credential_creates_proposal() {
|
||||
@@ -46,6 +47,7 @@ fn spend_credential_creates_proposal() {
|
||||
},
|
||||
max_voting_period: Duration::Height(1000),
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
let multisig_contract_addr = app
|
||||
.instantiate_contract(
|
||||
@@ -77,6 +79,7 @@ fn spend_credential_creates_proposal() {
|
||||
|
||||
let msg = MigrateMsg {
|
||||
coconut_bandwidth_address: coconut_bandwidth_contract_addr.to_string(),
|
||||
coconut_dkg_address: "".to_string(),
|
||||
};
|
||||
app.migrate_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::helpers::{
|
||||
contract_dkg, contract_group, contract_multisig, mock_app, MigrateMsg, MEMBER1, OWNER,
|
||||
};
|
||||
use crate::spend_credential_creates_proposal::{
|
||||
TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS, TEST_COCONUT_DKG_CONTRACT_ADDRESS, TEST_COIN_DENOM,
|
||||
};
|
||||
use coconut_dkg_common::msg::ExecuteMsg::{
|
||||
AdvanceEpochState, CommitVerificationKeyShare, RegisterDealer,
|
||||
};
|
||||
use coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg;
|
||||
use cosmwasm_std::{coins, Addr, Decimal};
|
||||
use cw4::Member;
|
||||
use cw4_group::msg::InstantiateMsg as GroupInstantiateMsg;
|
||||
use cw_multi_test::Executor;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
use multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
|
||||
|
||||
#[test]
|
||||
fn dkg_create_proposal() {
|
||||
let init_funds = coins(10000000000, TEST_COIN_DENOM);
|
||||
let mut app = mock_app(&init_funds);
|
||||
let member1 = Member {
|
||||
addr: MEMBER1.to_string(),
|
||||
weight: 10,
|
||||
};
|
||||
|
||||
let group_code_id = app.store_code(contract_group());
|
||||
let msg = GroupInstantiateMsg {
|
||||
admin: Some(OWNER.to_string()),
|
||||
members: vec![member1],
|
||||
};
|
||||
let group_contract_addr = app
|
||||
.instantiate_contract(
|
||||
group_code_id,
|
||||
Addr::unchecked(OWNER),
|
||||
&msg,
|
||||
&[],
|
||||
"group",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let multisig_code_id = app.store_code(contract_multisig());
|
||||
let msg = MultisigInstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
threshold: Threshold::AbsolutePercentage {
|
||||
percentage: Decimal::from_ratio(2u128, 3u128),
|
||||
},
|
||||
max_voting_period: Duration::Time(1000),
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
let multisig_contract_addr = app
|
||||
.instantiate_contract(
|
||||
multisig_code_id,
|
||||
Addr::unchecked(OWNER),
|
||||
&msg,
|
||||
&[],
|
||||
"multisig",
|
||||
Some(OWNER.to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let coconut_dkg_code_id = app.store_code(contract_dkg());
|
||||
let msg = DkgInstantiateMsg {
|
||||
group_addr: group_contract_addr.to_string(),
|
||||
multisig_addr: multisig_contract_addr.to_string(),
|
||||
admin: Addr::unchecked(OWNER).to_string(),
|
||||
mix_denom: TEST_COIN_DENOM.to_string(),
|
||||
};
|
||||
let coconut_dkg_contract_addr = app
|
||||
.instantiate_contract(
|
||||
coconut_dkg_code_id,
|
||||
Addr::unchecked(OWNER),
|
||||
&msg,
|
||||
&[],
|
||||
"coconut dkg",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let msg = MigrateMsg {
|
||||
coconut_bandwidth_address: coconut_dkg_contract_addr.to_string(),
|
||||
coconut_dkg_address: coconut_dkg_contract_addr.to_string(),
|
||||
};
|
||||
app.migrate_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
multisig_contract_addr,
|
||||
&msg,
|
||||
multisig_code_id,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
app.execute_contract(
|
||||
Addr::unchecked(MEMBER1),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&RegisterDealer {
|
||||
bte_key_with_proof: "bte_key_with_proof".to_string(),
|
||||
announce_address: "127.0.0.1:8000".to_string(),
|
||||
},
|
||||
&coins(1000000000, TEST_COIN_DENOM),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..2 {
|
||||
app.execute_contract(
|
||||
Addr::unchecked(OWNER),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&AdvanceEpochState {},
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let msg = CommitVerificationKeyShare {
|
||||
share: "share".to_string(),
|
||||
};
|
||||
let res = app
|
||||
.execute_contract(
|
||||
Addr::unchecked(MEMBER1),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&msg,
|
||||
&vec![],
|
||||
)
|
||||
.unwrap();
|
||||
let proposal_id = res
|
||||
.events
|
||||
.into_iter()
|
||||
.find(|e| &e.ty == "wasm")
|
||||
.unwrap()
|
||||
.attributes
|
||||
.into_iter()
|
||||
.find(|attr| &attr.key == "proposal_id")
|
||||
.unwrap()
|
||||
.value
|
||||
.parse::<u64>()
|
||||
.unwrap();
|
||||
assert_eq!(1, proposal_id);
|
||||
}
|
||||
@@ -4,3 +4,4 @@
|
||||
mod deposit_and_release;
|
||||
mod helpers;
|
||||
mod spend_credential_creates_proposal;
|
||||
mod submit_vk_creates_proposal;
|
||||
|
||||
@@ -17,12 +17,12 @@ crate-type = ["cdylib", "rlib"]
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.2" }
|
||||
cw2 = { version = "0.13.2" }
|
||||
cw3 = { version = "0.13.2" }
|
||||
cw3-fixed-multisig = { version = "0.13.2", features = ["library"] }
|
||||
cw4 = { version = "0.13.2" }
|
||||
cw-storage-plus = { version = "0.13.2" }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw3 = { version = "0.13.4" }
|
||||
cw3-fixed-multisig = { version = "0.13.4", features = ["library"] }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
@@ -32,5 +32,5 @@ multisig-contract-common = { path= "../../../common/cosmwasm-smart-contracts/mul
|
||||
|
||||
[dev-dependencies]
|
||||
cosmwasm-schema = { version = "1.0.0" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.1" }
|
||||
cw-multi-test = { version = "0.13.1" }
|
||||
cw4-group = { path = "../cw4-group", version = "0.13.4" }
|
||||
cw-multi-test = { version = "0.13.4" }
|
||||
|
||||
@@ -37,11 +37,12 @@ pub fn instantiate(
|
||||
addr: msg.group_addr.clone(),
|
||||
}
|
||||
})?);
|
||||
// This might need to be changed via a migration, due to circular dependency
|
||||
// Those might need to be changed via a migration, due to circular dependency
|
||||
// of deploying the two contracts
|
||||
let coconut_bandwidth_addr = deps
|
||||
.api
|
||||
.addr_validate(&msg.coconut_bandwidth_contract_address)?;
|
||||
let coconut_dkg_addr = deps.api.addr_validate(&msg.coconut_dkg_contract_address)?;
|
||||
let total_weight = group_addr.total_weight(&deps.querier)?;
|
||||
msg.threshold.validate(total_weight)?;
|
||||
|
||||
@@ -52,6 +53,7 @@ pub fn instantiate(
|
||||
max_voting_period: msg.max_voting_period,
|
||||
group_addr,
|
||||
coconut_bandwidth_addr,
|
||||
coconut_dkg_addr,
|
||||
};
|
||||
CONFIG.save(deps.storage, &cfg)?;
|
||||
|
||||
@@ -62,6 +64,7 @@ pub fn instantiate(
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
let mut cfg = CONFIG.load(deps.storage)?;
|
||||
cfg.coconut_bandwidth_addr = deps.api.addr_validate(&msg.coconut_bandwidth_address)?;
|
||||
cfg.coconut_dkg_addr = deps.api.addr_validate(&msg.coconut_dkg_address)?;
|
||||
CONFIG.save(deps.storage, &cfg)?;
|
||||
Ok(Default::default())
|
||||
}
|
||||
@@ -102,8 +105,8 @@ pub fn execute_propose(
|
||||
// only members of the multisig can create a proposal
|
||||
let cfg = CONFIG.load(deps.storage)?;
|
||||
|
||||
// Only the coconut bandwidth contract can create proposals
|
||||
if info.sender != cfg.coconut_bandwidth_addr {
|
||||
// Only the coconut bandwidth or dkg contracts can create proposals
|
||||
if info.sender != cfg.coconut_bandwidth_addr && info.sender != cfg.coconut_dkg_addr {
|
||||
return Err(ContractError::Unauthorized {});
|
||||
}
|
||||
// The contract doesn't have any say in the voting outcome
|
||||
@@ -460,6 +463,7 @@ mod tests {
|
||||
const SOMEBODY: &str = "somebody";
|
||||
const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
|
||||
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
const TEST_COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
|
||||
|
||||
fn member<T: Into<String>>(addr: T, weight: u64) -> Member {
|
||||
Member {
|
||||
@@ -539,6 +543,7 @@ mod tests {
|
||||
threshold,
|
||||
max_voting_period,
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
app.instantiate_contract(flex_id, Addr::unchecked(OWNER), &msg, &[], "flex", None)
|
||||
.unwrap()
|
||||
@@ -655,6 +660,7 @@ mod tests {
|
||||
},
|
||||
max_voting_period,
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
let err = app
|
||||
.instantiate_contract(
|
||||
@@ -677,6 +683,7 @@ mod tests {
|
||||
threshold: Threshold::AbsoluteCount { weight: 100 },
|
||||
max_voting_period,
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
let err = app
|
||||
.instantiate_contract(
|
||||
@@ -699,6 +706,7 @@ mod tests {
|
||||
threshold: Threshold::AbsoluteCount { weight: 1 },
|
||||
max_voting_period,
|
||||
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
|
||||
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
|
||||
};
|
||||
let flex_addr = app
|
||||
.instantiate_contract(
|
||||
|
||||
@@ -13,6 +13,7 @@ pub struct Config {
|
||||
// Total weight and voters are queried from this contract
|
||||
pub group_addr: Cw4Contract,
|
||||
pub coconut_bandwidth_addr: Addr,
|
||||
pub coconut_dkg_addr: Addr,
|
||||
}
|
||||
|
||||
// unique items
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cw4-group"
|
||||
version = "0.13.1"
|
||||
version = "0.13.4"
|
||||
authors = ["Ethan Frey <ethanfrey@users.noreply.github.com>"]
|
||||
edition = "2018"
|
||||
description = "Simple cw4 implementation of group membership controlled by admin "
|
||||
@@ -24,11 +24,11 @@ crate-type = ["cdylib", "rlib"]
|
||||
library = []
|
||||
|
||||
[dependencies]
|
||||
cw-utils = { version = "0.13.2" }
|
||||
cw2 = { version = "0.13.2" }
|
||||
cw4 = { version = "0.13.2" }
|
||||
cw-controllers = { version = "0.13.2" }
|
||||
cw-storage-plus = { version = "0.13.2" }
|
||||
cw-utils = { version = "0.13.4" }
|
||||
cw2 = { version = "0.13.4" }
|
||||
cw4 = { version = "0.13.4" }
|
||||
cw-controllers = { version = "0.13.4" }
|
||||
cw-storage-plus = { version = "0.13.4" }
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
schemars = "0.8.1"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
|
||||
@@ -8,11 +8,11 @@ use log::*;
|
||||
use coconut_interface::{Credential, VerificationKey};
|
||||
use validator_client::{
|
||||
nymd::{
|
||||
cosmwasm_client::logs::{find_attribute, PROPOSAL_ID},
|
||||
cosmwasm_client::logs::{find_attribute, BANDWIDTH_PROPOSAL_ID},
|
||||
traits::{CoconutBandwidthSigningClient, MultisigQueryClient, MultisigSigningClient},
|
||||
Coin, Fee, NymdClient, SigningNymdClient,
|
||||
Coin, Fee, SigningNymdClient,
|
||||
},
|
||||
ApiClient,
|
||||
Client, CoconutApiClient,
|
||||
};
|
||||
|
||||
use super::authenticated::RequestHandlingError;
|
||||
@@ -21,16 +21,16 @@ const ONE_HOUR_SEC: u64 = 3600;
|
||||
const MAX_FEEGRANT_UNYM: u128 = 10000;
|
||||
|
||||
pub(crate) struct CoconutVerifier {
|
||||
api_clients: Vec<ApiClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
api_clients: Vec<CoconutApiClient>,
|
||||
nymd_client: Client<SigningNymdClient>,
|
||||
mix_denom_base: String,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
}
|
||||
|
||||
impl CoconutVerifier {
|
||||
pub fn new(
|
||||
api_clients: Vec<ApiClient>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
api_clients: Vec<CoconutApiClient>,
|
||||
nymd_client: Client<SigningNymdClient>,
|
||||
mix_denom_base: String,
|
||||
aggregated_verification_key: VerificationKey,
|
||||
) -> Result<Self, RequestHandlingError> {
|
||||
@@ -59,17 +59,18 @@ impl CoconutVerifier {
|
||||
|
||||
let res = self
|
||||
.nymd_client
|
||||
.nymd
|
||||
.spend_credential(
|
||||
Coin::new(
|
||||
credential.voucher_value().into(),
|
||||
self.mix_denom_base.clone(),
|
||||
),
|
||||
credential.blinded_serial_number(),
|
||||
self.nymd_client.address().to_string(),
|
||||
self.nymd_client.nymd.address().to_string(),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let proposal_id = find_attribute(&res.logs, "wasm", PROPOSAL_ID)
|
||||
let proposal_id = find_attribute(&res.logs, "wasm", BANDWIDTH_PROPOSAL_ID)
|
||||
.ok_or(RequestHandlingError::ProposalIdError {
|
||||
reason: String::from("proposal id not found"),
|
||||
})?
|
||||
@@ -79,7 +80,7 @@ impl CoconutVerifier {
|
||||
reason: String::from("proposal id could not be parsed to u64"),
|
||||
})?;
|
||||
|
||||
let proposal = self.nymd_client.get_proposal(proposal_id).await?;
|
||||
let proposal = self.nymd_client.nymd.get_proposal(proposal_id).await?;
|
||||
if !credential.has_blinded_serial_number(&proposal.description)? {
|
||||
return Err(RequestHandlingError::ProposalIdError {
|
||||
reason: String::from("proposal has different serial number"),
|
||||
@@ -89,13 +90,13 @@ impl CoconutVerifier {
|
||||
let req = validator_api_requests::coconut::VerifyCredentialBody::new(
|
||||
credential.clone(),
|
||||
proposal_id,
|
||||
self.nymd_client.address().clone(),
|
||||
self.nymd_client.nymd.address().clone(),
|
||||
);
|
||||
for client in self.api_clients.iter() {
|
||||
let api_cosmos_addr = client.get_cosmos_address().await?.addr;
|
||||
self.nymd_client
|
||||
.nymd
|
||||
.grant_allowance(
|
||||
&api_cosmos_addr,
|
||||
&client.cosmos_address,
|
||||
vec![Coin::new(MAX_FEEGRANT_UNYM, self.mix_denom_base.clone())],
|
||||
SystemTime::now().checked_add(Duration::from_secs(ONE_HOUR_SEC)),
|
||||
// It would be nice to be able to filter deeper, but for now only the msg type filter is avaialable
|
||||
@@ -104,20 +105,24 @@ impl CoconutVerifier {
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let ret = client.verify_bandwidth_credential(&req).await;
|
||||
let ret = client.api_client.verify_bandwidth_credential(&req).await;
|
||||
self.nymd_client
|
||||
.nymd
|
||||
.revoke_allowance(
|
||||
&api_cosmos_addr,
|
||||
&client.cosmos_address,
|
||||
"Cleanup the previous allowance for releasing funds".to_string(),
|
||||
revoke_fee.clone(),
|
||||
)
|
||||
.await?;
|
||||
if !ret?.verification_result {
|
||||
debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.validator_api.current_url());
|
||||
debug!("Validator {} didn't accept the credential. It will probably vote No on the spending proposal", client.api_client.validator_api.current_url());
|
||||
}
|
||||
}
|
||||
|
||||
self.nymd_client.execute_proposal(proposal_id, None).await?;
|
||||
self.nymd_client
|
||||
.nymd
|
||||
.execute_proposal(proposal_id, None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+27
-32
@@ -25,9 +25,9 @@ use crate::config::persistence::pathfinder::GatewayPathfinder;
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
#[cfg(feature = "coconut")]
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
use credentials::coconut::utils::obtain_aggregate_verification_key;
|
||||
#[cfg(feature = "coconut")]
|
||||
use validator_client::nymd;
|
||||
use validator_client::{Client, CoconutApiClient};
|
||||
|
||||
use self::storage::PersistentStorage;
|
||||
|
||||
@@ -242,32 +242,24 @@ where
|
||||
#[cfg(feature = "coconut")]
|
||||
fn random_nymd_client(
|
||||
&self,
|
||||
) -> validator_client::nymd::NymdClient<validator_client::nymd::SigningNymdClient> {
|
||||
) -> validator_client::Client<validator_client::nymd::SigningNymdClient> {
|
||||
let endpoints = self.config.get_validator_nymd_endpoints();
|
||||
let validator_nymd = endpoints
|
||||
.choose(&mut thread_rng())
|
||||
.expect("The list of validators is empty");
|
||||
|
||||
let network_details = NymNetworkDetails::new_from_env();
|
||||
let client_config = nymd::Config::try_from_nym_network_details(&network_details)
|
||||
.expect("failed to construct valid validator client config with the provided network");
|
||||
|
||||
validator_client::nymd::NymdClient::connect_with_mnemonic(
|
||||
client_config,
|
||||
validator_nymd.as_ref(),
|
||||
self.config.get_cosmos_mnemonic(),
|
||||
None,
|
||||
let client_config = validator_client::Config::try_from_nym_network_details(
|
||||
&network_details,
|
||||
)
|
||||
.expect("Could not connect with mnemonic")
|
||||
}
|
||||
.expect("failed to construct valid validator client config with the provided network");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
fn all_api_clients(&self) -> Vec<validator_client::ApiClient> {
|
||||
self.config
|
||||
.get_validator_api_endpoints()
|
||||
.into_iter()
|
||||
.map(validator_client::ApiClient::new)
|
||||
.collect()
|
||||
let mut client = Client::new_signing(client_config, self.config.get_cosmos_mnemonic())
|
||||
.expect("Could not connect with mnemonic");
|
||||
client
|
||||
.change_nymd(validator_nymd.clone())
|
||||
.expect("Could not use the random nymd URL");
|
||||
client
|
||||
}
|
||||
|
||||
// TODO: ask DH whether this function still makes sense in ^0.10
|
||||
@@ -306,20 +298,23 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let validators_verification_key =
|
||||
obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints())
|
||||
let coconut_verifier = {
|
||||
let nymd_client = self.random_nymd_client();
|
||||
let api_clients = CoconutApiClient::all_coconut_api_clients(&nymd_client)
|
||||
.await
|
||||
.expect("Could not query all api clients");
|
||||
let validators_verification_key = obtain_aggregate_verification_key(&api_clients)
|
||||
.await
|
||||
.expect("failed to contact validators to obtain their verification keys");
|
||||
#[cfg(feature = "coconut")]
|
||||
let nymd_client = self.random_nymd_client();
|
||||
#[cfg(feature = "coconut")]
|
||||
let coconut_verifier = CoconutVerifier::new(
|
||||
self.all_api_clients(),
|
||||
nymd_client,
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM).expect("mix denom base not set"),
|
||||
validators_verification_key,
|
||||
)
|
||||
.expect("Could not create coconut verifier");
|
||||
CoconutVerifier::new(
|
||||
api_clients,
|
||||
nymd_client,
|
||||
std::env::var(network_defaults::var_names::MIX_DENOM)
|
||||
.expect("mix denom base not set"),
|
||||
validators_verification_key,
|
||||
)
|
||||
.expect("Could not create coconut verifier")
|
||||
};
|
||||
|
||||
let mix_forwarding_channel = self.start_packet_forwarder();
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ credential-storage = { path = "../common/credential-storage" }
|
||||
credentials = { path = "../common/credentials", optional = true }
|
||||
crypto = { path = "../common/crypto" }
|
||||
logging = { path = "../common/logging"}
|
||||
cw3 = { version = "0.13.4", optional = true }
|
||||
dkg = { path = "../common/crypto/dkg", optional = true }
|
||||
gateway-client = { path = "../common/client-libs/gateway-client" }
|
||||
inclusion-probability = { path = "../common/inclusion-probability" }
|
||||
@@ -93,6 +94,7 @@ version-checker = { path = "../common/version-checker" }
|
||||
coconut = [
|
||||
"coconut-interface",
|
||||
"credentials",
|
||||
"cw3",
|
||||
"gateway-client/coconut",
|
||||
"credentials/coconut",
|
||||
"validator-api-requests/coconut",
|
||||
@@ -121,5 +123,5 @@ vergen = { version = "7", default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
cw3 = "0.13.2"
|
||||
cw-utils = "0.13.2"
|
||||
cw3 = "0.13.4"
|
||||
cw-utils = "0.13.4"
|
||||
|
||||
@@ -5,8 +5,9 @@ use crate::coconut::error::Result;
|
||||
use coconut_bandwidth_contract_common::spend_credential::SpendCredentialResponse;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
use cw3::ProposalResponse;
|
||||
use validator_client::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use validator_client::nymd::{AccountId, Fee, TxResponse};
|
||||
|
||||
@@ -15,6 +16,7 @@ pub trait Client {
|
||||
async fn address(&self) -> AccountId;
|
||||
async fn get_tx(&self, tx_hash: &str) -> Result<TxResponse>;
|
||||
async fn get_proposal(&self, proposal_id: u64) -> Result<ProposalResponse>;
|
||||
async fn list_proposals(&self) -> Result<Vec<ProposalResponse>>;
|
||||
async fn get_spent_credential(
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
@@ -23,9 +25,18 @@ pub trait Client {
|
||||
async fn get_self_registered_dealer_details(&self) -> Result<DealerDetailsResponse>;
|
||||
async fn get_current_dealers(&self) -> Result<Vec<DealerDetails>>;
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>>;
|
||||
async fn get_verification_key_shares(&self) -> Result<Vec<ContractVKShare>>;
|
||||
async fn vote_proposal(&self, proposal_id: u64, vote_yes: bool, fee: Option<Fee>)
|
||||
-> Result<()>;
|
||||
async fn register_dealer(&self, bte_key: EncodedBTEPublicKeyWithProof)
|
||||
-> Result<ExecuteResult>;
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> Result<()>;
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
) -> Result<ExecuteResult>;
|
||||
async fn submit_dealing(&self, dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult>;
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
) -> Result<ExecuteResult>;
|
||||
}
|
||||
|
||||
@@ -2,28 +2,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::coconut::error::Result;
|
||||
use crate::nymd_client::Client;
|
||||
use coconut_interface::VerificationKey;
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
use url::Url;
|
||||
use credentials::coconut::utils::obtain_aggregate_verification_key;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
use validator_client::CoconutApiClient;
|
||||
|
||||
#[async_trait]
|
||||
pub trait APICommunicationChannel {
|
||||
async fn aggregated_verification_key(&self) -> Result<VerificationKey>;
|
||||
}
|
||||
|
||||
pub struct QueryCommunicationChannel {
|
||||
validator_apis: Vec<Url>,
|
||||
pub(crate) struct QueryCommunicationChannel {
|
||||
nymd_client: Client<SigningNymdClient>,
|
||||
}
|
||||
|
||||
impl QueryCommunicationChannel {
|
||||
pub fn new(validator_apis: Vec<Url>) -> Self {
|
||||
QueryCommunicationChannel { validator_apis }
|
||||
pub fn new(nymd_client: Client<SigningNymdClient>) -> Self {
|
||||
QueryCommunicationChannel { nymd_client }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl APICommunicationChannel for QueryCommunicationChannel {
|
||||
async fn aggregated_verification_key(&self) -> Result<VerificationKey> {
|
||||
Ok(obtain_aggregate_verification_key(&self.validator_apis).await?)
|
||||
let client = self.nymd_client.0.read().await;
|
||||
let coconut_api_clients = CoconutApiClient::all_coconut_api_clients(&client).await?;
|
||||
let vk = obtain_aggregate_verification_key(&coconut_api_clients).await?;
|
||||
Ok(vk)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@ use crate::coconut::client::Client;
|
||||
use crate::coconut::error::CoconutError;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState, NodeIndex};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cw3::ProposalResponse;
|
||||
use validator_client::nymd::cosmwasm_client::logs::{find_attribute, NODE_INDEX};
|
||||
use validator_client::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use validator_client::nymd::AccountId;
|
||||
|
||||
pub(crate) struct DkgClient {
|
||||
@@ -14,6 +17,10 @@ pub(crate) struct DkgClient {
|
||||
}
|
||||
|
||||
impl DkgClient {
|
||||
// Some queries simply don't work the first time
|
||||
// Until we determine why that is, retry the query a few more times
|
||||
const RETRIES: usize = 3;
|
||||
|
||||
pub(crate) fn new<C>(nymd_client: C) -> Self
|
||||
where
|
||||
C: Client + Send + Sync + 'static,
|
||||
@@ -28,7 +35,14 @@ impl DkgClient {
|
||||
}
|
||||
|
||||
pub(crate) async fn get_current_epoch_state(&self) -> Result<EpochState, CoconutError> {
|
||||
self.inner.get_current_epoch_state().await
|
||||
let mut ret = self.inner.get_current_epoch_state().await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
ret = self.inner.get_current_epoch_state().await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn get_self_registered_dealer_details(
|
||||
@@ -45,14 +59,35 @@ impl DkgClient {
|
||||
&self,
|
||||
idx: usize,
|
||||
) -> Result<Vec<ContractDealing>, CoconutError> {
|
||||
self.inner.get_dealings(idx).await
|
||||
let mut ret = self.inner.get_dealings(idx).await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if ret.is_ok() {
|
||||
return ret;
|
||||
}
|
||||
ret = self.inner.get_dealings(idx).await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn get_verification_key_shares(
|
||||
&self,
|
||||
) -> Result<Vec<ContractVKShare>, CoconutError> {
|
||||
self.inner.get_verification_key_shares().await
|
||||
}
|
||||
|
||||
pub(crate) async fn list_proposals(&self) -> Result<Vec<ProposalResponse>, CoconutError> {
|
||||
self.inner.list_proposals().await
|
||||
}
|
||||
|
||||
pub(crate) async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
) -> Result<NodeIndex, CoconutError> {
|
||||
let res = self.inner.register_dealer(bte_key).await?;
|
||||
let res = self
|
||||
.inner
|
||||
.register_dealer(bte_key, announce_address)
|
||||
.await?;
|
||||
let node_index = find_attribute(&res.logs, "wasm", NODE_INDEX)
|
||||
.ok_or(CoconutError::NodeIndexRecoveryError {
|
||||
reason: String::from("node index not found"),
|
||||
@@ -73,4 +108,39 @@ impl DkgClient {
|
||||
self.inner.submit_dealing(dealing_bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
let mut ret = self
|
||||
.inner
|
||||
.submit_verification_key_share(share.clone())
|
||||
.await;
|
||||
for _ in 0..Self::RETRIES {
|
||||
if let Ok(res) = ret {
|
||||
return Ok(res);
|
||||
}
|
||||
ret = self
|
||||
.inner
|
||||
.submit_verification_key_share(share.clone())
|
||||
.await;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub(crate) async fn vote_verification_key_share(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
vote_yes: bool,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.vote_proposal(proposal_id, vote_yes, None).await
|
||||
}
|
||||
|
||||
pub(crate) async fn execute_verification_key_share(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
) -> Result<(), CoconutError> {
|
||||
self.inner.execute_proposal(proposal_id).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,38 +9,3 @@ pub(crate) enum ComplaintReason {
|
||||
MalformedDealing(DkgError),
|
||||
DealingVerificationError(DkgError),
|
||||
}
|
||||
|
||||
// pub(crate) async fn complaint_period(
|
||||
// dkg_client: &DkgClient,
|
||||
// state: &mut State,
|
||||
// ) -> Result<(), CoconutError> {
|
||||
// let dkg_params = dkg::bte::setup();
|
||||
// let threshold = state
|
||||
// .threshold()
|
||||
// .expect("We should have a tentative threshold by now");
|
||||
// let dealings = dkg_client.get_dealings().await?;
|
||||
// for contract_dealing in dealings {
|
||||
// match Dealing::try_from_bytes(&contract_dealing.dealing) {
|
||||
// Ok(dealing) => {
|
||||
// if let Err(err) =
|
||||
// dealing.verify(&dkg_params, threshold, &state.current_receivers(), None)
|
||||
// {
|
||||
// state.remove_good_dealer(&contract_dealing.dealer);
|
||||
// state.add_bad_dealer(
|
||||
// contract_dealing.dealer,
|
||||
// ComplaintReason::DealingVerificationError(err),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Err(err) => {
|
||||
// state.remove_good_dealer(&contract_dealing.dealer);
|
||||
// state.add_bad_dealer(
|
||||
// contract_dealing.dealer,
|
||||
// ComplaintReason::MalformedDealing(err),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::state::{ConsistentState, State};
|
||||
use crate::coconut::dkg::verification_key::{
|
||||
verification_key_finalization, verification_key_validation,
|
||||
};
|
||||
use crate::coconut::dkg::{
|
||||
dealing::dealing_exchange, public_key::public_key_submission,
|
||||
verification_key::verification_key_submission,
|
||||
@@ -14,6 +17,7 @@ use coconut_dkg_common::types::EpochState;
|
||||
use dkg::bte::keys::KeyPair as DkgKeyPair;
|
||||
use rand::rngs::OsRng;
|
||||
use rand::RngCore;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
use task::ShutdownListener;
|
||||
use tokio::time::interval;
|
||||
@@ -35,13 +39,15 @@ pub(crate) fn init_keypair(config: &Config) -> Result<()> {
|
||||
|
||||
pub(crate) struct DkgController<R> {
|
||||
dkg_client: DkgClient,
|
||||
secret_key_path: PathBuf,
|
||||
verification_key_path: PathBuf,
|
||||
state: State,
|
||||
rng: R,
|
||||
polling_rate: Duration,
|
||||
}
|
||||
|
||||
impl<R: RngCore + Clone> DkgController<R> {
|
||||
pub(crate) fn new(
|
||||
pub(crate) async fn new(
|
||||
config: &Config,
|
||||
nymd_client: nymd_client::Client<SigningNymdClient>,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
@@ -51,20 +57,28 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
config.decryption_key_path(),
|
||||
config.public_key_with_proof_path(),
|
||||
))?;
|
||||
if let Ok(coconut_keypair_value) = pemstore::load_keypair(&pemstore::KeyPairPath::new(
|
||||
config.secret_key_path(),
|
||||
config.verification_key_path(),
|
||||
)) {
|
||||
coconut_keypair.set(coconut_keypair_value).await;
|
||||
}
|
||||
|
||||
Ok(DkgController {
|
||||
dkg_client: DkgClient::new(nymd_client),
|
||||
state: State::new(dkg_keypair, coconut_keypair),
|
||||
secret_key_path: config.secret_key_path(),
|
||||
verification_key_path: config.verification_key_path(),
|
||||
state: State::new(config.get_announce_address(), dkg_keypair, coconut_keypair),
|
||||
rng,
|
||||
polling_rate: config.get_dkg_contract_polling_rate(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn handle_epoch_state(&mut self) -> bool {
|
||||
async fn handle_epoch_state(&mut self) {
|
||||
match self.dkg_client.get_current_epoch_state().await {
|
||||
Err(e) => warn!("Could not get current epoch state {}", e),
|
||||
Ok(epoch_state) => {
|
||||
if let Err(e) = self.state.is_consistent(epoch_state) {
|
||||
if let Err(e) = self.state.is_consistent(epoch_state).await {
|
||||
error!(
|
||||
"Epoch state is corrupted - {}, the process should be terminated",
|
||||
e
|
||||
@@ -78,28 +92,38 @@ impl<R: RngCore + Clone> DkgController<R> {
|
||||
dealing_exchange(&self.dkg_client, &mut self.state, self.rng.clone()).await
|
||||
}
|
||||
EpochState::VerificationKeySubmission => {
|
||||
verification_key_submission(&self.dkg_client, &mut self.state).await
|
||||
let keypair_path = pemstore::KeyPairPath::new(
|
||||
self.secret_key_path.clone(),
|
||||
self.verification_key_path.clone(),
|
||||
);
|
||||
verification_key_submission(
|
||||
&self.dkg_client,
|
||||
&mut self.state,
|
||||
&keypair_path,
|
||||
)
|
||||
.await
|
||||
}
|
||||
EpochState::InProgress => return true,
|
||||
EpochState::VerificationKeyValidation => {
|
||||
verification_key_validation(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
verification_key_finalization(&self.dkg_client, &mut self.state).await
|
||||
}
|
||||
// Just wait, in case we need to redo dkg at some point
|
||||
EpochState::InProgress => Ok(()),
|
||||
};
|
||||
if let Err(e) = ret {
|
||||
warn!("Could not handle this iteration for the epoch state: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) async fn run(mut self, mut shutdown: ShutdownListener) {
|
||||
let mut interval = interval(self.polling_rate);
|
||||
while !shutdown.is_shutdown() {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => {
|
||||
if self.handle_epoch_state().await {
|
||||
// If dkg finished, we can finish this task
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ = interval.tick() => self.handle_epoch_state().await,
|
||||
_ = shutdown.recv() => {
|
||||
trace!("DkgController: Received shutdown");
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ pub(crate) async fn dealing_exchange(
|
||||
rng: impl RngCore + Clone,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.receiver_index().is_some() {
|
||||
info!("Already have index {}", state.receiver_index().unwrap());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -46,7 +45,7 @@ pub(crate) async fn dealing_exchange(
|
||||
.await?;
|
||||
}
|
||||
|
||||
info!("Setting index to {}", receiver_index.unwrap());
|
||||
info!("DKG: Finished submitting dealing");
|
||||
state.set_receiver_index(receiver_index);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -21,10 +21,12 @@ pub(crate) async fn public_key_submission(
|
||||
{
|
||||
details.assigned_index
|
||||
} else {
|
||||
dkg_client.register_dealer(bte_key).await?
|
||||
dkg_client
|
||||
.register_dealer(bte_key, state.announce_address().to_string())
|
||||
.await?
|
||||
};
|
||||
state.set_node_index(index);
|
||||
info!("Starting dkg protocol with index {}", index);
|
||||
info!("DKG: Using node index {}", index);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ use coconut_dkg_common::dealer::DealerDetails;
|
||||
use coconut_dkg_common::types::EpochState;
|
||||
use cosmwasm_std::Addr;
|
||||
use dkg::bte::{keys::KeyPair as DkgKeyPair, PublicKey, PublicKeyWithProof};
|
||||
use dkg::{NodeIndex, Threshold};
|
||||
use dkg::{NodeIndex, RecoveredVerificationKeys, Threshold};
|
||||
use std::collections::BTreeMap;
|
||||
use url::Url;
|
||||
|
||||
// note: each dealer is also a receiver which simplifies some logic significantly
|
||||
#[derive(Debug)]
|
||||
@@ -37,40 +38,50 @@ impl TryFrom<DealerDetails> for DkgParticipant {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub(crate) trait ConsistentState {
|
||||
fn node_index_value(&self) -> Result<NodeIndex, CoconutError>;
|
||||
fn receiver_index_value(&self) -> Result<usize, CoconutError>;
|
||||
fn threshold(&self) -> Result<Threshold, CoconutError>;
|
||||
fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
|
||||
async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError>;
|
||||
fn proposal_id_value(&self) -> Result<u64, CoconutError>;
|
||||
async fn is_consistent(&self, epoch_state: EpochState) -> Result<(), CoconutError> {
|
||||
match epoch_state {
|
||||
EpochState::PublicKeySubmission => {}
|
||||
EpochState::DealingExchange => {
|
||||
self.node_index_value()?;
|
||||
}
|
||||
EpochState::VerificationKeySubmission => {
|
||||
self.node_index_value()?;
|
||||
self.receiver_index_value()?;
|
||||
self.threshold()?;
|
||||
}
|
||||
EpochState::InProgress => {
|
||||
self.node_index_value()?;
|
||||
self.receiver_index_value()?;
|
||||
self.threshold()?;
|
||||
EpochState::VerificationKeyValidation => {
|
||||
self.coconut_keypair_is_some().await?;
|
||||
}
|
||||
EpochState::VerificationKeyFinalization => {
|
||||
self.proposal_id_value()?;
|
||||
}
|
||||
EpochState::InProgress => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct State {
|
||||
announce_address: Url,
|
||||
dkg_keypair: DkgKeyPair,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
node_index: Option<NodeIndex>,
|
||||
dealers: BTreeMap<Addr, Result<DkgParticipant, ComplaintReason>>,
|
||||
receiver_index: Option<usize>,
|
||||
threshold: Option<Threshold>,
|
||||
recovered_vks: Vec<RecoveredVerificationKeys>,
|
||||
proposal_id: Option<u64>,
|
||||
voted_vks: bool,
|
||||
executed_proposal: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ConsistentState for State {
|
||||
fn node_index_value(&self) -> Result<NodeIndex, CoconutError> {
|
||||
self.node_index.ok_or(CoconutError::UnrecoverableState {
|
||||
@@ -98,20 +109,49 @@ impl ConsistentState for State {
|
||||
Ok(threshold)
|
||||
}
|
||||
}
|
||||
|
||||
async fn coconut_keypair_is_some(&self) -> Result<(), CoconutError> {
|
||||
if self.coconut_keypair_is_some().await {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(CoconutError::UnrecoverableState {
|
||||
reason: String::from("Coconut keypair should have been set"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn proposal_id_value(&self) -> Result<u64, CoconutError> {
|
||||
self.proposal_id.ok_or(CoconutError::UnrecoverableState {
|
||||
reason: String::from("Proposal id should have benn set"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new(dkg_keypair: DkgKeyPair, coconut_keypair: CoconutKeyPair) -> Self {
|
||||
pub fn new(
|
||||
announce_address: Url,
|
||||
dkg_keypair: DkgKeyPair,
|
||||
coconut_keypair: CoconutKeyPair,
|
||||
) -> Self {
|
||||
State {
|
||||
announce_address,
|
||||
dkg_keypair,
|
||||
coconut_keypair,
|
||||
node_index: None,
|
||||
dealers: BTreeMap::new(),
|
||||
receiver_index: None,
|
||||
threshold: None,
|
||||
recovered_vks: vec![],
|
||||
proposal_id: None,
|
||||
voted_vks: false,
|
||||
executed_proposal: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announce_address(&self) -> &Url {
|
||||
&self.announce_address
|
||||
}
|
||||
|
||||
pub fn dkg_keypair(&self) -> &DkgKeyPair {
|
||||
&self.dkg_keypair
|
||||
}
|
||||
@@ -154,6 +194,22 @@ impl State {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn recovered_vks(&self) -> &Vec<RecoveredVerificationKeys> {
|
||||
&self.recovered_vks
|
||||
}
|
||||
|
||||
pub fn voted_vks(&self) -> bool {
|
||||
self.voted_vks
|
||||
}
|
||||
|
||||
pub fn executed_proposal(&self) -> bool {
|
||||
self.executed_proposal
|
||||
}
|
||||
|
||||
pub fn set_recovered_vks(&mut self, recovered_vks: Vec<RecoveredVerificationKeys>) {
|
||||
self.recovered_vks = recovered_vks;
|
||||
}
|
||||
|
||||
pub async fn set_coconut_keypair(&mut self, coconut_keypair: coconut_interface::KeyPair) {
|
||||
self.coconut_keypair.set(coconut_keypair).await
|
||||
}
|
||||
@@ -187,4 +243,16 @@ impl State {
|
||||
pub fn set_threshold(&mut self, threshold: Threshold) {
|
||||
self.threshold = Some(threshold);
|
||||
}
|
||||
|
||||
pub fn set_proposal_id(&mut self, proposal_id: u64) {
|
||||
self.proposal_id = Some(proposal_id);
|
||||
}
|
||||
|
||||
pub fn set_voted_vks(&mut self) {
|
||||
self.voted_vks = true;
|
||||
}
|
||||
|
||||
pub fn set_executed_proposal(&mut self) {
|
||||
self.executed_proposal = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,41 +5,34 @@ use crate::coconut::dkg::client::DkgClient;
|
||||
use crate::coconut::dkg::complaints::ComplaintReason;
|
||||
use crate::coconut::dkg::state::{ConsistentState, State};
|
||||
use crate::coconut::error::CoconutError;
|
||||
use coconut_dkg_common::event_attributes::DKG_PROPOSAL_ID;
|
||||
use coconut_dkg_common::types::{NodeIndex, TOTAL_DEALINGS};
|
||||
use coconut_dkg_common::verification_key::owner_from_cosmos_msgs;
|
||||
use coconut_interface::KeyPair as CoconutKeyPair;
|
||||
use cosmwasm_std::Addr;
|
||||
use credentials::coconut::bandwidth::{PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
|
||||
use cw3::{ProposalResponse, Status};
|
||||
use dkg::bte::{decrypt_share, setup};
|
||||
use dkg::{combine_shares, Dealing};
|
||||
use nymcoconut::{KeyPair, Parameters, SecretKey};
|
||||
use dkg::{combine_shares, try_recover_verification_keys, Dealing, Threshold};
|
||||
use nymcoconut::tests::helpers::transpose_matrix;
|
||||
use nymcoconut::{check_vk_pairing, Base58, KeyPair, Parameters, SecretKey, VerificationKey};
|
||||
use pemstore::KeyPairPath;
|
||||
use std::collections::BTreeMap;
|
||||
use validator_client::nymd::cosmwasm_client::logs::find_attribute;
|
||||
|
||||
// Filter the dealers based on what dealing they posted (or not) in the contract
|
||||
async fn deterministic_filter_dealers(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
threshold: Threshold,
|
||||
) -> Result<Vec<BTreeMap<NodeIndex, (Addr, Dealing)>>, CoconutError> {
|
||||
let mut dealings_maps = vec![];
|
||||
let initial_dealers_by_addr = state.current_dealers_by_addr();
|
||||
let initial_receivers = state.current_dealers_by_idx();
|
||||
let threshold = state.threshold()?;
|
||||
let params = setup();
|
||||
let retries = 3;
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS {
|
||||
let mut try_no = 0;
|
||||
let dealings = loop {
|
||||
// this is a really ugly way to get the dealings, but for some reason the first query
|
||||
// always fails with a RPC error.
|
||||
try_no += 1;
|
||||
if let Ok(dealings) = dkg_client.get_dealings(idx).await {
|
||||
break dealings;
|
||||
} else if try_no == retries {
|
||||
return Err(CoconutError::UnrecoverableState {
|
||||
reason: String::from("Could not get dealings"),
|
||||
});
|
||||
}
|
||||
};
|
||||
let dealings = dkg_client.get_dealings(idx).await?;
|
||||
let dealings_map =
|
||||
BTreeMap::from_iter(dealings.into_iter().filter_map(|contract_dealing| {
|
||||
match Dealing::try_from(&contract_dealing.dealing) {
|
||||
@@ -84,7 +77,8 @@ async fn deterministic_filter_dealers(
|
||||
}
|
||||
|
||||
fn derive_partial_keypair(
|
||||
state: &State,
|
||||
state: &mut State,
|
||||
threshold: Threshold,
|
||||
dealings_maps: Vec<BTreeMap<NodeIndex, (Addr, Dealing)>>,
|
||||
) -> Result<KeyPair, CoconutError> {
|
||||
let filtered_receivers_by_idx = state.current_dealers_by_idx();
|
||||
@@ -92,6 +86,7 @@ fn derive_partial_keypair(
|
||||
let dk = state.dkg_keypair().private_key();
|
||||
let node_index_value = state.receiver_index_value()?;
|
||||
let mut scalars = vec![];
|
||||
let mut recovered_vks = vec![];
|
||||
for dealings_map in dealings_maps.into_iter() {
|
||||
let filtered_dealings: Vec<_> = dealings_map
|
||||
.into_iter()
|
||||
@@ -103,6 +98,13 @@ fn derive_partial_keypair(
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let recovered = try_recover_verification_keys(
|
||||
&filtered_dealings,
|
||||
threshold,
|
||||
&filtered_receivers_by_idx,
|
||||
)?;
|
||||
recovered_vks.push(recovered);
|
||||
|
||||
let shares = filtered_dealings
|
||||
.iter()
|
||||
.map(|dealing| decrypt_share(dk, node_index_value, &dealing.ciphertexts, None))
|
||||
@@ -116,6 +118,7 @@ fn derive_partial_keypair(
|
||||
)?;
|
||||
scalars.push(scalar);
|
||||
}
|
||||
state.set_recovered_vks(recovered_vks);
|
||||
|
||||
let params = Parameters::new(PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES)?;
|
||||
let x = scalars.pop().unwrap();
|
||||
@@ -128,14 +131,114 @@ fn derive_partial_keypair(
|
||||
pub(crate) async fn verification_key_submission(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
keypair_path: &KeyPairPath,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.coconut_keypair_is_some().await {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let dealings_maps = deterministic_filter_dealers(dkg_client, state).await?;
|
||||
let coconut_keypair = derive_partial_keypair(state, dealings_maps)?;
|
||||
let threshold = state.threshold()?;
|
||||
let dealings_maps = deterministic_filter_dealers(dkg_client, state, threshold).await?;
|
||||
let coconut_keypair = derive_partial_keypair(state, threshold, dealings_maps)?;
|
||||
let vk_share = coconut_keypair.verification_key().to_bs58();
|
||||
pemstore::store_keypair(&coconut_keypair, keypair_path)?;
|
||||
let res = dkg_client.submit_verification_key_share(vk_share).await?;
|
||||
let proposal_id = find_attribute(&res.logs, "wasm", DKG_PROPOSAL_ID)
|
||||
.ok_or(CoconutError::ProposalIdError {
|
||||
reason: String::from("proposal id not found"),
|
||||
})?
|
||||
.value
|
||||
.parse::<u64>()
|
||||
.map_err(|_| CoconutError::ProposalIdError {
|
||||
reason: String::from("proposal id could not be parsed to u64"),
|
||||
})?;
|
||||
state.set_proposal_id(proposal_id);
|
||||
state.set_coconut_keypair(coconut_keypair).await;
|
||||
info!("DKG: Submitted own verification key");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_proposal(proposal: &ProposalResponse) -> Option<(Addr, u64)> {
|
||||
if proposal.status == Status::Open {
|
||||
if let Some(owner) = owner_from_cosmos_msgs(&proposal.msgs) {
|
||||
return Some((owner, proposal.id));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) async fn verification_key_validation(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.voted_vks() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let vk_shares = dkg_client.get_verification_key_shares().await?;
|
||||
let proposal_ids = BTreeMap::from_iter(
|
||||
dkg_client
|
||||
.list_proposals()
|
||||
.await?
|
||||
.iter()
|
||||
.filter_map(validate_proposal),
|
||||
);
|
||||
let filtered_receivers_by_idx: Vec<_> =
|
||||
state.current_dealers_by_idx().keys().copied().collect();
|
||||
let recovered_partials: Vec<_> = state
|
||||
.recovered_vks()
|
||||
.iter()
|
||||
.map(|recovered_vk| recovered_vk.recovered_partials.clone())
|
||||
.collect();
|
||||
let recovered_partials = transpose_matrix(recovered_partials);
|
||||
let params = Parameters::new(PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES)?;
|
||||
for contract_share in vk_shares {
|
||||
if let Some(proposal_id) = proposal_ids.get(&contract_share.owner).copied() {
|
||||
match VerificationKey::try_from_bs58(contract_share.share) {
|
||||
Ok(vk) => {
|
||||
if let Some(idx) = filtered_receivers_by_idx
|
||||
.iter()
|
||||
.position(|node_index| contract_share.node_index == *node_index)
|
||||
{
|
||||
if !check_vk_pairing(¶ms, &recovered_partials[idx], &vk) {
|
||||
dkg_client
|
||||
.vote_verification_key_share(proposal_id, false)
|
||||
.await?;
|
||||
} else {
|
||||
dkg_client
|
||||
.vote_verification_key_share(proposal_id, true)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
dkg_client
|
||||
.vote_verification_key_share(proposal_id, false)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state.set_voted_vks();
|
||||
info!("DKG: Validated the other verification keys");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn verification_key_finalization(
|
||||
dkg_client: &DkgClient,
|
||||
state: &mut State,
|
||||
) -> Result<(), CoconutError> {
|
||||
if state.executed_proposal() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let proposal_id = state.proposal_id_value()?;
|
||||
dkg_client
|
||||
.execute_verification_key_share(proposal_id)
|
||||
.await?;
|
||||
state.set_executed_proposal();
|
||||
info!("DKG: Finalized own verification key on chain");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ pub type Result<T> = std::result::Result<T, CoconutError>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CoconutError {
|
||||
#[error("{0}")]
|
||||
IOError(#[from] std::io::Error),
|
||||
|
||||
#[error("Could not parse Ed25519 data")]
|
||||
Ed25519ParseError(#[from] Ed25519RecoveryError),
|
||||
|
||||
@@ -93,6 +96,9 @@ pub enum CoconutError {
|
||||
|
||||
#[error("DKG has not finished yet in order to derive the coconut key")]
|
||||
KeyPairNotDerivedYet,
|
||||
|
||||
#[error("There was a problem with the proposal id: {reason}")]
|
||||
ProposalIdError { reason: String },
|
||||
}
|
||||
|
||||
impl<'r, 'o: 'r> Responder<'r, 'o> for CoconutError {
|
||||
|
||||
@@ -31,8 +31,7 @@ use crypto::shared_key::new_ephemeral_shared_key;
|
||||
use crypto::symmetric::stream_cipher;
|
||||
use keypair::KeyPair;
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_client::nymd::{Coin, Fee};
|
||||
use validator_client::validator_api::routes::{BANDWIDTH, COCONUT_ROUTES};
|
||||
@@ -186,8 +185,6 @@ impl InternalSignRequest {
|
||||
),
|
||||
routes![
|
||||
post_blind_sign,
|
||||
get_verification_key,
|
||||
get_cosmos_address,
|
||||
post_partial_bandwidth_credential,
|
||||
verify_bandwidth_credential
|
||||
],
|
||||
@@ -258,26 +255,6 @@ pub async fn post_partial_bandwidth_credential(
|
||||
Ok(Json(v))
|
||||
}
|
||||
|
||||
#[get("/verification-key")]
|
||||
pub async fn get_verification_key(
|
||||
state: &RocketState<State>,
|
||||
) -> Result<Json<VerificationKeyResponse>> {
|
||||
state
|
||||
.key_pair
|
||||
.get()
|
||||
.await
|
||||
.as_ref()
|
||||
.map(|keypair| Json(VerificationKeyResponse::new(keypair.verification_key())))
|
||||
.ok_or(CoconutError::KeyPairNotDerivedYet)
|
||||
}
|
||||
|
||||
#[get("/cosmos-address")]
|
||||
pub async fn get_cosmos_address(state: &RocketState<State>) -> Result<Json<CosmosAddressResponse>> {
|
||||
Ok(Json(CosmosAddressResponse::new(
|
||||
state.client.address().await,
|
||||
)))
|
||||
}
|
||||
|
||||
#[post("/verify-bandwidth-credential", data = "<verify_credential_body>")]
|
||||
pub async fn verify_bandwidth_credential(
|
||||
verify_credential_body: Json<VerifyCredentialBody>,
|
||||
|
||||
@@ -19,21 +19,18 @@ use credentials::coconut::params::{
|
||||
};
|
||||
use crypto::shared_key::recompute_shared_key;
|
||||
use crypto::symmetric::stream_cipher;
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
use nymcoconut::tests::helpers::theta_from_keys_and_attributes;
|
||||
use nymcoconut::{
|
||||
prepare_blind_sign, ttp_keygen, Base58, BlindSignRequest, BlindedSignature, KeyPair, Parameters,
|
||||
prepare_blind_sign, ttp_keygen, Base58, BlindSignRequest, BlindedSignature, Parameters,
|
||||
};
|
||||
use validator_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CosmosAddressResponse, VerificationKeyResponse,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
};
|
||||
use validator_client::nymd::Coin;
|
||||
use validator_client::nymd::{tx::Hash, AccountId, DeliverTx, Event, Fee, Tag, TxResponse};
|
||||
use validator_client::validator_api::routes::{
|
||||
API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_COSMOS_ADDRESS,
|
||||
COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL, COCONUT_ROUTES, COCONUT_VERIFICATION_KEY,
|
||||
COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
|
||||
API_VERSION, BANDWIDTH, COCONUT_BLIND_SIGN, COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
|
||||
COCONUT_ROUTES, COCONUT_VERIFY_BANDWIDTH_CREDENTIAL,
|
||||
};
|
||||
|
||||
use crate::coconut::State;
|
||||
@@ -41,8 +38,10 @@ use crate::ValidatorApiStorage;
|
||||
use async_trait::async_trait;
|
||||
use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsResponse};
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use cw3::ProposalResponse;
|
||||
use rand_07::rngs::OsRng;
|
||||
use rocket::http::Status;
|
||||
use rocket::local::asynchronous::Client;
|
||||
@@ -107,6 +106,10 @@ impl super::client::Client for DummyClient {
|
||||
})
|
||||
}
|
||||
|
||||
async fn list_proposals(&self) -> Result<Vec<ProposalResponse>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_spent_credential(
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
@@ -133,7 +136,11 @@ impl super::client::Client for DummyClient {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_dealings(&self, idx: usize) -> Result<Vec<ContractDealing>> {
|
||||
async fn get_dealings(&self, _idx: usize) -> Result<Vec<ContractDealing>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(&self) -> Result<Vec<ContractVKShare>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@@ -153,14 +160,26 @@ impl super::client::Client for DummyClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_proposal(&self, _proposal_id: u64) -> Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
_bte_key: EncodedBTEPublicKeyWithProof,
|
||||
_announce_address: String,
|
||||
) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn submit_dealing(&self, dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult> {
|
||||
async fn submit_dealing(&self, _dealing_bytes: ContractSafeBytes) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
_share: VerificationKeyShare,
|
||||
) -> Result<ExecuteResult> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -205,65 +224,6 @@ pub fn tx_entry_fixture(tx_hash: &str) -> TxResponse {
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_signer_verif_key(key_pair: KeyPair) {
|
||||
let verification_key = key_pair.verification_key();
|
||||
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
db_dir.push(&verification_key.to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(key_pair).await;
|
||||
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
staged_key_pair,
|
||||
comm_channel,
|
||||
storage,
|
||||
));
|
||||
|
||||
let client = Client::tracked(rocket)
|
||||
.await
|
||||
.expect("valid rocket instance");
|
||||
|
||||
let response = client
|
||||
.get(format!(
|
||||
"/{}/{}/{}/{}",
|
||||
API_VERSION, COCONUT_ROUTES, BANDWIDTH, COCONUT_VERIFICATION_KEY
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
||||
// This is a more direct way, but there's a bug which makes it hang https://github.com/SergioBenitez/Rocket/issues/1893
|
||||
// assert!(response
|
||||
// .into_json::<BlindedSignatureResponse>()
|
||||
// .await
|
||||
// .is_some());
|
||||
let verification_key_response =
|
||||
serde_json::from_str::<VerificationKeyResponse>(&response.into_string().await.unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(verification_key_response.key, verification_key);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn multiple_verification_key() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let num_authorities = 4;
|
||||
|
||||
let key_pairs = ttp_keygen(¶ms, num_authorities, num_authorities).unwrap();
|
||||
for key_pair in key_pairs.into_iter() {
|
||||
check_signer_verif_key(key_pair).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signed_before() {
|
||||
let tx_hash =
|
||||
@@ -697,49 +657,6 @@ async fn signature_test() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_cosmos_address() {
|
||||
let validator_address = AccountId::from_str(TEST_REWARDING_VALIDATOR_ADDRESS).unwrap();
|
||||
let nymd_client = DummyClient::new(
|
||||
validator_address.clone(),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
&Arc::new(RwLock::new(HashMap::new())),
|
||||
);
|
||||
let mut db_dir = std::env::temp_dir();
|
||||
let key_pair = ttp_keygen(&Parameters::new(4).unwrap(), 1, 1)
|
||||
.unwrap()
|
||||
.remove(0);
|
||||
db_dir.push(&key_pair.verification_key().to_bs58()[..8]);
|
||||
let storage = ValidatorApiStorage::init(db_dir).await.unwrap();
|
||||
let comm_channel = DummyCommunicationChannel::new(key_pair.verification_key());
|
||||
let staged_key_pair = crate::coconut::KeyPair::new();
|
||||
staged_key_pair.set(key_pair).await;
|
||||
let rocket = rocket::build().attach(InternalSignRequest::stage(
|
||||
nymd_client,
|
||||
TEST_COIN_DENOM.to_string(),
|
||||
staged_key_pair,
|
||||
comm_channel,
|
||||
storage.clone(),
|
||||
));
|
||||
let client = Client::tracked(rocket)
|
||||
.await
|
||||
.expect("valid rocket instance");
|
||||
|
||||
let response = client
|
||||
.get(format!(
|
||||
"/{}/{}/{}/{}",
|
||||
API_VERSION, COCONUT_ROUTES, BANDWIDTH, COCONUT_COSMOS_ADDRESS
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
let cosmos_addr_response =
|
||||
serde_json::from_str::<CosmosAddressResponse>(&response.into_string().await.unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(validator_address, cosmos_addr_response.addr);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn verification_of_bandwidth_credential() {
|
||||
// Setup variables
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::config::template::config_template;
|
||||
use config::defaults::DEFAULT_VALIDATOR_API_PORT;
|
||||
use config::NymConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
@@ -93,6 +94,11 @@ pub struct Base {
|
||||
|
||||
local_validator: Url,
|
||||
|
||||
/// Address announced to the directory server for the clients to connect to.
|
||||
// It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
|
||||
// later on by using name resolvable with a DNS query, such as `nymtech.net`.
|
||||
announce_address: Url,
|
||||
|
||||
/// Address of the validator contract managing the network
|
||||
mixnet_contract_address: String,
|
||||
|
||||
@@ -102,11 +108,17 @@ pub struct Base {
|
||||
|
||||
impl Default for Base {
|
||||
fn default() -> Self {
|
||||
let default_validator: Url = DEFAULT_LOCAL_VALIDATOR
|
||||
.parse()
|
||||
.expect("default local validator is malformed!");
|
||||
let mut default_announce_address = default_validator.clone();
|
||||
default_announce_address
|
||||
.set_port(Some(DEFAULT_VALIDATOR_API_PORT))
|
||||
.expect("default local validator is malformed!");
|
||||
Base {
|
||||
id: String::default(),
|
||||
local_validator: DEFAULT_LOCAL_VALIDATOR
|
||||
.parse()
|
||||
.expect("default local validator is malformed!"),
|
||||
local_validator: default_validator,
|
||||
announce_address: default_announce_address,
|
||||
mixnet_contract_address: String::default(),
|
||||
mnemonic: "exact antique hybrid width raise anchor puzzle degree fee quit long crack net vague hip despair write put useless civil mechanic broom music day".to_string(),
|
||||
}
|
||||
@@ -270,8 +282,11 @@ pub struct CoconutSigner {
|
||||
/// Specifies whether rewarding service is enabled in this process.
|
||||
enabled: bool,
|
||||
|
||||
/// Path to the signing keypair
|
||||
keypair_path: PathBuf,
|
||||
/// Path to the coconut verification key.
|
||||
verification_key_path: PathBuf,
|
||||
|
||||
/// Path to the coconut secret key.
|
||||
secret_key_path: PathBuf,
|
||||
|
||||
/// Path to the dkg dealer decryption key.
|
||||
decryption_key_path: PathBuf,
|
||||
@@ -281,16 +296,21 @@ pub struct CoconutSigner {
|
||||
|
||||
/// Duration of the interval for polling the dkg contract.
|
||||
dkg_contract_polling_rate: Duration,
|
||||
|
||||
/// Specifies list of all validators on the network issuing coconut credentials.
|
||||
/// A special care must be taken to ensure they are in correct order.
|
||||
/// The list must also contain THIS validator that is running the test
|
||||
all_validator_apis: Vec<Url>,
|
||||
}
|
||||
|
||||
impl CoconutSigner {
|
||||
pub const DKG_DECRYPTION_KEY_FILE: &'static str = "dkg_decryption_key.pem";
|
||||
pub const DKG_PUBLIC_KEY_WITH_PROOF_FILE: &'static str = "dkg_public_key_with_proof.pem";
|
||||
pub const COCONUT_VERIFICATION_KEY_FILE: &'static str = "coconut_verification_key.pem";
|
||||
pub const COCONUT_SECRET_KEY_FILE: &'static str = "coconut_secret_key.pem";
|
||||
|
||||
fn default_coconut_verification_key_path() -> PathBuf {
|
||||
Config::default_data_directory(None).join(Self::COCONUT_VERIFICATION_KEY_FILE)
|
||||
}
|
||||
|
||||
fn default_coconut_secret_key_path() -> PathBuf {
|
||||
Config::default_data_directory(None).join(Self::COCONUT_SECRET_KEY_FILE)
|
||||
}
|
||||
|
||||
fn default_dkg_decryption_key_path() -> PathBuf {
|
||||
Config::default_data_directory(None).join(Self::DKG_DECRYPTION_KEY_FILE)
|
||||
@@ -305,11 +325,11 @@ impl Default for CoconutSigner {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: Default::default(),
|
||||
keypair_path: Default::default(),
|
||||
verification_key_path: CoconutSigner::default_coconut_verification_key_path(),
|
||||
secret_key_path: CoconutSigner::default_coconut_secret_key_path(),
|
||||
decryption_key_path: CoconutSigner::default_dkg_decryption_key_path(),
|
||||
public_key_with_proof_path: CoconutSigner::default_dkg_public_key_with_proof_path(),
|
||||
dkg_contract_polling_rate: DEFAULT_DKG_CONTRACT_POLLING_RATE,
|
||||
all_validator_apis: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,6 +345,10 @@ impl Config {
|
||||
Config::default_data_directory(Some(id)).join(NodeStatusAPI::DB_FILE);
|
||||
self.network_monitor.credentials_database_path =
|
||||
Config::default_data_directory(Some(id)).join(NetworkMonitor::DB_FILE);
|
||||
self.coconut_signer.verification_key_path = Config::default_data_directory(Some(id))
|
||||
.join(CoconutSigner::COCONUT_VERIFICATION_KEY_FILE);
|
||||
self.coconut_signer.secret_key_path =
|
||||
Config::default_data_directory(Some(id)).join(CoconutSigner::COCONUT_SECRET_KEY_FILE);
|
||||
self.coconut_signer.decryption_key_path =
|
||||
Config::default_data_directory(Some(id)).join(CoconutSigner::DKG_DECRYPTION_KEY_FILE);
|
||||
self.coconut_signer.public_key_with_proof_path = Config::default_data_directory(Some(id))
|
||||
@@ -358,6 +382,12 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_announce_address(mut self, announce_address: Url) -> Self {
|
||||
self.base.announce_address = announce_address;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_custom_mixnet_contract<S: Into<String>>(mut self, mixnet_contract: S) -> Self {
|
||||
self.base.mixnet_contract_address = mixnet_contract.into();
|
||||
self
|
||||
@@ -368,18 +398,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_keypair_path(mut self, keypair_path: PathBuf) -> Self {
|
||||
self.coconut_signer.keypair_path = keypair_path;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_custom_validator_apis(mut self, validator_api_urls: Vec<Url>) -> Self {
|
||||
self.coconut_signer.all_validator_apis = validator_api_urls;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_minimum_interval_monitor_threshold(mut self, threshold: u8) -> Self {
|
||||
self.rewarding.minimum_interval_monitor_threshold = threshold;
|
||||
self
|
||||
@@ -426,6 +444,11 @@ impl Config {
|
||||
self.base.local_validator.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn get_announce_address(&self) -> Url {
|
||||
self.base.announce_address.clone()
|
||||
}
|
||||
|
||||
pub fn get_mixnet_contract_address(&self) -> String {
|
||||
self.base.mixnet_contract_address.clone()
|
||||
}
|
||||
@@ -487,8 +510,13 @@ impl Config {
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn _keypair_path(&self) -> PathBuf {
|
||||
self.coconut_signer.keypair_path.clone()
|
||||
pub fn verification_key_path(&self) -> PathBuf {
|
||||
self.coconut_signer.verification_key_path.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn secret_key_path(&self) -> PathBuf {
|
||||
self.coconut_signer.secret_key_path.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -506,12 +534,6 @@ impl Config {
|
||||
self.coconut_signer.dkg_contract_polling_rate
|
||||
}
|
||||
|
||||
// fix dead code warnings as this method is only ever used with coconut feature
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn get_all_validator_api_endpoints(&self) -> Vec<Url> {
|
||||
self.coconut_signer.all_validator_apis.clone()
|
||||
}
|
||||
|
||||
// TODO: Remove if still unused
|
||||
#[allow(dead_code)]
|
||||
pub fn get_minimum_interval_monitor_threshold(&self) -> u8 {
|
||||
|
||||
@@ -16,6 +16,11 @@ id = '{{ base.id }}'
|
||||
# Validator server to which the API will be getting information about the network.
|
||||
local_validator = '{{ base.local_validator }}'
|
||||
|
||||
# Address announced to the directory server for the clients to connect to.
|
||||
# It is useful, say, in NAT scenarios or wanting to more easily update actual IP address
|
||||
# later on by using name resolvable with a DNS query, such as `nymtech.net`.
|
||||
announce_address = '{{ base.announce_address }}'
|
||||
|
||||
# Address of the validator contract managing the network.
|
||||
mixnet_contract_address = '{{ base.mixnet_contract_address }}'
|
||||
|
||||
@@ -93,8 +98,11 @@ minimum_interval_monitor_threshold = {{ rewarding.minimum_interval_monitor_thres
|
||||
# Specifies whether rewarding service is enabled in this process.
|
||||
enabled = {{ coconut_signer.enabled }}
|
||||
|
||||
# Path to the signing keypair
|
||||
keypair_path = '{{ coconut_signer.keypair_path }}'
|
||||
# Path to the coconut verification key
|
||||
verification_key_path = '{{ coconut_signer.verification_key_path }}'
|
||||
|
||||
# Path to the coconut verification key
|
||||
secret_key_path = '{{ coconut_signer.secret_key_path }}'
|
||||
|
||||
# Path to the dkg dealer decryption key
|
||||
decryption_key_path = '{{ coconut_signer.decryption_key_path }}'
|
||||
@@ -102,14 +110,5 @@ decryption_key_path = '{{ coconut_signer.decryption_key_path }}'
|
||||
# Path to the dkg dealer public key with proof
|
||||
public_key_with_proof_path = '{{ coconut_signer.public_key_with_proof_path }}'
|
||||
|
||||
# Specifies list of all validators on the network issuing coconut credentials.
|
||||
# A special care must be taken to ensure they are in correct order.
|
||||
# The list must also contain THIS validator that is running the test
|
||||
all_validator_apis = [
|
||||
{{#each coconut_signer.all_validator_apis }}
|
||||
'{{this}}',
|
||||
{{/each}}
|
||||
]
|
||||
|
||||
"#
|
||||
}
|
||||
|
||||
+24
-31
@@ -12,8 +12,6 @@ use crate::nymd_client::Client;
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
use ::config::defaults::mainnet::read_var_if_not_default;
|
||||
use ::config::defaults::setup_env;
|
||||
#[cfg(feature = "coconut")]
|
||||
use ::config::defaults::var_names::API_VALIDATOR;
|
||||
use ::config::defaults::var_names::{CONFIGURED, MIXNET_CONTRACT_ADDRESS, MIX_DENOM};
|
||||
use ::config::NymConfig;
|
||||
use anyhow::Result;
|
||||
@@ -35,6 +33,8 @@ use std::time::Duration;
|
||||
use std::{fs, process};
|
||||
use task::ShutdownNotifier;
|
||||
use tokio::sync::Notify;
|
||||
#[cfg(feature = "coconut")]
|
||||
use url::Url;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
|
||||
use crate::epoch_operations::RewardedSetUpdater;
|
||||
@@ -71,9 +71,7 @@ const NYMD_VALIDATOR_ARG: &str = "nymd-validator";
|
||||
const ENABLED_CREDENTIALS_MODE_ARG_NAME: &str = "enabled-credentials-mode";
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
const API_VALIDATORS_ARG: &str = "api-validators";
|
||||
#[cfg(feature = "coconut")]
|
||||
const KEYPAIR_ARG: &str = "keypair";
|
||||
const ANNOUNCE_ADDRESS: &str = "announce-address";
|
||||
#[cfg(feature = "coconut")]
|
||||
const COCONUT_ENABLED: &str = "enable-coconut";
|
||||
|
||||
@@ -193,21 +191,15 @@ fn parse_args() -> ArgMatches {
|
||||
#[cfg(feature = "coconut")]
|
||||
let base_app = base_app
|
||||
.arg(
|
||||
Arg::with_name(KEYPAIR_ARG)
|
||||
.help("Path to the secret key file")
|
||||
.takes_value(true)
|
||||
.long(KEYPAIR_ARG),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(API_VALIDATORS_ARG)
|
||||
.help("specifies list of all validators on the network issuing coconut credentials. Ensure they are properly ordered")
|
||||
.long(API_VALIDATORS_ARG)
|
||||
.takes_value(true)
|
||||
Arg::with_name(ANNOUNCE_ADDRESS)
|
||||
.help("Announced address where coconut clients will connect.")
|
||||
.long(ANNOUNCE_ADDRESS)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(COCONUT_ENABLED)
|
||||
.help("Flag to indicate whether coconut signer authority is enabled on this API")
|
||||
.requires_all(&[KEYPAIR_ARG, MNEMONIC_ARG, API_VALIDATORS_ARG])
|
||||
.requires_all(&[MNEMONIC_ARG, ANNOUNCE_ADDRESS])
|
||||
.long(COCONUT_ENABLED),
|
||||
);
|
||||
base_app.get_matches()
|
||||
@@ -276,12 +268,10 @@ fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
if let Some(raw_validators) = matches.value_of(API_VALIDATORS_ARG) {
|
||||
config = config.with_custom_validator_apis(::config::parse_validators(raw_validators));
|
||||
} else if std::env::var(CONFIGURED).is_ok() {
|
||||
if let Some(raw_validators) = read_var_if_not_default(API_VALIDATOR) {
|
||||
config = config.with_custom_validator_apis(::config::parse_validators(&raw_validators))
|
||||
}
|
||||
if let Some(announce_address) = matches.value_of(ANNOUNCE_ADDRESS) {
|
||||
config = config.with_announce_address(
|
||||
Url::parse(announce_address).expect("Could not parse announce address"),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(raw_validator) = matches.value_of(NYMD_VALIDATOR_ARG) {
|
||||
@@ -338,11 +328,6 @@ fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
if let Some(keypair_path) = matches.value_of(KEYPAIR_ARG) {
|
||||
config = config.with_keypair_path(keypair_path.into())
|
||||
}
|
||||
|
||||
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||
config = config.with_disabled_credentials_mode(false)
|
||||
}
|
||||
@@ -385,6 +370,7 @@ fn setup_liftoff_notify(notify: Arc<Notify>) -> AdHoc {
|
||||
|
||||
fn setup_network_monitor<'a>(
|
||||
config: &'a Config,
|
||||
_nymd_client: Client<SigningNymdClient>,
|
||||
system_version: &str,
|
||||
rocket: &Rocket<Ignite>,
|
||||
) -> Option<NetworkMonitorBuilder<'a>> {
|
||||
@@ -398,6 +384,7 @@ fn setup_network_monitor<'a>(
|
||||
|
||||
Some(NetworkMonitorBuilder::new(
|
||||
config,
|
||||
_nymd_client,
|
||||
system_version,
|
||||
node_status_storage,
|
||||
validator_cache,
|
||||
@@ -453,10 +440,10 @@ async fn setup_rocket(
|
||||
#[cfg(feature = "coconut")]
|
||||
let rocket = if config.get_coconut_signer_enabled() {
|
||||
rocket.attach(InternalSignRequest::stage(
|
||||
_nymd_client,
|
||||
_nymd_client.clone(),
|
||||
_mix_denom,
|
||||
coconut_keypair,
|
||||
QueryCommunicationChannel::new(config.get_all_validator_api_endpoints()),
|
||||
QueryCommunicationChannel::new(_nymd_client),
|
||||
storage.clone().unwrap(),
|
||||
))
|
||||
} else {
|
||||
@@ -554,7 +541,12 @@ async fn run_validator_api(matches: ArgMatches) -> Result<()> {
|
||||
coconut_keypair.clone(),
|
||||
)
|
||||
.await?;
|
||||
let monitor_builder = setup_network_monitor(&config, system_version, &rocket);
|
||||
let monitor_builder = setup_network_monitor(
|
||||
&config,
|
||||
signing_nymd_client.clone(),
|
||||
system_version,
|
||||
&rocket,
|
||||
);
|
||||
|
||||
let validator_cache = rocket.state::<ValidatorCache>().unwrap().clone();
|
||||
let node_status_cache = rocket.state::<NodeStatusCache>().unwrap().clone();
|
||||
@@ -562,7 +554,8 @@ async fn run_validator_api(matches: ArgMatches) -> Result<()> {
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
let dkg_controller =
|
||||
DkgController::new(&config, signing_nymd_client.clone(), coconut_keypair, OsRng)?;
|
||||
DkgController::new(&config, signing_nymd_client.clone(), coconut_keypair, OsRng)
|
||||
.await?;
|
||||
let shutdown_listener = shutdown.subscribe();
|
||||
tokio::spawn(async move { dkg_controller.run(shutdown_listener).await });
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use futures::channel::mpsc;
|
||||
use gateway_client::bandwidth::BandwidthController;
|
||||
use std::sync::Arc;
|
||||
use task::ShutdownNotifier;
|
||||
use validator_client::nymd::SigningNymdClient;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::contract_cache::ValidatorCache;
|
||||
@@ -20,6 +21,7 @@ use crate::network_monitor::monitor::receiver::{
|
||||
use crate::network_monitor::monitor::sender::PacketSender;
|
||||
use crate::network_monitor::monitor::summary_producer::SummaryProducer;
|
||||
use crate::network_monitor::monitor::Monitor;
|
||||
use crate::nymd_client::Client;
|
||||
use crate::storage::ValidatorApiStorage;
|
||||
|
||||
pub(crate) mod chunker;
|
||||
@@ -32,6 +34,7 @@ pub(crate) const ROUTE_TESTING_TEST_NONCE: u64 = 0;
|
||||
|
||||
pub(crate) struct NetworkMonitorBuilder<'a> {
|
||||
config: &'a Config,
|
||||
_nymd_client: Client<SigningNymdClient>,
|
||||
system_version: String,
|
||||
node_status_storage: ValidatorApiStorage,
|
||||
validator_cache: ValidatorCache,
|
||||
@@ -40,12 +43,14 @@ pub(crate) struct NetworkMonitorBuilder<'a> {
|
||||
impl<'a> NetworkMonitorBuilder<'a> {
|
||||
pub(crate) fn new(
|
||||
config: &'a Config,
|
||||
_nymd_client: Client<SigningNymdClient>,
|
||||
system_version: &str,
|
||||
node_status_storage: ValidatorApiStorage,
|
||||
validator_cache: ValidatorCache,
|
||||
) -> Self {
|
||||
NetworkMonitorBuilder {
|
||||
config,
|
||||
_nymd_client,
|
||||
system_version: system_version.to_string(),
|
||||
node_status_storage,
|
||||
validator_cache,
|
||||
@@ -74,11 +79,18 @@ impl<'a> NetworkMonitorBuilder<'a> {
|
||||
);
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_credentials_database_path())
|
||||
.await,
|
||||
self.config.get_all_validator_api_endpoints(),
|
||||
);
|
||||
let bandwidth_controller = {
|
||||
let client = self._nymd_client.0.read().await;
|
||||
let coconut_api_clients =
|
||||
validator_client::CoconutApiClient::all_coconut_api_clients(&client)
|
||||
.await
|
||||
.expect("Could not query api clients");
|
||||
BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_credentials_database_path())
|
||||
.await,
|
||||
coconut_api_clients,
|
||||
)
|
||||
};
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_credentials_database_path())
|
||||
|
||||
@@ -90,15 +90,6 @@ struct FreshGatewayClientData {
|
||||
gateways_status_updater: GatewayClientUpdateSender,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
gateway_response_timeout: Duration,
|
||||
|
||||
// I guess in the future this struct will require aggregated verification key and....
|
||||
// ... something for obtaining actual credential
|
||||
|
||||
// TODO:
|
||||
// SECURITY:
|
||||
// for coconut bandwidth credentials we currently have no double spending protection, just to
|
||||
// get things running we're re-using the same credential for all gateways all the time.
|
||||
// THIS IS VERY BAD!!
|
||||
bandwidth_controller: BandwidthController<PersistentStorage>,
|
||||
disabled_credentials_mode: bool,
|
||||
}
|
||||
@@ -140,8 +131,6 @@ pub(crate) struct PacketSender {
|
||||
// behaviour is unlikely.
|
||||
active_gateway_clients: ActiveGatewayClients,
|
||||
|
||||
// I guess that will be required later on if credentials are got per gateway
|
||||
// aggregated_verification_key: Arc<VerificationKey>,
|
||||
fresh_gateway_client_data: Arc<FreshGatewayClientData>,
|
||||
gateway_connection_timeout: Duration,
|
||||
max_concurrent_clients: usize,
|
||||
|
||||
@@ -30,9 +30,11 @@ use coconut_dkg_common::dealer::{ContractDealing, DealerDetails, DealerDetailsRe
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_dkg_common::types::{EncodedBTEPublicKeyWithProof, EpochState};
|
||||
#[cfg(feature = "coconut")]
|
||||
use coconut_dkg_common::verification_key::{ContractVKShare, VerificationKeyShare};
|
||||
#[cfg(feature = "coconut")]
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
#[cfg(feature = "coconut")]
|
||||
use multisig_contract_common::msg::ProposalResponse;
|
||||
use cw3::ProposalResponse;
|
||||
#[cfg(feature = "coconut")]
|
||||
use validator_client::nymd::{
|
||||
cosmwasm_client::types::ExecuteResult,
|
||||
@@ -291,6 +293,10 @@ where
|
||||
Ok(self.0.read().await.nymd.get_proposal(proposal_id).await?)
|
||||
}
|
||||
|
||||
async fn list_proposals(&self) -> crate::coconut::error::Result<Vec<ProposalResponse>> {
|
||||
Ok(self.0.read().await.get_all_nymd_proposals().await?)
|
||||
}
|
||||
|
||||
async fn get_spent_credential(
|
||||
&self,
|
||||
blinded_serial_number: String,
|
||||
@@ -332,6 +338,17 @@ where
|
||||
Ok(self.0.read().await.get_all_nymd_epoch_dealings(idx).await?)
|
||||
}
|
||||
|
||||
async fn get_verification_key_shares(
|
||||
&self,
|
||||
) -> crate::coconut::error::Result<Vec<ContractVKShare>> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.await
|
||||
.get_all_nymd_verification_key_shares()
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn vote_proposal(
|
||||
&self,
|
||||
proposal_id: u64,
|
||||
@@ -347,16 +364,27 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn execute_proposal(&self, proposal_id: u64) -> crate::coconut::error::Result<()> {
|
||||
self.0
|
||||
.read()
|
||||
.await
|
||||
.nymd
|
||||
.execute_proposal(proposal_id, None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
announce_address: String,
|
||||
) -> Result<ExecuteResult, CoconutError> {
|
||||
Ok(self
|
||||
.0
|
||||
.write()
|
||||
.await
|
||||
.nymd
|
||||
.register_dealer(bte_key, None)
|
||||
.register_dealer(bte_key, announce_address, None)
|
||||
.await?)
|
||||
}
|
||||
|
||||
@@ -372,4 +400,17 @@ where
|
||||
.submit_dealing_bytes(dealing_bytes, None)
|
||||
.await?)
|
||||
}
|
||||
|
||||
async fn submit_verification_key_share(
|
||||
&self,
|
||||
share: VerificationKeyShare,
|
||||
) -> crate::coconut::error::Result<ExecuteResult> {
|
||||
Ok(self
|
||||
.0
|
||||
.write()
|
||||
.await
|
||||
.nymd
|
||||
.submit_verification_key_share(share, None)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user