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:
Bogdan-Ștefan Neacşu
2022-11-22 11:16:02 +02:00
committed by GitHub
parent f1deebc0f1
commit 2db1bc8efa
88 changed files with 1623 additions and 603 deletions
+14 -3
View File
@@ -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>;
}
+12 -7
View File
@@ -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)
}
}
+73 -3
View File
@@ -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(())
// }
+37 -13
View File
@@ -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");
}
+1 -2
View File
@@ -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(())
+4 -2
View File
@@ -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(())
}
+76 -8
View File
@@ -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;
}
}
+123 -20
View File
@@ -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(&params, &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(())
}
+6
View File
@@ -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 {
+1 -24
View File
@@ -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>,
+29 -112
View File
@@ -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(&params, 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
+54 -32
View File
@@ -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 {
+10 -11
View File
@@ -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
View File
@@ -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 });
}
+17 -5
View File
@@ -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,
+43 -2
View File
@@ -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?)
}
}