avail_rust_core/
decoded_transaction.rs1use super::transaction::{AlreadyEncoded, EXTRINSIC_FORMAT_VERSION, TransactionSigned};
2use crate::TransactionCall;
3use codec::{Compact, Decode, Encode, Error, Input};
4use serde::{Deserialize, Serialize};
5
6pub trait HasTxDispatchIndex {
7 const DISPATCH_INDEX: (u8, u8);
9}
10
11pub trait TransactionConvertible {
12 fn to_call(&self) -> TransactionCall;
13}
14
15pub trait TransactionDecodable {
16 fn decode_call(call: &[u8]) -> Option<Box<Self>>;
20
21 fn decode_hex_call(call: &str) -> Option<Box<Self>>;
26
27 fn decode_call_data(call_data: &[u8]) -> Option<Box<Self>>;
29
30 fn decode_hex_transaction(transaction: &str) -> Option<Box<Self>>;
35
36 fn decode_transaction(transaction: &[u8]) -> Option<Box<Self>>;
41}
42
43impl<T: HasTxDispatchIndex + Encode> TransactionConvertible for T {
44 fn to_call(&self) -> TransactionCall {
45 TransactionCall::new(Self::DISPATCH_INDEX.0, Self::DISPATCH_INDEX.1, self.encode())
46 }
47}
48
49impl<T: HasTxDispatchIndex + Decode> TransactionDecodable for T {
50 #[inline(always)]
51 fn decode_hex_transaction(transaction: &str) -> Option<Box<T>> {
52 let opaque = OpaqueTransaction::try_from(transaction).ok()?;
53 Self::decode_call(&opaque.call)
54 }
55
56 #[inline(always)]
57 fn decode_transaction(transaction_bytes: &[u8]) -> Option<Box<T>> {
58 let opaque = OpaqueTransaction::try_from(transaction_bytes).ok()?;
59 Self::decode_call(&opaque.call)
60 }
61
62 #[inline(always)]
63 fn decode_hex_call(call: &str) -> Option<Box<T>> {
64 let hex_decoded = hex::decode(call.trim_start_matches("0x")).ok()?;
65 Self::decode_call(&hex_decoded)
66 }
67
68 fn decode_call(call: &[u8]) -> Option<Box<T>> {
69 if !tx_filter_in(call, Self::DISPATCH_INDEX) {
71 return None;
72 }
73
74 if call.len() <= 2 {
75 try_decode_transaction(&[])
76 } else {
77 try_decode_transaction(&call[2..])
78 }
79 }
80
81 fn decode_call_data(call_data: &[u8]) -> Option<Box<Self>> {
82 try_decode_transaction(call_data)
84 }
85}
86
87#[inline(never)]
89fn try_decode_transaction<T: Decode>(mut event_data: &[u8]) -> Option<Box<T>> {
90 T::decode(&mut event_data).ok().map(Box::new)
91}
92
93#[inline(never)]
95fn tx_filter_in(call: &[u8], dispatch_index: (u8, u8)) -> bool {
96 if call.len() < 3 {
97 return false;
98 }
99
100 let (pallet_id, call_id) = (call[0], call[1]);
101 if dispatch_index.0 != pallet_id || dispatch_index.1 != call_id {
102 return false;
103 }
104
105 true
106}
107
108#[derive(Clone)]
109pub struct OpaqueTransaction {
110 pub signature: Option<TransactionSigned>,
114 pub call: Vec<u8>,
116}
117
118impl OpaqueTransaction {
119 pub fn pallet_index(&self) -> u8 {
120 self.call[0]
121 }
122
123 pub fn call_index(&self) -> u8 {
124 self.call[1]
125 }
126}
127
128impl TryFrom<Vec<u8>> for OpaqueTransaction {
129 type Error = codec::Error;
130
131 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
132 Self::try_from(value.as_slice())
133 }
134}
135
136impl TryFrom<&Vec<u8>> for OpaqueTransaction {
137 type Error = codec::Error;
138
139 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
140 Self::try_from(value.as_slice())
141 }
142}
143
144impl TryFrom<&[u8]> for OpaqueTransaction {
145 type Error = codec::Error;
146
147 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
148 let mut value = value;
149 Self::decode(&mut value)
150 }
151}
152
153impl TryFrom<String> for OpaqueTransaction {
154 type Error = codec::Error;
155
156 fn try_from(value: String) -> Result<Self, Self::Error> {
157 Self::try_from(value.as_str())
158 }
159}
160
161impl TryFrom<&str> for OpaqueTransaction {
162 type Error = codec::Error;
163
164 fn try_from(value: &str) -> Result<Self, Self::Error> {
165 let Ok(hex_decoded) = hex::decode(value.trim_start_matches("0x")) else {
166 return Err("Failed to hex decode transaction".into());
167 };
168
169 Self::decode(&mut hex_decoded.as_slice())
170 }
171}
172
173impl Decode for OpaqueTransaction {
174 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
175 let expected_length: Compact<u32> = Decode::decode(input)?;
179 let before_length = input.remaining_len()?;
180
181 let version = input.read_byte()?;
182
183 let is_signed = version & 0b1000_0000 != 0;
184 let version = version & 0b0111_1111;
185 if version != EXTRINSIC_FORMAT_VERSION {
186 return Err("Invalid transaction version".into());
187 }
188
189 let signature = is_signed.then(|| Decode::decode(input)).transpose()?;
190 let call: AlreadyEncoded = Decode::decode(input)?;
191
192 if let Some((before_length, after_length)) = input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
193 {
194 let length = before_length.saturating_sub(after_length);
195
196 if length != expected_length.0 as usize {
197 return Err("Invalid length prefix".into());
198 }
199 }
200
201 Ok(Self {
202 signature,
203 call: call.0,
204 })
205 }
206}
207
208impl Encode for OpaqueTransaction {
209 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
210 let mut encoded_tx_inner = Vec::new();
211 if let Some(signed) = &self.signature {
212 0x84u8.encode_to(&mut encoded_tx_inner);
213 signed.address.encode_to(&mut encoded_tx_inner);
214 signed.signature.encode_to(&mut encoded_tx_inner);
215 signed.tx_extra.encode_to(&mut encoded_tx_inner);
216 } else {
217 0x4u8.encode_to(&mut encoded_tx_inner);
218 }
219
220 encoded_tx_inner.extend(&self.call);
221 let mut encoded_tx = Compact(encoded_tx_inner.len() as u32).encode();
222 encoded_tx.append(&mut encoded_tx_inner);
223
224 dest.write(&encoded_tx)
225 }
226}
227
228impl Serialize for OpaqueTransaction {
229 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230 where
231 S: serde::Serializer,
232 {
233 let bytes = self.encode();
234 impl_serde::serialize::serialize(&bytes, serializer)
235 }
236}
237
238impl<'a> Deserialize<'a> for OpaqueTransaction {
239 fn deserialize<D>(de: D) -> Result<Self, D::Error>
240 where
241 D: serde::Deserializer<'a>,
242 {
243 let r = impl_serde::serialize::deserialize(de)?;
244 Decode::decode(&mut &r[..]).map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
245 }
246}
247
248#[cfg(test)]
249pub mod test {
250 use super::TransactionConvertible;
251 use std::borrow::Cow;
252
253 use codec::Encode;
254 use subxt_core::utils::{AccountId32, Era};
255
256 use crate::{
257 MultiAddress, MultiSignature, Transaction, TransactionExtra, avail::data_availability::tx::SubmitData,
258 decoded_transaction::OpaqueTransaction, transaction::TransactionSigned,
259 };
260
261 #[test]
262 fn test_encoding_decoding() {
263 let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
264
265 let account_id = AccountId32([1u8; 32]);
266 let signature = [1u8; 64];
267 let signed = TransactionSigned {
268 address: MultiAddress::Id(account_id),
269 signature: MultiSignature::Sr25519(signature),
270 tx_extra: TransactionExtra {
271 era: Era::Mortal { period: 4, phase: 2 },
272 nonce: 1,
273 tip: 2u128,
274 app_id: 3,
275 },
276 };
277
278 let tx = Transaction {
279 signed: Some(signed.clone()),
280 call: Cow::Owned(call.clone()),
281 };
282
283 let encoded_tx = tx.encode();
284
285 let opaque = OpaqueTransaction::try_from(&encoded_tx).unwrap();
287 let opaque_encoded = opaque.encode();
288
289 assert_eq!(encoded_tx, opaque_encoded);
290 }
291
292 #[test]
293 fn test_serialize_deserialize() {
294 let call = SubmitData { data: vec![0, 1, 2, 3] }.to_call();
295
296 let account_id = AccountId32([1u8; 32]);
297 let signature = [1u8; 64];
298 let signed = TransactionSigned {
299 address: MultiAddress::Id(account_id),
300 signature: MultiSignature::Sr25519(signature),
301 tx_extra: TransactionExtra {
302 era: Era::Mortal { period: 4, phase: 2 },
303 nonce: 1,
304 tip: 2u128,
305 app_id: 3,
306 },
307 };
308
309 let tx = Transaction {
310 signed: Some(signed.clone()),
311 call: Cow::Owned(call.clone()),
312 };
313
314 let encoded_tx = tx.encode();
315 let expected_serialized = std::format!("0x{}", hex::encode(&encoded_tx));
316
317 let serialized = serde_json::to_string(&tx).unwrap();
319 assert_eq!(serialized.trim_matches('"'), expected_serialized);
320
321 let tx_deserialized: Transaction = serde_json::from_str(&serialized).unwrap();
323 assert_eq!(encoded_tx, tx_deserialized.encode());
324
325 let opaque = OpaqueTransaction::try_from(&encoded_tx).unwrap();
327 let serialized = serde_json::to_string(&opaque).unwrap();
328 assert_eq!(serialized.trim_matches('"'), expected_serialized);
329
330 let opaque_deserialized: OpaqueTransaction = serde_json::from_str(&serialized).unwrap();
332 assert_eq!(encoded_tx, opaque_deserialized.encode());
333 }
334}