use crate::{
scale_info::StaticTypeInfo,
traits::{
DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf,
TransactionExtensionMetadata,
},
transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction},
};
use alloc::{collections::BTreeMap, vec::Vec};
use codec::Encode;
use core::fmt::Debug;
use sp_weights::Weight;
mod at_vers;
mod invalid;
mod multi;
mod variant;
pub use at_vers::PipelineAtVers;
pub use invalid::InvalidVersion;
pub use multi::MultiVersion;
pub use variant::ExtensionVariant;
pub trait PipelineVersion {
fn version(&self) -> u8;
}
pub trait Pipeline<Call: Dispatchable>:
Encode
+ DecodeWithVersion
+ DecodeWithVersionWithMemTracking
+ Debug
+ StaticTypeInfo
+ Send
+ Sync
+ Clone
+ PipelineVersion
{
fn build_metadata(builder: &mut PipelineMetadataBuilder);
fn validate_only(
&self,
origin: DispatchOriginOf<Call>,
call: &Call,
info: &DispatchInfoOf<Call>,
len: usize,
source: TransactionSource,
) -> Result<ValidTransaction, TransactionValidityError>;
fn dispatch_transaction(
self,
origin: DispatchOriginOf<Call>,
call: Call,
info: &DispatchInfoOf<Call>,
len: usize,
) -> crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Call>>;
fn weight(&self, call: &Call) -> Weight;
}
pub trait DecodeWithVersion: Sized {
fn decode_with_version<I: codec::Input>(
extension_version: u8,
input: &mut I,
) -> Result<Self, codec::Error>;
}
pub trait DecodeWithVersionWithMemTracking: DecodeWithVersion {}
pub struct PipelineMetadataBuilder {
pub by_version: BTreeMap<u8, Vec<u32>>,
pub in_versions: Vec<TransactionExtensionMetadata>,
}
impl PipelineMetadataBuilder {
pub fn new() -> Self {
Self { by_version: BTreeMap::new(), in_versions: Vec::new() }
}
pub fn push_versioned_extension(
&mut self,
ext_version: u8,
ext_items: Vec<TransactionExtensionMetadata>,
) {
if self.by_version.contains_key(&ext_version) {
log::warn!("Duplicate definition for transaction extension version: {}", ext_version);
debug_assert!(
false,
"Duplicate definition for transaction extension version: {ext_version}",
);
return;
}
let mut ext_item_indices = Vec::with_capacity(ext_items.len());
for ext_item in ext_items {
let ext_item_index =
match self.in_versions.iter().position(|ext| ext.identifier == ext_item.identifier)
{
Some(index) => index,
None => {
self.in_versions.push(ext_item);
self.in_versions.len() - 1
},
};
ext_item_indices.push(ext_item_index as u32);
}
self.by_version.insert(ext_version, ext_item_indices);
}
}
#[cfg(test)]
mod tests {
use super::*;
use scale_info::meta_type;
#[test]
fn test_metadata_builder() {
let mut builder = PipelineMetadataBuilder::new();
let ext_item_a = TransactionExtensionMetadata {
identifier: "ExtensionA",
ty: meta_type::<u64>(),
implicit: meta_type::<(u32, u8)>(),
};
let ext_item_b = TransactionExtensionMetadata {
identifier: "ExtensionB",
ty: meta_type::<bool>(),
implicit: meta_type::<String>(),
};
builder.push_versioned_extension(1, vec![ext_item_a.clone()]);
builder.push_versioned_extension(2, vec![ext_item_b.clone(), ext_item_a.clone()]);
assert_eq!(builder.by_version.len(), 2);
{
let v1_indices = builder.by_version.get(&1).expect("Version 1 must be present");
assert_eq!(v1_indices.len(), 1, "Version 1 should have exactly one extension");
assert_eq!(builder.in_versions[v1_indices[0] as usize].identifier, "ExtensionA");
}
{
let v2_indices = builder.by_version.get(&2).expect("Version 2 must be present");
assert_eq!(v2_indices.len(), 2, "Version 2 should have exactly two extensions");
assert_eq!(builder.in_versions[v2_indices[0] as usize].identifier, "ExtensionB");
assert_eq!(builder.in_versions[v2_indices[1] as usize].identifier, "ExtensionA");
}
assert_eq!(builder.in_versions.len(), 2);
}
}