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