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,139 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: MIT-0
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use core::{fmt, marker::PhantomData};
use codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_support::{pezpallet_prelude::TransactionSource, traits::OriginTrait, Parameter};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default,
traits::{
DispatchInfoOf, DispatchOriginOf, IdentifyAccount, TransactionExtension, ValidateResult,
Verify,
},
transaction_validity::{InvalidTransaction, ValidTransaction},
};
use crate::pezpallet_coownership::{Config, Origin};
/// Helper struct to organize the data needed for signature verification of both parties involved.
#[derive(Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
pub struct AuthCredentials<Signer, Signature> {
first: (Signer, Signature),
second: (Signer, Signature),
}
/// Extension that, if activated by providing a pair of signers and signatures, will authorize a
/// coowner origin of the two signers. Both signers have to construct their signatures on all of the
/// data that follows this extension in the `TransactionExtension` pipeline, their implications and
/// the call. Essentially re-sign the transaction from this point onwards in the pipeline by using
/// the `inherited_implication`, as shown below.
#[derive(Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct AuthorizeCoownership<T, Signer, Signature> {
inner: Option<AuthCredentials<Signer, Signature>>,
_phantom: PhantomData<T>,
}
impl<T: Config, Signer, Signature> Default for AuthorizeCoownership<T, Signer, Signature> {
fn default() -> Self {
Self { inner: None, _phantom: Default::default() }
}
}
impl<T: Config, Signer, Signature> AuthorizeCoownership<T, Signer, Signature> {
/// Creates an active extension that will try to authorize the coownership origin.
pub fn new(first: (Signer, Signature), second: (Signer, Signature)) -> Self {
Self { inner: Some(AuthCredentials { first, second }), _phantom: Default::default() }
}
}
impl<T: Config, Signer, Signature> fmt::Debug for AuthorizeCoownership<T, Signer, Signature> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "AuthorizeCoownership")
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync, Signer, Signature> TransactionExtension<T::RuntimeCall>
for AuthorizeCoownership<T, Signer, Signature>
where
Signer: IdentifyAccount<AccountId = T::AccountId> + Parameter + Send + Sync + 'static,
Signature: Verify<Signer = Signer> + Parameter + Send + Sync + 'static,
{
const IDENTIFIER: &'static str = "AuthorizeCoownership";
type Implicit = ();
type Val = ();
type Pre = ();
fn validate(
&self,
mut origin: DispatchOriginOf<T::RuntimeCall>,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
inherited_implication: &impl codec::Encode,
_source: TransactionSource,
) -> ValidateResult<Self::Val, T::RuntimeCall> {
// If the extension is inactive, just move on in the pipeline.
let Some(auth) = &self.inner else {
return Ok((ValidTransaction::default(), (), origin));
};
let first_account = auth.first.0.clone().into_account();
let second_account = auth.second.0.clone().into_account();
// Construct the payload to sign using the `inherited_implication`.
let msg = inherited_implication.using_encoded(pezsp_io::hashing::blake2_256);
// Both parties' signatures must be correct for the origin to be authorized.
// In a prod environment, we're just return a `InvalidTransaction::BadProof` if the
// signature isn't valid, but we return these custom errors to be able to assert them in
// tests.
if !auth.first.1.verify(&msg[..], &first_account) {
Err(InvalidTransaction::Custom(100))?
}
if !auth.second.1.verify(&msg[..], &second_account) {
Err(InvalidTransaction::Custom(200))?
}
// Construct a `pezpallet_coownership::Origin`.
let local_origin = Origin::Coowners(first_account, second_account);
// Turn it into a local `PalletsOrigin`.
let local_origin = <T as Config>::PalletsOrigin::from(local_origin);
// Then finally into a pallet `RuntimeOrigin`.
let local_origin = <T as Config>::RuntimeOrigin::from(local_origin);
// Which the `set_caller_from` function will convert into the overarching `RuntimeOrigin`
// created by `construct_runtime!`.
origin.set_caller_from(local_origin);
// Make sure to return the new origin.
Ok((ValidTransaction::default(), (), origin))
}
// We're not doing any special logic in `TransactionExtension::prepare`, so just impl a default.
impl_tx_ext_default!(T::RuntimeCall; weight prepare);
}
@@ -0,0 +1,174 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: MIT-0
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! # Authorization Transaction Extension Example Pallet
//!
//! **This pallet serves as an example and is not meant to be used in production.**
//!
//! FRAME Transaction Extension reference implementation, origin mutation, origin authorization and
//! integration in a `TransactionExtension` pipeline.
//!
//! The [TransactionExtension](pezsp_runtime::traits::TransactionExtension) used in this example is
//! [AuthorizeCoownership](extensions::AuthorizeCoownership). If activated, the extension will
//! authorize 2 signers as coowners, with a [coowner origin](pezpallet_coownership::Origin) specific to
//! the [coownership example pallet](pezpallet_coownership), by validating a signature of the rest of
//! the transaction from each party. This means any extensions after ours in the pipeline, their
//! implicits and the actual call. The extension pipeline used in our example checks the genesis
//! hash, transaction version and mortality of the transaction after the `AuthorizeCoownership` runs
//! as we want these transactions to run regardless of what origin passes through them and/or we
//! want their implicit data in any signature authorization happening earlier in the pipeline.
//!
//! In this example, aside from the [AuthorizeCoownership](extensions::AuthorizeCoownership)
//! extension, we use the following pallets:
//! - [pezpallet_coownership] - provides a coowner origin and the functionality to authorize it.
//! - [pezpallet_assets] - a dummy asset pallet that tracks assets, identified by an
//! [AssetId](pezpallet_assets::AssetId), and their respective owners, which can be either an
//! [account](pezpallet_assets::Owner::Single) or a [pair of owners](pezpallet_assets::Owner::Double).
//!
//! Assets are created in [pezpallet_assets] using the
//! [create_asset](pezpallet_assets::Call::create_asset) call, which accepts traditionally signed
//! origins (a single account) or coowner origins, authorized through the
//! [CoownerOrigin](pezpallet_assets::Config::CoownerOrigin) type.
//!
//! ### Example runtime setup
#![doc = docify::embed!("src/mock.rs", example_runtime)]
//!
//! ### Example usage
#![doc = docify::embed!("src/tests.rs", create_coowned_asset_works)]
//!
//! This example does not focus on any pallet logic or syntax, but rather on `TransactionExtension`
//! functionality. The pallets used are just skeletons to provide storage state and custom origin
//! choices and requirements, as shown in the examples. Any weight and/or
//! transaction fee is out of scope for this example.
#![cfg_attr(not(feature = "std"), no_std)]
pub mod extensions;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
extern crate alloc;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
#[pezframe_support::pallet(dev_mode)]
pub mod pezpallet_coownership {
use super::*;
use pezframe_support::traits::OriginTrait;
#[pallet::config]
pub trait Config: pezframe_system::Config {
/// The aggregated origin which the dispatch will take.
type RuntimeOrigin: OriginTrait<PalletsOrigin = Self::PalletsOrigin>
+ From<Self::PalletsOrigin>
+ IsType<<Self as pezframe_system::Config>::RuntimeOrigin>;
/// The caller origin, overarching type of all pallets origins.
type PalletsOrigin: From<Origin<Self>> + TryInto<Origin<Self>, Error = Self::PalletsOrigin>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
/// Origin that this pallet can authorize. For the purposes of this example, it's just two
/// accounts that own something together.
#[pallet::origin]
#[derive(
Clone,
PartialEq,
Eq,
RuntimeDebug,
Encode,
Decode,
DecodeWithMemTracking,
MaxEncodedLen,
TypeInfo,
)]
pub enum Origin<T: Config> {
Coowners(T::AccountId, T::AccountId),
}
}
#[pezframe_support::pallet(dev_mode)]
pub mod pezpallet_assets {
use super::*;
pub type AssetId = u32;
/// Type that describes possible owners of a particular asset.
#[derive(Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)]
pub enum Owner<AccountId> {
Single(AccountId),
Double(AccountId, AccountId),
}
#[pallet::config]
pub trait Config: pezframe_system::Config {
/// Type that can authorize an account pair coowner origin.
type CoownerOrigin: EnsureOrigin<
Self::RuntimeOrigin,
Success = (Self::AccountId, Self::AccountId),
>;
}
/// Map that holds the owner information for each asset it manages.
#[pallet::storage]
pub type AssetOwners<T> =
StorageMap<_, Blake2_128Concat, AssetId, Owner<<T as pezframe_system::Config>::AccountId>>;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::error]
pub enum Error<T> {
/// Asset already exists.
AlreadyExists,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Simple call that just creates an asset with a specific `AssetId`. This call will fail if
/// there is already an asset with the same `AssetId`.
///
/// The origin is either a single account (traditionally signed origin) or a coowner origin.
#[pallet::call_index(0)]
pub fn create_asset(origin: OriginFor<T>, asset_id: AssetId) -> DispatchResult {
let owner: Owner<T::AccountId> = match T::CoownerOrigin::try_origin(origin) {
Ok((first, second)) => Owner::Double(first, second),
Err(origin) => ensure_signed(origin).map(|account| Owner::Single(account))?,
};
AssetOwners::<T>::try_mutate(asset_id, |maybe_owner| {
if maybe_owner.is_some() {
return Err(Error::<T>::AlreadyExists);
}
*maybe_owner = Some(owner);
Ok(())
})?;
Ok(())
}
}
}
@@ -0,0 +1,134 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: MIT-0
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
use crate::*;
pub(crate) use example_runtime::*;
use extensions::AuthorizeCoownership;
use pezframe_support::derive_impl;
use pezframe_system::{CheckEra, CheckGenesis, CheckNonce, CheckTxVersion};
use pezpallet_verify_signature::VerifySignature;
use pezsp_runtime::{
generic,
traits::{BlakeTwo256, IdentifyAccount, IdentityLookup, Verify},
BuildStorage, MultiSignature, MultiSigner,
};
#[docify::export]
mod example_runtime {
use super::*;
/// Our `TransactionExtension` fit for general transactions.
pub type TxExtension = (
// Validate the signature of regular account transactions (substitutes the old signed
// transaction).
VerifySignature<Runtime>,
// Nonce check (and increment) for the caller.
CheckNonce<Runtime>,
// If activated, will mutate the origin to a `pezpallet_coownership` origin of 2 accounts that
// own something.
AuthorizeCoownership<Runtime, MultiSigner, MultiSignature>,
// Some other extensions that we want to run for every possible origin and we want captured
// in any and all signature and authorization schemes (such as the traditional account
// signature or the double signature in `pezpallet_coownership`).
CheckGenesis<Runtime>,
CheckTxVersion<Runtime>,
CheckEra<Runtime>,
);
/// Convenience type to more easily construct the signature to be signed in case
/// `AuthorizeCoownership` is activated.
pub type InnerTxExtension = (CheckGenesis<Runtime>, CheckTxVersion<Runtime>, CheckEra<Runtime>);
pub type UncheckedExtrinsic =
generic::UncheckedExtrinsic<AccountId, RuntimeCall, Signature, TxExtension>;
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub type Signature = MultiSignature;
pub type BlockNumber = u32;
// For testing the pallet, we construct a mock runtime.
pezframe_support::construct_runtime!(
pub enum Runtime
{
System: pezframe_system,
VerifySignaturePallet: pezpallet_verify_signature,
Assets: pezpallet_assets,
Coownership: pezpallet_coownership,
}
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Runtime {
type AccountId = AccountId;
type Block = Block;
type Lookup = IdentityLookup<Self::AccountId>;
}
impl pezpallet_verify_signature::Config for Runtime {
type Signature = MultiSignature;
type AccountIdentifier = MultiSigner;
type WeightInfo = ();
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper = ();
}
/// Type that enables any pallet to ask for a coowner origin.
pub struct EnsureCoowner;
impl EnsureOrigin<RuntimeOrigin> for EnsureCoowner {
type Success = (AccountId, AccountId);
fn try_origin(o: RuntimeOrigin) -> Result<Self::Success, RuntimeOrigin> {
match o.clone().into() {
Ok(pezpallet_coownership::Origin::<Runtime>::Coowners(first, second)) =>
Ok((first, second)),
_ => Err(o),
}
}
#[cfg(feature = "runtime-benchmarks")]
fn try_successful_origin() -> Result<RuntimeOrigin, ()> {
unimplemented!()
}
}
impl pezpallet_assets::Config for Runtime {
type CoownerOrigin = EnsureCoowner;
}
impl pezpallet_coownership::Config for Runtime {
type RuntimeOrigin = RuntimeOrigin;
type PalletsOrigin = OriginCaller;
}
}
// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
pub fn new_test_ext() -> pezsp_io::TestExternalities {
let t = RuntimeGenesisConfig {
// We use default for brevity, but you can configure as desired if needed.
system: Default::default(),
}
.build_storage()
.unwrap();
t.into()
}
@@ -0,0 +1,280 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: MIT-0
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//! Tests for pezpallet-example-authorization-tx-extension.
use codec::Encode;
use pezframe_support::{
assert_noop,
dispatch::GetDispatchInfo,
pezpallet_prelude::{InvalidTransaction, TransactionValidityError},
};
use pezpallet_verify_signature::VerifySignature;
use pezsp_keyring::Sr25519Keyring;
use pezsp_runtime::{
generic::ExtensionVersion,
traits::{Applyable, Checkable, IdentityLookup, TransactionExtension},
MultiSignature, MultiSigner,
};
use crate::{extensions::AuthorizeCoownership, mock::*, pezpallet_assets};
#[test]
fn create_asset_works() {
new_test_ext().execute_with(|| {
let alice_keyring = Sr25519Keyring::Alice;
let alice_account = AccountId::from(alice_keyring.public());
// Simple call to create asset with Id `42`.
let create_asset_call =
RuntimeCall::Assets(pezpallet_assets::Call::create_asset { asset_id: 42 });
let ext_version: ExtensionVersion = 0;
// Create extension that will be used for dispatch.
let initial_nonce = 23;
let tx_ext = (
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::default(),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create the transaction signature, to be used in the top level `VerifyMultiSignature`
// extension.
let tx_sign = MultiSignature::Sr25519(
(&(ext_version, &create_asset_call), &tx_ext, tx_ext.implicit().unwrap())
.using_encoded(|e| alice_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Add the signature to the extension.
let tx_ext = (
VerifySignature::new_with_signature(tx_sign, alice_account.clone()),
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::default(),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create the transaction and we're ready for dispatch.
let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext);
// Check Extrinsic validity and apply it.
let uxt_info = uxt.get_dispatch_info();
let uxt_len = uxt.using_encoded(|e| e.len());
// Manually pay for Alice's nonce.
pezframe_system::Account::<Runtime>::mutate(&alice_account, |info| {
info.nonce = initial_nonce;
info.providers = 1;
});
// Check should pass.
let xt = <UncheckedExtrinsic as Checkable<IdentityLookup<AccountId>>>::check(
uxt,
&Default::default(),
)
.unwrap();
// Apply the extrinsic.
let res = xt.apply::<Runtime>(&uxt_info, uxt_len).unwrap();
// Asserting the results.
assert_eq!(pezframe_system::Account::<Runtime>::get(&alice_account).nonce, initial_nonce + 1);
assert_eq!(
pezpallet_assets::AssetOwners::<Runtime>::get(42),
Some(pezpallet_assets::Owner::<AccountId>::Single(alice_account))
);
assert!(res.is_ok());
});
}
#[docify::export]
#[test]
fn create_coowned_asset_works() {
new_test_ext().execute_with(|| {
let alice_keyring = Sr25519Keyring::Alice;
let bob_keyring = Sr25519Keyring::Bob;
let charlie_keyring = Sr25519Keyring::Charlie;
let alice_account = AccountId::from(alice_keyring.public());
let bob_account = AccountId::from(bob_keyring.public());
let charlie_account = AccountId::from(charlie_keyring.public());
// Simple call to create asset with Id `42`.
let create_asset_call =
RuntimeCall::Assets(pezpallet_assets::Call::create_asset { asset_id: 42 });
let ext_version: ExtensionVersion = 0;
// Create the inner transaction extension, to be signed by our coowners, Alice and Bob.
let inner_ext: InnerTxExtension = (
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create the payload Alice and Bob need to sign.
let inner_payload =
(&(ext_version, &create_asset_call), &inner_ext, inner_ext.implicit().unwrap());
// Create Alice's signature.
let alice_inner_sig = MultiSignature::Sr25519(
inner_payload.using_encoded(|e| alice_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Create Bob's signature.
let bob_inner_sig = MultiSignature::Sr25519(
inner_payload.using_encoded(|e| bob_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Create the transaction extension, to be signed by the submitter of the extrinsic, let's
// have it be Charlie.
let initial_nonce = 23;
let tx_ext = (
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::new(
(alice_keyring.into(), alice_inner_sig.clone()),
(bob_keyring.into(), bob_inner_sig.clone()),
),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create Charlie's transaction signature, to be used in the top level
// `VerifyMultiSignature` extension.
let tx_sign = MultiSignature::Sr25519(
(&(ext_version, &create_asset_call), &tx_ext, tx_ext.implicit().unwrap())
.using_encoded(|e| charlie_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Add the signature to the extension.
let tx_ext = (
VerifySignature::new_with_signature(tx_sign, charlie_account.clone()),
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::new(
(alice_keyring.into(), alice_inner_sig),
(bob_keyring.into(), bob_inner_sig),
),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create the transaction and we're ready for dispatch.
let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext);
// Check Extrinsic validity and apply it.
let uxt_info = uxt.get_dispatch_info();
let uxt_len = uxt.using_encoded(|e| e.len());
// Manually pay for Charlie's nonce.
pezframe_system::Account::<Runtime>::mutate(&charlie_account, |info| {
info.nonce = initial_nonce;
info.providers = 1;
});
// Check should pass.
let xt = <UncheckedExtrinsic as Checkable<IdentityLookup<AccountId>>>::check(
uxt,
&Default::default(),
)
.unwrap();
// Apply the extrinsic.
let res = xt.apply::<Runtime>(&uxt_info, uxt_len).unwrap();
// Asserting the results.
assert!(res.is_ok());
assert_eq!(pezframe_system::Account::<Runtime>::get(charlie_account).nonce, initial_nonce + 1);
assert_eq!(
pezpallet_assets::AssetOwners::<Runtime>::get(42),
Some(pezpallet_assets::Owner::<AccountId>::Double(alice_account, bob_account))
);
});
}
#[test]
fn inner_authorization_works() {
new_test_ext().execute_with(|| {
let alice_keyring = Sr25519Keyring::Alice;
let bob_keyring = Sr25519Keyring::Bob;
let charlie_keyring = Sr25519Keyring::Charlie;
let charlie_account = AccountId::from(charlie_keyring.public());
// Simple call to create asset with Id `42`.
let create_asset_call =
RuntimeCall::Assets(pezpallet_assets::Call::create_asset { asset_id: 42 });
let ext_version: ExtensionVersion = 0;
// Create the inner transaction extension, to be signed by our coowners, Alice and Bob. They
// are going to sign this transaction as a mortal one.
let inner_ext: InnerTxExtension = (
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
// Sign with mortal era check.
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::mortal(4, 0)),
);
// Create the payload Alice and Bob need to sign.
let inner_payload = (&create_asset_call, &inner_ext, inner_ext.implicit().unwrap());
// Create Alice's signature.
let alice_inner_sig = MultiSignature::Sr25519(
inner_payload.using_encoded(|e| alice_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Create Bob's signature.
let bob_inner_sig = MultiSignature::Sr25519(
inner_payload.using_encoded(|e| bob_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Create the transaction extension, to be signed by the submitter of the extrinsic, let's
// have it be Charlie.
let initial_nonce = 23;
let tx_ext = (
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::new(
(alice_keyring.into(), alice_inner_sig.clone()),
(bob_keyring.into(), bob_inner_sig.clone()),
),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
// Construct the transaction as immortal with a different era check.
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create Charlie's transaction signature, to be used in the top level
// `VerifyMultiSignature` extension.
let tx_sign = MultiSignature::Sr25519(
(&(ext_version, &create_asset_call), &tx_ext, tx_ext.implicit().unwrap())
.using_encoded(|e| charlie_keyring.sign(&pezsp_io::hashing::blake2_256(e))),
);
// Add the signature to the extension that Charlie signed.
let tx_ext = (
VerifySignature::new_with_signature(tx_sign, charlie_account.clone()),
pezframe_system::CheckNonce::<Runtime>::from(initial_nonce),
AuthorizeCoownership::<Runtime, MultiSigner, MultiSignature>::new(
(alice_keyring.into(), alice_inner_sig),
(bob_keyring.into(), bob_inner_sig),
),
pezframe_system::CheckGenesis::<Runtime>::new(),
pezframe_system::CheckTxVersion::<Runtime>::new(),
// Construct the transaction as immortal with a different era check.
pezframe_system::CheckEra::<Runtime>::from(pezsp_runtime::generic::Era::immortal()),
);
// Create the transaction and we're ready for dispatch.
let uxt = UncheckedExtrinsic::new_transaction(create_asset_call, tx_ext);
// Check Extrinsic validity and apply it.
let uxt_info = uxt.get_dispatch_info();
let uxt_len = uxt.using_encoded(|e| e.len());
// Manually pay for Charlie's nonce.
pezframe_system::Account::<Runtime>::mutate(charlie_account, |info| {
info.nonce = initial_nonce;
info.providers = 1;
});
// Check should pass.
let xt = <UncheckedExtrinsic as Checkable<IdentityLookup<AccountId>>>::check(
uxt,
&Default::default(),
)
.unwrap();
// The extrinsic should fail as the signature for the `AuthorizeCoownership` doesn't work
// for the provided payload with the changed transaction mortality.
assert_noop!(
xt.apply::<Runtime>(&uxt_info, uxt_len),
TransactionValidityError::Invalid(InvalidTransaction::Custom(100))
);
});
}