Skip to content

Commit

Permalink
A0-4343: Implement runtime api for storing abft scores on chain. (#1879)
Browse files Browse the repository at this point in the history
# Description

Adds runtime api for the client side to store abft performance scores on
chain. It's implemented using unsigned transactions since finality
committee is considerably smaller than block production committee.

## Type of change

- New feature (non-breaking change which adds functionality)

---------

Co-authored-by: Michal Swietek <mike1729@users.noreply.github.com>
  • Loading branch information
mike1729 and mike1729 authored Dec 6, 2024
1 parent 03ae362 commit 2ef119f
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 97 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

291 changes: 221 additions & 70 deletions aleph-client/src/aleph_zero.rs

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions bin/fake-runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ use pallet_aleph_runtime_api::*;
use pallet_transaction_payment::FeeDetails;
use pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo;
use primitives::{
AccountId, ApiError as AlephApiError, AuraId, AuthorityId as AlephId, Balance, Block, Nonce,
Perbill, SessionAuthorityData, SessionCommittee, SessionIndex, SessionValidatorError,
Version as FinalityVersion,
crypto::SignatureSet, AccountId, ApiError as AlephApiError, AuraId, AuthorityId as AlephId,
AuthoritySignature, Balance, Block, Nonce, Perbill, Score, SessionAuthorityData,
SessionCommittee, SessionIndex, SessionValidatorError, Version as FinalityVersion,
};
use sp_consensus_aura::SlotDuration;
use sp_core::OpaqueMetadata;
Expand Down Expand Up @@ -195,6 +195,10 @@ pub mod fake_runtime {
fn current_era_payout() -> (Balance, Balance) {
unimplemented!()
}

fn submit_abft_score(_score: Score, _signature: SignatureSet<AuthoritySignature>) -> Option<()>{
unimplemented!()
}
}

/// There’s an important remark on how this fake runtime must be implemented - it does not need to
Expand Down
20 changes: 12 additions & 8 deletions bin/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustm
use pallet_tx_pause::RuntimeCallNameOf;
use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
use primitives::{
staking::MAX_NOMINATORS_REWARDED_PER_VALIDATOR, wrap_methods, Address,
crypto::SignatureSet, staking::MAX_NOMINATORS_REWARDED_PER_VALIDATOR, wrap_methods, Address,
AlephNodeSessionKeys as SessionKeys, ApiError as AlephApiError, AuraId, AuthorityId as AlephId,
BlockNumber as AlephBlockNumber, Header as AlephHeader, SessionAuthorityData, SessionCommittee,
SessionIndex, SessionInfoProvider, SessionValidatorError,
TotalIssuanceProvider as TotalIssuanceProviderT, Version as FinalityVersion,
ADDRESSES_ENCODING, DEFAULT_BAN_REASON_LENGTH, DEFAULT_MAX_WINNERS, DEFAULT_SESSIONS_PER_ERA,
DEFAULT_SESSION_PERIOD, MAX_BLOCK_SIZE, MILLISECS_PER_BLOCK, TOKEN,
AuthoritySignature, BlockNumber as AlephBlockNumber, Header as AlephHeader, Score,
SessionAuthorityData, SessionCommittee, SessionIndex, SessionInfoProvider,
SessionValidatorError, TotalIssuanceProvider as TotalIssuanceProviderT,
Version as FinalityVersion, ADDRESSES_ENCODING, DEFAULT_BAN_REASON_LENGTH, DEFAULT_MAX_WINNERS,
DEFAULT_SESSIONS_PER_ERA, DEFAULT_SESSION_PERIOD, MAX_BLOCK_SIZE, MILLISECS_PER_BLOCK, TOKEN,
};
pub use primitives::{AccountId, AccountIndex, Balance, Hash, Nonce, Signature};
use sp_api::impl_runtime_apis;
Expand Down Expand Up @@ -80,10 +80,10 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("aleph-node"),
impl_name: create_runtime_str!("aleph-node"),
authoring_version: 1,
spec_version: 15_000_000,
spec_version: 16_000_000,
impl_version: 1,
apis: RUNTIME_API_VERSIONS,
transaction_version: 18,
transaction_version: 19,
state_version: 0,
};

Expand Down Expand Up @@ -1238,6 +1238,10 @@ impl_runtime_apis! {

ExponentialEraPayout::era_payout(total_issuance, MILLISECONDS_PER_ERA)
}

fn submit_abft_score(score: Score, signature: SignatureSet<AuthoritySignature>) -> Option<()> {
Aleph::submit_abft_score(score, signature)
}
}

