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

Collecting validator verification key in Substrate - generic way #463

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
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
17 changes: 7 additions & 10 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,17 @@ jobs:
- name: Check Build
run: |
cargo build --release --features try-runtime
- name: Check Try-Runtime
run: |
try-runtime --runtime ./target/release/wbuild/cere-dev-runtime/cere_dev_runtime.compact.compressed.wasm \
on-runtime-upgrade --disable-idempotency-checks live --uri wss://archive.devnet.cere.network:443
- name: Run dev chain
run: |
timeout --preserve-status 30s ./target/release/cere --dev
# - name: Check Try-Runtime
# run: |
# try-runtime --runtime ./target/release/wbuild/cere-dev-runtime/cere_dev_runtime.compact.compressed.wasm \
# on-runtime-upgrade --disable-idempotency-checks live --uri wss://archive.devnet.cere.network:443
# - name: Run dev chain
# run: |
# timeout --preserve-status 30s ./target/release/cere --dev
- name: Check Build for Benchmarking
run: >
pushd node &&
cargo check --features=runtime-benchmarks --release
- name: Check Build for Try-Runtime
run: |
cargo check --features=try-runtime --release

clippy:
name: Run Clippy
Expand Down
1 change: 1 addition & 0 deletions node/service/src/chain_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ pub fn cere_dev_genesis(
.collect(),
phantom: Default::default(),
},
ddc_verification: Default::default(),
}
}

Expand Down
1 change: 1 addition & 0 deletions pallets/ddc-payouts/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ impl<T: Config> ValidatorVisitor<T> for MockValidatorVisitor
where
<T as frame_system::Config>::AccountId: From<AccountId>,
{
#[cfg(feature = "runtime-benchmarks")]
fn setup_validators(_validators: Vec<T::AccountId>) {
unimplemented!()
}
Expand Down
155 changes: 103 additions & 52 deletions pallets/ddc-verification/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*};
pub mod weights;
use itertools::Itertools;
use rand::{prelude::*, rngs::SmallRng, SeedableRng};
use sp_core::crypto::UncheckedFrom;
pub use sp_io::crypto::sr25519_public_keys;
use sp_runtime::traits::IdentifyAccount;
use sp_staking::StakingInterface;
use sp_std::fmt::Debug;

