1use tetcore_std::{fmt, prelude::*};
21use tet_io::hashing::blake2_256;
22use codec::{Decode, Encode, EncodeLike, Input, Error};
23use crate::{
24 traits::{
25 self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, ExtrinsicMetadata,
26 IdentifyAccount,
27 },
28 generic::CheckedExtrinsic,
29 transaction_validity::{TransactionValidityError, InvalidTransaction},
30 OpaqueExtrinsic,
31};
32
33const EXTRINSIC_VERSION: u8 = 4;
35
36#[derive(PartialEq, Eq, Clone)]
39pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>
40where
41 Extra: SignedExtension
42{
43 pub signature: Option<(Address, Signature, Extra)>,
47 pub function: Call,
49}
50
51#[cfg(feature = "std")]
52impl<Address, Call, Signature, Extra> tetsy_util_mem::MallocSizeOf
53 for UncheckedExtrinsic<Address, Call, Signature, Extra>
54where
55 Extra: SignedExtension
56{
57 fn size_of(&self, _ops: &mut tetsy_util_mem::MallocSizeOfOps) -> usize {
58 0
60 }
61}
62
63impl<Address, Call, Signature, Extra: SignedExtension>
64 UncheckedExtrinsic<Address, Call, Signature, Extra>
65{
66 pub fn new_signed(
68 function: Call,
69 signed: Address,
70 signature: Signature,
71 extra: Extra
72 ) -> Self {
73 UncheckedExtrinsic {
74 signature: Some((signed, signature, extra)),
75 function,
76 }
77 }
78
79 pub fn new_unsigned(function: Call) -> Self {
81 UncheckedExtrinsic {
82 signature: None,
83 function,
84 }
85 }
86}
87
88impl<Address, Call, Signature, Extra: SignedExtension> Extrinsic
89 for UncheckedExtrinsic<Address, Call, Signature, Extra>
90{
91 type Call = Call;
92
93 type SignaturePayload = (
94 Address,
95 Signature,
96 Extra,
97 );
98
99 fn is_signed(&self) -> Option<bool> {
100 Some(self.signature.is_some())
101 }
102
103 fn new(function: Call, signed_data: Option<Self::SignaturePayload>) -> Option<Self> {
104 Some(if let Some((address, signature, extra)) = signed_data {
105 UncheckedExtrinsic::new_signed(function, address, signature, extra)
106 } else {
107 UncheckedExtrinsic::new_unsigned(function)
108 })
109 }
110}
111
112impl<Address, AccountId, Call, Signature, Extra, Lookup>
113 Checkable<Lookup>
114for
115 UncheckedExtrinsic<Address, Call, Signature, Extra>
116where
117 Address: Member + MaybeDisplay,
118 Call: Encode + Member,
119 Signature: Member + traits::Verify,
120 <Signature as traits::Verify>::Signer: IdentifyAccount<AccountId=AccountId>,
121 Extra: SignedExtension<AccountId=AccountId>,
122 AccountId: Member + MaybeDisplay,
123 Lookup: traits::Lookup<Source=Address, Target=AccountId>,
124{
125 type Checked = CheckedExtrinsic<AccountId, Call, Extra>;
126
127 fn check(self, lookup: &Lookup) -> Result<Self::Checked, TransactionValidityError> {
128 Ok(match self.signature {
129 Some((signed, signature, extra)) => {
130 let signed = lookup.lookup(signed)?;
131 let raw_payload = SignedPayload::new(self.function, extra)?;
132 if !raw_payload.using_encoded(|payload| signature.verify(payload, &signed)) {
133 return Err(InvalidTransaction::BadProof.into())
134 }
135
136 let (function, extra, _) = raw_payload.deconstruct();
137 CheckedExtrinsic {
138 signed: Some((signed, extra)),
139 function,
140 }
141 }
142 None => CheckedExtrinsic {
143 signed: None,
144 function: self.function,
145 },
146 })
147 }
148}
149
150impl<Address, Call, Signature, Extra> ExtrinsicMetadata
151 for UncheckedExtrinsic<Address, Call, Signature, Extra>
152 where
153 Extra: SignedExtension,
154{
155 const VERSION: u8 = EXTRINSIC_VERSION;
156 type SignedExtensions = Extra;
157}
158
159pub struct SignedPayload<Call, Extra: SignedExtension>((
165 Call,
166 Extra,
167 Extra::AdditionalSigned,
168));
169
170impl<Call, Extra> SignedPayload<Call, Extra> where
171 Call: Encode,
172 Extra: SignedExtension,
173{
174 pub fn new(call: Call, extra: Extra) -> Result<Self, TransactionValidityError> {
178 let additional_signed = extra.additional_signed()?;
179 let raw_payload = (call, extra, additional_signed);
180 Ok(Self(raw_payload))
181 }
182
183 pub fn from_raw(call: Call, extra: Extra, additional_signed: Extra::AdditionalSigned) -> Self {
185 Self((call, extra, additional_signed))
186 }
187
188 pub fn deconstruct(self) -> (Call, Extra, Extra::AdditionalSigned) {
190 self.0
191 }
192}
193
194impl<Call, Extra> Encode for SignedPayload<Call, Extra> where
195 Call: Encode,
196 Extra: SignedExtension,
197{
198 fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
202 self.0.using_encoded(|payload| {
203 if payload.len() > 256 {
204 f(&blake2_256(payload)[..])
205 } else {
206 f(payload)
207 }
208 })
209 }
210}
211
212impl<Call, Extra> EncodeLike for SignedPayload<Call, Extra>
213where
214 Call: Encode,
215 Extra: SignedExtension,
216{}
217
218impl<Address, Call, Signature, Extra> Decode
219 for UncheckedExtrinsic<Address, Call, Signature, Extra>
220where
221 Address: Decode,
222 Signature: Decode,
223 Call: Decode,
224 Extra: SignedExtension,
225{
226 fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
227 let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
232
233 let version = input.read_byte()?;
234
235 let is_signed = version & 0b1000_0000 != 0;
236 let version = version & 0b0111_1111;
237 if version != EXTRINSIC_VERSION {
238 return Err("Invalid transaction version".into());
239 }
240
241 Ok(UncheckedExtrinsic {
242 signature: if is_signed { Some(Decode::decode(input)?) } else { None },
243 function: Decode::decode(input)?,
244 })
245 }
246}
247
248impl<Address, Call, Signature, Extra> Encode
249 for UncheckedExtrinsic<Address, Call, Signature, Extra>
250where
251 Address: Encode,
252 Signature: Encode,
253 Call: Encode,
254 Extra: SignedExtension,
255{
256 fn encode(&self) -> Vec<u8> {
257 super::encode_with_vec_prefix::<Self, _>(|v| {
258 match self.signature.as_ref() {
260 Some(s) => {
261 v.push(EXTRINSIC_VERSION | 0b1000_0000);
262 s.encode_to(v);
263 }
264 None => {
265 v.push(EXTRINSIC_VERSION & 0b0111_1111);
266 }
267 }
268 self.function.encode_to(v);
269 })
270 }
271}
272
273impl<Address, Call, Signature, Extra> EncodeLike
274 for UncheckedExtrinsic<Address, Call, Signature, Extra>
275where
276 Address: Encode,
277 Signature: Encode,
278 Call: Encode,
279 Extra: SignedExtension,
280{}
281
282#[cfg(feature = "std")]
283impl<Address: Encode, Signature: Encode, Call: Encode, Extra: SignedExtension> serde::Serialize
284 for UncheckedExtrinsic<Address, Call, Signature, Extra>
285{
286 fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
287 self.using_encoded(|bytes| seq.serialize_bytes(bytes))
288 }
289}
290
291#[cfg(feature = "std")]
292impl<'a, Address: Decode, Signature: Decode, Call: Decode, Extra: SignedExtension> serde::Deserialize<'a>
293 for UncheckedExtrinsic<Address, Call, Signature, Extra>
294{
295 fn deserialize<D>(de: D) -> Result<Self, D::Error> where
296 D: serde::Deserializer<'a>,
297 {
298 let r = tet_core::bytes::deserialize(de)?;
299 Decode::decode(&mut &r[..])
300 .map_err(|e| serde::de::Error::custom(format!("Decode error: {}", e)))
301 }
302}
303
304impl<Address, Call, Signature, Extra> fmt::Debug
305 for UncheckedExtrinsic<Address, Call, Signature, Extra>
306where
307 Address: fmt::Debug,
308 Call: fmt::Debug,
309 Extra: SignedExtension,
310{
311 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
312 write!(
313 f,
314 "UncheckedExtrinsic({:?}, {:?})",
315 self.signature.as_ref().map(|x| (&x.0, &x.2)),
316 self.function,
317 )
318 }
319}
320
321impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
322 for OpaqueExtrinsic
323where
324 Address: Encode,
325 Signature: Encode,
326 Call: Encode,
327 Extra: SignedExtension,
328{
329 fn from(extrinsic: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
330 OpaqueExtrinsic::from_bytes(extrinsic.encode().as_slice())
331 .expect(
332 "both OpaqueExtrinsic and UncheckedExtrinsic have encoding that is compatible with \
333 raw Vec<u8> encoding; qed"
334 )
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341 use tet_io::hashing::blake2_256;
342 use crate::codec::{Encode, Decode};
343 use crate::traits::{SignedExtension, IdentityLookup};
344 use crate::testing::TestSignature as TestSig;
345
346 type TestContext = IdentityLookup<u64>;
347 type TestAccountId = u64;
348 type TestCall = Vec<u8>;
349
350 const TEST_ACCOUNT: TestAccountId = 0;
351
352 #[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Ord, PartialOrd)]
354 struct TestExtra;
355 impl SignedExtension for TestExtra {
356 const IDENTIFIER: &'static str = "TestExtra";
357 type AccountId = u64;
358 type Call = ();
359 type AdditionalSigned = ();
360 type Pre = ();
361
362 fn additional_signed(&self) -> tetcore_std::result::Result<(), TransactionValidityError> { Ok(()) }
363 }
364
365 type Ex = UncheckedExtrinsic<TestAccountId, TestCall, TestSig, TestExtra>;
366 type CEx = CheckedExtrinsic<TestAccountId, TestCall, TestExtra>;
367
368 #[test]
369 fn unsigned_codec_should_work() {
370 let ux = Ex::new_unsigned(vec![0u8; 0]);
371 let encoded = ux.encode();
372 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
373 }
374
375 #[test]
376 fn signed_codec_should_work() {
377 let ux = Ex::new_signed(
378 vec![0u8; 0],
379 TEST_ACCOUNT,
380 TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
381 TestExtra
382 );
383 let encoded = ux.encode();
384 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
385 }
386
387 #[test]
388 fn large_signed_codec_should_work() {
389 let ux = Ex::new_signed(
390 vec![0u8; 0],
391 TEST_ACCOUNT,
392 TestSig(TEST_ACCOUNT, (vec![0u8; 257], TestExtra)
393 .using_encoded(blake2_256)[..].to_owned()),
394 TestExtra
395 );
396 let encoded = ux.encode();
397 assert_eq!(Ex::decode(&mut &encoded[..]), Ok(ux));
398 }
399
400 #[test]
401 fn unsigned_check_should_work() {
402 let ux = Ex::new_unsigned(vec![0u8; 0]);
403 assert!(!ux.is_signed().unwrap_or(false));
404 assert!(<Ex as Checkable<TestContext>>::check(ux, &Default::default()).is_ok());
405 }
406
407 #[test]
408 fn badly_signed_check_should_fail() {
409 let ux = Ex::new_signed(
410 vec![0u8; 0],
411 TEST_ACCOUNT,
412 TestSig(TEST_ACCOUNT, vec![0u8; 0]),
413 TestExtra,
414 );
415 assert!(ux.is_signed().unwrap_or(false));
416 assert_eq!(
417 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
418 Err(InvalidTransaction::BadProof.into()),
419 );
420 }
421
422 #[test]
423 fn signed_check_should_work() {
424 let ux = Ex::new_signed(
425 vec![0u8; 0],
426 TEST_ACCOUNT,
427 TestSig(TEST_ACCOUNT, (vec![0u8; 0], TestExtra).encode()),
428 TestExtra,
429 );
430 assert!(ux.is_signed().unwrap_or(false));
431 assert_eq!(
432 <Ex as Checkable<TestContext>>::check(ux, &Default::default()),
433 Ok(CEx { signed: Some((TEST_ACCOUNT, TestExtra)), function: vec![0u8; 0] }),
434 );
435 }
436
437 #[test]
438 fn encoding_matches_vec() {
439 let ex = Ex::new_unsigned(vec![0u8; 0]);
440 let encoded = ex.encode();
441 let decoded = Ex::decode(&mut encoded.as_slice()).unwrap();
442 assert_eq!(decoded, ex);
443 let as_vec: Vec<u8> = Decode::decode(&mut encoded.as_slice()).unwrap();
444 assert_eq!(as_vec.encode(), encoded);
445 }
446
447 #[test]
448 fn conversion_to_opaque() {
449 let ux = Ex::new_unsigned(vec![0u8; 0]);
450 let encoded = ux.encode();
451 let opaque: OpaqueExtrinsic = ux.into();
452 let opaque_encoded = opaque.encode();
453 assert_eq!(opaque_encoded, encoded);
454 }
455}