impl pallet_nomination_pools_runtime_api::NominationPoolsApi<Block, AccountId, Balance> for Runtime {
Expand Down
4 changes: 2 additions & 2 deletions finality-aleph/src/abft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,15 @@ impl<S: 'static> IntoIterator for SignatureSet<S> {

impl From<SignatureSet<Signature>> for PrimitivesSignatureSet<AuthoritySignature> {
fn from(signature_set: SignatureSet<Signature>) -> PrimitivesSignatureSet<AuthoritySignature> {
let score_sigantures: Vec<IndexedSignature<AuthoritySignature>> = signature_set
let score_signatures: Vec<IndexedSignature<AuthoritySignature>> = signature_set
.0
.into_iter()
.map(|(idx, s)| IndexedSignature {
index: idx.0 as u64,
signature: s.0,
})
.collect();
PrimitivesSignatureSet(score_sigantures)
PrimitivesSignatureSet(score_signatures)
}
}

Expand Down
6 changes: 4 additions & 2 deletions pallets/aleph-runtime-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#![cfg_attr(not(feature = "std"), no_std)]

use primitives::{
AccountId, ApiError, AuthorityId, Balance, Perbill, SessionAuthorityData, SessionCommittee,
SessionIndex, SessionValidatorError, Version,
crypto::SignatureSet, AccountId, ApiError, AuthorityId, AuthoritySignature, Balance, Perbill,
Score, SessionAuthorityData, SessionCommittee, SessionIndex, SessionValidatorError, Version,
};
pub use sp_consensus_aura::sr25519::AuthorityId as AuraId;
use sp_std::vec::Vec;
Expand Down Expand Up @@ -37,5 +37,7 @@ sp_api::decl_runtime_apis! {
fn yearly_inflation() -> Perbill;
/// Returns payout. First tuple item is a validators payout, 2nd is the rest.
fn current_era_payout() -> (Balance, Balance);
/// Submits score for a nonce in a session of performance of finality committee members.
fn submit_abft_score(score: Score, signature: SignatureSet<AuthoritySignature>) -> Option<()>;
}
}
2 changes: 1 addition & 1 deletion pallets/aleph/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pallet-aleph"
version = "0.6.0"
version = "0.7.0"
license = "Apache 2.0"
authors.workspace = true
edition.workspace = true
Expand Down
113 changes: 105 additions & 8 deletions pallets/aleph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use primitives::{
Balance, SessionIndex, Version, VersionChange, DEFAULT_FINALITY_VERSION,
LEGACY_FINALITY_VERSION, TOKEN,
};
use sp_runtime::Perbill;
use sp_std::prelude::*;

