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

feat(node grpc): add grpc authentication to the node #5928

Merged
merged 5 commits into from
Nov 13, 2023
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
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions applications/minotari_merge_mining_proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ tari_comms = { path = "../../comms/core" }
tari_core = { path = "../../base_layer/core", default-features = false, features = ["transactions"] }
minotari_app_utilities = { path = "../minotari_app_utilities" }
tari_utilities = { version = "0.6" }
minotari_node_grpc_client = {path="../../clients/rust/base_node_grpc_client" }
minotari_wallet_grpc_client = {path="../../clients/rust/wallet_grpc_client" }
minotari_node_grpc_client = { path = "../../clients/rust/base_node_grpc_client" }
minotari_wallet_grpc_client = { path = "../../clients/rust/wallet_grpc_client" }
minotari_app_grpc = { path = "../minotari_app_grpc" }

anyhow = "1.0.53"
crossterm = { version = "0.25.0" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@
use std::{cmp, sync::Arc};

use log::*;
use minotari_node_grpc_client::{grpc, BaseNodeGrpcClient};
use minotari_wallet_grpc_client::WalletGrpcClient;
use minotari_app_grpc::{
authentication::ClientAuthenticationInterceptor,
tari_rpc::{base_node_client::BaseNodeClient, wallet_client::WalletClient},
};
use minotari_node_grpc_client::grpc;
use tari_core::proof_of_work::{monero_rx, monero_rx::FixedByteArray, Difficulty};
use tonic::{codegen::InterceptedService, transport::Channel};

use crate::{
block_template_data::{BlockTemplateData, BlockTemplateDataBuilder},
Expand All @@ -41,14 +45,14 @@ const LOG_TARGET: &str = "minotari_mm_proxy::proxy::block_template_protocol";
/// Structure holding grpc connections.
pub struct BlockTemplateProtocol<'a> {
config: Arc<MergeMiningProxyConfig>,
base_node_client: &'a mut BaseNodeGrpcClient<tonic::transport::Channel>,
wallet_client: &'a mut WalletGrpcClient<tonic::transport::Channel>,
base_node_client: &'a mut BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
wallet_client: &'a mut WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
}

impl<'a> BlockTemplateProtocol<'a> {
pub fn new(
base_node_client: &'a mut BaseNodeGrpcClient<tonic::transport::Channel>,
wallet_client: &'a mut WalletGrpcClient<tonic::transport::Channel>,
base_node_client: &'a mut BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
wallet_client: &'a mut WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
config: Arc<MergeMiningProxyConfig>,
) -> Self {
Self {
Expand Down
3 changes: 3 additions & 0 deletions applications/minotari_merge_mining_proxy/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ pub struct MergeMiningProxyConfig {
pub monerod_use_auth: bool,
/// The Minotari base node's GRPC address
pub base_node_grpc_address: Option<Multiaddr>,
/// GRPC authentication for base node
pub base_node_grpc_authentication: GrpcAuthentication,
/// The Minotari wallet's GRPC address
pub console_wallet_grpc_address: Option<Multiaddr>,
/// GRPC authentication for console wallet
Expand Down Expand Up @@ -80,6 +82,7 @@ impl Default for MergeMiningProxyConfig {
monerod_password: String::new(),
monerod_use_auth: false,
base_node_grpc_address: None,
base_node_grpc_authentication: GrpcAuthentication::default(),
console_wallet_grpc_address: None,
console_wallet_grpc_authentication: GrpcAuthentication::default(),
listener_address: "/ip4/127.0.0.1/tcp/18081".parse().unwrap(),
Expand Down
13 changes: 7 additions & 6 deletions applications/minotari_merge_mining_proxy/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ use bytes::Bytes;
use hyper::{header::HeaderValue, service::Service, Body, Method, Request, Response, StatusCode, Uri};
use json::json;
use jsonrpc::error::StandardError;
use minotari_node_grpc_client::{grpc, BaseNodeGrpcClient};
use minotari_wallet_grpc_client::WalletGrpcClient;
use minotari_node_grpc_client::{grpc, grpc::base_node_client::BaseNodeClient};
use minotari_wallet_grpc_client::{grpc::wallet_client::WalletClient, ClientAuthenticationInterceptor};
use reqwest::{ResponseBuilderExt, Url};
use serde_json as json;
use tari_core::proof_of_work::{
Expand All @@ -50,6 +50,7 @@ use tari_core::proof_of_work::{
randomx_factory::RandomXFactory,
};
use tari_utilities::hex::Hex;
use tonic::{codegen::InterceptedService, transport::Channel};
use tracing::{debug, error, info, instrument, trace, warn};

use crate::{
Expand All @@ -75,8 +76,8 @@ impl MergeMiningProxyService {
pub fn new(
config: MergeMiningProxyConfig,
http_client: reqwest::Client,
base_node_client: BaseNodeGrpcClient<tonic::transport::Channel>,
wallet_client: WalletGrpcClient<tonic::transport::Channel>,
base_node_client: BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
wallet_client: WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
block_templates: BlockTemplateRepository,
randomx_factory: RandomXFactory,
) -> Self {
Expand Down Expand Up @@ -157,8 +158,8 @@ struct InnerService {
config: Arc<MergeMiningProxyConfig>,
block_templates: BlockTemplateRepository,
http_client: reqwest::Client,
base_node_client: BaseNodeGrpcClient<tonic::transport::Channel>,
wallet_client: WalletGrpcClient<tonic::transport::Channel>,
base_node_client: BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
wallet_client: WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>,
initial_sync_achieved: Arc<AtomicBool>,
current_monerod_server: Arc<RwLock<Option<String>>>,
last_assigned_monerod_server: Arc<RwLock<Option<String>>>,
Expand Down
76 changes: 54 additions & 22 deletions applications/minotari_merge_mining_proxy/src/run_merge_miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use std::convert::Infallible;
use std::{convert::Infallible, str::FromStr};

use futures::future;
use hyper::{service::make_service_fn, Server};
use log::*;
use minotari_node_grpc_client::BaseNodeGrpcClient;
use minotari_wallet_grpc_client::WalletGrpcClient;
use minotari_node_grpc_client::grpc::base_node_client::BaseNodeClient;
use minotari_wallet_grpc_client::{grpc::wallet_client::WalletClient, ClientAuthenticationInterceptor};
use tari_common::{
configuration::bootstrap::{grpc_default_port, ApplicationType},
load_configuration,
Expand All @@ -35,6 +35,10 @@ use tari_common::{
use tari_comms::utils::multiaddr::multiaddr_to_socketaddr;
use tari_core::proof_of_work::randomx_factory::RandomXFactory;
use tokio::time::Duration;
use tonic::{
codegen::InterceptedService,
transport::{Channel, Endpoint},
};

use crate::{
block_template_data::BlockTemplateRepository,
Expand All @@ -59,25 +63,9 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> {
.build()
.map_err(MmProxyError::ReqwestError)?;

let base_node = multiaddr_to_socketaddr(
config
.base_node_grpc_address
.as_ref()
.expect("No base node address provided"),
)?;
info!(target: LOG_TARGET, "Connecting to base node at {}", base_node);
println!("Connecting to base node at {}", base_node);
let base_node_client = BaseNodeGrpcClient::connect(format!("http://{}", base_node)).await?;
let wallet_addr = multiaddr_to_socketaddr(
config
.console_wallet_grpc_address
.as_ref()
.expect("No waller address provided"),
)?;
info!(target: LOG_TARGET, "Connecting to wallet at {}", wallet_addr);
let wallet_addr = format!("http://{}", wallet_addr);
let wallet_client =
WalletGrpcClient::connect_with_auth(&wallet_addr, &config.console_wallet_grpc_authentication).await?;
let base_node_client = connect_base_node(&config).await?;
let wallet_client = connect_wallet(&config).await?;

let listen_addr = multiaddr_to_socketaddr(&config.listener_address)?;
let randomx_factory = RandomXFactory::new(config.max_randomx_vms);
let randomx_service = MergeMiningProxyService::new(
Expand Down Expand Up @@ -110,6 +98,50 @@ pub async fn start_merge_miner(cli: Cli) -> Result<(), anyhow::Error> {
}
}

async fn connect_wallet(
config: &MergeMiningProxyConfig,
) -> Result<WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>, MmProxyError> {
let wallet_addr = format!(
"http://{}",
multiaddr_to_socketaddr(
&config
.console_wallet_grpc_address
.clone()
.expect("Wallet grpc address not found")
)?
);
info!(target: LOG_TARGET, "👛 Connecting to wallet at {}", wallet_addr);
let channel = Endpoint::from_str(&wallet_addr)?.connect().await?;
let wallet_conn = WalletClient::with_interceptor(
channel,
ClientAuthenticationInterceptor::create(&config.console_wallet_grpc_authentication)?,
);

Ok(wallet_conn)
}

async fn connect_base_node(
config: &MergeMiningProxyConfig,
) -> Result<BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>, MmProxyError> {
let base_node_addr = format!(
"http://{}",
multiaddr_to_socketaddr(
&config
.base_node_grpc_address
.clone()
.expect("Base node grpc address not found")
)?
);
info!(target: LOG_TARGET, "👛 Connecting to base node at {}", base_node_addr);
let channel = Endpoint::from_str(&base_node_addr)?.connect().await?;
let node_conn = BaseNodeClient::with_interceptor(
channel,
ClientAuthenticationInterceptor::create(&config.base_node_grpc_authentication)?,
);

Ok(node_conn)
}

fn setup_grpc_config(config: &mut MergeMiningProxyConfig) {
if config.base_node_grpc_address.is_none() {
config.base_node_grpc_address = Some(
Expand Down
3 changes: 3 additions & 0 deletions applications/minotari_miner/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ use tari_comms::multiaddr::Multiaddr;
pub struct MinerConfig {
/// GRPC address of base node
pub base_node_grpc_address: Option<Multiaddr>,
/// GRPC authentication for base node
pub base_node_grpc_authentication: GrpcAuthentication,
/// GRPC address of console wallet
pub wallet_grpc_address: Option<Multiaddr>,
/// GRPC authentication for console wallet
Expand Down Expand Up @@ -97,6 +99,7 @@ impl Default for MinerConfig {
fn default() -> Self {
Self {
base_node_grpc_address: None,
base_node_grpc_authentication: GrpcAuthentication::default(),
wallet_grpc_address: None,
wallet_grpc_authentication: GrpcAuthentication::default(),
num_mining_threads: num_cpus::get(),
Expand Down
49 changes: 35 additions & 14 deletions applications/minotari_miner/src/run_miner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub const LOG_TARGET: &str = "minotari::miner::main";
pub const LOG_TARGET_FILE: &str = "minotari::logging::miner::main";

type WalletGrpcClient = WalletClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>;
type BaseNodeGrpcClient = BaseNodeClient<InterceptedService<Channel, ClientAuthenticationInterceptor>>;

#[allow(clippy::too_many_lines)]
pub async fn start_miner(cli: Cli) -> Result<(), ExitError> {
Expand Down Expand Up @@ -167,18 +168,18 @@ pub async fn start_miner(cli: Cli) -> Result<(), ExitError> {
}
}

async fn connect(config: &MinerConfig) -> Result<(BaseNodeClient<Channel>, WalletGrpcClient), MinerError> {
let base_node_addr = format!(
"http://{}",
multiaddr_to_socketaddr(
&config
.base_node_grpc_address
.clone()
.expect("no base node grpc address found"),
)?
);
info!(target: LOG_TARGET, "🔗 Connecting to base node at {}", base_node_addr);
let node_conn = BaseNodeClient::connect(base_node_addr).await?;
async fn connect(config: &MinerConfig) -> Result<(BaseNodeGrpcClient, WalletGrpcClient), MinerError> {
let node_conn = match connect_base_node(config).await {
Ok(client) => client,
Err(e) => {
error!(target: LOG_TARGET, "Could not connect to base node");
error!(
target: LOG_TARGET,
"Is its grpc running? try running it with `--enable-grpc` or enable it in config"
);
return Err(e);
},
};

let wallet_conn = match connect_wallet(config).await {
Ok(client) => client,
Expand Down Expand Up @@ -215,8 +216,28 @@ async fn connect_wallet(config: &MinerConfig) -> Result<WalletGrpcClient, MinerE
Ok(wallet_conn)
}

async fn connect_base_node(config: &MinerConfig) -> Result<BaseNodeGrpcClient, MinerError> {
let base_node_addr = format!(
"http://{}",
multiaddr_to_socketaddr(
&config
.base_node_grpc_address
.clone()
.expect("Base node grpc address not found")
)?
);
info!(target: LOG_TARGET, "👛 Connecting to base node at {}", base_node_addr);
let channel = Endpoint::from_str(&base_node_addr)?.connect().await?;
let node_conn = BaseNodeClient::with_interceptor(
channel,
ClientAuthenticationInterceptor::create(&config.base_node_grpc_authentication)?,
);

Ok(node_conn)
}

async fn mining_cycle(
node_conn: &mut BaseNodeClient<Channel>,
node_conn: &mut BaseNodeGrpcClient,
wallet_conn: &mut WalletGrpcClient,
config: &MinerConfig,
cli: &Cli,
Expand Down Expand Up @@ -336,7 +357,7 @@ pub async fn display_report(report: &MiningReport, num_mining_threads: usize) {

/// If config
async fn validate_tip(
node_conn: &mut BaseNodeClient<Channel>,
node_conn: &mut BaseNodeGrpcClient,
height: u64,
mine_until_height: Option<u64>,
) -> Result<(), MinerError> {
Expand Down
2 changes: 2 additions & 0 deletions applications/minotari_node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub struct Cli {
pub watch: Option<String>,
#[clap(long, alias = "profile")]
pub profile_with_tokio_console: bool,
#[clap(long, env = "MINOTARI_NODE_ENABLE_GRPC", alias = "enable-grpc")]
pub grpc_enabled: bool,
}

impl ConfigOverrideProvider for Cli {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2023, The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use anyhow::{anyhow, Error};
use async_trait::async_trait;
use clap::Parser;
use minotari_app_grpc::authentication::salted_password::create_salted_hashed_password;

use super::{CommandContext, HandleCommand};

/// Hashes the GRPC authentication password from the config and returns an argon2 hash
#[derive(Debug, Parser)]
pub struct Args {}

#[async_trait]
impl HandleCommand<Args> for CommandContext {
async fn handle_command(&mut self, _: Args) -> Result<(), Error> {
self.hash_grpc_password().await
}
}

impl CommandContext {
pub async fn hash_grpc_password(&mut self) -> Result<(), Error> {
match self
.config
.base_node
.grpc_authentication
.username_password()
.ok_or_else(|| anyhow!("GRPC basic auth is not configured"))
{
Ok((username, password)) => {
match create_salted_hashed_password(password.reveal()).map_err(|e| anyhow!(e.to_string())) {
Ok(hashed_password) => {
println!("Your hashed password is:");
println!("{}", *hashed_password);
println!();
println!(
"Use HTTP basic auth with username '{}' and the hashed password to make GRPC requests",
username
);
},
Err(e) => eprintln!("HashGrpcPassword error! {}", e),
}
},
Err(e) => eprintln!("HashGrpcPassword error! {}", e),
}

Ok(())
}
}
Loading
Loading