Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add endpoint for exposing current consensus node configuration #1858

Merged
merged 7 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions consensus/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,21 @@ links = "mc-consensus-api"
[dependencies]
mc-api = { path = "../../api" }
mc-attest-api = { path = "../../attest/api" }
mc-ledger-db = { path = "../../ledger/db" }
mc-transaction-core = { path = "../../transaction/core" }

futures = "0.3"
grpcio = "0.10.2"
protobuf = "2.27.1"

[dev-dependencies]
mc-crypto-multisig = { path = "../../crypto/multisig" }
mc-transaction-core-test-utils = { path = "../../transaction/core/test-utils" }
mc-util-serial = { path = "../../util/serial" }

rand_core = "0.6"
rand_hc = "0.3"

[build-dependencies]
mc-util-build-grpc = { path = "../../util/build/grpc" }
mc-util-build-script = { path = "../../util/build/script" }
Expand Down
1 change: 1 addition & 0 deletions consensus/api/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fn main() {
&[
"consensus_client.proto",
"consensus_common.proto",
"consensus_config.proto",
"consensus_peer.proto",
],
);
Expand Down
5 changes: 5 additions & 0 deletions consensus/api/proto/consensus_client.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// Consensus service client-facing data types and service descriptors.

syntax = "proto3";
import "google/protobuf/empty.proto";
import "attest.proto";
import "consensus_common.proto";
import "consensus_config.proto";
import "external.proto";

package consensus_client;
Expand Down Expand Up @@ -75,4 +77,7 @@ service ConsensusClientAPI {

/// Propose a new MintTx.
rpc ProposeMintTx(external.MintTx) returns (ProposeMintTxResponse);

/// Get current node configuration.
rpc GetNodeConfig(google.protobuf.Empty) returns (consensus_config.ConsensusNodeConfig);
eranrund marked this conversation as resolved.
Show resolved Hide resolved
}
72 changes: 72 additions & 0 deletions consensus/api/proto/consensus_config.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2022 The MobileCoin Foundation

// Consensus service configuration data types.

syntax = "proto3";
import "blockchain.proto";
import "external.proto";

package consensus_config;

option go_package = "mobilecoin/api";


// A single active minting configuration.
message ActiveMintConfig {
// The actual mint configuration.
external.MintConfig mint_config = 1;

// How many tokens have been minted using this configuration.
uint64 total_minted = 2;
}

// Active minting configurations for a single token.
message ActiveMintConfigs {
/// Active configs
repeated ActiveMintConfig configs = 1;

// The original MintConfigTx that this configuration resulted from.
external.MintConfigTx mint_config_tx = 2;
}

// Token configuration (per-token configuration).
message TokenConfig {
// The token id this configuration is for.
uint64 token_id = 1;

// The current minimum fee for this token.
uint64 minimum_fee = 2;

// Minting governors (optional, only when minting is configured).
external.Ed25519SignerSet governors = 3;

// Currently active mint configurations for this token (optional, only if a valid MintConfigTx has been previously issued).
ActiveMintConfigs active_mint_configs = 4;
}

// Consensus node configuration.
message ConsensusNodeConfig {
// Minting trust root public key.
external.Ed25519Public minting_trust_root = 1;

// token id -> configuration map.
map<uint64, TokenConfig> token_config_map = 2;

// Governors signature.
external.Ed25519Signature governors_signature = 3;

// Peer responder id.
string peer_responder_id = 4;

// Client responser id.
string client_responder_id = 5;

// Block signing key.
external.Ed25519Public block_signing_key = 6;

// Currently configured block version.
uint32 block_version = 7;

// SCP message signing key.
external.Ed25519Public scp_message_signing_key = 8;
}
170 changes: 169 additions & 1 deletion consensus/api/src/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
use crate::{
consensus_client::{MintValidationResult, MintValidationResultCode},
consensus_common::ProposeTxResult,
consensus_config,
};
use mc_api::ConversionError;
use mc_transaction_core::{
mint::MintValidationError, ring_signature, validation::TransactionValidationError as Error,
BlockVersion, TokenId,
Expand Down Expand Up @@ -214,5 +216,171 @@ impl TryInto<MintValidationError> for MintValidationResult {
}
}

