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
3 changes: 2 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions crates/bioauth-consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ publish = false
pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", optional = true }

async-trait = "0.1.42"
codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
sc-client-api = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sp-api = { git = "https://github.com/humanode-network/substrate", branch = "master" }
Expand Down
47 changes: 24 additions & 23 deletions crates/bioauth-consensus/src/aura.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
//! Aura consensus integration.

use sp_api::{BlockId, Decode, ProvideRuntimeApi};
use sp_application_crypto::Public;
use sp_api::{BlockId, ProvideRuntimeApi};
use sp_blockchain::HeaderBackend;
use sp_consensus_aura::{AuraApi, Slot};
use sp_runtime::generic::OpaqueDigestItemId;
use sp_consensus_aura::{digests::CompatibleDigestItem, AuraApi};
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> {
pub struct BlockAuthorExtractor<Block: BlockT, Client, AuraAuthorityId> {
/// Client provides access to the runtime.
client: Arc<Client>,
/// The type from the block used in the chain.
/// The type of the block used in the chain.
_phantom_block: PhantomData<Block>,
/// The type used an authority id in aura.
_phantom_aura_authority_id: PhantomData<AuraAuthorityId>,
}

/// An error that can occur during block author extraction with the aura consensus.
Expand All @@ -26,44 +26,47 @@ pub enum AuraBlockAuthorExtractorError {
/// 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> {
impl<Block: BlockT, Client, AuraAuthorityId> BlockAuthorExtractor<Block, Client, AuraAuthorityId> {
/// Create a new [`AuraBlockAuthorExtractor`].
pub fn new(client: Arc<Client>) -> Self {
Self {
client,
_phantom_block: PhantomData,
_phantom_aura_authority_id: PhantomData,
}
}
}

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

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

fn extract_block_author(
&self,
at: &BlockId<Self::Block>,
block_header: &<Self::Block as BlockT>::Header,
) -> Result<Vec<u8>, Self::Error> {
) -> Result<AuraAuthorityId, Self::Error> {
// Extract aura authorities list.
let authorities = self
.client
Expand All @@ -72,23 +75,21 @@ where
.map_err(AuraBlockAuthorExtractorError::UnableToExtractAuthorities)?;

// Extract the slot of a block.
let mut slot = block_header
let slot = block_header
.digest()
.log(|l| l.try_as_raw(OpaqueDigestItemId::PreRuntime(b"aura")))
.logs()
.iter()
.find_map(CompatibleDigestItem::<()>::as_aura_pre_digest)
.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;
let author_index = *slot % 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())
Ok(author_public_key.clone())
}
}
26 changes: 18 additions & 8 deletions crates/bioauth-consensus/src/bioauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ use std::{marker::PhantomData, sync::Arc};

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

/// An error that can occur during aura authorization verification.
Expand All @@ -24,33 +26,41 @@ pub enum AuraAuthorizationVerifierError {
UnableToExtractStoredAuthTickets(sp_api::ApiError),
}

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

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

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

fn is_authorized(
&self,
Expand All @@ -66,7 +76,7 @@ where

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

Ok(is_authorized)
}
Expand Down
8 changes: 3 additions & 5 deletions crates/bioauth-consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,8 @@ where
BlockImport<Block, Error = ConsensusError, Transaction = TransactionFor<Client, Block>>,
TransactionFor<Client, Block>: 'static,
BAX: BlockAuthorExtractor<Block = Block> + Send,
AV: AuthorizationVerifier<Block = Block> + Send,
<BAX as BlockAuthorExtractor>::PublicKeyType: Send,
<BAX as BlockAuthorExtractor>::PublicKeyType:
AsRef<<AV as AuthorizationVerifier>::PublicKeyType>,
AV: AuthorizationVerifier<Block = Block, PublicKeyType = BAX::PublicKeyType> + Send,
<BAX as BlockAuthorExtractor>::PublicKeyType: Send + Sync,
<BAX as BlockAuthorExtractor>::Error: std::error::Error + Send + Sync + 'static,
<AV as AuthorizationVerifier>::Error: std::error::Error + Send + Sync + 'static,
BE: Backend<Block>,
Expand Down Expand Up @@ -142,7 +140,7 @@ where

let is_authorized = self
.authorization_verifier
.is_authorized(&at, author_public_key.as_ref())
.is_authorized(&at, &author_public_key)
.map_err(|err| mkerr(BioauthBlockImportError::AuthorizationVerifier(err)))?;

