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

Switch pallet-bioauth authorities to WeakBoundedVec and enable generate_storage_info #208

Merged
merged 9 commits into from
Dec 17, 2021
12 changes: 9 additions & 3 deletions crates/humanode-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use sp_version::NativeVersion;
use sp_version::RuntimeVersion;

// A few exports that help ease life for downstream crates.
use codec::{Decode, Encode};
use codec::{Decode, Encode, MaxEncodedLen};
pub use frame_support::{
construct_runtime, parameter_types,
traits::{KeyOwnerProofSystem, Randomness},
Expand Down Expand Up @@ -207,7 +207,9 @@ impl frame_system::Config for Runtime {
}

/// The wrapper for the robonode public key, that enables ssotring it in the state.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, TypeInfo)]
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, TypeInfo, MaxEncodedLen,
)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct RobonodePublicKeyWrapper([u8; 32]);

Expand Down Expand Up @@ -417,6 +419,8 @@ const TIMESTAMP_HOUR: UnixMilliseconds = 60 * TIMESTAMP_MINUTE;

parameter_types! {
pub const AuthenticationsExpireAfter: UnixMilliseconds = 72 * TIMESTAMP_HOUR;
pub const MaxAuthentications: u32 = 512;
pub const MaxNonces: u32 = 102400;
}

