Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify creating and signing extrinsics #490

Merged
merged 13 commits into from
Mar 30, 2022
2 changes: 1 addition & 1 deletion codegen/src/api/calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn generate_calls(
impl<'a, T, X> TransactionApi<'a, T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
X: ::subxt::ExtrinsicParams<T>,
{
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
Expand Down
6 changes: 3 additions & 3 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl RuntimeGenerator {
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>
X: ::subxt::ExtrinsicParams<T>
{
fn from(client: ::subxt::Client<T>) -> Self {
Self { client, marker: ::core::marker::PhantomData }
Expand All @@ -298,7 +298,7 @@ impl RuntimeGenerator {
impl<'a, T, X> RuntimeApi<T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
X: ::subxt::ExtrinsicParams<T>,
{
pub fn constants(&'a self) -> ConstantsApi {
ConstantsApi
Expand Down Expand Up @@ -368,7 +368,7 @@ impl RuntimeGenerator {
impl<'a, T, X> TransactionApi<'a, T, X>
where
T: ::subxt::Config,
X: ::subxt::SignedExtra<T>,
X: ::subxt::ExtrinsicParams<T>,
{
#(
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X> {
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/balance_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit(&signer)
.sign_and_submit_default(&signer)
.await?;

println!("Balance transfer extrinsic submitted: {}", hash);
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/custom_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit(&signer)
.sign_and_submit_default(&signer)
.await?;

println!("Balance transfer extrinsic submitted: {}", hash);
Expand Down
6 changes: 3 additions & 3 deletions examples/examples/submit_and_watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ async fn simple_transfer() -> Result<(), Box<dyn std::error::Error>> {
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit_then_watch(&signer)
.sign_and_submit_then_watch_default(&signer)
.await?
.wait_for_finalized_success()
.await?;
Expand Down Expand Up @@ -93,7 +93,7 @@ async fn simple_transfer_separate_events() -> Result<(), Box<dyn std::error::Err
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit_then_watch(&signer)
.sign_and_submit_then_watch_default(&signer)
.await?
.wait_for_finalized()
.await?;
Expand Down Expand Up @@ -144,7 +144,7 @@ async fn handle_transfer_events() -> Result<(), Box<dyn std::error::Error>> {
.tx()
.balances()
.transfer(dest, 10_000)
.sign_and_submit_then_watch(&signer)
.sign_and_submit_then_watch_default(&signer)
.await?;

while let Some(ev) = balance_transfer_progress.next().await {
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/subscribe_all_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), transfer_amount)
.sign_and_submit(&signer)
.sign_and_submit_default(&signer)
.await
.unwrap();

Expand Down
2 changes: 1 addition & 1 deletion examples/examples/subscribe_one_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
.sign_and_submit(&signer)
.sign_and_submit_default(&signer)
.await
.unwrap();
async_std::task::sleep(Duration::from_secs(10)).await;
Expand Down
2 changes: 1 addition & 1 deletion examples/examples/subscribe_some_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
api.tx()
.balances()
.transfer(AccountKeyring::Bob.to_account_id().into(), 1_000_000_000)
.sign_and_submit(&signer)
.sign_and_submit_default(&signer)
.await
.unwrap();
async_std::task::sleep(Duration::from_secs(10)).await;
Expand Down
153 changes: 112 additions & 41 deletions subxt/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,24 @@ use crate::{
BasicError,
HasModuleError,
},
extrinsic::{
self,
SignedExtra,
Signer,
UncheckedExtrinsic,
},
rpc::{
Rpc,
RpcClient,
RuntimeVersion,
SystemProperties,
},
extrinsic::{
Signer,
ExtrinsicParams,
},
storage::StorageClient,
transaction::TransactionProgress,
Call,
Config,
Encoded,
Metadata,
};
use codec::Decode;
use codec::{Compact, Decode, Encode};
use derivative::Derivative;
use std::sync::Arc;

Expand Down Expand Up @@ -188,7 +187,7 @@ pub struct SubmittableExtrinsic<'client, T: Config, X, C, E: Decode, Evs: Decode
impl<'client, T, X, C, E, Evs> SubmittableExtrinsic<'client, T, X, C, E, Evs>
where
T: Config,
X: SignedExtra<T>,
X: ExtrinsicParams<T>,
C: Call + Send + Sync,
E: Decode + HasModuleError,
Evs: Decode,
Expand All @@ -202,20 +201,33 @@ where
}
}

/// Creates and signs an extrinsic and submits it to the chain. Passes default parameters
/// to construct the "signed extra" and "additional" payloads needed by the extrinsic.
///
/// Returns a [`TransactionProgress`], which can be used to track the status of the transaction
/// and obtain details about it, once it has made it into a block.
pub async fn sign_and_submit_then_watch_default(
self,
signer: &(dyn Signer<T> + Send + Sync),
) -> Result<TransactionProgress<'client, T, E, Evs>, BasicError>
where X::OtherParams: Default
{
self.sign_and_submit_then_watch(signer, Default::default()).await
}

/// Creates and signs an extrinsic and submits it to the chain.
///
/// Returns a [`TransactionProgress`], which can be used to track the status of the transaction
/// and obtain details about it, once it has made it into a block.
pub async fn sign_and_submit_then_watch(
self,
signer: &(dyn Signer<T, X> + Send + Sync),
signer: &(dyn Signer<T> + Send + Sync),
other_params: X::OtherParams,
) -> Result<TransactionProgress<'client, T, E, Evs>, BasicError>
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
where X::OtherParams: Default
{
// Sign the call data to create our extrinsic.
let extrinsic = self.create_signed(signer, Default::default()).await?;
let extrinsic = self.create_signed(signer, other_params).await?;

// Get a hash of the extrinsic (we'll need this later).
let ext_hash = T::Hashing::hash_of(&extrinsic);
Expand All @@ -226,6 +238,25 @@ where
Ok(TransactionProgress::new(sub, self.client, ext_hash))
}

/// Creates and signs an extrinsic and submits to the chain for block inclusion. Passes
/// default parameters to construct the "signed extra" and "additional" payloads needed
/// by the extrinsic.
///
/// Returns `Ok` with the extrinsic hash if it is valid extrinsic.
///
/// # Note
///
/// Success does not mean the extrinsic has been included in the block, just that it is valid
/// and has been included in the transaction pool.
pub async fn sign_and_submit_default(
self,
signer: &(dyn Signer<T> + Send + Sync),
) -> Result<T::Hash, BasicError>
where X::OtherParams: Default
{
self.sign_and_submit(signer, Default::default()).await
}

/// Creates and signs an extrinsic and submits to the chain for block inclusion.
///
/// Returns `Ok` with the extrinsic hash if it is valid extrinsic.
Expand All @@ -236,26 +267,22 @@ where
/// and has been included in the transaction pool.
pub async fn sign_and_submit(
self,
signer: &(dyn Signer<T, X> + Send + Sync),
signer: &(dyn Signer<T> + Send + Sync),
other_params: X::OtherParams,
) -> Result<T::Hash, BasicError>
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
where X::OtherParams: Default
{
let extrinsic = self.create_signed(signer, Default::default()).await?;
let extrinsic = self.create_signed(signer, other_params).await?;
self.client.rpc().submit_extrinsic(extrinsic).await
}

/// Creates a signed extrinsic.
/// Creates a returns a raw signed extrinsic, without submitting it.
pub async fn create_signed(
&self,
signer: &(dyn Signer<T, X> + Send + Sync),
additional_params: X::Parameters,
) -> Result<UncheckedExtrinsic<T, X>, BasicError>
where
<<X as SignedExtra<T>>::Extra as SignedExtension>::AdditionalSigned:
Send + Sync + 'static,
{
signer: &(dyn Signer<T> + Send + Sync),
other_params: X::OtherParams,
) -> Result<Encoded, BasicError> {
// 1. get nonce
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! This does make more sense when following the code logic!

let account_nonce = if let Some(nonce) = signer.nonce() {
nonce
} else {
Expand All @@ -264,21 +291,65 @@ where
.system_account_next_index(signer.account_id())
.await?
};
let call = self
.client
.metadata()
.pallet(C::PALLET)
.and_then(|pallet| pallet.encode_call(&self.call))?;

let signed = extrinsic::create_signed(
&self.client.runtime_version,
self.client.genesis_hash,

// 2. SCALE encode call data to bytes (pallet u8, call u8, call params).
let call_data: Vec<u8> = {
let mut encoded = Vec::new();
let pallet = self
.client
.metadata()
.pallet(C::PALLET)?;
encoded.push(pallet.index());
encoded.push(pallet.call_index::<C>()?);
self.call.encode_to(&mut encoded);
encoded
};

// 3. construct our custom additional/extra params.
let additional_and_extra_params = X::new(
self.client.runtime_version.spec_version,
self.client.runtime_version.transaction_version,
account_nonce,
call,
signer,
additional_params,
)
.await?;
Ok(signed)
self.client.genesis_hash,
other_params,
);

// 4. construct payload to be signed
let payload_to_be_signed = {
let mut encoded = call_data.clone();
additional_and_extra_params.encode_extra_to(&mut encoded);
additional_and_extra_params.encode_additional_to(&mut encoded);
if encoded.len() > 256 {
sp_core::blake2_256(&encoded).to_vec()
} else {
encoded
}
};

// 5. Encode extrinsic, now that we have the parts we need.
// (this may not be 100% correct but should be close enough for this example)
ascjones marked this conversation as resolved.
Show resolved Hide resolved
let extrinsic = {
let mut encoded_inner = Vec::new();
// "is signed" + transaction protocol version (4)
(0b10000000 + 4u8).encode_to(&mut encoded_inner);
// from address for signature
signer.account_id().encode_to(&mut encoded_inner);
// the signature bytes
signer.encode_signature_to(&payload_to_be_signed, &mut encoded_inner);
// attach custom extra params
additional_and_extra_params.encode_extra_to(&mut encoded_inner);
// and now, call data
encoded_inner.extend(call_data);
// now, prefix byte length:
let len = Compact(encoded_inner.len() as u64);
ascjones marked this conversation as resolved.
Show resolved Hide resolved
let mut encoded = Vec::new();
len.encode_to(&mut encoded);
encoded.extend(encoded_inner);
encoded
};

// Wrap in Encoded to ensure that any more "encode" calls leave it in the right state.
// maybe we can just return the raw bytes..
Ok(Encoded(extrinsic))
}
}
3 changes: 2 additions & 1 deletion subxt/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ pub trait Config: 'static {
+ Default
+ AtLeast32Bit
+ Copy
+ scale_info::TypeInfo;
+ scale_info::TypeInfo
+ Into<u64>;

/// The block number type used by the runtime.
type BlockNumber: Parameter
Expand Down
Loading