avail_rust_core/
decoded_transaction.rs

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	// Pallet ID, Call ID
12	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	/// The signature, address, number of extrinsics have come before from
42	/// the same signer and an era describing the longevity of this transaction,
43	/// if this is a signed extrinsic.
44	pub signature: Option<TransactionSigned>,
45	/// The function that should be called.
46	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		// This is a little more complicated than usual since the binary format must be compatible
87		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
88		// will be a prefix of vector length.
89		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	/// The signature, address, number of extrinsics have come before from
162	/// the same signer and an era describing the longevity of this transaction,
163	/// if this is a signed extrinsic.
164	pub signature: Option<TransactionSigned>,
165	/// The function that should be called.
166	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		// This is a little more complicated than usual since the binary format must be compatible
205		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
206		// will be a prefix of vector length.
207		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		// Opaque Transaction
313		let opaque = OpaqueTransaction::try_from(&encoded_tx).unwrap();
314		let opaque_encoded = opaque.encode();
315
316		assert_eq!(encoded_tx, opaque_encoded);
317
318		// Decoded Transaction
319		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		// Transaction Serialized
351		let serialized = serde_json::to_string(&tx).unwrap();
352		assert_eq!(serialized.trim_matches('"'), expected_serialized);
353
354		// Transaction Deserialized
355		let tx_deserialized: Transaction = serde_json::from_str(&serialized).unwrap();
356		assert_eq!(encoded_tx, tx_deserialized.encode());
357
358		// Opaque Serialized
359		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		// Opaque Deserialized
364		let opaque_deserialized: OpaqueTransaction = serde_json::from_str(&serialized).unwrap();
365		assert_eq!(encoded_tx, opaque_deserialized.encode());
366
367		// Decoded Serialized
368		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		// Decoded Deserialized
373		let decoded_deserialized: DecodedTransaction = serde_json::from_str(&serialized).unwrap();
374		assert_eq!(encoded_tx, decoded_deserialized.encode());
375	}
376}