feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,104 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
unsigned::{miner::OffchainWorkerMiner, Call, Config, Pallet},
verifier::Verifier,
CurrentPhase, Phase,
};
use pezframe_benchmarking::v2::*;
use pezframe_election_provider_support::ElectionProvider;
use pezframe_support::{assert_ok, pezpallet_prelude::*};
use pezframe_system::RawOrigin;
use pezsp_std::boxed::Box;
#[benchmarks(where T: crate::Config + crate::signed::Config + crate::verifier::Config)]
mod benchmarks {
use super::*;
#[benchmark(pov_mode = Measured)]
fn validate_unsigned() -> Result<(), BenchmarkError> {
#[cfg(test)]
crate::mock::ElectionStart::set(pezsp_runtime::traits::Bounded::max_value());
crate::Pallet::<T>::start().unwrap();
crate::Pallet::<T>::roll_until_matches(|| {
matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
});
let call: Call<T> = OffchainWorkerMiner::<T>::mine_solution(T::MinerPages::get(), false)
.map(|solution| Call::submit_unsigned { paged_solution: Box::new(solution) })
.unwrap();
#[block]
{
assert_ok!(Pallet::<T>::validate_unsigned(TransactionSource::Local, &call));
}
Ok(())
}
#[benchmark(pov_mode = Measured)]
fn submit_unsigned() -> Result<(), BenchmarkError> {
#[cfg(test)]
crate::mock::ElectionStart::set(pezsp_runtime::traits::Bounded::max_value());
crate::Pallet::<T>::start().unwrap();
// roll to unsigned phase open
crate::Pallet::<T>::roll_until_matches(|| {
matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
});
// TODO: we need to better ensure that this is actually worst case
let solution =
OffchainWorkerMiner::<T>::mine_solution(T::MinerPages::get(), false).unwrap();
// nothing is queued
assert!(T::Verifier::queued_score().is_none());
#[block]
{
assert_ok!(Pallet::<T>::submit_unsigned(RawOrigin::None.into(), Box::new(solution)));
}
// something is queued
assert!(T::Verifier::queued_score().is_some());
Ok(())
}
#[benchmark(extra, pov_mode = Measured)]
fn mine_solution(p: Linear<1, { T::Pages::get() }>) -> Result<(), BenchmarkError> {
#[cfg(test)]
crate::mock::ElectionStart::set(pezsp_runtime::traits::Bounded::max_value());
crate::Pallet::<T>::start().unwrap();
// roll to unsigned phase open
crate::Pallet::<T>::roll_until_matches(|| {
matches!(CurrentPhase::<T>::get(), Phase::Unsigned(_))
});
#[block]
{
OffchainWorkerMiner::<T>::mine_solution(p, true).unwrap();
}
Ok(())
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::ExtBuilder::full().build_unchecked(),
crate::mock::Runtime
);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,648 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! ## The unsigned phase, and its miner.
//!
//! This pallet deals with unsigned submissions. These are backup, "possibly" multi-page submissions
//! from validators.
//!
//! This pallet has two miners, described in [`unsigned::miner`].
//!
//! As it stands, a validator can, during the unsigned phase, submit up to
//! [`unsigned::Config::MinerPages`] pages. While this can be more than 1, it can likely not be a
//! full, high quality solution. This is because unsigned validator solutions are verified on the
//! fly, all within a single block. The exact value of this parameter should be determined by the
//! benchmarks of a runtime.
//!
//! We could implement a protocol to allow multi-block, multi-page collaborative submissions from
//! different validators, but it is not trivial. Moreover, recall that the unsigned phase is merely
//! a backup and we should primarily rely on offchain staking miners to fulfill this role during
//! `Phase::Signed`.
//!
//! ## Future Idea: Multi-Page unsigned submission
//!
//! the following is the idea of how to implement multi-page unsigned, which we don't have.
//!
//! All validators will run their miners and compute the full paginated solution. They submit all
//! pages as individual unsigned transactions to their local tx-pool.
//!
//! Upon validation, if any page is now present the corresponding transaction is dropped.
//!
//! At each block, the first page that may be valid is included as a high priority operational
//! transaction. This page is validated on the fly to be correct. Since this transaction is sourced
//! from a validator, we can panic if they submit an invalid transaction.
//!
//! Then, once the final page is submitted, some extra checks are done, as explained in
//! [`crate::verifier`]:
//!
//! 1. bounds
//! 2. total score
//!
//! These checks might still fail. If they do, the solution is dropped. At this point, we don't know
//! which validator may have submitted a slightly-faulty solution.
//!
//! In order to prevent this, the transaction validation process always includes a check to ensure
//! all of the previous pages that have been submitted match what the local validator has computed.
//! If they match, the validator knows that they are putting skin in a game that is valid.
//!
//! If any bad paged are detected, the next validator can bail. This process means:
//!
//! * As long as all validators are honest, and run the same miner code, a correct solution is
//! found.
//! * As little as one malicious validator can stall the process, but no one is accidentally
//! slashed, and no panic happens.
//!
//! Alternatively, we can keep track of submitters, and report a slash if it occurs. Or, if
//! the signed process is bullet-proof, we can be okay with the status quo.
/// Export weights
pub use crate::weights::traits::pezpallet_election_provider_multi_block_unsigned::*;
/// Exports of this pallet
pub use pallet::*;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
/// The miner.
pub mod miner;
#[pezframe_support::pallet]
mod pallet {
use super::WeightInfo;
use crate::{
types::*,
unsigned::miner::{self},
verifier::Verifier,
CommonError,
};
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::{offchain::CreateBare, pezpallet_prelude::*};
use pezsp_runtime::traits::SaturatedConversion;
use pezsp_std::prelude::*;
/// convert a [`crate::CommonError`] to a custom InvalidTransaction with the inner code being
/// the index of the variant.
fn base_error_to_invalid(error: CommonError) -> InvalidTransaction {
let index = error.encode().pop().unwrap_or(0);
InvalidTransaction::Custom(index)
}
pub(crate) type UnsignedWeightsOf<T> = <T as Config>::WeightInfo;
#[pallet::config]
#[pallet::disable_pezframe_system_supertrait_check]
pub trait Config: crate::Config + CreateBare<Call<Self>> {
/// The repeat threshold of the offchain worker.
///
/// For example, if it is `5`, that means that at least 5 blocks will elapse between
/// attempts to submit the worker's solution.
type OffchainRepeat: Get<BlockNumberFor<Self>>;
/// The solver used in hte offchain worker miner
type OffchainSolver: pezframe_election_provider_support::NposSolver<
AccountId = Self::AccountId,
>;
/// Whether the offchain worker miner would attempt to store the solutions in a local
/// database and reuse then. If set to `false`, it will try and re-mine solutions every
/// time.
type OffchainStorage: Get<bool>;
/// The priority of the unsigned transaction submitted in the unsigned-phase
type MinerTxPriority: Get<TransactionPriority>;
/// The number of pages that the offchain miner will try and submit.
type MinerPages: Get<PageIndex>;
/// Runtime weight information of this pallet.
type WeightInfo: WeightInfo;
}
#[pallet::pallet]
pub struct Pallet<T>(PhantomData<T>);
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Submit an unsigned solution.
///
/// This works very much like an inherent, as only the validators are permitted to submit
/// anything. By default validators will compute this call in their `offchain_worker` hook
/// and try and submit it back.
///
/// This is different from signed page submission mainly in that the solution page is
/// verified on the fly.
///
/// The `paged_solution` may contain at most [`Config::MinerPages`] pages. They are
/// interpreted as msp -> lsp, as per [`crate::Pallet::msp_range_for`].
///
/// For example, if `Pages = 4`, and `MinerPages = 2`, our full snapshot range would be [0,
/// 1, 2, 3], with 3 being msp. But, in this case, then the `paged_raw_solution.pages` is
/// expected to correspond to `[snapshot(2), snapshot(3)]`.
#[pallet::weight((UnsignedWeightsOf::<T>::submit_unsigned(), DispatchClass::Operational))]
#[pallet::call_index(0)]
pub fn submit_unsigned(
origin: OriginFor<T>,
paged_solution: Box<PagedRawSolution<T::MinerConfig>>,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
let error_message = "Invalid unsigned submission must produce invalid block and \
deprive validator from their authoring reward.";
// phase, round, claimed score, page-count and hash are checked in pre-dispatch. we
// don't check them here anymore.
debug_assert!(Self::validate_unsigned_checks(&paged_solution).is_ok());
let claimed_score = paged_solution.score;
// we select the most significant pages, based on `T::MinerPages`.
let page_indices = crate::Pallet::<T>::msp_range_for(T::MinerPages::get() as usize);
<T::Verifier as Verifier>::verify_synchronous_multi(
paged_solution.solution_pages,
page_indices,
claimed_score,
)
.expect(error_message);
Ok(None.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::submit_unsigned { paged_solution, .. } = call {
match source {
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ },
_ => return InvalidTransaction::Call.into(),
}
let _ = Self::validate_unsigned_checks(paged_solution.as_ref())
.map_err(|err| {
sublog!(
debug,
"unsigned",
"unsigned transaction validation failed due to {:?}",
err
);
err
})
.map_err(base_error_to_invalid)?;
ValidTransaction::with_tag_prefix("OffchainElection")
// The higher the score.minimal_stake, the better a paged_solution is.
.priority(
T::MinerTxPriority::get()
.saturating_add(paged_solution.score.minimal_stake.saturated_into()),
)
// Used to deduplicate unsigned solutions: each validator should produce one
// paged_solution per round at most, and solutions are not propagate.
.and_provides(paged_solution.round)
// Transaction should stay in the pool for the duration of the unsigned phase.
.longevity(T::UnsignedPhase::get().saturated_into::<u64>())
// We don't propagate this. This can never be validated at a remote node.
.propagate(false)
.build()
} else {
InvalidTransaction::Call.into()
}
}
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
if let Call::submit_unsigned { paged_solution, .. } = call {
Self::validate_unsigned_checks(paged_solution.as_ref())
.map_err(base_error_to_invalid)
.map_err(Into::into)
} else {
Err(InvalidTransaction::Call.into())
}
}
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn integrity_test() {
assert!(
UnsignedWeightsOf::<T>::submit_unsigned().all_lte(T::BlockWeights::get().max_block),
"weight of `submit_unsigned` is too high"
);
assert!(
<T as Config>::MinerPages::get() as usize <=
<T as crate::Config>::Pages::get() as usize,
"number of pages in the unsigned phase is too high"
);
}
#[cfg(feature = "try-runtime")]
fn try_state(now: BlockNumberFor<T>) -> Result<(), pezsp_runtime::TryRuntimeError> {
Self::do_try_state(now)
}
fn offchain_worker(now: BlockNumberFor<T>) {
use pezsp_runtime::offchain::storage_lock::{BlockAndTime, StorageLock};
// Create a lock with the maximum deadline of number of blocks in the unsigned phase.
// This should only come useful in an **abrupt** termination of execution, otherwise the
// guard will be dropped upon successful execution.
let mut lock =
StorageLock::<BlockAndTime<pezframe_system::Pallet<T>>>::with_block_deadline(
miner::OffchainWorkerMiner::<T>::OFFCHAIN_LOCK,
T::UnsignedPhase::get().saturated_into(),
);
match lock.try_lock() {
Ok(_guard) => {
Self::do_synchronized_offchain_worker(now);
},
Err(deadline) => {
sublog!(
trace,
"unsigned",
"offchain worker lock not released, deadline is {:?}",
deadline
);
},
};
}
}
impl<T: Config> Pallet<T> {
/// Internal logic of the offchain worker, to be executed only when the offchain lock is
/// acquired with success.
fn do_synchronized_offchain_worker(now: BlockNumberFor<T>) {
use miner::OffchainWorkerMiner;
let current_phase = crate::Pallet::<T>::current_phase();
sublog!(
trace,
"unsigned",
"lock for offchain worker acquired. Phase = {:?}",
current_phase
);
// do the repeat frequency check just one, if we are in unsigned phase.
if current_phase.is_unsigned() {
if let Err(reason) = OffchainWorkerMiner::<T>::ensure_offchain_repeat_frequency(now)
{
sublog!(
debug,
"unsigned",
"offchain worker repeat frequency check failed: {:?}",
reason
);
return;
}
}
if current_phase.is_unsigned_opened_now() {
// Mine a new solution, (maybe) cache it, and attempt to submit it
let initial_output = if T::OffchainStorage::get() {
OffchainWorkerMiner::<T>::mine_check_maybe_save_submit(true)
} else {
OffchainWorkerMiner::<T>::mine_check_maybe_save_submit(false)
};
sublog!(debug, "unsigned", "initial offchain worker output: {:?}", initial_output);
} else if current_phase.is_unsigned() {
// Maybe resubmit the cached solution, else re-compute.
let resubmit_output = if T::OffchainStorage::get() {
OffchainWorkerMiner::<T>::restore_or_compute_then_maybe_submit()
} else {
OffchainWorkerMiner::<T>::mine_check_maybe_save_submit(false)
};
sublog!(debug, "unsigned", "later offchain worker output: {:?}", resubmit_output);
};
}
/// The checks that should happen in the `ValidateUnsigned`'s `pre_dispatch` and
/// `validate_unsigned` functions.
///
/// These check both for snapshot independent checks, and some checks that are specific to
/// the unsigned phase.
pub(crate) fn validate_unsigned_checks(
paged_solution: &PagedRawSolution<T::MinerConfig>,
) -> Result<(), CommonError> {
Self::unsigned_specific_checks(paged_solution)
.and(crate::Pallet::<T>::snapshot_independent_checks(paged_solution, None))
.map_err(Into::into)
}
/// The checks that are specific to the (this) unsigned pallet.
///
/// ensure solution has the correct phase, and it has only 1 page.
pub fn unsigned_specific_checks(
paged_solution: &PagedRawSolution<T::MinerConfig>,
) -> Result<(), CommonError> {
ensure!(
crate::Pallet::<T>::current_phase().is_unsigned(),
CommonError::EarlySubmission
);
ensure!(
paged_solution.solution_pages.len() == T::MinerPages::get() as usize,
CommonError::WrongPageCount
);
ensure!(
paged_solution.solution_pages.len() <= <T as crate::Config>::Pages::get() as usize,
CommonError::WrongPageCount
);
Ok(())
}
#[cfg(any(test, feature = "runtime-benchmarks", feature = "try-runtime"))]
pub(crate) fn do_try_state(
_now: BlockNumberFor<T>,
) -> Result<(), pezsp_runtime::TryRuntimeError> {
Ok(())
}
}
}
#[cfg(test)]
mod validate_unsigned {
use pezframe_election_provider_support::Support;
use pezframe_support::{
pezpallet_prelude::InvalidTransaction,
unsigned::{TransactionSource, TransactionValidityError, ValidateUnsigned},
};
use super::Call;
use crate::{mock::*, types::*, verifier::Verifier};
#[test]
fn retracts_weak_score_accepts_better() {
ExtBuilder::unsigned().build_and_execute(|| {
roll_to_snapshot_created();
let base_minimal_stake = 55;
let solution = mine_full_solution().unwrap();
load_mock_signed_and_start(solution.clone());
roll_to_full_verification();
// Some good solution is queued now.
assert_eq!(
<VerifierPallet as Verifier>::queued_score(),
Some(ElectionScore {
minimal_stake: base_minimal_stake,
sum_stake: 130,
sum_stake_squared: 8650
})
);
roll_to_unsigned_open();
// This is just worse.
let attempt = fake_solution(ElectionScore {
minimal_stake: base_minimal_stake - 1,
..Default::default()
});
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(2)),
);
// This is better, but the number of winners is incorrect.
let attempt = fake_solution(ElectionScore {
minimal_stake: base_minimal_stake + 1,
..Default::default()
});
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(4)),
);
// Note that we now have to use a solution with 2 winners, just to pass all of the
// snapshot independent checks.
let mut paged = raw_paged_from_supports(
vec![vec![
(40, Support { total: 10, voters: vec![(3, 5)] }),
(30, Support { total: 10, voters: vec![(3, 5)] }),
]],
0,
);
paged.score =
ElectionScore { minimal_stake: base_minimal_stake + 1, ..Default::default() };
let call = Call::submit_unsigned { paged_solution: Box::new(paged) };
assert!(UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).is_ok());
})
}
#[test]
fn retracts_wrong_round() {
ExtBuilder::unsigned().build_and_execute(|| {
roll_to_unsigned_open();
let mut attempt =
fake_solution(ElectionScore { minimal_stake: 5, ..Default::default() });
attempt.round += 1;
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
// WrongRound is index 1
TransactionValidityError::Invalid(InvalidTransaction::Custom(1)),
);
})
}
#[test]
fn retracts_too_many_pages_unsigned() {
ExtBuilder::unsigned().build_and_execute(|| {
// NOTE: unsigned solutions should have just 1 page, regardless of the configured
// page count.
roll_to_unsigned_open();
let attempt = mine_full_solution().unwrap();
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
// WrongPageCount is index 3
TransactionValidityError::Invalid(InvalidTransaction::Custom(3)),
);
let attempt = mine_solution(2).unwrap();
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(3)),
);
let attempt = mine_solution(1).unwrap();
let call = Call::submit_unsigned { paged_solution: Box::new(attempt) };
assert!(UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).is_ok(),);
})
}
#[test]
fn retracts_wrong_winner_count() {
ExtBuilder::unsigned().desired_targets(2).build_and_execute(|| {
roll_to_unsigned_open();
let paged = raw_paged_from_supports(
vec![vec![(40, Support { total: 10, voters: vec![(3, 10)] })]],
0,
);
let call = Call::submit_unsigned { paged_solution: Box::new(paged) };
assert_eq!(
UnsignedPallet::validate_unsigned(TransactionSource::Local, &call).unwrap_err(),
// WrongWinnerCount is index 4
TransactionValidityError::Invalid(InvalidTransaction::Custom(4)),
);
});
}
#[test]
fn retracts_wrong_phase() {
ExtBuilder::unsigned().signed_phase(5, 6).build_and_execute(|| {
let solution = raw_paged_solution_low_score();
let call = Call::submit_unsigned { paged_solution: Box::new(solution.clone()) };
// initial
assert_eq!(MultiBlock::current_phase(), Phase::Off);
assert!(matches!(
<UnsignedPallet as ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call
)
.unwrap_err(),
// because EarlySubmission is index 0.
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
));
assert!(matches!(
<UnsignedPallet as ValidateUnsigned>::pre_dispatch(&call).unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
));
// signed
roll_to_signed_open();
assert!(MultiBlock::current_phase().is_signed());
assert!(matches!(
<UnsignedPallet as ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call
)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
));
assert!(matches!(
<UnsignedPallet as ValidateUnsigned>::pre_dispatch(&call).unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Custom(0))
));
// unsigned
roll_to_unsigned_open();
assert!(MultiBlock::current_phase().is_unsigned());
assert_ok!(<UnsignedPallet as ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call
));
assert_ok!(<UnsignedPallet as ValidateUnsigned>::pre_dispatch(&call));
})
}
#[test]
fn priority_is_set() {
ExtBuilder::unsigned()
.miner_tx_priority(20)
.desired_targets(0)
.build_and_execute(|| {
roll_to_unsigned_open();
assert!(MultiBlock::current_phase().is_unsigned());
let solution =
fake_solution(ElectionScore { minimal_stake: 5, ..Default::default() });
let call = Call::submit_unsigned { paged_solution: Box::new(solution.clone()) };
assert_eq!(
<UnsignedPallet as ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call
)
.unwrap()
.priority,
25
);
})
}
}
#[cfg(test)]
mod call {
use crate::{mock::*, verifier::Verifier, Snapshot};
#[test]
fn unsigned_submission_e2e() {
let (mut ext, pool) = ExtBuilder::unsigned().build_offchainify();
ext.execute_with_sanity_checks(|| {
roll_to_unsigned_open();
// snapshot is created..
assert_full_snapshot();
// ..txpool is empty..
assert_eq!(pool.read().transactions.len(), 0);
// ..but nothing queued.
assert_eq!(<VerifierPallet as Verifier>::queued_score(), None);
// now the OCW should submit something.
roll_next_with_ocw(Some(pool.clone()));
assert_eq!(pool.read().transactions.len(), 1);
assert_eq!(<VerifierPallet as Verifier>::queued_score(), None);
// and now it should be applied.
roll_next_with_ocw(Some(pool.clone()));
assert_eq!(pool.read().transactions.len(), 0);
assert!(matches!(<VerifierPallet as Verifier>::queued_score(), Some(_)));
})
}
#[test]
#[should_panic(
expected = "Invalid unsigned submission must produce invalid block and deprive validator from their authoring reward."
)]
fn unfeasible_solution_panics() {
let (mut ext, pool) = ExtBuilder::unsigned().build_offchainify();
ext.execute_with_sanity_checks(|| {
roll_to_unsigned_open();
// snapshot is created..
assert_full_snapshot();
// ..txpool is empty..
assert_eq!(pool.read().transactions.len(), 0);
// ..but nothing queued.
assert_eq!(<VerifierPallet as Verifier>::queued_score(), None);
// now the OCW should submit something.
roll_next_with_ocw(Some(pool.clone()));
assert_eq!(pool.read().transactions.len(), 1);
assert_eq!(<VerifierPallet as Verifier>::queued_score(), None);
// now we change the snapshot -- this should ensure that the solution becomes invalid.
// Note that we don't change the known fingerprint of the solution.
Snapshot::<Runtime>::remove_target(2);
// and now it should be applied.
roll_next_with_ocw(Some(pool.clone()));
assert_eq!(pool.read().transactions.len(), 0);
assert!(matches!(<VerifierPallet as Verifier>::queued_score(), Some(_)));
})
}
}