1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright 2019-2024 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.

//! The "default" Substrate/Polkadot UncheckedExtrinsic.
//! This is used in codegen for runtime API calls.
//!
//! The inner bytes represent the encoded extrinsic expected by the
//! runtime APIs. Deriving `EncodeAsType` would lead to the inner
//! bytes to be re-encoded (length prefixed).

use core::marker::PhantomData;

use codec::{Decode, Encode};
use scale_decode::{visitor::DecodeAsTypeResult, DecodeAsType, IntoVisitor, TypeResolver, Visitor};

use super::{Encoded, Static};
use alloc::vec::Vec;

/// The unchecked extrinsic from substrate.
#[derive(Clone, Debug, Eq, PartialEq, Encode)]
pub struct UncheckedExtrinsic<Address, Call, Signature, Extra>(
    Static<Encoded>,
    #[codec(skip)] PhantomData<(Address, Call, Signature, Extra)>,
);

impl<Address, Call, Signature, Extra> UncheckedExtrinsic<Address, Call, Signature, Extra> {
    /// Construct a new [`UncheckedExtrinsic`].
    pub fn new(bytes: Vec<u8>) -> Self {
        Self(Static(Encoded(bytes)), PhantomData)
    }

    /// Get the bytes of the encoded extrinsic.
    pub fn bytes(&self) -> &[u8] {
        self.0 .0 .0.as_slice()
    }
}

impl<Address, Call, Signature, Extra> Decode
    for UncheckedExtrinsic<Address, Call, Signature, Extra>
{
    fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
        // The bytes for an UncheckedExtrinsic are first a compact
        // encoded length, and then the bytes following. This is the
        // same encoding as a Vec, so easiest ATM is just to decode
        // into that, and then encode the vec bytes to get our extrinsic
        // bytes, which we save into an `Encoded` to preserve as-is.
        let xt_vec: Vec<u8> = Decode::decode(input)?;
        Ok(UncheckedExtrinsic::new(xt_vec))
    }
}

impl<Address, Call, Signature, Extra> scale_encode::EncodeAsType
    for UncheckedExtrinsic<Address, Call, Signature, Extra>
{
    fn encode_as_type_to<R: TypeResolver>(
        &self,
        type_id: R::TypeId,
        types: &R,
        out: &mut Vec<u8>,
    ) -> Result<(), scale_encode::Error> {
        self.0.encode_as_type_to(type_id, types, out)
    }
}

impl<Address, Call, Signature, Extra> From<Vec<u8>>
    for UncheckedExtrinsic<Address, Call, Signature, Extra>
{
    fn from(bytes: Vec<u8>) -> Self {
        UncheckedExtrinsic::new(bytes)
    }
}

impl<Address, Call, Signature, Extra> From<UncheckedExtrinsic<Address, Call, Signature, Extra>>
    for Vec<u8>
{
    fn from(bytes: UncheckedExtrinsic<Address, Call, Signature, Extra>) -> Self {
        bytes.0 .0 .0
    }
}

pub struct UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R: TypeResolver>(
    PhantomData<(Address, Call, Signature, Extra, R)>,
);

impl<Address, Call, Signature, Extra, R: TypeResolver> Visitor
    for UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R>
{
    type Value<'scale, 'info> = UncheckedExtrinsic<Address, Call, Signature, Extra>;
    type Error = scale_decode::Error;
    type TypeResolver = R;

    fn unchecked_decode_as_type<'scale, 'info>(
        self,
        input: &mut &'scale [u8],
        type_id: R::TypeId,
        types: &'info R,
    ) -> DecodeAsTypeResult<Self, Result<Self::Value<'scale, 'info>, Self::Error>> {
        DecodeAsTypeResult::Decoded(Self::Value::decode_as_type(input, type_id, types))
    }
}

impl<Address, Call, Signature, Extra> IntoVisitor
    for UncheckedExtrinsic<Address, Call, Signature, Extra>
{
    type AnyVisitor<R: TypeResolver> =
        UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R>;

    fn into_visitor<R: TypeResolver>(
    ) -> UncheckedExtrinsicDecodeAsTypeVisitor<Address, Call, Signature, Extra, R> {
        UncheckedExtrinsicDecodeAsTypeVisitor(PhantomData)
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    use alloc::vec;

    #[test]
    fn unchecked_extrinsic_encoding() {
        // A tx is basically some bytes with a compact length prefix; ie an encoded vec:
        let tx_bytes = vec![1u8, 2, 3].encode();

        let unchecked_extrinsic = UncheckedExtrinsic::<(), (), (), ()>::new(tx_bytes.clone());
        let encoded_tx_bytes = unchecked_extrinsic.encode();

        // The encoded representation must not alter the provided bytes.
        assert_eq!(tx_bytes, encoded_tx_bytes);

        // However, for decoding we expect to be able to read the extrinsic from the wire
        // which would be length prefixed.
        let decoded_tx = UncheckedExtrinsic::<(), (), (), ()>::decode(&mut &tx_bytes[..]).unwrap();
        let decoded_tx_bytes = decoded_tx.bytes();
        let encoded_tx_bytes = decoded_tx.encode();

        assert_eq!(decoded_tx_bytes, encoded_tx_bytes);
        // Ensure we can decode the tx and fetch only the tx bytes.
        assert_eq!(vec![1, 2, 3], encoded_tx_bytes);
    }
}