if !is_authorized {
Expand Down
50 changes: 27 additions & 23 deletions crates/bioauth-consensus/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use mockall::predicate::*;
use mockall::*;
use node_primitives::{Block, BlockNumber, Hash, Header};
use pallet_bioauth::{BioauthApi, StoredAuthTicket};
use pallet_bioauth::{self, BioauthApi, StoredAuthTicket};
use sc_client_api::Finalizer;
use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};
use sp_api::{ApiError, ApiRef, NativeOrEncoded, ProvideRuntimeApi, TransactionFor};
use sp_application_crypto::{Pair, Public};
use sp_application_crypto::Pair;
use sp_blockchain::{well_known_cache_keys, HeaderBackend};
use sp_consensus::{BlockOrigin, Error as ConsensusError};
use sp_consensus_aura::{
Expand All @@ -16,10 +16,14 @@ use std::{collections::HashMap, str::FromStr, sync::Arc};

use crate::{BioauthBlockImport, BioauthBlockImportError};

type MockValidatorPublicKey = AuraId;

type MockAuthTicket = StoredAuthTicket<MockValidatorPublicKey>;

mock! {
RuntimeApi {
fn stored_auth_tickets(&self, _at: &sp_api::BlockId<Block>) -> Result<
NativeOrEncoded<Vec<StoredAuthTicket>>,
NativeOrEncoded<Vec<MockAuthTicket>>,
ApiError
>;

Expand Down Expand Up @@ -108,10 +112,10 @@ impl<'a> BlockImport<Block> for &'a MockClient {
}

sp_api::mock_impl_runtime_apis! {
impl BioauthApi<Block> for MockWrapperRuntimeApi {
impl BioauthApi<Block, MockValidatorPublicKey> for MockWrapperRuntimeApi {
#[advanced]
fn stored_auth_tickets(&self, at: &sp_api::BlockId<Block>) -> Result<
NativeOrEncoded<Vec<StoredAuthTicket>>,
NativeOrEncoded<Vec<MockAuthTicket>>,
ApiError
> {
self.0.stored_auth_tickets(at)
Expand Down Expand Up @@ -375,12 +379,15 @@ async fn it_denies_block_import_with_not_bioauth_authorized() {
mock_runtime_api
.expect_stored_auth_tickets()
.returning(|_| {
Ok(NativeOrEncoded::from(vec![
pallet_bioauth::StoredAuthTicket {
public_key: "invalid_author".as_bytes().to_vec(),
nonce: "1".as_bytes().to_vec(),
},
]))
Ok(NativeOrEncoded::from(vec![MockAuthTicket {
public_key: sp_consensus_aura::sr25519::AuthorityPair::from_string(
&format!("//{}", "Bob"),
None,
)
.expect("static values are valid; qed")
.public(),
nonce: b"1".to_vec(),
}]))
});

let runtime_api = MockWrapperRuntimeApi(Arc::new(mock_runtime_api));
Expand Down Expand Up @@ -433,18 +440,15 @@ async fn it_permits_block_import_with_valid_data() {
mock_runtime_api
.expect_stored_auth_tickets()
.returning(|_| {
Ok(NativeOrEncoded::from(vec![
pallet_bioauth::StoredAuthTicket {
public_key: sp_consensus_aura::sr25519::AuthorityPair::from_string(
&format!("//{}", "Alice"),
None,
)
.expect("static values are valid; qed")
.public()
.to_raw_vec(),
nonce: "1".as_bytes().to_vec(),
},
]))
Ok(NativeOrEncoded::from(vec![MockAuthTicket {
public_key: sp_consensus_aura::sr25519::AuthorityPair::from_string(
&format!("//{}", "Alice"),
None,
)
.expect("static values are valid; qed")
.public(),
nonce: b"1".to_vec(),
}]))
});

let runtime_api = MockWrapperRuntimeApi(Arc::new(mock_runtime_api));
Expand Down
6 changes: 3 additions & 3 deletions crates/humanode-peer/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> {
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
],
vec![pallet_bioauth::StoredAuthTicket {
public_key: authority_keys_from_seed("Alice").as_slice().to_vec(),
public_key: authority_keys_from_seed("Alice"),
nonce: "1".as_bytes().to_vec(),
}],
robonode_public_key,
Expand Down Expand Up @@ -130,7 +130,7 @@ pub fn development_config() -> Result<ChainSpec, String> {
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
vec![pallet_bioauth::StoredAuthTicket {
public_key: authority_keys_from_seed("Alice").as_slice().to_vec(),
public_key: authority_keys_from_seed("Alice"),
nonce: "1".as_bytes().to_vec(),
}],
robonode_public_key,
Expand All @@ -155,7 +155,7 @@ fn testnet_genesis(
initial_authorities: Vec<AuraId>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
stored_auth_tickets: Vec<StoredAuthTicket>,
stored_auth_tickets: Vec<StoredAuthTicket<AuraId>>,
robonode_public_key: RobonodePublicKeyWrapper,
) -> GenesisConfig {
GenesisConfig {
Expand Down
6 changes: 3 additions & 3 deletions crates/humanode-peer/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use sc_executor::native_executor_instance;
pub use sc_executor::NativeExecutor;
use sc_service::{Error as ServiceError, KeystoreContainer, PartialComponents, TaskManager};
use sp_consensus::SlotData;
use sp_consensus_aura::sr25519::AuthorityPair as AuraPair;
use sp_consensus_aura::sr25519::{AuthorityId as AuraId, AuthorityPair as AuraPair};
use tracing::*;

use crate::configuration::Configuration;
Expand Down Expand Up @@ -54,8 +54,8 @@ pub fn new_partial(
FullBackend,
Block,
FullClient,
bioauth_consensus::aura::BlockAuthorExtractor<Block, FullClient>,
bioauth_consensus::bioauth::AuthorizationVerifier<Block, FullClient>,
bioauth_consensus::aura::BlockAuthorExtractor<Block, FullClient, AuraId>,
bioauth_consensus::bioauth::AuthorizationVerifier<Block, FullClient, AuraId>,
>,
SlotDuration,
Duration,
Expand Down
2 changes: 2 additions & 0 deletions crates/humanode-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ substrate-wasm-builder = { git = "https://github.com/humanode-network/substrate"

[dependencies]
pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", default-features = false }
primitives-auth-ticket = { version = "0.1", path = "../primitives-auth-ticket", default-features = false }
robonode-crypto = { version = "0.1", path = "../robonode-crypto", default-features = false }

codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
Expand Down Expand Up @@ -52,6 +53,7 @@ runtime-benchmarks = [
"sp-runtime/runtime-benchmarks",
]
std = [
"primitives-auth-ticket/std",
"codec/std",
"serde",
"frame-executive/std",
Expand Down
Loading