sp_runtime/traits/vers_tx_ext/
mod.rs1use crate::{
21 scale_info::StaticTypeInfo,
22 traits::{
23 DispatchInfoOf, DispatchOriginOf, Dispatchable, PostDispatchInfoOf,
24 TransactionExtensionMetadata,
25 },
26 transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction},
27};
28use alloc::{collections::BTreeMap, vec::Vec};
29use codec::Encode;
30use core::fmt::Debug;
31use sp_weights::Weight;
32
33mod at_vers;
34mod invalid;
35mod multi;
36mod variant;
37pub use at_vers::PipelineAtVers;
38pub use invalid::InvalidVersion;
39pub use multi::MultiVersion;
40pub use variant::ExtensionVariant;
41
42pub trait PipelineVersion {
49 fn version(&self) -> u8;
51}
52
53pub trait Pipeline<Call: Dispatchable>:
59 Encode
60 + DecodeWithVersion
61 + DecodeWithVersionWithMemTracking
62 + Debug
63 + StaticTypeInfo
64 + Send
65 + Sync
66 + Clone
67 + PipelineVersion
68{
69 fn build_metadata(builder: &mut PipelineMetadataBuilder);
71
72 fn validate_only(
74 &self,
75 origin: DispatchOriginOf<Call>,
76 call: &Call,
77 info: &DispatchInfoOf<Call>,
78 len: usize,
79 source: TransactionSource,
80 ) -> Result<ValidTransaction, TransactionValidityError>;
81
82 fn dispatch_transaction(
84 self,
85 origin: DispatchOriginOf<Call>,
86 call: Call,
87 info: &DispatchInfoOf<Call>,
88 len: usize,
89 ) -> crate::ApplyExtrinsicResultWithInfo<PostDispatchInfoOf<Call>>;
90
91 fn weight(&self, call: &Call) -> Weight;
94}
95
96pub trait DecodeWithVersion: Sized {
98 fn decode_with_version<I: codec::Input>(
100 extension_version: u8,
101 input: &mut I,
102 ) -> Result<Self, codec::Error>;
103}
104
105pub trait DecodeWithVersionWithMemTracking: DecodeWithVersion {}
108
109pub struct PipelineMetadataBuilder {
111 pub by_version: BTreeMap<u8, Vec<u32>>,
114 pub in_versions: Vec<TransactionExtensionMetadata>,
116}
117
118impl PipelineMetadataBuilder {
119 pub fn new() -> Self {
121 Self { by_version: BTreeMap::new(), in_versions: Vec::new() }
122 }
123
124 pub fn push_versioned_extension(
126 &mut self,
127 ext_version: u8,
128 ext_items: Vec<TransactionExtensionMetadata>,
129 ) {
130 if self.by_version.contains_key(&ext_version) {
131 log::warn!("Duplicate definition for transaction extension version: {}", ext_version);
132 debug_assert!(
133 false,
134 "Duplicate definition for transaction extension version: {ext_version}",
135 );
136 return;
137 }
138
139 let mut ext_item_indices = Vec::with_capacity(ext_items.len());
140 for ext_item in ext_items {
141 let ext_item_index =
142 match self.in_versions.iter().position(|ext| ext.identifier == ext_item.identifier)
143 {
144 Some(index) => index,
145 None => {
146 self.in_versions.push(ext_item);
147 self.in_versions.len() - 1
148 },
149 };
150 ext_item_indices.push(ext_item_index as u32);
151 }
152 self.by_version.insert(ext_version, ext_item_indices);
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159 use scale_info::meta_type;
160
161 #[test]
162 fn test_metadata_builder() {
163 let mut builder = PipelineMetadataBuilder::new();
164
165 let ext_item_a = TransactionExtensionMetadata {
166 identifier: "ExtensionA",
167 ty: meta_type::<u64>(),
168 implicit: meta_type::<(u32, u8)>(),
169 };
170 let ext_item_b = TransactionExtensionMetadata {
171 identifier: "ExtensionB",
172 ty: meta_type::<bool>(),
173 implicit: meta_type::<String>(),
174 };
175
176 builder.push_versioned_extension(1, vec![ext_item_a.clone()]);
178 builder.push_versioned_extension(2, vec![ext_item_b.clone(), ext_item_a.clone()]);
180
181 assert_eq!(builder.by_version.len(), 2);
187
188 {
190 let v1_indices = builder.by_version.get(&1).expect("Version 1 must be present");
191 assert_eq!(v1_indices.len(), 1, "Version 1 should have exactly one extension");
192 assert_eq!(builder.in_versions[v1_indices[0] as usize].identifier, "ExtensionA");
194 }
195
196 {
198 let v2_indices = builder.by_version.get(&2).expect("Version 2 must be present");
199 assert_eq!(v2_indices.len(), 2, "Version 2 should have exactly two extensions");
200 assert_eq!(builder.in_versions[v2_indices[0] as usize].identifier, "ExtensionB");
206 assert_eq!(builder.in_versions[v2_indices[1] as usize].identifier, "ExtensionA");
208 }
209
210 assert_eq!(builder.in_versions.len(), 2);
212 }
213}