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