subxt_core/utils/
unchecked_extrinsic.rs

1// Copyright 2019-2024 Parity Technologies (UK) Ltd.
2// This file is dual-licensed as Apache-2.0 or GPL-3.0.
3// see LICENSE for license details.
4
5//! The "default" Substrate/Polkadot UncheckedExtrinsic.
6//! This is used in codegen for runtime API calls.
7//!
8//! The inner bytes represent the encoded extrinsic expected by the
9//! runtime APIs. Deriving `EncodeAsType` would lead to the inner
10//! bytes to be re-encoded (length prefixed).
11
12use core::marker::PhantomData;
13
14use codec::{Decode, Encode};
15use scale_decode::{DecodeAsType, IntoVisitor, TypeResolver, Visitor, visitor::DecodeAsTypeResult};
16
17use super::{Encoded, Static};
18use alloc::vec::Vec;
19
20/// The unchecked extrinsic from substrate.
21#[derive(Clone, Debug, Eq, PartialEq, Encode)]
22pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>(
23    Static<Encoded>,
24    #[codec(skip)] PhantomData<(Address, Call, Signature, Extra)>,
25);
26
27impl<Address, Call, Signature, Extra> UncheckedExtrinsic<Address, Call, Signature, Extra> {
28    /// Construct a new [`UncheckedExtrinsic`].
29    pub fn new(bytes: Vec<u8>) -> Self {
30        Self(Static(Encoded(bytes)), PhantomData)
31    }
32
33    /// Get the bytes of the encoded extrinsic.
34    pub fn bytes(&self) -> &[u8] {
35        self.0.0.0.as_slice()
36    }
37}
38
39impl<Address, Call, Signature, Extra> Decode
40    for UncheckedExtrinsic<Address, Call, Signature, Extra>
41{
42    fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
43        // The bytes for an UncheckedExtrinsic are first a compact
44        // encoded length, and then the bytes following. This is the
45        // same encoding as a Vec, so easiest ATM is just to decode
46        // into that, and then encode the vec bytes to get our extrinsic
47        // bytes, which we save into an `Encoded` to preserve as-is.
48        let xt_vec: Vec<u8> = Decode::decode(input)?;
49        Ok(UncheckedExtrinsic::new(xt_vec))
50    }
51}
52
53impl<Address, Call, Signature, Extra> scale_encode::EncodeAsType
54    for UncheckedExtrinsic<Address, Call, Signature, Extra>
55{
56    fn encode_as_type_to<R: TypeResolver>(
57        &self,
58        type_id: R::TypeId,
59        types: &R,
60        out: &mut Vec<u8>,
61    ) -> Result<(), scale_encode::Error> {
62        self.0.encode_as_type_to(type_id, types, out)
63    }
64}
65
66impl<Address, Call, Signature, Extra> From<Vec<u8>>
67    for UncheckedExtrinsic<Address, Call, Signature, Extra>
68{
69    fn from(bytes: Vec<u8>) -> Self {
70        UncheckedExtrinsic::new(bytes)
71    }
72}
73
74impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
75    for Vec<u8>
76{
77    fn from(bytes: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
78        bytes.0.0.0
79    }
80}
81
82pub struct UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R: TypeResolver>(
83    PhantomData<(Address, Call, Signature, Extra, R)>,
84);
85
86impl<Address, Call, Signature, Extra, R: TypeResolver> Visitor
87    for UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R>
88{
89    type Value<'scale, 'info> = UncheckedExtrinsic<Address, Call, Signature, Extra>;
90    type Error = scale_decode::Error;
91    type TypeResolver = R;
92
93    fn unchecked_decode_as_type<'scale, 'info>(
94        self,
95        input: &mut &'scale [u8],
96        type_id: R::TypeId,
97        types: &'info R,
98    ) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>> {
99        DecodeAsTypeResult::Decoded(Self::Value::decode_as_type(input, type_id, types))
100    }
101}
102
103impl<Address, Call, Signature, Extra> IntoVisitor
104    for UncheckedExtrinsic<Address, Call, Signature, Extra>
105{
106    type AnyVisitor<R: TypeResolver> =
107        UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R>;
108
109    fn into_visitor<R: TypeResolver>()
110    -> UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R> {
111        UncheckedExtrinsicDecodeAsTypeVisitor(PhantomData)
112    }
113}
114
115#[cfg(test)]
116pub mod tests {
117    use super::*;
118
119    use alloc::vec;
120
121    #[test]
122    fn unchecked_extrinsic_encoding() {
123        // A tx is basically some bytes with a compact length prefix; ie an encoded vec:
124        let tx_bytes = vec![1u8, 2, 3].encode();
125
126        let unchecked_extrinsic = UncheckedExtrinsic::<(), (), (), ()>::new(tx_bytes.clone());
127        let encoded_tx_bytes = unchecked_extrinsic.encode();
128
129        // The encoded representation must not alter the provided bytes.
130        assert_eq!(tx_bytes, encoded_tx_bytes);
131
132        // However, for decoding we expect to be able to read the extrinsic from the wire
133        // which would be length prefixed.
134        let decoded_tx = UncheckedExtrinsic::<(), (), (), ()>::decode(&mut &tx_bytes[..]).unwrap();
135        let decoded_tx_bytes = decoded_tx.bytes();
136        let encoded_tx_bytes = decoded_tx.encode();
137
138        assert_eq!(decoded_tx_bytes, encoded_tx_bytes);
139        // Ensure we can decode the tx and fetch only the tx bytes.
140        assert_eq!(vec![1, 2, 3], encoded_tx_bytes);
141    }
142}