/// Convert mc_ledger_db::ActiveMintConfig -->
/// consensus_config::ActiveMintConfig
impl From<&mc_ledger_db::ActiveMintConfig> for consensus_config::ActiveMintConfig {
fn from(src: &mc_ledger_db::ActiveMintConfig) -> Self {
let mut dst = Self::new();
dst.set_mint_config((&src.mint_config).into());
dst.set_total_minted(src.total_minted);
dst
}
}

/// Convert consensus_config::ActiveMintConfig -->
/// mc_ledger_db::ActiveMintConfig
impl TryFrom<&consensus_config::ActiveMintConfig> for mc_ledger_db::ActiveMintConfig {
type Error = ConversionError;

fn try_from(src: &consensus_config::ActiveMintConfig) -> Result<Self, Self::Error> {
let mint_config = src.get_mint_config().try_into()?;
Ok(Self {
mint_config,
total_minted: src.get_total_minted(),
})
}
}

/// Convert mc_ledger_db::ActiveMintConfigs -->
/// consensus_config::ActiveMintConfigs
impl From<&mc_ledger_db::ActiveMintConfigs> for consensus_config::ActiveMintConfigs {
fn from(src: &mc_ledger_db::ActiveMintConfigs) -> Self {
let mut dst = Self::new();
dst.set_configs(src.configs.iter().map(|config| config.into()).collect());
dst.set_mint_config_tx((&src.mint_config_tx).into());
dst
}
}

/// Convert consensus_config::ActiveMintConfigs -->
/// mc_ledger_db::ActiveMintConfigs
impl TryFrom<&consensus_config::ActiveMintConfigs> for mc_ledger_db::ActiveMintConfigs {
type Error = ConversionError;

fn try_from(src: &consensus_config::ActiveMintConfigs) -> Result<Self, Self::Error> {
let configs = src
.get_configs()
.iter()
.map(TryInto::try_into)
.collect::<Result<Vec<_>, _>>()?;
let mint_config_tx = src.get_mint_config_tx().try_into()?;
Ok(Self {
configs,
mint_config_tx,
})
}
}

#[cfg(test)]
mod conversion_tests {}
mod conversion_tests {
use super::*;
use mc_crypto_multisig::SignerSet;
use mc_transaction_core::mint::MintConfig;
use mc_transaction_core_test_utils::create_mint_config_tx_and_signers;
use mc_util_serial::{decode, encode};
use protobuf::Message;
use rand_core::SeedableRng;
use rand_hc::Hc128Rng;

#[test]
fn test_convert_active_mint_config() {
let mut rng = Hc128Rng::from_seed([1u8; 32]);
let (_mint_config_tx, signers) = create_mint_config_tx_and_signers(2.into(), &mut rng);
let signer_set = SignerSet::new(signers.iter().map(|s| s.public_key()).collect(), 1);

let source = mc_ledger_db::ActiveMintConfig {
mint_config: MintConfig {
token_id: 123,
signer_set,
mint_limit: 10000,
},
total_minted: 102,
};

// decode(encode(source)) should be the identity function.
{
let bytes = encode(&source);
let recovered = decode(&bytes).unwrap();
assert_eq!(source, recovered);
}

// Converting mc_ledger_db::ActiveMintConfig ->
// consensus_config::ActiveMintConfig -> mc_ledger_db::ActiveMintConfig
// should be the identity function.
{
let external = consensus_config::ActiveMintConfig::from(&source);
let recovered = mc_ledger_db::ActiveMintConfig::try_from(&external).unwrap();
assert_eq!(source, recovered);
}

// Encoding with prost, decoding with protobuf should be the identity
// function.
{
let bytes = encode(&source);
let recovered = consensus_config::ActiveMintConfig::parse_from_bytes(&bytes).unwrap();
assert_eq!(recovered, consensus_config::ActiveMintConfig::from(&source));
}

// Encoding with protobuf, decoding with prost should be the identity function.
{
let external = consensus_config::ActiveMintConfig::from(&source);
let bytes = external.write_to_bytes().unwrap();
let recovered: mc_ledger_db::ActiveMintConfig = decode(&bytes).unwrap();
assert_eq!(source, recovered);
}
}

#[test]
fn test_convert_active_mint_configs() {
let mut rng = Hc128Rng::from_seed([1u8; 32]);
let (mint_config_tx, signers) = create_mint_config_tx_and_signers(2.into(), &mut rng);
let signer_set = SignerSet::new(signers.iter().map(|s| s.public_key()).collect(), 1);

let source = mc_ledger_db::ActiveMintConfigs {
configs: vec![mc_ledger_db::ActiveMintConfig {
mint_config: MintConfig {
token_id: 123,
signer_set,
mint_limit: 10000,
},
total_minted: 102,
}],
mint_config_tx,
};

// decode(encode(source)) should be the identity function.
{
let bytes = encode(&source);
let recovered = decode(&bytes).unwrap();
assert_eq!(source, recovered);
}

// Converting mc_ledger_db::ActiveMintConfigs ->
// consensus_config::ActiveMintConfigs -> mc_ledger_db::ActiveMintConfigs
// should be the identity function.
{
let external = consensus_config::ActiveMintConfigs::from(&source);
let recovered = mc_ledger_db::ActiveMintConfigs::try_from(&external).unwrap();
assert_eq!(source, recovered);
}

// Encoding with prost, decoding with protobuf should be the identity
// function.
{
let bytes = encode(&source);
let recovered = consensus_config::ActiveMintConfigs::parse_from_bytes(&bytes).unwrap();
assert_eq!(
recovered,
consensus_config::ActiveMintConfigs::from(&source)
);
}

// Encoding with protobuf, decoding with prost should be the identity function.
{
let external = consensus_config::ActiveMintConfigs::from(&source);
let bytes = external.write_to_bytes().unwrap();
let recovered: mc_ledger_db::ActiveMintConfigs = decode(&bytes).unwrap();
assert_eq!(source, recovered);
}
}
}
57 changes: 57 additions & 0 deletions consensus/service/src/api/client_api_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use mc_consensus_api::{
consensus_client::{ProposeMintConfigTxResponse, ProposeMintTxResponse},
consensus_client_grpc::ConsensusClientApi,
consensus_common::{ProposeTxResponse, ProposeTxResult},
consensus_config::{ConsensusNodeConfig, TokenConfig},
empty::Empty,
};
use mc_consensus_enclave::ConsensusEnclave;
use mc_consensus_service_config::Config;
Expand Down Expand Up @@ -154,6 +156,48 @@ impl ClientApiService {
counters::PROPOSE_MINT_TX.inc();
Ok(response)
}

