1use super::transaction::{AlreadyEncoded, EXTRINSIC_FORMAT_VERSION, TransactionSigned};
2use crate::TransactionCall;
3#[cfg(not(feature = "generated_metadata"))]
4use crate::avail::RuntimeCall;
5#[cfg(feature = "generated_metadata")]
6use crate::avail_generated::runtime_types::da_runtime::RuntimeCall;
7use codec::{Compact, Decode, Encode, Error, Input};
8use serde::{Deserialize, Serialize};
9
10pub trait HasTxDispatchIndex {
11 const DISPATCH_INDEX: (u8, u8);
13}
14
15pub trait TransactionCallLike {
16 fn to_call(&self) -> TransactionCall;
17 fn from_ext(raw: &[u8]) -> Option<Box<Self>>;
18}
19
20impl<T: HasTxDispatchIndex + Encode + Decode> TransactionCallLike for T {
21 fn to_call(&self) -> TransactionCall {
22 TransactionCall::new(Self::DISPATCH_INDEX.0, Self::DISPATCH_INDEX.1, self.encode())
23 }
24
25 fn from_ext(raw_ext: &[u8]) -> Option<Box<T>> {
26 if raw_ext.len() < 2 {
27 return None;
28 }
29
30 let (pallet_id, call_id) = (raw_ext[0], raw_ext[1]);
31 if Self::DISPATCH_INDEX.0 != pallet_id || Self::DISPATCH_INDEX.1 != call_id {
32 return None;
33 }
34
35 Self::decode(&mut &raw_ext[2..]).ok().map(Box::new)
36 }
37}
38
39#[derive(Clone)]
40pub struct OpaqueTransaction {
41 pub signature: Option<TransactionSigned>,
45 pub call: Vec<u8>,
47}
48
49impl OpaqueTransaction {
50 pub fn pallet_index(&self) -> u8 {
51 self.call[0]
52 }
53
54 pub fn call_index(&self) -> u8 {
55 self.call[1]
56 }
57}
58
59impl TryFrom<Vec<u8>> for OpaqueTransaction {
60 type Error = codec::Error;
61
62 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
63 Self::try_from(value.as_slice())
64 }
65}
66
67impl TryFrom<&Vec<u8>> for OpaqueTransaction {
68 type Error = codec::Error;
69
70 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
71 Self::try_from(value.as_slice())
72 }
73}
74
75impl TryFrom<&[u8]> for OpaqueTransaction {
76 type Error = codec::Error;
77
78 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
79 let mut value = value;
80 Self::decode(&mut value)
81 }
82}
83
84impl Decode for OpaqueTransaction {
85 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
86 let expected_length: Compact<u32> = Decode::decode(input)?;
90 let before_length = input.remaining_len()?;
91
92 let version = input.read_byte()?;
93
94 let is_signed = version & 0b1000_0000 != 0;
95 let version = version & 0b0111_1111;
96 if version != EXTRINSIC_FORMAT_VERSION {
97 return Err("Invalid transaction version".into());
98 }
99
100 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
101 let call: AlreadyEncoded = Decode::decode(input)?;
102
103 if let Some((before_length, after_length)) = input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
104 {
105 let length = before_length.saturating_sub(after_length);
106
107 if length != expected_length.0 as usize {
108 return Err("Invalid length prefix".into());
109 }
110 }
111
112 Ok(Self {
113 signature,
114 call: call.0,
115 })
116 }
117}
118
119impl Encode for OpaqueTransaction {
120 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
121 let mut encoded_tx_inner = Vec::new();
122 if let Some(signed) = &self.signature {
123 0x84u8.encode_to(&mut encoded_tx_inner);
124 signed.address.encode_to(&mut encoded_tx_inner);
125 signed.signature.encode_to(&mut encoded_tx_inner);
126 signed.tx_extra.encode_to(&mut encoded_tx_inner);
127 } else {
128 0x4u8.encode_to(&mut encoded_tx_inner);
129 }
130
131 encoded_tx_inner.extend(&self.call);
132 let mut encoded_tx = Compact(encoded_tx_inner.len() as u32).encode();
133 encoded_tx.append(&mut encoded_tx_inner);
134
135 dest.write(&encoded_tx)
136 }
137}
138
139impl Serialize for OpaqueTransaction {
140 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141 where
142 S: serde::Serializer,
143 {
144 let bytes = self.encode();
145 impl_serde::serialize::serialize(&bytes, serializer)
146 }
147}
148
149impl<'a> Deserialize<'a> for OpaqueTransaction {
150 fn deserialize<D>(de: D) -> Result<Self, D::Error>
151 where
152 D: serde::Deserializer<'a>,
153 {
154 let r = impl_serde::serialize::deserialize(de)?;
155 Decode::decode(&mut &r[..]).map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
156 }
157}
158
159#[derive(Clone)]
160pub struct DecodedTransaction {
161 pub signature: Option<TransactionSigned>,
165 pub call: RuntimeCall,
167}
168
169impl DecodedTransaction {
170 pub fn app_id(&self) -> Option<u32> {
171 self.signature.as_ref().map(|s| s.tx_extra.app_id)
172 }
173
174 #[cfg(not(feature = "generated_metadata"))]
175 pub fn pallet_index(&self) -> u8 {
176 self.call.pallet_index()
177 }
178
179 #[cfg(not(feature = "generated_metadata"))]
180 pub fn call_index(&self) -> u8 {
181 self.call.call_index()
182 }
183}
184
185impl TryFrom<&Vec<u8>> for DecodedTransaction {
186 type Error = codec::Error;
187
188 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
189 Self::try_from(value.as_slice())
190 }
191}
192
193impl TryFrom<&[u8]> for DecodedTransaction {
194 type Error = codec::Error;
195
196 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
197 let mut value = value;
198 Self::decode(&mut value)
199 }
200}
201
202impl Decode for DecodedTransaction {
203 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
204 let expected_length: Compact<u32> = Decode::decode(input)?;
208 let before_length = input.remaining_len()?;
209
210 let version = input.read_byte()?;
211
212 let is_signed = version & 0b1000_0000 != 0;
213 let version = version & 0b0111_1111;
214 if version != EXTRINSIC_FORMAT_VERSION {
215 return Err("Invalid transaction version".into());
216 }
217
218 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
219 let call = Decode::decode(input)?;
220
221 if let Some((before_length, after_length)) = input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
222 {
223 let length = before_length.saturating_sub(after_length);
224
225 if length != expected_length.0 as usize {
226 return Err("Invalid length prefix".into());
227 }
228 }
229
230 Ok(Self { signature, call })
231 }
232}
233
234impl Encode for DecodedTransaction {
235 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
236 let mut encoded_tx_inner = Vec::new();
237 if let Some(signed) = &self.signature {
238 0x84u8.encode_to(&mut encoded_tx_inner);
239 signed.address.encode_to(&mut encoded_tx_inner);
240 signed.signature.encode_to(&mut encoded_tx_inner);
241 signed.tx_extra.encode_to(&mut encoded_tx_inner);
242 } else {
243 0x4u8.encode_to(&mut encoded_tx_inner);
244 }
245
246 self.call.encode_to(&mut encoded_tx_inner);
247 let mut encoded_tx = Compact(encoded_tx_inner.len() as u32).encode();
248 encoded_tx.append(&mut encoded_tx_inner);
249
250 dest.write(&encoded_tx)
251 }
252}
253
254impl Serialize for DecodedTransaction {
255 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
256 where
257 S: serde::Serializer,
258 {
259 let bytes = self.encode();
260 impl_serde::serialize::serialize(&bytes, serializer)
261 }
262}
263
264impl<'a> Deserialize<'a> for DecodedTransaction {
265 fn deserialize<D>(de: D) -> Result<Self, D::Error>
266 where
267 D: serde::Deserializer<'a>,
268 {
269 let r = impl_serde::serialize::deserialize(de)?;
270 Decode::decode(&mut &r[..]).map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
271 }
272}
273
274#[cfg(test)]
275pub mod test {
276 use super::TransactionCallLike;
277 use std::borrow::Cow;
278
279 use codec::Encode;
280 use subxt_core::utils::{AccountId32, Era};
281
282 use crate::{
283 DecodedTransaction, MultiAddress, MultiSignature, Transaction, TransactionExtra,
284 avail::data_availability::tx::SubmitData, decoded_transaction::OpaqueTransaction,
285 transaction::TransactionSigned,
286 };
287
288 #[test]
289 fn test_encoding_decoding() {
290 let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
291
292 let account_id = AccountId32([1u8; 32]);
293 let signature = [1u8; 64];
294 let signed = TransactionSigned {
295 address: MultiAddress::Id(account_id),
296 signature: MultiSignature::Sr25519(signature),
297 tx_extra: TransactionExtra {
298 era: Era::Mortal { period: 4, phase: 2 },
299 nonce: 1,
300 tip: 2u128,
301 app_id: 3,
302 },
303 };
304
305 let tx = Transaction {
306 signed: Some(signed.clone()),
307 call: Cow::Owned(call.clone()),
308 };
309
310 let encoded_tx = tx.encode();
311
312 let opaque = OpaqueTransaction::try_from(&encoded_tx).unwrap();
314 let opaque_encoded = opaque.encode();
315
316 assert_eq!(encoded_tx, opaque_encoded);
317
318 let decoded = DecodedTransaction::try_from(&encoded_tx).unwrap();
320 let decoded_encoded = decoded.encode();
321
322 assert_eq!(encoded_tx, decoded_encoded);
323 }
324
325 #[test]
326 fn test_serialize_deserialize() {
327 let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
328
329 let account_id = AccountId32([1u8; 32]);
330 let signature = [1u8; 64];
331 let signed = TransactionSigned {
332 address: MultiAddress::Id(account_id),
333 signature: MultiSignature::Sr25519(signature),
334 tx_extra: TransactionExtra {
335 era: Era::Mortal { period: 4, phase: 2 },
336 nonce: 1,
337 tip: 2u128,
338 app_id: 3,
339 },
340 };
341
342 let tx = Transaction {
343 signed: Some(signed.clone()),
344 call: Cow::Owned(call.clone()),
345 };
346
347 let encoded_tx = tx.encode();
348 let expected_serialized = std::format!("0x{}", hex::encode(&encoded_tx));
349
350 let serialized = serde_json::to_string(&tx).unwrap();
352 assert_eq!(serialized.trim_matches('"'), expected_serialized);
353
354 let tx_deserialized: Transaction = serde_json::from_str(&serialized).unwrap();
356 assert_eq!(encoded_tx, tx_deserialized.encode());
357
358 let opaque = OpaqueTransaction::try_from(&encoded_tx).unwrap();
360 let serialized = serde_json::to_string(&opaque).unwrap();
361 assert_eq!(serialized.trim_matches('"'), expected_serialized);
362
363 let opaque_deserialized: OpaqueTransaction = serde_json::from_str(&serialized).unwrap();
365 assert_eq!(encoded_tx, opaque_deserialized.encode());
366
367 let decoded = DecodedTransaction::try_from(&encoded_tx).unwrap();
369 let serialized = serde_json::to_string(&decoded).unwrap();
370 assert_eq!(serialized.trim_matches('"'), expected_serialized);
371
372 let decoded_deserialized: DecodedTransaction = serde_json::from_str(&serialized).unwrap();
374 assert_eq!(encoded_tx, decoded_deserialized.encode());
375 }
376}