ac_primitives/extrinsics/
extrinsic_v4.rs

1/*
2   Copyright 2019 Supercomputing Systems AG
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8	   http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15
16*/
17
18//! Primitives for substrate extrinsics.
19
20use crate::OpaqueExtrinsic;
21use alloc::{format, vec::Vec};
22use codec::{Decode, Encode, Error, Input};
23use core::fmt;
24use scale_info::TypeInfo;
25#[allow(deprecated)]
26use sp_runtime::traits::Extrinsic;
27
28/// Current version of the [`UncheckedExtrinsic`] encoded format.
29const V4: u8 = 4;
30
31#[allow(deprecated)]
32pub mod deprecated {
33	use super::*;
34	/// Mirrors the currently used Extrinsic format (V4) from substrate. Has less traits and methods though.
35	/// The SignedExtra used does not need to implement SignedExtension here.
36	// see https://github.com/paritytech/substrate/blob/7d233c2446b5a60662400a0a4bcfb78bb3b79ff7/primitives/runtime/src/generic/unchecked_extrinsic.rs
37	#[deprecated = "Use the `UncheckedExtrinsic` with Version 5 instead"]
38	#[derive(Clone, Eq, PartialEq)]
39
40	pub struct UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra> {
41		/// The signature, address, number of extrinsics have come before from
42		/// the same signer and an era describing the longevity of this transaction,
43		/// if this is a signed extrinsic.
44		pub signature: Option<(Address, Signature, SignedExtra)>,
45		/// The function that should be called.
46		pub function: Call,
47	}
48
49	impl<Address, Call, Signature, SignedExtra>
50		UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
51	{
52		/// New instance of a signed extrinsic.
53		pub fn new_signed(
54			function: Call,
55			signed: Address,
56			signature: Signature,
57			extra: SignedExtra,
58		) -> Self {
59			UncheckedExtrinsicV4 { signature: Some((signed, signature, extra)), function }
60		}
61
62		/// New instance of an unsigned extrinsic.
63		pub fn new_unsigned(function: Call) -> Self {
64			Self { signature: None, function }
65		}
66	}
67
68	impl<Address, Call, Signature, SignedExtra> Extrinsic
69		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
70	where
71		Address: TypeInfo,
72		Signature: TypeInfo,
73		Call: TypeInfo,
74		SignedExtra: TypeInfo,
75	{
76		type Call = Call;
77
78		type SignaturePayload = (Address, Signature, SignedExtra);
79
80		fn is_signed(&self) -> Option<bool> {
81			Some(self.signature.is_some())
82		}
83
84		fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
85			Some(if let Some((address, signature, extra)) = signed_data {
86				Self::new_signed(function, address, signature, extra)
87			} else {
88				Self::new_unsigned(function)
89			})
90		}
91	}
92
93	impl<Address, Call, Signature, SignedExtra> fmt::Debug
94		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
95	where
96		Address: fmt::Debug,
97		Signature: fmt::Debug,
98		Call: fmt::Debug,
99		SignedExtra: fmt::Debug,
100	{
101		fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102			write!(
103				f,
104				"UncheckedExtrinsic({:?}, {:?})",
105				self.signature.as_ref().map(|x| (&x.0, &x.2)),
106				self.function
107			)
108		}
109	}
110
111	// https://github.com/paritytech/substrate/blob/1612e39131e3fe57ba4c78447fb1cbf7c4f8830e/primitives/runtime/src/generic/unchecked_extrinsic.rs#L289C5-L320
112	impl<Address, Call, Signature, SignedExtra> Encode
113		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
114	where
115		Address: Encode,
116		Signature: Encode,
117		Call: Encode,
118		SignedExtra: Encode,
119	{
120		fn encode(&self) -> Vec<u8> {
121			encode_with_vec_prefix::<Self, _>(|v| {
122				match self.signature.as_ref() {
123					Some(s) => {
124						v.push(V4 | 0b1000_0000);
125						s.encode_to(v);
126					},
127					None => {
128						v.push(V4 & 0b0111_1111);
129					},
130				}
131				self.function.encode_to(v);
132			})
133		}
134	}
135
136	// https://github.com/paritytech/substrate/blob/1612e39131e3fe57ba4c78447fb1cbf7c4f8830e/primitives/runtime/src/generic/unchecked_extrinsic.rs#L250-L287
137	impl<Address, Call, Signature, SignedExtra> Decode
138		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
139	where
140		Address: Decode,
141		Signature: Decode,
142		Call: Decode,
143		SignedExtra: Decode,
144	{
145		fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
146			// This is a little more complicated than usual since the binary format must be compatible
147			// with substrate's generic `Vec<u8>` type. Basically this just means accepting that there
148			// will be a prefix of vector length (we don't need
149			// to use this).
150			let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
151
152			let version = input.read_byte()?;
153
154			let is_signed = version & 0b1000_0000 != 0;
155			let version = version & 0b0111_1111;
156			if version != V4 {
157				return Err("Invalid transaction version".into())
158			}
159
160			Ok(UncheckedExtrinsicV4 {
161				signature: if is_signed { Some(Decode::decode(input)?) } else { None },
162				function: Decode::decode(input)?,
163			})
164		}
165	}
166
167	impl<Address, Call, Signature, SignedExtra> serde::Serialize
168		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
169	where
170		Address: Encode,
171		Signature: Encode,
172		Call: Encode,
173		SignedExtra: Encode,
174	{
175		fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error>
176		where
177			S: ::serde::Serializer,
178		{
179			self.using_encoded(|bytes| impl_serde::serialize::serialize(bytes, seq))
180		}
181	}
182
183	// https://github.com/paritytech/substrate/blob/1612e39131e3fe57ba4c78447fb1cbf7c4f8830e/primitives/runtime/src/generic/unchecked_extrinsic.rs#L346-L357
184	impl<'a, Address, Call, Signature, SignedExtra> serde::Deserialize<'a>
185		for UncheckedExtrinsicV4<Address, Call, Signature, SignedExtra>
186	where
187		Address: Decode,
188		Signature: Decode,
189		Call: Decode,
190		SignedExtra: Decode,
191	{
192		fn deserialize<D>(de: D) -> Result<Self, D::Error>
193		where
194			D: serde::Deserializer<'a>,
195		{
196			let r = impl_serde::serialize::deserialize(de)?;
197			Decode::decode(&mut &r[..])
198				.map_err(|e| serde::de::Error::custom(format!("Decode error: {e}")))
199		}
200	}
201
202	// https://github.com/paritytech/substrate/blob/1612e39131e3fe57ba4c78447fb1cbf7c4f8830e/primitives/runtime/src/generic/unchecked_extrinsic.rs#L376-L390
203	impl<Address, Call, Signature, Extra>
204		From<UncheckedExtrinsicV4<Address, Call, Signature, Extra>> for OpaqueExtrinsic
205	where
206		Address: Encode,
207		Signature: Encode,
208		Call: Encode,
209		Extra: Encode,
210	{
211		fn from(extrinsic: UncheckedExtrinsicV4<Address, Call, Signature, Extra>) -> Self {
212			Self::from_bytes(extrinsic.encode().as_slice()).expect(
213			"both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \
214				raw Vec<u8> encoding; qed",
215		)
216		}
217	}
218
219	/// Same function as in primitives::generic. Needed to be copied as it is private there.
220	fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
221		let size = core::mem::size_of::<T>();
222		let reserve = match size {
223			0..=0b0011_1111 => 1,
224			0b0100_0000..=0b0011_1111_1111_1111 => 2,
225			_ => 4,
226		};
227		let mut v = Vec::with_capacity(reserve + size);
228		v.resize(reserve, 0);
229		encoder(&mut v);
230
231		// need to prefix with the total length to ensure it's binary compatible with
232		// Vec<u8>.
233		let mut length: Vec<()> = Vec::new();
234		length.resize(v.len() - reserve, ());
235		length.using_encoded(|s| {
236			v.splice(0..reserve, s.iter().cloned());
237		});
238
239		v
240	}
241}