From c1cad344c7841d64ee2e3874db27143aae22dc3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bigna=20H=C3=A4rdi?= <73821294+haerdib@users.noreply.github.com> Date: Mon, 9 Jan 2023 09:07:09 +0100 Subject: [PATCH] Enforce `UncheckedExtrinsicV4` for `submit_extrinsic` and add `_opaque_extrinsic` (#421) --- examples/examples/batch_payout.rs | 2 +- examples/examples/benchmark_bulk_xt.rs | 3 +- .../examples/compose_extrinsic_offline.rs | 3 +- .../contract_instantiate_with_code.rs | 6 +- examples/examples/custom_nonce.rs | 3 +- examples/examples/event_error_details.rs | 4 +- examples/examples/get_account_identity.rs | 3 +- examples/examples/sudo.rs | 4 +- .../transfer_with_tungstenite_client.rs | 3 +- examples/examples/transfer_with_ws_client.rs | 3 +- src/api/rpc_api/author.rs | 126 +++++++++++++++--- testing/examples/author_tests.rs | 15 +-- 12 files changed, 130 insertions(+), 45 deletions(-) diff --git a/examples/examples/batch_payout.rs b/examples/examples/batch_payout.rs index 3387cedc7..33fc512f6 100644 --- a/examples/examples/batch_payout.rs +++ b/examples/examples/batch_payout.rs @@ -99,7 +99,7 @@ async fn main() { if payout_calls_len > 0 { let batching = api.batch(payout_calls); let results_hash = api - .submit_and_watch_extrinsic_until(batching.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(batching, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/examples/examples/benchmark_bulk_xt.rs b/examples/examples/benchmark_bulk_xt.rs index 99b058045..39d8ad4dd 100644 --- a/examples/examples/benchmark_bulk_xt.rs +++ b/examples/examples/benchmark_bulk_xt.rs @@ -18,7 +18,6 @@ // run this against test node with // > substrate-test-node --dev --execution native --ws-port 9979 -ltxpool=debug -use codec::Encode; use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall}; use sp_keyring::AccountKeyring; use substrate_api_client::{ @@ -53,7 +52,7 @@ async fn main() { let xt = api.compose_extrinsic_offline(call, nonce); println!("Sending extrinsic with nonce {}", nonce); - let _tx_hash = api.submit_extrinsic(xt.encode()).unwrap(); + let _tx_hash = api.submit_extrinsic(xt).unwrap(); nonce += 1; } diff --git a/examples/examples/compose_extrinsic_offline.rs b/examples/examples/compose_extrinsic_offline.rs index 198133fc2..e390f0e0d 100644 --- a/examples/examples/compose_extrinsic_offline.rs +++ b/examples/examples/compose_extrinsic_offline.rs @@ -16,7 +16,6 @@ //! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic //! without asking the node for nonce and does not need to know the metadata -use codec::Encode; use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall}; use sp_keyring::AccountKeyring; use sp_runtime::{generic::Era, MultiAddress}; @@ -65,7 +64,7 @@ async fn main() { // Send and watch extrinsic until in block (online). let block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/examples/examples/contract_instantiate_with_code.rs b/examples/examples/contract_instantiate_with_code.rs index e8669fdad..85fb0b042 100644 --- a/examples/examples/contract_instantiate_with_code.rs +++ b/examples/examples/contract_instantiate_with_code.rs @@ -15,7 +15,7 @@ //! This example is community maintained and not CI tested, therefore it may not work as is. -use codec::{Decode, Encode}; +use codec::Decode; use kitchensink_runtime::Runtime; use sp_keyring::AccountKeyring; use substrate_api_client::{ @@ -70,7 +70,7 @@ async fn main() { println!("[+] Creating a contract instance with extrinsic:\n\n{:?}\n", xt); let block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); @@ -85,6 +85,6 @@ async fn main() { let xt = api.contract_call(args.contract.into(), 500_000, 500_000, vec![0u8]); println!("[+] Calling the contract with extrinsic Extrinsic:\n{:?}\n\n", xt); - let report = api.submit_and_watch_extrinsic_until(xt.encode(), XtStatus::Finalized).unwrap(); + let report = api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized).unwrap(); println!("[+] Extrinsic got finalized. Extrinsic Hash: {:?}", report.extrinsic_hash); } diff --git a/examples/examples/custom_nonce.rs b/examples/examples/custom_nonce.rs index ff76613b6..0ed16d5ae 100644 --- a/examples/examples/custom_nonce.rs +++ b/examples/examples/custom_nonce.rs @@ -16,7 +16,6 @@ //! This example shows how to use the compose_extrinsic_offline macro which generates an extrinsic //! without asking the node for nonce and does not need to know the metadata -use codec::Encode; use kitchensink_runtime::{BalancesCall, Runtime, RuntimeCall}; use sp_keyring::AccountKeyring; use sp_runtime::{generic::Era, MultiAddress}; @@ -59,7 +58,7 @@ async fn main() { println!("[+] Composed Extrinsic:\n {:?}\n", xt); // Send and watch extrinsic until InBlock. - match api.submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) { + match api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) { Err(error) => { println!("Retrieved error {:?}", error); assert!(format!("{:?}", error).contains("Future")); diff --git a/examples/examples/event_error_details.rs b/examples/examples/event_error_details.rs index b8b74c1cb..0e177af32 100644 --- a/examples/examples/event_error_details.rs +++ b/examples/examples/event_error_details.rs @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -use codec::{Decode, Encode}; +use codec::Decode; use kitchensink_runtime::Runtime; use sp_keyring::AccountKeyring; use sp_runtime::{AccountId32 as AccountId, MultiAddress}; @@ -60,7 +60,7 @@ async fn main() { println!("[+] Composed extrinsic: {:?}\n", xt); // Send and watch extrinsic until Ready. - let _tx_hash = api.submit_and_watch_extrinsic_until(xt.encode(), XtStatus::Ready).unwrap(); + let _tx_hash = api.submit_and_watch_extrinsic_until(xt, XtStatus::Ready).unwrap(); println!("[+] Transaction got included into the TxPool."); // Subscribe to system events. We expect the transfer to fail as Alice wants to transfer all her balance. diff --git a/examples/examples/get_account_identity.rs b/examples/examples/get_account_identity.rs index 593d31dc8..23887498a 100644 --- a/examples/examples/get_account_identity.rs +++ b/examples/examples/get_account_identity.rs @@ -15,7 +15,6 @@ //! Example to show how to get the account identity display name from the identity pallet. -use codec::Encode; use frame_support::traits::Currency; use kitchensink_runtime::Runtime as KitchensinkRuntime; use pallet_identity::{Data, IdentityInfo, Registration}; @@ -65,7 +64,7 @@ async fn main() { // Send and watch extrinsic until InBlock. let _block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/examples/examples/sudo.rs b/examples/examples/sudo.rs index 305a38c6e..21e35684f 100644 --- a/examples/examples/sudo.rs +++ b/examples/examples/sudo.rs @@ -16,7 +16,7 @@ //! This example shows how to use the compose_extrinsic macro to create an extrinsic for any (custom) //! module, whereas the desired module and call are supplied as a string. -use codec::{Compact, Encode}; +use codec::Compact; use kitchensink_runtime::Runtime; use sp_keyring::AccountKeyring; use substrate_api_client::{ @@ -56,7 +56,7 @@ async fn main() { // Send and watch extrinsic until in block. let block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/examples/examples/transfer_with_tungstenite_client.rs b/examples/examples/transfer_with_tungstenite_client.rs index dab028eaa..ac281a018 100755 --- a/examples/examples/transfer_with_tungstenite_client.rs +++ b/examples/examples/transfer_with_tungstenite_client.rs @@ -15,7 +15,6 @@ //! Very simple example that shows how to use a predefined extrinsic from the extrinsic module. -use codec::Encode; use kitchensink_runtime::Runtime; use sp_core::{ crypto::{Pair, Ss58Codec}, @@ -61,7 +60,7 @@ fn main() { // Send and watch extrinsic until in block. let block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/examples/examples/transfer_with_ws_client.rs b/examples/examples/transfer_with_ws_client.rs index 0843862c2..0f2b14203 100755 --- a/examples/examples/transfer_with_ws_client.rs +++ b/examples/examples/transfer_with_ws_client.rs @@ -15,7 +15,6 @@ //! Very simple example that shows how to use a predefined extrinsic from the extrinsic module. -use codec::Encode; use kitchensink_runtime::Runtime; use sp_core::{ crypto::{Pair, Ss58Codec}, @@ -59,7 +58,7 @@ fn main() { // Send and watch extrinsic until in block. let block_hash = api - .submit_and_watch_extrinsic_until(xt.encode(), XtStatus::InBlock) + .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) .unwrap() .block_hash .unwrap(); diff --git a/src/api/rpc_api/author.rs b/src/api/rpc_api/author.rs index 7e5cdd1a0..61fea6d3c 100644 --- a/src/api/rpc_api/author.rs +++ b/src/api/rpc_api/author.rs @@ -17,7 +17,7 @@ use crate::{ api::{Error, Result}, rpc::{HandleSubscription, Request, Subscribe}, utils, Api, Events, ExtrinsicReport, FromHexString, GetBlock, GetStorage, Phase, ToHexString, - TransactionStatus, XtStatus, + TransactionStatus, UncheckedExtrinsicV4, XtStatus, }; use ac_compose_macros::rpc_params; use ac_node_api::EventDetails; @@ -35,9 +35,19 @@ pub type TransactionSubscriptionFor = pub trait SubmitExtrinsic { type Hash; - /// Submit an extrsinic to the substrate node, without watching. - /// Retruns the extrinsic hash. - fn submit_extrinsic(&self, encoded_extrinsic: Vec) -> Result; + /// Submit an encodable extrinsic to the substrate node. + /// Returns the extrinsic hash. + fn submit_extrinsic( + &self, + extrinsic: UncheckedExtrinsicV4, + ) -> Result + where + Call: Encode, + SignedExtra: Encode; + + /// Submit an encoded, opaque extrsinic to the substrate node. + /// Returns the extrinsic hash. + fn submit_opaque_extrinsic(&self, encoded_extrinsic: Vec) -> Result; } impl SubmitExtrinsic for Api @@ -48,7 +58,18 @@ where { type Hash = Runtime::Hash; - fn submit_extrinsic(&self, encoded_extrinsic: Vec) -> Result { + fn submit_extrinsic( + &self, + extrinsic: UncheckedExtrinsicV4, + ) -> Result + where + Call: Encode, + SignedExtra: Encode, + { + self.submit_opaque_extrinsic(extrinsic.encode()) + } + + fn submit_opaque_extrinsic(&self, encoded_extrinsic: Vec) -> Result { let hex_encoded_xt = encoded_extrinsic.to_hex(); debug!("sending extrinsic: {:?}", hex_encoded_xt); let xt_hash = @@ -62,17 +83,39 @@ where Client: Subscribe, Hash: DeserializeOwned, { - /// Submit an extrinsic an return a websocket Subscription to watch the - /// extrinsic progress. - fn submit_and_watch_extrinsic( + /// Submit an extrinsic an return a websocket Subscription + /// to watch the extrinsic progress. + fn submit_and_watch_extrinsic( + &self, + extrinsic: UncheckedExtrinsicV4, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode; + + /// Submit an encoded, opaque extrinsic an return a websocket Subscription to + /// watch the extrinsic progress. + fn submit_and_watch_opaque_extrinsic( &self, encoded_extrinsic: Vec, ) -> Result>; - /// Submit an extrinsic and watch in until the desired status is reached, - /// if no error is encountered previously. + /// Submit an extrinsic and watch in until the desired status + /// is reached, if no error is encountered previously. // This method is blocking. - fn submit_and_watch_extrinsic_until( + fn submit_and_watch_extrinsic_until( + &self, + extrinsic: UncheckedExtrinsicV4, + watch_until: XtStatus, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode; + + /// Submit an encoded, opaque extrinsic and watch in until the desired status + /// is reached, if no error is encountered previously. + // This method is blocking. + fn submit_and_watch_opaque_extrinsic_until( &self, encoded_extrinsic: Vec, watch_until: XtStatus, @@ -89,7 +132,21 @@ where /// - wait_for_finalized = true => Finalized /// and check if the extrinsic has been successful or not. // This method is blocking. - fn submit_and_watch_extrinsic_until_success( + fn submit_and_watch_extrinsic_until_success( + &self, + extrinsic: UncheckedExtrinsicV4, + wait_for_finalized: bool, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode; + + /// Submit an encoded, opaque extrinsic and watch it until + /// - wait_for_finalized = false => InBlock + /// - wait_for_finalized = true => Finalized + /// and check if the extrinsic has been successful or not. + // This method is blocking. + fn submit_and_watch_opaque_extrinsic_until_success( &self, encoded_extrinsic: Vec, wait_for_finalized: bool, @@ -105,7 +162,17 @@ where Runtime::Hashing: HashTrait, Runtime::Hash: FromHexString, { - fn submit_and_watch_extrinsic( + fn submit_and_watch_extrinsic( + &self, + extrinsic: UncheckedExtrinsicV4, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode, + { + self.submit_and_watch_opaque_extrinsic(extrinsic.encode()) + } + fn submit_and_watch_opaque_extrinsic( &self, encoded_extrinsic: Vec, ) -> Result> { @@ -118,14 +185,26 @@ where .map_err(|e| e.into()) } - fn submit_and_watch_extrinsic_until( + fn submit_and_watch_extrinsic_until( + &self, + extrinsic: UncheckedExtrinsicV4, + watch_until: XtStatus, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode, + { + self.submit_and_watch_opaque_extrinsic_until(extrinsic.encode(), watch_until) + } + + fn submit_and_watch_opaque_extrinsic_until( &self, encoded_extrinsic: Vec, watch_until: XtStatus, ) -> Result> { let tx_hash = Runtime::Hashing::hash_of(&encoded_extrinsic); let mut subscription: TransactionSubscriptionFor = - self.submit_and_watch_extrinsic(encoded_extrinsic)?; + self.submit_and_watch_opaque_extrinsic(encoded_extrinsic)?; while let Some(transaction_status) = subscription.next() { let transaction_status = transaction_status?; @@ -164,7 +243,19 @@ where Runtime::Hashing: HashTrait, Runtime::Hash: FromHexString, { - fn submit_and_watch_extrinsic_until_success( + fn submit_and_watch_extrinsic_until_success( + &self, + extrinsic: UncheckedExtrinsicV4, + wait_for_finalized: bool, + ) -> Result> + where + Call: Encode, + SignedExtra: Encode, + { + self.submit_and_watch_opaque_extrinsic_until_success(extrinsic.encode(), wait_for_finalized) + } + + fn submit_and_watch_opaque_extrinsic_until_success( &self, encoded_extrinsic: Vec, wait_for_finalized: bool, @@ -173,7 +264,8 @@ where true => XtStatus::Finalized, false => XtStatus::InBlock, }; - let mut report = self.submit_and_watch_extrinsic_until(encoded_extrinsic, xt_status)?; + let mut report = + self.submit_and_watch_opaque_extrinsic_until(encoded_extrinsic, xt_status)?; let block_hash = report.block_hash.ok_or(Error::NoBlockHash)?; let extrinsic_index = diff --git a/testing/examples/author_tests.rs b/testing/examples/author_tests.rs index d179f10b7..6fc091791 100644 --- a/testing/examples/author_tests.rs +++ b/testing/examples/author_tests.rs @@ -15,7 +15,6 @@ //! Tests for the author rpc interface functions. -use codec::Encode; use kitchensink_runtime::Runtime; use sp_keyring::AccountKeyring; use std::{thread, time::Duration}; @@ -36,13 +35,13 @@ async fn main() { let bob = MultiAddress::Id(AccountKeyring::Bob.to_account_id()); // Submit extrinisc. - let xt0 = api.balance_transfer(bob.clone(), 1000).encode(); + let xt0 = api.balance_transfer(bob.clone(), 1000); let _tx_hash = api.submit_extrinsic(xt0).unwrap(); // Submit and watch. thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. let api1 = api.clone(); - let xt1 = api.balance_transfer(bob.clone(), 1000).encode(); + let xt1 = api.balance_transfer(bob.clone(), 1000); let watch_handle = thread::spawn(move || { let mut tx_subscription = api1.submit_and_watch_extrinsic(xt1).unwrap(); let tx_status = tx_subscription.next().unwrap().unwrap(); @@ -58,13 +57,13 @@ async fn main() { // Test different _watch_untils. thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. - let xt2 = api.balance_transfer(bob.clone(), 1000).encode(); + let xt2 = api.balance_transfer(bob.clone(), 1000); let report = api.submit_and_watch_extrinsic_until(xt2, XtStatus::Ready).unwrap(); assert!(report.block_hash.is_none()); println!("Success: submit_and_watch_extrinsic_until Ready"); thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. - let xt3 = api.balance_transfer(bob.clone(), 1000).encode(); + let xt3 = api.balance_transfer(bob.clone(), 1000); // The xt is not broadcast - we only have one node running. Therefore, InBlock is returned. let _some_hash = api .submit_and_watch_extrinsic_until(xt3, XtStatus::Broadcast) @@ -75,7 +74,7 @@ async fn main() { let api2 = api.clone(); thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. - let xt4 = api2.balance_transfer(bob.clone(), 1000).encode(); + let xt4 = api2.balance_transfer(bob.clone(), 1000); let until_in_block_handle = thread::spawn(move || { let _block_hash = api2 .submit_and_watch_extrinsic_until(xt4, XtStatus::InBlock) @@ -87,7 +86,7 @@ async fn main() { let api3 = api.clone(); thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. - let xt5 = api.balance_transfer(bob.clone(), 1000).encode(); + let xt5 = api.balance_transfer(bob.clone(), 1000); let until_finalized_handle = thread::spawn(move || { let _block_hash = api3 .submit_and_watch_extrinsic_until(xt5, XtStatus::Finalized) @@ -99,7 +98,7 @@ async fn main() { // Test Success. thread::sleep(Duration::from_secs(6)); // Wait a little to avoid transaction too low priority error. - let xt6 = api.balance_transfer(bob, 1000).encode(); + let xt6 = api.balance_transfer(bob, 1000); let events = api .submit_and_watch_extrinsic_until_success(xt6, false)