impl pallet_bioauth::Config for Runtime {
Expand All @@ -432,6 +436,8 @@ impl pallet_bioauth::Config for Runtime {
type CurrentMoment = CurrentMoment;
type AuthenticationsExpireAfter = AuthenticationsExpireAfter;
type WeightInfo = pallet_bioauth::weights::SubstrateWeight<Runtime>;
type MaxAuthentications = MaxAuthentications;
type MaxNonces = MaxNonces;
}

// Create the runtime by composing the FRAME pallets that were previously
Expand Down Expand Up @@ -553,7 +559,7 @@ impl_runtime_apis! {

impl bioauth_consensus_api::BioauthConsensusApi<Block, AuraId> for Runtime {
fn is_authorized(id: &AuraId) -> bool {
Bioauth::active_authentications()
Bioauth::active_authentications().into_inner()
.iter()
.any(|stored_public_key| &stored_public_key.public_key == id)
}
Expand Down
164 changes: 119 additions & 45 deletions crates/pallet-bioauth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
// Fix clippy for sp_api::decl_runtime_apis!
#![allow(clippy::too_many_arguments, clippy::unnecessary_mut_passed)]

use codec::{Decode, Encode};
use frame_support::traits::IsSubType;
use codec::{Decode, Encode, MaxEncodedLen};
use frame_support::weights::DispatchInfo;
use frame_support::{parameter_types, traits::IsSubType, WeakBoundedVec};
pub use pallet::*;
use scale_info::TypeInfo;
#[cfg(feature = "std")]
Expand Down Expand Up @@ -85,9 +85,17 @@ pub struct Authenticate<OpaqueAuthTicket, Commitment> {
pub ticket_signature: Commitment,
}

parameter_types! {
/// Bytes Max number at nonce in this pallet.
pub const AuthTicketNonceMaxBytes: u32 = 256;
}

/// The nonce used by robonode in the auth tickets.
pub type AuthTicketNonce = Vec<u8>;

/// The nonce type in this pallet with bounded number of bytes at the nonce.
pub type BoundedAuthTicketNonce = WeakBoundedVec<u8, AuthTicketNonceMaxBytes>;

/// The auth ticket passed to us from the robonode.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
Expand All @@ -100,7 +108,7 @@ pub struct AuthTicket<PublicKey> {

/// The state that we keep in the blockchain for an active authentication.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
#[derive(PartialEq, Eq, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo, MaxEncodedLen)]
pub struct Authentication<PublicKey, Moment> {
/// The public key of a validator.
pub public_key: PublicKey,
Expand All @@ -119,11 +127,13 @@ pub struct Authentication<PublicKey, Moment> {
pub mod pallet {
use crate::{
weights::WeightInfo, AuthTicket, AuthTicketNonce, Authenticate, Authentication,
CurrentMoment, TryConvert, ValidatorSetUpdater, Verifier,
BoundedAuthTicketNonce, CurrentMoment, TryConvert, ValidatorSetUpdater, Verifier,
};

use codec::MaxEncodedLen;
use frame_support::{
dispatch::DispatchResult, pallet_prelude::*, sp_tracing::error, storage::types::ValueQuery,
WeakBoundedVec,
};
use frame_system::pallet_prelude::*;
use sp_runtime::{app_crypto::MaybeHash, traits::AtLeast32Bit};
Expand All @@ -143,10 +153,15 @@ pub mod pallet {
+ Parameter
+ MaybeSerializeDeserialize
+ Verifier<Self::RobonodeSignature>
+ Default;
+ Default
+ MaxEncodedLen;

/// The public key of the validator.
type ValidatorPublicKey: Member + Parameter + MaybeSerializeDeserialize + MaybeHash;
type ValidatorPublicKey: Member
+ Parameter
+ MaybeSerializeDeserialize
+ MaybeHash
+ MaxEncodedLen;

/// The opaque auth ticket type.
type OpaqueAuthTicket: Parameter + AsRef<[u8]> + Send + Sync;
Expand All @@ -158,7 +173,12 @@ pub mod pallet {
>;

/// Type used for expressing timestamp.
type Moment: Parameter + Default + AtLeast32Bit + Copy + MaybeSerializeDeserialize;
type Moment: Parameter
+ Default
+ AtLeast32Bit
+ Copy
+ MaybeSerializeDeserialize
+ MaxEncodedLen;

/// Type used for pretty printing the timestamp.
type DisplayMoment: From<Self::Moment> + core::fmt::Display;
Expand All @@ -174,10 +194,17 @@ pub mod pallet {

/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;

/// The maximum number of authentications.
type MaxAuthentications: Get<u32>;

/// The maximum number of nonces.
type MaxNonces: Get<u32>;
}

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::generate_storage_info]
pub struct Pallet<T>(_);

/// The public key of the robonode.
Expand All @@ -188,13 +215,17 @@ pub mod pallet {
/// A list of all consumed nonces.
#[pallet::storage]
#[pallet::getter(fn consumed_auth_ticket_nonces)]
pub type ConsumedAuthTicketNonces<T> = StorageValue<_, Vec<AuthTicketNonce>, ValueQuery>;
pub type ConsumedAuthTicketNonces<T: Config> =
StorageValue<_, WeakBoundedVec<BoundedAuthTicketNonce, T::MaxNonces>, ValueQuery>;

/// A list of all active authentications.
#[pallet::storage]
#[pallet::getter(fn active_authentications)]
pub type ActiveAuthentications<T: Config> =
StorageValue<_, Vec<Authentication<T::ValidatorPublicKey, T::Moment>>, ValueQuery>;
pub type ActiveAuthentications<T: Config> = StorageValue<
_,
WeakBoundedVec<Authentication<T::ValidatorPublicKey, T::Moment>, T::MaxAuthentications>,
ValueQuery,
>;

#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
Expand All @@ -219,9 +250,27 @@ pub mod pallet {
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
let bounded_consumed_auth_ticket_nonces = WeakBoundedVec::<_, T::MaxNonces>::try_from(
self.consumed_auth_ticket_nonces
.iter()
.cloned()
.map(|nonce| {
BoundedAuthTicketNonce::try_from(nonce)
.expect("Initial nonce len must be less than AuthTicketNonceMaxBytes")
})
.collect::<Vec<_>>(),
)
.expect("Initial nonces must be less than T::MaxNonces");

let bounded_active_authentications =
WeakBoundedVec::<_, T::MaxAuthentications>::try_from(
self.active_authentications.clone(),
)
.expect("Initial authentications must be less than T::MaxAuthentications");

<RobonodePublicKey<T>>::put(&self.robonode_public_key);
<ConsumedAuthTicketNonces<T>>::put(&self.consumed_auth_ticket_nonces);
<ActiveAuthentications<T>>::put(&self.active_authentications);
<ConsumedAuthTicketNonces<T>>::put(bounded_consumed_auth_ticket_nonces);
<ActiveAuthentications<T>>::put(bounded_active_authentications);

<Pallet<T>>::issue_validators_set_init(&self.active_authentications);
}
Expand Down Expand Up @@ -287,7 +336,7 @@ pub mod pallet {
/// Validate the incloming authentication attempt, checking the auth ticket data against
/// the passed input.
fn validate_authentication_attempt<'a, T: Config>(
consumed_auth_ticket_nonces: &'a [AuthTicketNonce],
consumed_auth_ticket_nonces: &'a [BoundedAuthTicketNonce],
active_authentications: &'a [Authentication<T::ValidatorPublicKey, T::Moment>],
auth_ticket: &AuthTicket<T::ValidatorPublicKey>,
) -> Result<(), AuthenticationAttemptValidationError<'a, T>> {
Expand Down Expand Up @@ -320,39 +369,57 @@ pub mod pallet {
ensure_none(origin)?;

let auth_ticket = Self::extract_auth_ticket_checked(req)?;

let public_key = auth_ticket.public_key.clone();

// Update storage.
<ConsumedAuthTicketNonces<T>>::try_mutate::<_, Error<T>, _>(
move |consumed_auth_ticket_nonces| {
<ActiveAuthentications<T>>::try_mutate::<_, Error<T>, _>(
move |active_authentications| {
validate_authentication_attempt::<T>(
consumed_auth_ticket_nonces,
active_authentications,
&auth_ticket,
)?;

// Update internal state.
let current_moment = T::CurrentMoment::now();
consumed_auth_ticket_nonces.push(auth_ticket.nonce);
active_authentications.push(Authentication {
public_key: public_key.clone(),
expires_at: current_moment + T::AuthenticationsExpireAfter::get(),
});

// Issue an update to the external validators set.
Self::issue_validators_set_update(active_authentications.as_slice());

// Emit an event.
Self::deposit_event(Event::NewAuthentication(public_key));
Ok(())
},
)?;
Ok(())
},
)?;
let consumed_auth_ticket_nonces = <ConsumedAuthTicketNonces<T>>::get();
let active_authentications = <ActiveAuthentications<T>>::get();

validate_authentication_attempt::<T>(
&consumed_auth_ticket_nonces,
&active_authentications,
&auth_ticket,
)
.map_err(Error::<T>::from)?;

// Update internal state.
let current_moment = T::CurrentMoment::now();
let mut updated_consumed_auth_ticket_nonces = consumed_auth_ticket_nonces.into_inner();
updated_consumed_auth_ticket_nonces.push(BoundedAuthTicketNonce::force_from(
auth_ticket.nonce,
Some(
"Warning: The number of auth-ticket nonce bytes are more than expected. \
A runtime configuration adjustment may be needed.",
),
));

let mut updated_active_authentications = active_authentications.into_inner();
updated_active_authentications.push(Authentication {
public_key: public_key.clone(),
expires_at: current_moment + T::AuthenticationsExpireAfter::get(),
});

Self::issue_validators_set_update(updated_active_authentications.as_slice());
Self::deposit_event(Event::NewAuthentication(public_key));

let bounded_updated_consumed_auth_ticket_nonces = WeakBoundedVec::<_, T::MaxNonces>::force_from(
updated_consumed_auth_ticket_nonces,
Some(
"Warning: The number of consumed auth-ticket nonces are more than expected. \
A runtime configuration adjustment may be needed.",
),
);

let bounded_updated_active_authentications =
WeakBoundedVec::<_, T::MaxAuthentications>::force_from(
updated_active_authentications,
Some(
"Warning: The number of active authentications are more than expected. \
A runtime configuration adjustment may be needed.",
),
);

<ActiveAuthentications<T>>::put(bounded_updated_active_authentications);
<ConsumedAuthTicketNonces<T>>::put(bounded_updated_consumed_auth_ticket_nonces);
Ok(())
}
}
Expand All @@ -374,8 +441,15 @@ pub mod pallet {
let update_required =
possibly_expired_authentications_len != active_authentications.len();
if update_required {
// We use force_from and None as a resulted active authentications Vec
// can't become bigger than it was. Just filtering was done before.
let bounded_active_authentications =
WeakBoundedVec::<_, T::MaxAuthentications>::force_from(
active_authentications.clone(),
None,
);
Self::issue_validators_set_update(active_authentications.as_slice());
<ActiveAuthentications<T>>::put(active_authentications);
<ActiveAuthentications<T>>::put(bounded_active_authentications);
}

T::WeightInfo::on_initialize(update_required)
Expand Down
Loading