/// Get the node's configuration.
fn get_node_config_impl(&self) -> Result<ConsensusNodeConfig, ConsensusGrpcError> {
let tokens_config = self.config.tokens();

let token_config_map = tokens_config
.tokens()
.iter()
.map(|token_config| {
let mut grpc_token_config = TokenConfig::new();
grpc_token_config.set_token_id(*token_config.token_id());
grpc_token_config
.set_minimum_fee(token_config.minimum_fee_or_default().unwrap_or(0));
if let Some(governors) = token_config.governors() {
grpc_token_config.set_governors(governors.into());
}

let active_mint_configs = self
.ledger
.get_active_mint_configs(token_config.token_id())?;
if let Some(active_mint_configs) = active_mint_configs.as_ref() {
grpc_token_config.set_active_mint_configs(active_mint_configs.into());
}

Ok((*token_config.token_id(), grpc_token_config))
})
.collect::<Result<_, ConsensusGrpcError>>()?;

let mut response = ConsensusNodeConfig::new();
response.set_minting_trust_root((&self.enclave.get_minting_trust_root()?).into());
response.set_token_config_map(token_config_map);
if let Some(governors_signature) = tokens_config.governors_signature.as_ref() {
response.set_governors_signature(governors_signature.into());
}
response.set_peer_responder_id(self.config.peer_responder_id.to_string());
response.set_client_responder_id(self.config.client_responder_id.to_string());
response.set_block_signing_key((&self.enclave.get_signer()?).into());
response.set_block_version(*self.config.block_version);
response.set_scp_message_signing_key((&self.config.msg_signer_key.public_key()).into());

Ok(response)
}
}

impl ConsensusClientApi for ClientApiService {
Expand Down Expand Up @@ -268,6 +312,19 @@ impl ConsensusClientApi for ClientApiService {
send_result(ctx, sink, result, logger)
});
}

fn get_node_config(
&mut self,
ctx: RpcContext,
_empty: Empty,
sink: UnarySink<ConsensusNodeConfig>,
) {
let result = self.get_node_config_impl().map_err(RpcStatus::from);

mc_common::logger::scoped_global_logger(&rpc_logger(&ctx, &self.logger), |logger| {
send_result(ctx, sink, result, logger)
});
}
}

#[cfg(test)]
Expand Down
Loading