pub mod payload;
pub mod signer;
use crate::config::{Config, ExtrinsicParams, ExtrinsicParamsEncoder, Hasher};
use crate::error::{Error, MetadataError};
use crate::metadata::Metadata;
use crate::utils::Encoded;
use alloc::borrow::{Cow, ToOwned};
use alloc::vec::Vec;
use codec::{Compact, Encode};
use payload::Payload;
use signer::Signer as SignerT;
use sp_crypto_hashing::blake2_256;
pub use crate::client::{ClientState, RuntimeVersion};
pub fn validate<Call: Payload>(call: &Call, metadata: &Metadata) -> Result<(), Error> {
if let Some(details) = call.validation_details() {
let expected_hash = metadata
.pallet_by_name_err(details.pallet_name)?
.call_hash(details.call_name)
.ok_or_else(|| MetadataError::CallNameNotFound(details.call_name.to_owned()))?;
if details.hash != expected_hash {
return Err(MetadataError::IncompatibleCodegen.into());
}
}
Ok(())
}
pub fn call_data<Call: Payload>(call: &Call, metadata: &Metadata) -> Result<Vec<u8>, Error> {
let mut bytes = Vec::new();
call.encode_call_data_to(metadata, &mut bytes)?;
Ok(bytes)
}
pub fn create_unsigned<T: Config, Call: Payload>(
call: &Call,
metadata: &Metadata,
) -> Result<Transaction<T>, Error> {
validate(call, metadata)?;
let extrinsic = {
let mut encoded_inner = Vec::new();
4u8.encode_to(&mut encoded_inner);
call.encode_call_data_to(metadata, &mut encoded_inner)?;
let len = Compact(
u32::try_from(encoded_inner.len()).expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
Ok(Transaction::from_bytes(extrinsic))
}
pub fn create_partial_signed<T: Config, Call: Payload>(
call: &Call,
client_state: &ClientState<T>,
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
) -> Result<PartialTransaction<T>, Error> {
validate(call, &client_state.metadata)?;
let call_data = call_data(call, &client_state.metadata)?;
let additional_and_extra_params =
<T::ExtrinsicParams as ExtrinsicParams<T>>::new(client_state, params)?;
Ok(PartialTransaction {
call_data,
additional_and_extra_params,
})
}
pub fn create_signed<T, Call, Signer>(
call: &Call,
client_state: &ClientState<T>,
signer: &Signer,
params: <T::ExtrinsicParams as ExtrinsicParams<T>>::Params,
) -> Result<Transaction<T>, Error>
where
T: Config,
Call: Payload,
Signer: SignerT<T>,
{
validate(call, &client_state.metadata)?;
let partial_signed = create_partial_signed(call, client_state, params)?;
Ok(partial_signed.sign(signer))
}
pub struct PartialTransaction<T: Config> {
call_data: Vec<u8>,
additional_and_extra_params: T::ExtrinsicParams,
}
impl<T: Config> PartialTransaction<T> {
fn with_signer_payload<F, R>(&self, f: F) -> R
where
F: for<'a> FnOnce(Cow<'a, [u8]>) -> R,
{
let mut bytes = self.call_data.clone();
self.additional_and_extra_params.encode_extra_to(&mut bytes);
self.additional_and_extra_params
.encode_additional_to(&mut bytes);
if bytes.len() > 256 {
f(Cow::Borrowed(blake2_256(&bytes).as_ref()))
} else {
f(Cow::Owned(bytes))
}
}
pub fn signer_payload(&self) -> Vec<u8> {
self.with_signer_payload(|bytes| bytes.to_vec())
}
pub fn call_data(&self) -> &[u8] {
&self.call_data
}
pub fn sign<Signer>(&self, signer: &Signer) -> Transaction<T>
where
Signer: SignerT<T>,
{
let signature = self.with_signer_payload(|bytes| signer.sign(&bytes));
self.sign_with_address_and_signature(&signer.address(), &signature)
}
pub fn sign_with_address_and_signature(
&self,
address: &T::Address,
signature: &T::Signature,
) -> Transaction<T> {
let extrinsic = {
let mut encoded_inner = Vec::new();
(0b10000000 + 4u8).encode_to(&mut encoded_inner);
address.encode_to(&mut encoded_inner);
signature.encode_to(&mut encoded_inner);
self.additional_and_extra_params
.encode_extra_to(&mut encoded_inner);
encoded_inner.extend(&self.call_data);
let len = Compact(
u32::try_from(encoded_inner.len()).expect("extrinsic size expected to be <4GB"),
);
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};
Transaction::from_bytes(extrinsic)
}
}
pub struct Transaction<T> {
encoded: Encoded,
marker: core::marker::PhantomData<T>,
}
impl<T: Config> Transaction<T> {
pub fn from_bytes(tx_bytes: Vec<u8>) -> Self {
Self {
encoded: Encoded(tx_bytes),
marker: core::marker::PhantomData,
}
}
pub fn hash(&self) -> T::Hash {
T::Hasher::hash_of(&self.encoded)
}
pub fn encoded(&self) -> &[u8] {
&self.encoded.0
}
pub fn into_encoded(self) -> Vec<u8> {
self.encoded.0
}
}