/// The current storage version.
Expand All @@ -28,13 +29,18 @@ pub(crate) const LOG_TARGET: &str = "pallet-aleph";
#[frame_support::pallet]
#[pallet_doc("../README.md")]
pub mod pallet {
use frame_support::{pallet_prelude::*, sp_runtime::RuntimeAppPublic};
use frame_support::{
dispatch::{DispatchResult, DispatchResultWithPostInfo, Pays},
pallet_prelude::{TransactionSource, TransactionValidityError, ValueQuery, *},
sp_runtime::RuntimeAppPublic,
};
use frame_system::{
ensure_root,
ensure_none, ensure_root,
pallet_prelude::{BlockNumberFor, OriginFor},
};
use pallet_session::SessionManager;
use primitives::{SessionInfoProvider, TotalIssuanceProvider};
use primitives::{Score, ScoreNonce, SessionInfoProvider, TotalIssuanceProvider};
use sp_runtime::traits::ValidateUnsigned;
use sp_std::collections::btree_map::BTreeMap;
#[cfg(feature = "std")]
use sp_std::marker::PhantomData;
Expand All @@ -43,7 +49,9 @@ pub mod pallet {
use crate::traits::NextSessionAuthorityProvider;

#[pallet::config]
pub trait Config: frame_system::Config {
pub trait Config:
frame_system::Config + frame_system::offchain::SendTransactionTypes<Call<Self>>
{
type AuthorityId: Member + Parameter + RuntimeAppPublic + MaybeSerializeDeserialize;
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
type SessionInfoProvider: SessionInfoProvider<BlockNumberFor<Self>>;
Expand Down Expand Up @@ -130,9 +138,14 @@ pub mod pallet {
pub(super) type FinalityScheduledVersionChange<T: Config> =
StorageValue<_, VersionChange, OptionQuery>;

// clear this storage on session end
#[pallet::storage]
#[pallet::getter(fn abft_scores)]
pub type AbftScores<T: Config> = StorageMap<_, Twox64Concat, SessionIndex, Score>;

#[pallet::storage]
pub(super) type AbftSignature<T: Config> =
StorageValue<_, SignatureSet<Signature<T>>, OptionQuery>;
#[pallet::getter(fn last_score_nonce)]
pub(super) type LastScoreNonce<T: Config> = StorageValue<_, ScoreNonce, ValueQuery>;

impl<T: Config> Pallet<T> {
pub(crate) fn initialize_authorities(
Expand Down Expand Up @@ -295,9 +308,58 @@ pub mod pallet {
}
}

pub fn verify_multisignature(msg: &Vec<u8>, sgn: &SignatureSet<Signature<T>>) -> bool {
fn check_session_id(session_id: SessionIndex) -> Result<(), TransactionValidityError> {
let current_session_id = Self::current_session();
if current_session_id < session_id {
return Err(InvalidTransaction::Future.into());
}
if current_session_id > session_id {
return Err(InvalidTransaction::Stale.into());
}

Ok(())
}

fn check_nonce(nonce: ScoreNonce) -> Result<(), TransactionValidityError> {
let last_nonce = Self::last_score_nonce();
if nonce <= last_nonce {
return Err(InvalidTransaction::Stale.into());
}

Ok(())
}

fn check_score(
score: &Score,
signature: &SignatureSet<Signature<T>>,
) -> Result<(), TransactionValidityError> {
Self::check_session_id(score.session_id)?;
Self::check_nonce(score.nonce)?;
Self::verify_score(score, signature)?;

Ok(())
}

pub fn verify_score(
score: &Score,
signature: &SignatureSet<Signature<T>>,
) -> Result<(), TransactionValidityError> {
let msg = score.encode();
let authority_verifier = AuthorityVerifier::new(Self::authorities());
AuthorityVerifier::is_complete(&authority_verifier, msg, sgn)
if !AuthorityVerifier::is_complete(&authority_verifier, &msg, signature) {
return Err(InvalidTransaction::BadProof.into());
}
Ok(())
}

pub fn submit_abft_score(
score: Score,
signature: SignatureSet<Signature<T>>,
) -> Option<()> {
use frame_system::offchain::SubmitTransaction;

let call = Call::unsigned_submit_abft_score { score, signature };
SubmitTransaction::<T, Call<T>>::submit_unsigned_transaction(call.into()).ok()
}
}

Expand Down Expand Up @@ -381,6 +443,41 @@ pub mod pallet {

Ok(())
}

// fix weight, take into account validate_unsigned
#[pallet::call_index(3)]
#[pallet::weight(T::BlockWeights::get().max_block * Perbill::from_percent(10))]
/// Stores abft score
pub fn unsigned_submit_abft_score(
origin: OriginFor<T>,
score: Score,
_signature: SignatureSet<Signature<T>>, // We don't check signature as it was checked by ValidateUnsigned trait
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;

<LastScoreNonce<T>>::put(score.nonce);
AbftScores::<T>::insert(score.session_id, score);

Ok(Pays::No.into())
}
}

#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;

fn validate_unsigned(_source: TransactionSource, call: &Self::Call) -> TransactionValidity {
if let Call::unsigned_submit_abft_score { score, signature } = call {
Self::check_score(score, signature)?;
ValidTransaction::with_tag_prefix("AbftScore")
.priority(score.nonce as u64) // this ensures that later nonces are first in tx queue
.longevity(TransactionLongevity::MAX) // consider restricting longevity
.propagate(true)
.build()
} else {
InvalidTransaction::Call.into()
}
}
}

impl<T: Config> BoundToRuntimeAppPublic for Pallet<T> {
Expand Down
13 changes: 11 additions & 2 deletions primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,15 @@ pub mod staking {
}
}

pub type ScoreNonce = u32;

#[derive(PartialEq, Decode, Encode, TypeInfo, Debug, Clone)]
pub struct Score {
pub session_id: SessionIndex,
pub nonce: ScoreNonce,
pub points: Vec<u32>,
}

pub mod crypto {
use core::marker::PhantomData;

Expand All @@ -424,13 +433,13 @@ pub mod crypto {

use super::AuthoritySignature;

#[derive(Decode, Encode, TypeInfo, Debug, Clone)]
#[derive(PartialEq, Decode, Encode, TypeInfo, Debug, Clone)]
pub struct IndexedSignature<S> {
pub index: u64,
pub signature: S,
}

#[derive(Decode, Encode, TypeInfo, Debug, Clone)]
#[derive(PartialEq, Decode, Encode, TypeInfo, Debug, Clone)]
pub struct SignatureSet<S>(pub Vec<IndexedSignature<S>>);

#[cfg_attr(feature = "std", derive(Hash))]
Expand Down

0 comments on commit 2ef119f

Please sign in to comment.