Expand All @@ -61,7 +64,7 @@ pub mod migrations;
#[frame_support::pallet]
pub mod pallet {

use ddc_primitives::{AggregatorInfo, BucketId, MergeActivityHash, KEY_TYPE};
use ddc_primitives::{AggregatorInfo, BucketId, MergeActivityHash, DAC_VERIFICATION_KEY_TYPE};
use frame_support::PalletId;
use sp_core::crypto::AccountId32;
use sp_runtime::SaturatedConversion;
Expand Down Expand Up @@ -264,7 +267,10 @@ pub mod pallet {
era_id: DdcEra,
validator: T::AccountId,
},
FailedToFetchCurrentValidator {
FailedToCollectVerificationKey {
validator: T::AccountId,
},
FailedToFetchVerificationKey {
validator: T::AccountId,
},
FailedToFetchNodeProvider {
Expand Down Expand Up @@ -421,7 +427,8 @@ pub mod pallet {
cluster_id: ClusterId,
era_id: DdcEra,
},
FailedToFetchCurrentValidator,
FailedToCollectVerificationKey,
FailedToFetchVerificationKey,
FailedToFetchNodeProvider,
FailedToFetchClusterNodes,
FailedToFetchDacNodes,
Expand Down Expand Up @@ -911,44 +918,33 @@ pub mod pallet {
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn offchain_worker(block_number: BlockNumberFor<T>) {
if !sp_io::offchain::is_validator() {
if block_number.saturated_into::<u32>() % T::BLOCK_TO_START as u32 != 0 {
return;
}

let signer = Signer::<T, T::OffchainIdentifierId>::any_account();
if !signer.can_sign() {
log::error!("🚨 No OCW is available.");
if !sp_io::offchain::is_validator() {
return;
}

// todo! Need to uncomment this code
// if Self::fetch_current_validator().is_err() {
// let _ = signer.send_signed_transaction(|account| {
// Self::store_current_validator(account.id.encode());
//
// Call::set_current_validator {}
// });
// }
// todo! need to remove below code
if (block_number.saturated_into::<u32>() % 70) == 0 {
let _ = signer.send_signed_transaction(|account| {
Self::store_current_validator(account.id.encode());
log::info!("🏭📋‍ Setting current validator... {:?}", account.id);
Call::set_current_validator {}
});
}
let verification_key = unwrap_or_log_error!(
Self::collect_verification_pub_key(),
"❌ Error collecting validator verification key"
);

let signer = Signer::<T, T::OffchainIdentifierId>::any_account()
.with_filter(vec![verification_key.clone()]);

if (block_number.saturated_into::<u32>() % T::BLOCK_TO_START as u32) != 0 {
if !signer.can_sign() {
log::error!("🚨 OCW signer is not available");
return;
}

log::info!("👋 Hello from pallet-ddc-verification.");
Self::store_verification_account_id(verification_key.clone().into_account());

let clusters_ids = unwrap_or_log_error!(
T::ClusterManager::get_clusters(ClusterStatus::Activated),
"🏭❌ Error retrieving clusters to validate"
"❌ Error retrieving clusters to validate"
);

log::info!("🎡 {:?} of 'Activated' clusters found", clusters_ids.len());

for cluster_id in clusters_ids {
Expand Down Expand Up @@ -2540,17 +2536,57 @@ pub mod pallet {
format!("offchain::activities::{:?}::{:?}", cluster_id, era_id).into_bytes()
}

pub(crate) fn store_current_validator(validator: Vec<u8>) {
let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes();
pub(crate) fn collect_verification_pub_key() -> Result<T::Public, OCWError> {
let session_verification_keys = <T::OffchainIdentifierId as AppCrypto<
T::Public,
T::Signature,
>>::RuntimeAppPublic::all()
.into_iter()
.filter_map(|key| {
let generic_public = <T::OffchainIdentifierId as AppCrypto<
T::Public,
T::Signature,
>>::GenericPublic::from(key);
let public_key: T::Public = generic_public.into();
let account_id = public_key.clone().into_account();
if <ValidatorSet<T>>::get().contains(&account_id) {
Option::Some(public_key)
} else {
Option::None
}
})
.collect::<Vec<_>>();

if session_verification_keys.len() != 1 {
log::error!(
"🚨 Unexpected number of session verification keys is found. Expected: 1, Actual: {:?}",
session_verification_keys.len()
);
return Err(OCWError::FailedToCollectVerificationKey);
}

session_verification_keys
.into_iter()
.next() // first
.ok_or(OCWError::FailedToCollectVerificationKey)
}

pub(crate) fn store_verification_account_id(account_id: T::AccountId) {
let validator: Vec<u8> = account_id.encode();
let key = format!("offchain::validator::{:?}", DAC_VERIFICATION_KEY_TYPE).into_bytes();
sp_io::offchain::local_storage_set(StorageKind::PERSISTENT, &key, &validator);
}

pub(crate) fn fetch_current_validator() -> Result<Vec<u8>, OCWError> {
let key = format!("offchain::validator::{:?}", KEY_TYPE).into_bytes();
pub(crate) fn fetch_verification_account_id() -> Result<T::AccountId, OCWError> {
let key = format!("offchain::validator::{:?}", DAC_VERIFICATION_KEY_TYPE).into_bytes();

match sp_io::offchain::local_storage_get(StorageKind::PERSISTENT, &key) {
Some(data) => Ok(data),
None => Err(OCWError::FailedToFetchCurrentValidator),
Some(data) => {
let account_id = T::AccountId::decode(&mut &data[..])
.map_err(|_| OCWError::FailedToFetchVerificationKey)?;
Ok(account_id)
},
None => Err(OCWError::FailedToFetchVerificationKey),
}
}

Expand Down Expand Up @@ -2922,9 +2958,7 @@ pub mod pallet {
cluster_id: &ClusterId,
dac_nodes: &[(NodePubKey, StorageNodeParams)],
) -> Result<Option<EraActivity>, OCWError> {
let this_validator_data = Self::fetch_current_validator()?;
let this_validator = T::AccountId::decode(&mut &this_validator_data[..])
.map_err(|_| OCWError::FailedToFetchCurrentValidator)?;
let this_validator = Self::fetch_verification_account_id()?;

let last_validated_era_by_this_validator =
Self::get_last_validated_era(cluster_id, this_validator)?
Expand Down Expand Up @@ -3760,8 +3794,13 @@ pub mod pallet {
validator: caller.clone(),
});
},
OCWError::FailedToFetchCurrentValidator => {
Self::deposit_event(Event::FailedToFetchCurrentValidator {
OCWError::FailedToCollectVerificationKey => {
Self::deposit_event(Event::FailedToCollectVerificationKey {
validator: caller.clone(),
});
},
OCWError::FailedToFetchVerificationKey => {
Self::deposit_event(Event::FailedToFetchVerificationKey {
validator: caller.clone(),
});
},
Expand Down Expand Up @@ -4012,21 +4051,8 @@ pub mod pallet {
T::ClusterValidator::set_last_validated_era(&cluster_id, era_id)
}

// todo! Need to remove this
#[pallet::call_index(11)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::create_billing_reports())] // todo! implement weights
pub fn set_current_validator(origin: OriginFor<T>) -> DispatchResult {
let validator = ensure_signed(origin)?;

if !<ValidatorSet<T>>::get().contains(&validator) {
ValidatorSet::<T>::append(validator);
}

Ok(())
}

// todo! remove this after devnet testing
#[pallet::call_index(12)]
#[pallet::call_index(11)]
#[pallet::weight(<T as pallet::Config>::WeightInfo::create_billing_reports())] // todo! implement weights
pub fn set_era_validations(
origin: OriginFor<T>,
Expand Down Expand Up @@ -4065,9 +4091,11 @@ pub mod pallet {
}

impl<T: Config> ValidatorVisitor<T> for Pallet<T> {
#[cfg(feature = "runtime-benchmarks")]
fn setup_validators(validators: Vec<T::AccountId>) {
ValidatorSet::<T>::put(validators);
}

fn is_ocw_validator(caller: T::AccountId) -> bool {
if ValidatorToStashKey::<T>::contains_key(caller.clone()) {
<ValidatorSet<T>>::get().contains(&caller)
Expand Down Expand Up @@ -4151,4 +4179,27 @@ pub mod pallet {

fn on_disabled(_i: u32) {}
}

#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub validators: Vec<T::AccountId>,
}

impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
GenesisConfig { validators: Default::default() }
}
}

#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T>
where
T::AccountId: UncheckedFrom<T::Hash> + AsRef<[u8]>,
{
fn build(&self) {
for validator in &self.validators {
<ValidatorSet<T>>::append(validator);
}
}
}
}
14 changes: 14 additions & 0 deletions pallets/ddc-verification/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ impl<T: Config> CustomerVisitor<T> for MockCustomerVisitor {
Ok(account_1)
}
}

pub(crate) const VALIDATOR_VERIFICATION_PUB_KEY_HEX: &str =
"4e7b7f176f8778a2dbef829f50466170634e747ab5c5e64cb131c9c5a01d975f";
pub(crate) const VALIDATOR_VERIFICATION_PRIV_KEY_HEX: &str =
"b6186f80dce7190294665ab53860de2841383bb202c562bb8b81a624351fa318";

// Build genesis storage according to the mock runtime.
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut storage = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
Expand Down Expand Up @@ -322,6 +328,14 @@ pub fn new_test_ext() -> sp_io::TestExternalities {
pallet_staking::GenesisConfig::<Test> { stakers: stakers.clone(), ..Default::default() }
.assimilate_storage(&mut storage);

let arr = hex::decode(VALIDATOR_VERIFICATION_PUB_KEY_HEX)
.expect("Test verification pub key to be extracted");

let verification_key = AccountId::decode(&mut &arr[..]).unwrap();

let _ = pallet_ddc_verification::GenesisConfig::<Test> { validators: vec![verification_key] }
.assimilate_storage(&mut storage);

sp_io::TestExternalities::new(storage)
}

Expand Down
Loading
Loading