From 2092f899d6b0c773bd55279d79011bec8c71f1be Mon Sep 17 00:00:00 2001 From: librelois Date: Tue, 25 Jan 2022 14:46:00 +0100 Subject: [PATCH 1/3] Add runtime API `ConvertTransactionRuntime API` --- Cargo.toml | 2 +- client/rpc/src/eth.rs | 131 ++++++++++++++++++++++++++++++------ primitives/rpc/src/lib.rs | 14 ++++ template/node/src/rpc.rs | 5 +- template/runtime/src/lib.rs | 8 +++ 5 files changed, 139 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f65bacd093..e6fcdf9a58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,4 @@ members = [ "primitives/self-contained", "template/node", "template/runtime", -] \ No newline at end of file +] diff --git a/client/rpc/src/eth.rs b/client/rpc/src/eth.rs index 72b8f22072..20aaa25a74 100644 --- a/client/rpc/src/eth.rs +++ b/client/rpc/src/eth.rs @@ -31,7 +31,7 @@ use fc_rpc_core::{ }, EthApi as EthApiT, EthFilterApi as EthFilterApiT, NetApi as NetApiT, Web3Api as Web3ApiT, }; -use fp_rpc::{ConvertTransaction, EthereumRuntimeRPCApi, TransactionStatus}; +use fp_rpc::{ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi, TransactionStatus}; use futures::{future::TryFutureExt, StreamExt}; use jsonrpc_core::{futures::future, BoxFuture, Result}; use lru::LruCache; @@ -84,8 +84,10 @@ pub struct EthApi { impl EthApi where C: ProvideRuntimeApi, - C::Api: EthereumRuntimeRPCApi, - C::Api: BlockBuilder, + C::Api: sp_api::ApiExt + + BlockBuilder + + ConvertTransactionRuntimeApi + + EthereumRuntimeRPCApi, B: BlockT + Send + Sync + 'static, A: ChainApi + 'static, C: Send + Sync + 'static, @@ -535,15 +537,17 @@ impl EthApiT for EthApi where C: ProvideRuntimeApi + StorageProvider, C: HeaderBackend + HeaderMetadata + 'static, - C::Api: EthereumRuntimeRPCApi, - C::Api: BlockBuilder, + C::Api: sp_api::ApiExt + + BlockBuilder + + ConvertTransactionRuntimeApi + + EthereumRuntimeRPCApi, BE: Backend + 'static, BE::State: StateBackend, B: BlockT + Send + Sync + 'static, C: Send + Sync + 'static, P: TransactionPool + Send + Sync + 'static, A: ChainApi + 'static, - CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, + CT: fp_rpc::ConvertTransaction<::Extrinsic> + Send + Sync + 'static, { fn protocol_version(&self) -> Result { Ok(1) @@ -1023,14 +1027,59 @@ where None => return Box::pin(future::err(internal_err("no signer available"))), }; let transaction_hash = transaction.hash(); + + let block_hash = BlockId::hash(self.client.info().best_hash); + let api_version = match self + .client + .runtime_api() + .api_version::>(&block_hash) + { + Ok(api_version) => api_version, + _ => return Box::pin(future::err(internal_err("cannot access runtime api"))), + }; + + let extrinsic = match api_version { + Some(2) => match self + .client + .runtime_api() + .convert_transaction(&block_hash, transaction) + { + Ok(extrinsic) => extrinsic, + Err(_) => return Box::pin(future::err(internal_err("cannot access runtime api"))), + }, + Some(1) => { + if let ethereum::TransactionV2::Legacy(legacy_transaction) = transaction { + // To be compatible with runtimes that do not support transactions v2 + #[allow(deprecated)] + match self + .client + .runtime_api() + .convert_transaction_before_version_2(&block_hash, legacy_transaction) + { + Ok(extrinsic) => extrinsic, + Err(_) => { + return Box::pin(future::err(internal_err("cannot access runtime api"))) + } + } + } else { + return Box::pin(future::err(internal_err( + "This runtime not support eth transactions v2", + ))); + } + } + None => self + .convert_transaction + .convert_transaction(transaction.clone()), + _ => { + return Box::pin(future::err(internal_err( + "ConvertTransactionRuntimeApi version not supported", + ))) + } + }; + Box::pin( self.pool - .submit_one( - &BlockId::hash(hash), - TransactionSource::Local, - self.convert_transaction - .convert_transaction(transaction.clone()), - ) + .submit_one(&block_hash, TransactionSource::Local, extrinsic) .map_ok(move |_| transaction_hash) .map_err(|err| { internal_err(format!("submit transaction to pool failed: {:?}", err)) @@ -1064,15 +1113,59 @@ where }; let transaction_hash = transaction.hash(); - let hash = self.client.info().best_hash; + + let block_hash = BlockId::hash(self.client.info().best_hash); + let api_version = match self + .client + .runtime_api() + .api_version::>(&block_hash) + { + Ok(api_version) => api_version, + _ => return Box::pin(future::err(internal_err("cannot access runtime api"))), + }; + + let extrinsic = match api_version { + Some(2) => match self + .client + .runtime_api() + .convert_transaction(&block_hash, transaction) + { + Ok(extrinsic) => extrinsic, + Err(_) => return Box::pin(future::err(internal_err("cannot access runtime api"))), + }, + Some(1) => { + if let ethereum::TransactionV2::Legacy(legacy_transaction) = transaction { + // To be compatible with runtimes that do not support transactions v2 + #[allow(deprecated)] + match self + .client + .runtime_api() + .convert_transaction_before_version_2(&block_hash, legacy_transaction) + { + Ok(extrinsic) => extrinsic, + Err(_) => { + return Box::pin(future::err(internal_err("cannot access runtime api"))) + } + } + } else { + return Box::pin(future::err(internal_err( + "This runtime not support eth transactions v2", + ))); + } + } + None => self + .convert_transaction + .convert_transaction(transaction.clone()), + _ => { + return Box::pin(future::err(internal_err( + "ConvertTransactionRuntimeApi version not supported", + ))) + } + }; + Box::pin( self.pool - .submit_one( - &BlockId::hash(hash), - TransactionSource::Local, - self.convert_transaction - .convert_transaction(transaction.clone()), - ) + .submit_one(&block_hash, TransactionSource::Local, extrinsic) .map_ok(move |_| transaction_hash) .map_err(|err| { internal_err(format!("submit transaction to pool failed: {:?}", err)) diff --git a/primitives/rpc/src/lib.rs b/primitives/rpc/src/lib.rs index 99d2305132..c465e79751 100644 --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -177,8 +177,22 @@ sp_api::decl_runtime_apis! { /// Return the elasticity multiplier. fn elasticity() -> Option; } + + #[api_version(2)] + pub trait ConvertTransactionRuntimeApi { + fn convert_transaction(transaction: ethereum::TransactionV2) -> ::Extrinsic; + #[changed_in(2)] + fn convert_transaction(transaction: ethereum::TransactionV0) -> ::Extrinsic; + } } pub trait ConvertTransaction { fn convert_transaction(&self, transaction: ethereum::TransactionV2) -> E; } + +pub struct PanicTransactionConverter; +impl ConvertTransaction for PanicTransactionConverter { + fn convert_transaction(&self, _transaction: ethereum::TransactionV2) -> E { + panic!("No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found") + } +} diff --git a/template/node/src/rpc.rs b/template/node/src/rpc.rs index 860b5a4de8..ebdfa1e469 100644 --- a/template/node/src/rpc.rs +++ b/template/node/src/rpc.rs @@ -78,7 +78,9 @@ where C: ProvideRuntimeApi + StorageProvider + AuxStore, C: HeaderBackend + HeaderMetadata, C: Send + Sync + 'static, - C::Api: fp_rpc::EthereumRuntimeRPCApi, + C::Api: sp_api::ApiExt + + fp_rpc::EthereumRuntimeRPCApi + + fp_rpc::ConvertTransactionRuntimeApi, BE: Backend + 'static, BE::State: StateBackend, { @@ -120,6 +122,7 @@ where C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: BlockBuilder, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, + C::Api: fp_rpc::ConvertTransactionRuntimeApi, C::Api: fp_rpc::EthereumRuntimeRPCApi, P: TransactionPool + 'static, A: ChainApi + 'static, diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 96eb8af070..1d2493b7da 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -689,6 +689,14 @@ impl_runtime_apis! { } } + impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { + fn convert_transaction(transaction: EthereumTransaction) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_ethereum::Call::::transact { transaction }.into(), + ) + } + } + impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi< Block, Balance, From 6e3e78ab78f651e3c22a7162518e2d4ad0e2a035 Mon Sep 17 00:00:00 2001 From: librelois Date: Thu, 27 Jan 2022 13:24:30 +0100 Subject: [PATCH 2/3] ref: make transaction_converter optional --- client/rpc/src/eth.rs | 24 ++++++++++++++++-------- primitives/rpc/src/lib.rs | 10 +++++++--- template/node/src/rpc.rs | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/client/rpc/src/eth.rs b/client/rpc/src/eth.rs index 20aaa25a74..fd0929cc46 100644 --- a/client/rpc/src/eth.rs +++ b/client/rpc/src/eth.rs @@ -68,7 +68,7 @@ pub struct EthApi { pool: Arc

, graph: Arc>, client: Arc, - convert_transaction: CT, + convert_transaction: Option, network: Arc>, is_authority: bool, signers: Vec>, @@ -96,7 +96,7 @@ where client: Arc, pool: Arc

, graph: Arc>, - convert_transaction: CT, + convert_transaction: Option, network: Arc>, signers: Vec>, overrides: Arc>, @@ -1067,9 +1067,13 @@ where ))); } } - None => self - .convert_transaction - .convert_transaction(transaction.clone()), + None => if let Some(ref convert_transaction) = self.convert_transaction { + convert_transaction.convert_transaction(transaction.clone()) + } else { + return Box::pin(future::err(internal_err( + "No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found" + ))) + }, _ => { return Box::pin(future::err(internal_err( "ConvertTransactionRuntimeApi version not supported", @@ -1153,9 +1157,13 @@ where ))); } } - None => self - .convert_transaction - .convert_transaction(transaction.clone()), + None => if let Some(ref convert_transaction) = self.convert_transaction { + convert_transaction.convert_transaction(transaction.clone()) + } else { + return Box::pin(future::err(internal_err( + "No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found" + ))) + }, _ => { return Box::pin(future::err(internal_err( "ConvertTransactionRuntimeApi version not supported", diff --git a/primitives/rpc/src/lib.rs b/primitives/rpc/src/lib.rs index c465e79751..95f63becaf 100644 --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -190,9 +190,13 @@ pub trait ConvertTransaction { fn convert_transaction(&self, transaction: ethereum::TransactionV2) -> E; } -pub struct PanicTransactionConverter; -impl ConvertTransaction for PanicTransactionConverter { +// `NoTransactionConverter` is a non-instantiable type (an enum with no variants), +// so we are guaranteed at compile time that `NoTransactionConverter` can never be instantiated. +pub enum NoTransactionConverter {} +impl ConvertTransaction for NoTransactionConverter { + // `convert_transaction` is a method taking `&self` as a parameter, so it can only be called via an instance of type Self, + // so we are guaranteed at compile time that this method can never be called. fn convert_transaction(&self, _transaction: ethereum::TransactionV2) -> E { - panic!("No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found") + unreachable!() } } diff --git a/template/node/src/rpc.rs b/template/node/src/rpc.rs index ebdfa1e469..2a3ed2cc61 100644 --- a/template/node/src/rpc.rs +++ b/template/node/src/rpc.rs @@ -172,7 +172,7 @@ where client.clone(), pool.clone(), graph, - frontier_template_runtime::TransactionConverter, + Some(frontier_template_runtime::TransactionConverter), network.clone(), signers, overrides.clone(), From 712ed448227ee20bbce8a6fff07283eae65f5e29 Mon Sep 17 00:00:00 2001 From: librelois Date: Thu, 27 Jan 2022 13:28:09 +0100 Subject: [PATCH 3/3] rustfmt --- client/rpc/src/eth.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/client/rpc/src/eth.rs b/client/rpc/src/eth.rs index fd0929cc46..be7dc6a002 100644 --- a/client/rpc/src/eth.rs +++ b/client/rpc/src/eth.rs @@ -1067,13 +1067,15 @@ where ))); } } - None => if let Some(ref convert_transaction) = self.convert_transaction { + None => { + if let Some(ref convert_transaction) = self.convert_transaction { convert_transaction.convert_transaction(transaction.clone()) } else { return Box::pin(future::err(internal_err( "No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found" - ))) - }, + ))); + } + } _ => { return Box::pin(future::err(internal_err( "ConvertTransactionRuntimeApi version not supported", @@ -1157,13 +1159,15 @@ where ))); } } - None => if let Some(ref convert_transaction) = self.convert_transaction { - convert_transaction.convert_transaction(transaction.clone()) - } else { - return Box::pin(future::err(internal_err( + None => { + if let Some(ref convert_transaction) = self.convert_transaction { + convert_transaction.convert_transaction(transaction.clone()) + } else { + return Box::pin(future::err(internal_err( "No TransactionConverter is provided and the runtime api ConvertTransactionRuntimeApi is not found" - ))) - }, + ))); + } + } _ => { return Box::pin(future::err(internal_err( "ConvertTransactionRuntimeApi version not supported",