avail_rust_core/
transaction.rs

1use super::{AccountId, MultiAddress, MultiSignature};
2use codec::{Compact, Decode, Encode};
3use primitive_types::H256;
4use serde::{Deserialize, Serialize};
5use std::borrow::Cow;
6use subxt_core::config::{Hasher, substrate::BlakeTwo256};
7use subxt_signer::sr25519::Keypair;
8
9pub use subxt_core::utils::Era;
10
11/// Current version of the [`UncheckedExtrinsic`] encoded format.
12///
13/// This version needs to be bumped if the encoded representation changes.
14/// It ensures that if the representation is changed and the format is not known,
15/// the decoding fails.
16pub const EXTRINSIC_FORMAT_VERSION: u8 = 4;
17
18#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
19pub struct TransactionExtra {
20	pub era: Era,
21	#[codec(compact)]
22	pub nonce: u32,
23	#[codec(compact)]
24	pub tip: u128,
25	#[codec(compact)]
26	pub app_id: u32,
27}
28
29#[derive(Debug, Clone, Encode, Decode)]
30pub struct TransactionAdditional {
31	pub spec_version: u32,
32	pub tx_version: u32,
33	pub genesis_hash: H256,
34	pub fork_hash: H256,
35}
36
37#[derive(Debug, Clone)]
38pub struct AlreadyEncoded(pub Vec<u8>);
39
40impl Encode for AlreadyEncoded {
41	fn size_hint(&self) -> usize {
42		self.0.len()
43	}
44
45	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
46		dest.write(&self.0);
47	}
48}
49
50impl Decode for AlreadyEncoded {
51	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
52		let length = input.remaining_len()?;
53		let Some(length) = length else {
54			return Err("Failed to decode transaction".into());
55		};
56		if length == 0 {
57			return Ok(Self(Vec::new()));
58		}
59		let mut value = vec![0u8; length];
60		input.read(&mut value)?;
61		Ok(Self(value))
62	}
63}
64
65impl From<Vec<u8>> for AlreadyEncoded {
66	fn from(value: Vec<u8>) -> Self {
67		AlreadyEncoded(value)
68	}
69}
70
71#[derive(Debug, Clone, Encode, Decode)]
72pub struct TransactionCall {
73	pub pallet_id: u8,
74	pub call_id: u8,
75	pub data: AlreadyEncoded,
76}
77
78impl TransactionCall {
79	pub fn new(pallet_id: u8, call_id: u8, data: Vec<u8>) -> Self {
80		Self {
81			pallet_id,
82			call_id,
83			data: AlreadyEncoded::from(data),
84		}
85	}
86}
87
88// There is no need for Encode and Decode
89#[derive(Debug, Clone)]
90pub struct TransactionPayload<'a> {
91	pub call: Cow<'a, TransactionCall>,
92	pub extra: TransactionExtra,
93	pub additional: TransactionAdditional,
94}
95
96impl<'a> TransactionPayload<'a> {
97	pub fn new(call: TransactionCall, extra: TransactionExtra, additional: TransactionAdditional) -> Self {
98		Self {
99			call: Cow::Owned(call),
100			extra,
101			additional,
102		}
103	}
104
105	pub fn new_borrowed(call: &'a TransactionCall, extra: TransactionExtra, additional: TransactionAdditional) -> Self {
106		Self {
107			call: Cow::Borrowed(call),
108			extra,
109			additional,
110		}
111	}
112
113	pub fn sign(&self, signer: &Keypair) -> [u8; 64] {
114		let call = self.call.as_ref();
115		let size_hint = call.size_hint() + self.extra.size_hint() + self.additional.size_hint();
116
117		let mut data: Vec<u8> = Vec::with_capacity(size_hint);
118		self.call.encode_to(&mut data);
119		self.extra.encode_to(&mut data);
120		self.additional.encode_to(&mut data);
121
122		if data.len() > 256 {
123			let hash = BlakeTwo256::hash(&data);
124			signer.sign(hash.as_ref()).0
125		} else {
126			signer.sign(&data).0
127		}
128	}
129}
130
131#[derive(Debug, Clone, Decode)]
132pub struct TransactionSigned {
133	pub address: MultiAddress,
134	pub signature: MultiSignature,
135	pub tx_extra: TransactionExtra,
136}
137
138impl Encode for TransactionSigned {
139	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
140		self.address.encode_to(dest);
141		self.signature.encode_to(dest);
142		self.tx_extra.encode_to(dest);
143	}
144}
145
146#[derive(Debug, Clone)]
147pub struct Transaction<'a> {
148	pub signed: Option<TransactionSigned>,
149	pub call: Cow<'a, TransactionCall>,
150}
151
152impl<'a> Transaction<'a> {
153	pub fn new(account_id: AccountId, signature: [u8; 64], payload: TransactionPayload<'a>) -> Self {
154		let address = MultiAddress::Id(account_id);
155		let signature = MultiSignature::Sr25519(signature);
156
157		let signed = Some(TransactionSigned {
158			address,
159			signature,
160			tx_extra: payload.extra.clone(),
161		});
162
163		Self {
164			signed,
165			call: payload.call,
166		}
167	}
168
169	pub fn encode(&self) -> Vec<u8> {
170		let mut encoded_tx_inner = Vec::new();
171		if let Some(signed) = &self.signed {
172			0x84u8.encode_to(&mut encoded_tx_inner);
173			signed.address.encode_to(&mut encoded_tx_inner);
174			signed.signature.encode_to(&mut encoded_tx_inner);
175			signed.tx_extra.encode_to(&mut encoded_tx_inner);
176		} else {
177			0x4u8.encode_to(&mut encoded_tx_inner);
178		}
179
180		let call = self.call.as_ref();
181		call.encode_to(&mut encoded_tx_inner);
182		let mut encoded_tx = Compact(encoded_tx_inner.len() as u32).encode();
183		encoded_tx.append(&mut encoded_tx_inner);
184
185		encoded_tx
186	}
187
188	pub fn hash(&self) -> H256 {
189		let encoded = self.encode();
190		BlakeTwo256::hash(&encoded)
191	}
192}
193
194impl<'a> TryFrom<&Vec<u8>> for Transaction<'a> {
195	type Error = codec::Error;
196
197	fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
198		Self::try_from(value.as_slice())
199	}
200}
201
202impl<'a> TryFrom<&[u8]> for Transaction<'a> {
203	type Error = codec::Error;
204
205	fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
206		let mut value = value;
207		Self::decode(&mut value)
208	}
209}
210
211impl<'a> Decode for Transaction<'a> {
212	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
213		// This is a little more complicated than usual since the binary format must be compatible
214		// with SCALE's generic `Vec<u8>` type. Basically this just means accepting that there
215		// will be a prefix of vector length.
216		let expected_length = Compact::<u32>::decode(input)?;
217		let before_length = input.remaining_len()?;
218
219		let version = input.read_byte()?;
220
221		let is_signed = version & 0b1000_0000 != 0;
222		let version = version & 0b0111_1111;
223		if version != EXTRINSIC_FORMAT_VERSION {
224			return Err("Invalid transaction version".into());
225		}
226
227		let signed = is_signed.then(|| TransactionSigned::decode(input)).transpose()?;
228		let call = TransactionCall::decode(input)?;
229
230		if let Some((before_length, after_length)) = input.remaining_len()?.and_then(|a| before_length.map(|b| (b, a)))
231		{
232			let length = before_length.saturating_sub(after_length);
233
234			if length != expected_length.0 as usize {
235				return Err("Invalid length prefix".into());
236			}
237		}
238
239		Ok(Self {
240			signed,
241			call: Cow::Owned(call),
242		})
243	}
244}
245
246impl<'a> Serialize for Transaction<'a> {
247	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
248	where
249		S: serde::Serializer,
250	{
251		let bytes = self.encode();
252		impl_serde::serialize::serialize(&bytes, serializer)
253	}
254}
255
256impl<'a, 'b> Deserialize<'a> for Transaction<'b> {
257	fn deserialize<D>(de: D) -> Result<Self, D::Error>
258	where
259		D: serde::Deserializer<'a>,
260	{
261		let r = impl_serde::serialize::deserialize(de)?;
262		Decode::decode(&mut &r[..]).map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
263	}
264}