Skip to content
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
65 changes: 64 additions & 1 deletion Cargo.lock

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

16 changes: 9 additions & 7 deletions crates/bioauth-flow/src/flow.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! The flow implementation.

use std::marker::PhantomData;
use std::{marker::PhantomData, ops::Deref};

use primitives_liveness_data::{LivenessData, OpaqueLivenessData};
use robonode_client::{AuthenticateRequest, EnrollRequest};
Expand Down Expand Up @@ -37,16 +37,16 @@ pub trait Signer<S> {
///
/// The goal of this component is to encapsulate interoperation with the handheld device
/// and the robonode.
pub struct Flow<PK, LDP> {
pub struct Flow<PK, LDP, RC> {
/// The provider of the liveness data.
pub liveness_data_provider: LDP,
/// The Robonode API client.
pub robonode_client: robonode_client::Client,
pub robonode_client: RC,
/// The type used to encode the public key.
pub public_key_type: PhantomData<PK>,
pub validator_public_key_type: PhantomData<PK>,
}

impl<PK, LDP> Flow<PK, LDP>
impl<PK, LDP, RC> Flow<PK, LDP, RC>
where
LDP: LivenessDataProvider,
{
Expand All @@ -60,11 +60,12 @@ where
}
}

impl<PK, LDP> Flow<PK, LDP>
impl<PK, LDP, RC> Flow<PK, LDP, RC>
where
PK: AsRef<[u8]>,
LDP: LivenessDataProvider,
<LDP as LivenessDataProvider>::Error: Send + Sync + std::error::Error + 'static,
RC: Deref<Target = robonode_client::Client>,
{
/// The enroll flow.
pub async fn enroll(&mut self, public_key: PK) -> Result<(), anyhow::Error> {
Expand All @@ -81,12 +82,13 @@ where
}
}

impl<PK, LDP> Flow<PK, LDP>
impl<PK, LDP, RC> Flow<PK, LDP, RC>
where
PK: Signer<Vec<u8>>,
<PK as Signer<Vec<u8>>>::Error: Send + Sync + std::error::Error + 'static,
LDP: LivenessDataProvider,
<LDP as LivenessDataProvider>::Error: Send + Sync + std::error::Error + 'static,
RC: Deref<Target = robonode_client::Client>,
{
/// The authentication flow.
///
Expand Down
2 changes: 2 additions & 0 deletions crates/humanode-peer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ async-trait = "0.1"
codec = { package = "parity-scale-codec", version = "2.0.0" }
futures = "0.3"
hex-literal = "0.3"
qr2term = "0.2"
reqwest = "0.11"
sc-basic-authorship = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" }
Expand All @@ -31,3 +32,4 @@ sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "
sp-timestamp = { git = "https://github.com/humanode-network/substrate", branch = "master" }
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
url = "2"
4 changes: 3 additions & 1 deletion crates/humanode-peer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ use sc_tracing::logging::LoggerBuilder;

mod chain_spec;
mod config;
mod qrcode;
mod service;
mod validator_key;

#[tokio::main]
async fn main() {
let logger = LoggerBuilder::new("");
logger.init().unwrap();

let mut task_manager = service::new_full(config::make()).unwrap();
let mut task_manager = service::new_full(config::make()).await.unwrap();

tokio::select! {
res = task_manager.future() => res.unwrap(),
Expand Down
44 changes: 44 additions & 0 deletions crates/humanode-peer/src/qrcode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! QR Code generation.

use tracing::{error, info};
use url::Url;

/// The information necessary for printing the Web App QR Code.
pub struct WebApp {
/// The Web App URL.
url: Url,
}

impl WebApp {
/// Create a new [`WebApp`] and validate that the resulting URL is valid.
pub fn new(base_url: &str, rpc_url: &str) -> Result<Self, String> {
let mut url = Url::parse(base_url).map_err(|err| err.to_string())?;
url.path_segments_mut()
.map_err(|_| "invalid base URL".to_owned())?
.push("humanode")
.push(rpc_url);
Ok(Self { url })
}

/// Print the QR Code for the Web App to the terminal.
pub fn print(&self) {
info!("Please visit {} to proceed", self.url);
qr2term::print_qr(self.url.as_str())
.unwrap_or_else(|error| error!(message = "Failed to generate QR Code", %error));
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_url_construction() {
let webapp = WebApp::new("https://example.com", "http://localhost:9933").unwrap();

assert_eq!(
webapp.url,
Url::parse("https://example.com/humanode/http:%2F%2Flocalhost:9933").unwrap()
);
}
}
62 changes: 59 additions & 3 deletions crates/humanode-peer/src/service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Initializing, bootstrapping and launching the node from a provided configuration.

use std::sync::Arc;
use std::{marker::PhantomData, sync::Arc};

use humanode_runtime::{self, opaque::Block, RuntimeApi};
use sc_client_api::ExecutorProvider;
Expand All @@ -10,6 +10,7 @@ pub use sc_executor::NativeExecutor;
use sc_service::{Configuration, Error as ServiceError, TaskManager};
use sp_consensus::SlotData;
use sp_consensus_aura::sr25519::AuthorityPair as AuraPair;
use tracing::*;

// Native executor for the runtime based on the runtime API that is available
// at the current compile time.
Expand All @@ -21,7 +22,7 @@ native_executor_instance!(

/// Create a "full" node (full is in terms of substrate).
/// We don't support other node types yet either way, so this is the only way to create a node.
pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
pub async fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
let (client, backend, keystore_container, mut task_manager) =
sc_service::new_full_parts::<Block, RuntimeApi, Executor>(&config, None)?;
let client = Arc::new(client);
Expand Down Expand Up @@ -93,7 +94,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
reqwest: reqwest::Client::new(),
});

let (bioauth_flow_rpc_slot, _bioauth_flow_provider_slot) =
let (bioauth_flow_rpc_slot, bioauth_flow_provider_slot) =
bioauth_flow::rpc::new_liveness_data_tx_slot();

let rpc_extensions_builder = {
Expand All @@ -113,6 +114,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
})
};

let rpc_port = config.rpc_http.expect("HTTP RPC must be on").port();
let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams {
network: Arc::clone(&network),
client: Arc::clone(&client),
Expand Down Expand Up @@ -163,5 +165,59 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {

network_starter.start_network();

let mut flow = bioauth_flow::flow::Flow {
liveness_data_provider: bioauth_flow::rpc::Provider::new(bioauth_flow_provider_slot),
robonode_client,
validator_public_key_type: PhantomData::<crate::validator_key::FakeTodo>,
};

let webapp_url = std::env::var("WEBAPP_URL")
.unwrap_or_else(|_| "https://webapp-test-1.dev.humanode.io".into());
// TODO: more advanced host address detection is needed to things work within the same LAN.
let rpc_url =
std::env::var("RPC_URL").unwrap_or_else(|_| format!("http://localhost:{}", rpc_port));
let webapp_qrcode =
crate::qrcode::WebApp::new(&webapp_url, &rpc_url).map_err(ServiceError::Other)?;

let bioauth_flow_future = Box::pin(async move {
info!("bioauth flow starting up");
let should_enroll = std::env::var("ENROLL").unwrap_or_default() == "true";
if should_enroll {
info!("bioauth flow - enrolling in progress");

webapp_qrcode.print();

flow.enroll(crate::validator_key::FakeTodo("TODO"))
.await
.expect("enroll failed");

info!("bioauth flow - enrolling complete");
}

info!("bioauth flow - authentication in progress");

webapp_qrcode.print();

let authenticate_response = loop {
let result = flow
.authenticate(crate::validator_key::FakeTodo("TODO"))
.await;
match result {
Ok(v) => break v,
Err(error) => {
error!(message = "bioauth flow - authentication failure", ?error);
}
};
};

info!("bioauth flow - authentication complete");

info!(message = "We've obtained an auth ticket", auth_ticket = ?authenticate_response.auth_ticket);
});

task_manager
.spawn_handle()
.spawn_blocking("bioauth-flow", bioauth_flow_future);

Ok(task_manager)
}
28 changes: 28 additions & 0 deletions crates/humanode-peer/src/validator_key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//! The validator key integration logic.

use std::convert::Infallible;

use bioauth_flow::flow::Signer;

/// A temporary fake implementation of the validator key, for the purposes of using it with the
/// bioauth enroll and authenticate during the integration while the real validator key is not
/// ready.
pub struct FakeTodo(pub &'static str);

#[async_trait::async_trait]
impl Signer<Vec<u8>> for FakeTodo {
type Error = Infallible;

async fn sign<'a, D>(&self, _data: D) -> Result<Vec<u8>, Self::Error>
where
D: AsRef<[u8]> + Send + 'a,
{
Ok(b"0123456789abcdef0123456789abcdef"[..].into())
}
}

impl AsRef<[u8]> for FakeTodo {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
Loading