avail_rust_core/
decoded_transaction.rs

1use super::substrate::extrinsic::EXTRINSIC_FORMAT_VERSION;
2use crate::{
3	decoded_events::check_header,
4	types::{ExtrinsicSignature, metadata::StringOrBytes},
5	utils::decode_already_decoded,
6};
7use codec::{Compact, Decode, Encode, Error, Input};
8use serde::{Deserialize, Serialize};
9
10pub trait HasHeader {
11	// Pallet ID, Variant ID
12	const HEADER_INDEX: (u8, u8);
13}
14
15pub trait TransactionEncodable {
16	/// SCALE encodes the event
17	///
18	/// If you need to Hex and SCALE encode then call `encode_as_hex_event`
19	fn to_call(&self) -> Vec<u8>;
20}
21
22pub trait TransactionDecodable: Sized {
23	fn from_call<'a>(call: impl Into<StringOrBytes<'a>>) -> Result<Self, String>;
24	fn from_ext<'a>(call: impl Into<StringOrBytes<'a>>) -> Result<Self, String>;
25}
26
27impl<T: HasHeader + Encode> TransactionEncodable for T {
28	fn to_call(&self) -> Vec<u8> {
29		let pallet_id = Self::HEADER_INDEX.0;
30		let variant_id = Self::HEADER_INDEX.1;
31		let mut encoded_event: Vec<u8> = vec![pallet_id, variant_id];
32		Self::encode_to(self, &mut encoded_event);
33
34		encoded_event
35	}
36}
37
38impl<T: HasHeader + Decode> TransactionDecodable for T {
39	fn from_call<'a>(call: impl Into<StringOrBytes<'a>>) -> Result<T, String> {
40		fn inner<T: HasHeader + Decode>(call: StringOrBytes) -> Result<T, String> {
41			let call: &[u8] = match &call {
42				StringOrBytes::StringRef(s) => {
43					&const_hex::decode(s.trim_start_matches("0x")).map_err(|x| x.to_string())?
44				},
45				StringOrBytes::BoxedString(s) => {
46					&const_hex::decode(s.trim_start_matches("0x")).map_err(|x| x.to_string())?
47				},
48				StringOrBytes::Bytes(b) => b,
49				StringOrBytes::BoxedBytes(b) => b,
50			};
51
52			check_header(call, T::HEADER_INDEX)?;
53
54			if call.len() <= 2 {
55				let mut data: &[u8] = &[];
56				Ok(T::decode(&mut data).map_err(|x| x.to_string())?)
57			} else {
58				let mut data = &call[2..];
59				Ok(T::decode(&mut data).map_err(|x| x.to_string())?)
60			}
61		}
62
63		inner(call.into())
64	}
65
66	fn from_ext<'a>(ext: impl Into<StringOrBytes<'a>>) -> Result<T, String> {
67		fn inner<T: HasHeader + Decode>(ext: StringOrBytes) -> Result<T, String> {
68			let ext: &[u8] = match &ext {
69				StringOrBytes::StringRef(s) => &const_hex::decode(s.trim_start_matches("0x"))
70					.map_err(|x: const_hex::FromHexError| x.to_string())?,
71				StringOrBytes::BoxedString(s) => {
72					&const_hex::decode(s.trim_start_matches("0x")).map_err(|x| x.to_string())?
73				},
74				StringOrBytes::Bytes(b) => b,
75				StringOrBytes::BoxedBytes(b) => b,
76			};
77
78			let ext = Extrinsic::<T>::try_from(ext)?;
79			Ok(ext.call)
80		}
81
82		inner(ext.into())
83	}
84}
85
86#[derive(Clone)]
87pub struct RawExtrinsic {
88	/// The signature, address, number of extrinsics have come before from
89	/// the same signer and an era describing the longevity of this transaction,
90	/// if this is a signed extrinsic.
91	pub signature: Option<ExtrinsicSignature>,
92	/// The function that should be called.
93	pub call: Vec<u8>,
94}
95
96impl<'a> TryFrom<StringOrBytes<'a>> for RawExtrinsic {
97	type Error = String;
98
99	fn try_from(value: StringOrBytes<'a>) -> Result<Self, Self::Error> {
100		match value {
101			StringOrBytes::StringRef(s) => Self::try_from(s),
102			StringOrBytes::BoxedString(s) => Self::try_from(&*s),
103			StringOrBytes::Bytes(b) => Self::try_from(b),
104			StringOrBytes::BoxedBytes(b) => Self::try_from(&*b),
105		}
106	}
107}
108
109impl TryFrom<Vec<u8>> for RawExtrinsic {
110	type Error = String;
111
112	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
113		Self::try_from(value.as_slice())
114	}
115}
116
117impl TryFrom<&Vec<u8>> for RawExtrinsic {
118	type Error = String;
119
120	fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
121		Self::try_from(value.as_slice())
122	}
123}
124
125impl TryFrom<&[u8]> for RawExtrinsic {
126	type Error = String;
127
128	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
129		let mut value = value;
130		Self::decode(&mut value).map_err(|x| x.to_string())
131	}
132}
133
134impl TryFrom<String> for RawExtrinsic {
135	type Error = String;
136
137	fn try_from(value: String) -> Result<Self, Self::Error> {
138		Self::try_from(value.as_str())
139	}
140}
141
142impl TryFrom<&str> for RawExtrinsic {
143	type Error = String;
144
145	fn try_from(value: &str) -> Result<Self, Self::Error> {
146		let Ok(hex_decoded) = const_hex::decode(value.trim_start_matches("0x")) else {
147			return Err("Failed to hex decode transaction".into());
148		};
149
150		Self::try_from(hex_decoded.as_slice())
151	}
152}
153
154impl Decode for RawExtrinsic {
155	fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
156		// This is a little more complicated than usual since the binary format must be compatible
157		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
158		// will be a prefix of vector length.
159		let expected_length: Compact<u32> = Decode::decode(input)?;
160		let before_length = input.remaining_len()?;
161
162		let version = input.read_byte()?;
163
164		let is_signed = version & 0b1000_0000 != 0;
165		let version = version & 0b0111_1111;
166		if version != EXTRINSIC_FORMAT_VERSION {
167			return Err("Invalid transaction version".into());
168		}
169
170		let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
171		let call = decode_already_decoded(input)?;
172
173		if let Some((before_length, after_length)) = input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
174		{
175			let length = before_length.saturating_sub(after_length);
176
177			if length != expected_length.0 as usize {
178				return Err("Invalid length prefix".into());
179			}
180		}
181
182		Ok(Self { signature, call })
183	}
184}
185
186impl Encode for RawExtrinsic {
187	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
188		let mut encoded_tx_inner = Vec::new();
189		if let Some(signed) = &self.signature {
190			0x84u8.encode_to(&mut encoded_tx_inner);
191			signed.address.encode_to(&mut encoded_tx_inner);
192			signed.signature.encode_to(&mut encoded_tx_inner);
193			signed.tx_extra.encode_to(&mut encoded_tx_inner);
194		} else {
195			0x4u8.encode_to(&mut encoded_tx_inner);
196		}
197
198		encoded_tx_inner.extend(&self.call);
199		let mut encoded_tx = Compact(encoded_tx_inner.len() as u32).encode();
200		encoded_tx.append(&mut encoded_tx_inner);
201
202		dest.write(&encoded_tx)
203	}
204}
205
206impl Serialize for RawExtrinsic {
207	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
208	where
209		S: serde::Serializer,
210	{
211		let bytes = self.encode();
212		impl_serde::serialize::serialize(&bytes, serializer)
213	}
214}
215
216impl<'a> Deserialize<'a> for RawExtrinsic {
217	fn deserialize<D>(de: D) -> Result<Self, D::Error>
218	where
219		D: serde::Deserializer<'a>,
220	{
221		let r = impl_serde::serialize::deserialize(de)?;
222		Decode::decode(&mut &r[..]).map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
223	}
224}
225
226#[derive(Debug, Clone)]
227pub struct SignedExtrinsic<T: HasHeader + Decode + Sized> {
228	pub signature: ExtrinsicSignature,
229	pub call: T,
230}
231
232impl<'a, T: HasHeader + Decode> TryFrom<StringOrBytes<'a>> for SignedExtrinsic<T> {
233	type Error = String;
234
235	fn try_from(value: StringOrBytes<'a>) -> Result<Self, Self::Error> {
236		match value {
237			StringOrBytes::StringRef(s) => Self::try_from(s),
238			StringOrBytes::BoxedString(s) => Self::try_from(&*s),
239			StringOrBytes::Bytes(b) => Self::try_from(b),
240			StringOrBytes::BoxedBytes(b) => Self::try_from(&*b),
241		}
242	}
243}
244
245impl<T: HasHeader + Decode> TryFrom<Vec<u8>> for SignedExtrinsic<T> {
246	type Error = String;
247
248	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
249		Self::try_from(value.as_slice())
250	}
251}
252
253impl<T: HasHeader + Decode> TryFrom<&Vec<u8>> for SignedExtrinsic<T> {
254	type Error = String;
255
256	fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
257		Self::try_from(value.as_slice())
258	}
259}
260
261impl<T: HasHeader + Decode> TryFrom<&[u8]> for SignedExtrinsic<T> {
262	type Error = String;
263
264	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
265		let ext = RawExtrinsic::try_from(value)?;
266		Self::try_from(ext)
267	}
268}
269
270impl<T: HasHeader + Decode> TryFrom<String> for SignedExtrinsic<T> {
271	type Error = String;
272
273	fn try_from(value: String) -> Result<Self, Self::Error> {
274		Self::try_from(value.as_str())
275	}
276}
277
278impl<T: HasHeader + Decode> TryFrom<&String> for SignedExtrinsic<T> {
279	type Error = String;
280
281	fn try_from(value: &String) -> Result<Self, Self::Error> {
282		Self::try_from(value.as_str())
283	}
284}
285
286impl<T: HasHeader + Decode> TryFrom<&str> for SignedExtrinsic<T> {
287	type Error = String;
288
289	fn try_from(value: &str) -> Result<Self, Self::Error> {
290		let ext = RawExtrinsic::try_from(value)?;
291		Self::try_from(ext)
292	}
293}
294
295impl<T: HasHeader + Decode> TryFrom<RawExtrinsic> for SignedExtrinsic<T> {
296	type Error = String;
297
298	fn try_from(value: RawExtrinsic) -> Result<Self, Self::Error> {
299		let signature = value.signature.ok_or("Extrinsic has no signature")?;
300		let call = T::from_call(&value.call)?;
301		Ok(Self { signature, call })
302	}
303}
304
305impl<T: HasHeader + Decode> TryFrom<&RawExtrinsic> for SignedExtrinsic<T> {
306	type Error = String;
307
308	fn try_from(value: &RawExtrinsic) -> Result<Self, Self::Error> {
309		let signature = value.signature.as_ref().ok_or("Extrinsic has no signature")?.clone();
310		let call = T::from_call(&value.call)?;
311		Ok(Self { signature, call })
312	}
313}
314
315#[derive(Debug, Clone)]
316pub struct Extrinsic<T: HasHeader + Decode + Sized> {
317	pub signature: Option<ExtrinsicSignature>,
318	pub call: T,
319}
320
321impl<'a, T: HasHeader + Decode> TryFrom<StringOrBytes<'a>> for Extrinsic<T> {
322	type Error = String;
323
324	fn try_from(value: StringOrBytes<'a>) -> Result<Self, Self::Error> {
325		match value {
326			StringOrBytes::StringRef(s) => Self::try_from(s),
327			StringOrBytes::BoxedString(s) => Self::try_from(&*s),
328			StringOrBytes::Bytes(b) => Self::try_from(b),
329			StringOrBytes::BoxedBytes(b) => Self::try_from(&*b),
330		}
331	}
332}
333
334impl<T: HasHeader + Decode> TryFrom<Vec<u8>> for Extrinsic<T> {
335	type Error = String;
336
337	fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
338		Self::try_from(value.as_slice())
339	}
340}
341
342impl<T: HasHeader + Decode> TryFrom<&Vec<u8>> for Extrinsic<T> {
343	type Error = String;
344
345	fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
346		Self::try_from(value.as_slice())
347	}
348}
349
350impl<T: HasHeader + Decode> TryFrom<&[u8]> for Extrinsic<T> {
351	type Error = String;
352
353	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
354		let ext = RawExtrinsic::try_from(value)?;
355		Self::try_from(ext)
356	}
357}
358
359impl<T: HasHeader + Decode> TryFrom<String> for Extrinsic<T> {
360	type Error = String;
361
362	fn try_from(value: String) -> Result<Self, Self::Error> {
363		Self::try_from(value.as_str())
364	}
365}
366
367impl<T: HasHeader + Decode> TryFrom<&String> for Extrinsic<T> {
368	type Error = String;
369
370	fn try_from(value: &String) -> Result<Self, Self::Error> {
371		Self::try_from(value.as_str())
372	}
373}
374
375impl<T: HasHeader + Decode> TryFrom<&str> for Extrinsic<T> {
376	type Error = String;
377
378	fn try_from(value: &str) -> Result<Self, Self::Error> {
379		let ext = RawExtrinsic::try_from(value)?;
380		Self::try_from(ext)
381	}
382}
383
384impl<T: HasHeader + Decode> TryFrom<RawExtrinsic> for Extrinsic<T> {
385	type Error = String;
386
387	fn try_from(value: RawExtrinsic) -> Result<Self, Self::Error> {
388		Self::try_from(&value)
389	}
390}
391
392impl<T: HasHeader + Decode> TryFrom<&RawExtrinsic> for Extrinsic<T> {
393	type Error = String;
394
395	fn try_from(value: &RawExtrinsic) -> Result<Self, Self::Error> {
396		let call = T::from_call(&value.call)?;
397		Ok(Self { signature: value.signature.clone(), call })
398	}
399}
400
401#[cfg(test)]
402pub mod test {
403	/* 	#[test]
404	fn test_encoding_decoding() {
405		let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
406
407		let account_id = AccountId32([1u8; 32]);
408		let signature = [1u8; 64];
409		let signed = SignedExtra {
410			address: MultiAddress::Id(account_id),
411			signature: MultiSignature::Sr25519(signature),
412			tx_extra: ExtrinsicExtra {
413				era: Era::Mortal { period: 4, phase: 2 },
414				nonce: 1,
415				tip: 2u128,
416				app_id: 3,
417			},
418		};
419
420		let tx = Extrinsic {
421			signature: Some(signed.clone()),
422			call: Cow::Owned(call.clone()),
423		};
424
425		let encoded_tx = tx.encode();
426
427		// Opaque Transaction
428		let opaque = RawExtrinsic::try_from(&encoded_tx).unwrap();
429		let opaque_encoded = opaque.encode();
430
431		assert_eq!(encoded_tx, opaque_encoded);
432	}
433
434	#[test]
435	fn test_serialize_deserialize() {
436		let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
437
438		let account_id = AccountId32([1u8; 32]);
439		let signature = [1u8; 64];
440		let signed = SignedExtra {
441			address: MultiAddress::Id(account_id),
442			signature: MultiSignature::Sr25519(signature),
443			tx_extra: ExtrinsicExtra {
444				era: Era::Mortal { period: 4, phase: 2 },
445				nonce: 1,
446				tip: 2u128,
447				app_id: 3,
448			},
449		};
450
451		let tx = Extrinsic {
452			signature: Some(signed.clone()),
453			call: Cow::Owned(call.clone()),
454		};
455
456		let encoded_tx = tx.encode();
457		let expected_serialized = std::format!("0x{}", const_hex::encode(&encoded_tx));
458
459		// Transaction Serialized
460		let serialized = serde_json::to_string(&tx).unwrap();
461		assert_eq!(serialized.trim_matches('"'), expected_serialized);
462
463		// Transaction Deserialized
464		let tx_deserialized: Extrinsic = serde_json::from_str(&serialized).unwrap();
465		assert_eq!(encoded_tx, tx_deserialized.encode());
466
467		// Opaque Serialized
468		let opaque = RawExtrinsic::try_from(&encoded_tx).unwrap();
469		let serialized = serde_json::to_string(&opaque).unwrap();
470		assert_eq!(serialized.trim_matches('"'), expected_serialized);
471
472		// Opaque Deserialized
473		let opaque_deserialized: RawExtrinsic = serde_json::from_str(&serialized).unwrap();
474		assert_eq!(encoded_tx, opaque_deserialized.encode());
475	} */
476}