mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Allow creating/submitting unsigned transactions, too. (#625)
* add tx.create_unsigned method * check unsigned extrinsic shape against pjs output to give us some confidence it's ok
This commit is contained in:
+1
-1
@@ -42,7 +42,7 @@ pub use self::{
|
||||
Signer,
|
||||
},
|
||||
tx_client::{
|
||||
SignedSubmittableExtrinsic,
|
||||
SubmittableExtrinsic,
|
||||
TxClient,
|
||||
},
|
||||
tx_payload::{
|
||||
|
||||
+55
-11
@@ -79,14 +79,52 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
/// Creates a raw signed extrinsic, without submitting it.
|
||||
/// Creates an unsigned extrinsic without submitting it.
|
||||
pub async fn create_unsigned<Call>(
|
||||
&self,
|
||||
call: &Call,
|
||||
) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
{
|
||||
// 1. Validate this call against the current node metadata if the call comes
|
||||
// with a hash allowing us to do so.
|
||||
self.validate(call)?;
|
||||
|
||||
// 2. Encode extrinsic
|
||||
let extrinsic = {
|
||||
let mut encoded_inner = Vec::new();
|
||||
// transaction protocol version (4) (is not signed, so no 1 bit at the front).
|
||||
4u8.encode_to(&mut encoded_inner);
|
||||
// encode call data after this byte.
|
||||
call.encode_call_data(&self.client.metadata(), &mut encoded_inner)?;
|
||||
// now, prefix byte length:
|
||||
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
|
||||
};
|
||||
|
||||
// Wrap in Encoded to ensure that any more "encode" calls leave it in the right state.
|
||||
Ok(SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
encoded: Encoded(extrinsic),
|
||||
marker: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a raw signed extrinsic without submitting it.
|
||||
pub async fn create_signed_with_nonce<Call>(
|
||||
&self,
|
||||
call: &Call,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
account_nonce: T::Index,
|
||||
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
|
||||
) -> Result<SignedSubmittableExtrinsic<T, C>, Error>
|
||||
) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
{
|
||||
@@ -160,7 +198,7 @@ impl<T: Config, C: OfflineClientT<T>> TxClient<T, C> {
|
||||
|
||||
// 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(SignedSubmittableExtrinsic {
|
||||
Ok(SubmittableExtrinsic {
|
||||
client: self.client.clone(),
|
||||
encoded: Encoded(extrinsic),
|
||||
marker: std::marker::PhantomData,
|
||||
@@ -175,7 +213,7 @@ impl<T: Config, C: OnlineClientT<T>> TxClient<T, C> {
|
||||
call: &Call,
|
||||
signer: &(dyn Signer<T> + Send + Sync),
|
||||
other_params: <T::ExtrinsicParams as ExtrinsicParams<T::Index, T::Hash>>::OtherParams,
|
||||
) -> Result<SignedSubmittableExtrinsic<T, C>, Error>
|
||||
) -> Result<SubmittableExtrinsic<T, C>, Error>
|
||||
where
|
||||
Call: TxPayload,
|
||||
{
|
||||
@@ -277,13 +315,24 @@ impl<T: Config, C: OnlineClientT<T>> TxClient<T, C> {
|
||||
}
|
||||
|
||||
/// This represents an extrinsic that has been signed and is ready to submit.
|
||||
pub struct SignedSubmittableExtrinsic<T, C> {
|
||||
pub struct SubmittableExtrinsic<T, C> {
|
||||
client: C,
|
||||
encoded: Encoded,
|
||||
marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T, C> SignedSubmittableExtrinsic<T, C>
|
||||
impl<T, C> SubmittableExtrinsic<T, C>
|
||||
where
|
||||
T: Config,
|
||||
C: OfflineClientT<T>,
|
||||
{
|
||||
/// Returns the SCALE encoded extrinsic bytes.
|
||||
pub fn encoded(&self) -> &[u8] {
|
||||
&self.encoded.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, C> SubmittableExtrinsic<T, C>
|
||||
where
|
||||
T: Config,
|
||||
C: OnlineClientT<T>,
|
||||
@@ -323,9 +372,4 @@ where
|
||||
) -> Result<ApplyExtrinsicResult, Error> {
|
||||
self.client.rpc().dry_run(self.encoded(), at).await
|
||||
}
|
||||
|
||||
/// Returns the SCALE encoded extrinsic bytes.
|
||||
pub fn encoded(&self) -> &[u8] {
|
||||
&self.encoded.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ async fn dry_run_passes() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
api.rpc()
|
||||
.dry_run(signed_extrinsic.encoded(), None)
|
||||
signed_extrinsic
|
||||
.dry_run(None)
|
||||
.await
|
||||
.expect("dryrunning failed")
|
||||
.expect("expected dryrunning to be successful")
|
||||
@@ -186,9 +186,8 @@ async fn dry_run_fails() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let dry_run_res = api
|
||||
.rpc()
|
||||
.dry_run(signed_extrinsic.encoded(), None)
|
||||
let dry_run_res = signed_extrinsic
|
||||
.dry_run(None)
|
||||
.await
|
||||
.expect("dryrunning failed")
|
||||
.expect("expected dryrun transaction to be valid");
|
||||
@@ -214,3 +213,35 @@ async fn dry_run_fails() {
|
||||
panic!("expected a runtime module error");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() {
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let tx = node_runtime::tx().balances().transfer(
|
||||
pair_signer(AccountKeyring::Alice.pair())
|
||||
.account_id()
|
||||
.clone()
|
||||
.into(),
|
||||
12345,
|
||||
);
|
||||
|
||||
let actual_tx = api.tx().create_unsigned(&tx).await.unwrap();
|
||||
|
||||
let actual_tx_bytes = actual_tx.encoded();
|
||||
|
||||
// How these were obtained:
|
||||
// - start local substrate node.
|
||||
// - open polkadot.js UI in browser and point at local node.
|
||||
// - open dev console (may need to refresh page now) and find the WS connection.
|
||||
// - create a balances.transfer to ALICE with 12345 and "submit unsigned".
|
||||
// - find the submitAndWatchExtrinsic call in the WS connection to get these bytes:
|
||||
let expected_tx_bytes = hex::decode(
|
||||
"9804060000d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27de5c0",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Make sure our encoding is the same as the encoding polkadot UI created.
|
||||
assert_eq!(actual_tx_bytes, expected_tx_bytes);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user