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
5 changes: 0 additions & 5 deletions Cargo.lock

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

16 changes: 8 additions & 8 deletions crates/bioauth-consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@ authors = ["Humanode Team <core@humanode.io>"]
publish = false

[dependencies]
humanode-runtime = { version = "0.1", path = "../humanode-runtime" }
pallet-bioauth = { version = "0.1", path = "../pallet-bioauth" }
pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", optional = true }

async-trait = "0.1.42"
pallet-aura = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-tracing = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-api = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-application-crypto = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-application-crypto = { git = "https://github.com/humanode-network/substrate", branch = "master", optional = true }
sp-blockchain = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-consensus-slots = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-consensus-aura = { git = "https://github.com/humanode-network/substrate", branch = "master", optional = true }
sp-runtime = { git = "https://github.com/humanode-network/substrate", branch = "master" }
thiserror = "1"

[dev-dependencies]
mockall = "0.10"
node-primitives = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-service = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-core = { git = "https://github.com/humanode-network/substrate", branch = "master" }
tokio = { version = "1", features = ["full"] }

[features]
default = []
test = ["all"]
all = ["aura-integration", "bioauth-pallet-integration"]
aura-integration = ["sp-consensus-aura", "sp-application-crypto"]
bioauth-pallet-integration = ["pallet-bioauth"]
94 changes: 94 additions & 0 deletions crates/bioauth-consensus/src/aura.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//! Aura consensus integration.

use sp_api::{BlockId, Decode, ProvideRuntimeApi};
use sp_application_crypto::Public;
use sp_blockchain::HeaderBackend;
use sp_consensus_aura::{AuraApi, Slot};
use sp_runtime::generic::OpaqueDigestItemId;
use sp_runtime::traits::{Block as BlockT, Header};
use std::{marker::PhantomData, sync::Arc};

/// Encapsulates block author extraction logic for aura consensus.
#[derive(Debug)]
pub struct BlockAuthorExtractor<Block: BlockT, Client> {
/// Client provides access to the runtime.
client: Arc<Client>,
/// The type from the block used in the chain.
_phantom_block: PhantomData<Block>,
}

/// An error that can occur during block author extraction with the aura consensus.
#[derive(Debug, thiserror::Error)]
pub enum AuraBlockAuthorExtractorError {
/// Unable to extract aura authorities from the chain state via the runtime.
#[error("unable to extract aura authorities: {0}")]
UnableToExtractAuthorities(sp_api::ApiError),
/// Unable to obtain the slot from the block header.
#[error("unable to obtaion the slot from the block header")]
UnableToObtainSlot,
/// Unable to decode the slot.
#[error("unable to decode the slot")]
UnableToDecodeSlot,
}

impl<Block: BlockT, Client> BlockAuthorExtractor<Block, Client> {
/// Create a new [`AuraBlockAuthorExtractor`].
pub fn new(client: Arc<Client>) -> Self {
Self {
client,
_phantom_block: PhantomData,
}
}
}

impl<Block: BlockT, Client> Clone for BlockAuthorExtractor<Block, Client> {
fn clone(&self) -> Self {
Self {
client: Arc::clone(&self.client),
_phantom_block: PhantomData,
}
}
}

impl<Block: BlockT, Client> crate::BlockAuthorExtractor for BlockAuthorExtractor<Block, Client>
where
Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
Client::Api: AuraApi<Block, sp_consensus_aura::sr25519::AuthorityId>,
{
type Error = AuraBlockAuthorExtractorError;
type Block = Block;
type PublicKeyType = Vec<u8>;

fn extract_block_author(
&self,
at: &BlockId<Self::Block>,
block_header: &<Self::Block as BlockT>::Header,
) -> Result<Vec<u8>, Self::Error> {
// Extract aura authorities list.
let authorities = self
.client
.runtime_api()
.authorities(at)
.map_err(AuraBlockAuthorExtractorError::UnableToExtractAuthorities)?;

// Extract the slot of a block.
let mut slot = block_header
.digest()
.log(|l| l.try_as_raw(OpaqueDigestItemId::PreRuntime(b"aura")))
.ok_or(AuraBlockAuthorExtractorError::UnableToObtainSlot)?;

// Decode slot number.
let slot_decoded = Slot::decode(&mut slot)
.map_err(|_| AuraBlockAuthorExtractorError::UnableToDecodeSlot)?;

// Author index in aura is current slot number mod authories.
let author_index = *slot_decoded % authorities.len() as u64;

// Determine the author of a block.
let author_public_key = authorities
.get(author_index as usize)
.expect("author index is mod authories list len; qed");

Ok(author_public_key.to_raw_vec())
}
}
73 changes: 73 additions & 0 deletions crates/bioauth-consensus/src/bioauth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Bioauth pallet integration.

use pallet_bioauth::BioauthApi;
use sp_api::{BlockId, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use sp_runtime::traits::Block as BlockT;
use std::{marker::PhantomData, sync::Arc};

/// Provides an authorization verifier on top of stored auth tickets.
#[derive(Debug)]
pub struct AuthorizationVerifier<Block: BlockT, Client> {
/// The client provides access to the runtime.
client: Arc<Client>,
/// The type from the block used in the chain.
_phantom_block: PhantomData<Block>,
}

/// An error that can occur during aura authorization verification.
#[derive(Debug, thiserror::Error)]
pub enum AuraAuthorizationVerifierError {
/// Something went wrong while extracting the stored auth tickets from the chain state via
/// the runtime.
#[error("unable to extract stored auth tickets: {0}")]
UnableToExtractStoredAuthTickets(sp_api::ApiError),
}

impl<Block: BlockT, Client> AuthorizationVerifier<Block, Client> {
/// Create a new [`AuraAuthorizationVerifier`].
pub fn new(client: Arc<Client>) -> Self {
Self {
client,
_phantom_block: PhantomData,
}
}
}

impl<Block: BlockT, Client> Clone for AuthorizationVerifier<Block, Client> {
fn clone(&self) -> Self {
Self {
client: Arc::clone(&self.client),
_phantom_block: PhantomData,
}
}
}

impl<Block: BlockT, Client> crate::AuthorizationVerifier for AuthorizationVerifier<Block, Client>
where
Client: HeaderBackend<Block> + ProvideRuntimeApi<Block>,
Client::Api: BioauthApi<Block>,
{
type Error = AuraAuthorizationVerifierError;
type Block = Block;
type PublicKeyType = [u8];

fn is_authorized(
&self,
at: &BlockId<Self::Block>,
author_public_key: &Self::PublicKeyType,
) -> Result<bool, Self::Error> {
// Get current stored tickets.
let stored_tickets = self
.client
.runtime_api()
.stored_auth_tickets(at)
.map_err(AuraAuthorizationVerifierError::UnableToExtractStoredAuthTickets)?;

let is_authorized = stored_tickets
.iter()
.any(|ticket| ticket.public_key == author_public_key);

Ok(is_authorized)
}
}
Loading