use sp_std::prelude::*;
#[cfg(feature = "std")]
use std::fmt;
use codec::{Compact, Decode, Encode, Error, Input};
use sp_core::blake2_256;
use sp_core::H256;
use sp_runtime::{generic::Era, MultiSignature};
pub use sp_runtime::{AccountId32 as AccountId, MultiAddress};
pub type AccountIndex = u64;
pub type GenericAddress = sp_runtime::MultiAddress<AccountId, ()>;
#[cfg_attr(feature = "std", derive(Debug))]
#[derive(Decode, Encode, Clone, Eq, PartialEq)]
pub struct GenericExtra(Era, Compact<u32>, Compact<u128>);
impl GenericExtra {
pub fn new(era: Era, nonce: u32) -> GenericExtra {
GenericExtra(era, Compact(nonce), Compact(0_u128))
}
}
impl Default for GenericExtra {
fn default() -> Self {
Self::new(Era::Immortal, 0)
}
}
pub type AdditionalSigned = (u32, u32, H256, H256, (), (), ());
#[derive(Encode)]
pub struct SignedPayload<'a, Call>((&'a Call, &'a GenericExtra, AdditionalSigned));
impl<'a, Call> SignedPayload<'a, Call>
where
Call: Encode,
{
pub fn from_raw(
call: &'a Call,
extra: &'a GenericExtra,
additional_signed: AdditionalSigned,
) -> Self {
Self((call, extra, additional_signed))
}
pub fn using_encoded<R, F: FnOnce(&[u8]) -> R>(&self, f: F) -> R {
self.0.using_encoded(|payload| {
if payload.len() > 256 {
f(&blake2_256(payload)[..])
} else {
f(payload)
}
})
}
}
#[derive(Clone, PartialEq)]
pub struct UncheckedExtrinsicV4<Call> {
pub signature: Option<(GenericAddress, MultiSignature, GenericExtra)>,
pub function: Call,
}
impl<Call> UncheckedExtrinsicV4<Call>
where
Call: Encode,
{
pub fn new_signed(
function: Call,
signed: GenericAddress,
signature: MultiSignature,
extra: GenericExtra,
) -> Self {
UncheckedExtrinsicV4 {
signature: Some((signed, signature, extra)),
function,
}
}
#[cfg(feature = "std")]
pub fn hex_encode(&self) -> String {
let mut hex_str = hex::encode(self.encode());
hex_str.insert_str(0, "0x");
hex_str
}
}
#[cfg(feature = "std")]
impl<Call> fmt::Debug for UncheckedExtrinsicV4<Call>
where
Call: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"UncheckedExtrinsic({:?}, {:?})",
self.signature.as_ref().map(|x| (&x.0, &x.2)),
self.function
)
}
}
const V4: u8 = 4;
impl<Call> Encode for UncheckedExtrinsicV4<Call>
where
Call: Encode,
{
fn encode(&self) -> Vec<u8> {
encode_with_vec_prefix::<Self, _>(|v| {
match self.signature.as_ref() {
Some(s) => {
v.push(V4 | 0b1000_0000);
s.encode_to(v);
}
None => {
v.push(V4 & 0b0111_1111);
}
}
self.function.encode_to(v);
})
}
}
impl<Call> Decode for UncheckedExtrinsicV4<Call>
where
Call: Decode + Encode,
{
fn decode<I: Input>(input: &mut I) -> Result<Self, Error> {
let _length_do_not_remove_me_see_above: Vec<()> = Decode::decode(input)?;
let version = input.read_byte()?;
let is_signed = version & 0b1000_0000 != 0;
let version = version & 0b0111_1111;
if version != V4 {
return Err("Invalid transaction version".into());
}
Ok(UncheckedExtrinsicV4 {
signature: if is_signed {
Some(Decode::decode(input)?)
} else {
None
},
function: Decode::decode(input)?,
})
}
}
fn encode_with_vec_prefix<T: Encode, F: Fn(&mut Vec<u8>)>(encoder: F) -> Vec<u8> {
let size = sp_std::mem::size_of::<T>();
let reserve = match size {
0..=0b0011_1111 => 1,
0b0100_0000..=0b0011_1111_1111_1111 => 2,
_ => 4,
};
let mut v = Vec::with_capacity(reserve + size);
v.resize(reserve, 0);
encoder(&mut v);
let mut length: Vec<()> = Vec::new();
length.resize(v.len() - reserve, ());
length.using_encoded(|s| {
v.splice(0..reserve, s.iter().cloned());
});
v
}
#[cfg(test)]
mod tests {
use super::*;
use crate::extrinsic::xt_primitives::{GenericAddress, GenericExtra};
use sp_runtime::MultiSignature;
#[test]
fn encode_decode_roundtrip_works() {
let xt = UncheckedExtrinsicV4::new_signed(
vec![1, 1, 1],
GenericAddress::default(),
MultiSignature::default(),
GenericExtra::default(),
);
let xt_enc = xt.encode();
assert_eq!(xt, Decode::decode(&mut xt_enc.as_slice()).unwrap())
}
}