From e1731f2d2ce92a54380ae57fc60b98a8e642b991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADnez=20Rodr=C3=ADguez?= <43847309+GabrielMartinezRodriguez@users.noreply.github.com> Date: Mon, 20 Nov 2023 17:54:43 +0100 Subject: [PATCH] Improvement zero gas transaction performance (#152) --- Cargo.lock | 3 + client/authorship/Cargo.toml | 1 + client/authorship/src/authorship.rs | 83 ++++++++++---------- pallets/zero-gas-transactions/src/lib.rs | 32 ++++---- test-utils/stability-runtime/Cargo.toml | 2 + test-utils/stability-runtime/src/lib.rs | 99 ++++++++++++++++++++++++ 6 files changed, 162 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe62eb9..bd5455d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10385,6 +10385,7 @@ name = "stability-test-runtime" version = "1.0.0" dependencies = [ "array-bytes 6.2.0", + "fp-rpc", "frame-executive", "frame-support", "frame-system", @@ -10396,6 +10397,7 @@ dependencies = [ "pallet-balances", "pallet-beefy-mmr", "pallet-ethereum", + "pallet-evm", "pallet-sudo", "pallet-timestamp", "parity-scale-codec", @@ -10503,6 +10505,7 @@ dependencies = [ "account", "bytes", "ethereum", + "fp-rpc", "futures 0.3.28", "futures-timer", "hex", diff --git a/client/authorship/Cargo.toml b/client/authorship/Cargo.toml index ee9941a..de5181b 100644 --- a/client/authorship/Cargo.toml +++ b/client/authorship/Cargo.toml @@ -20,6 +20,7 @@ prometheus-endpoint = { workspace = true } sc-block-builder = { workspace = true } sc-client-api = { workspace = true } sc-proposer-metrics = { workspace = true } +fp-rpc = { workspace = true } sc-telemetry = { workspace = true } sc-transaction-pool-api = { workspace = true } sp-api = { workspace = true } diff --git a/client/authorship/src/authorship.rs b/client/authorship/src/authorship.rs index 3bd3dac..bbd8f2e 100644 --- a/client/authorship/src/authorship.rs +++ b/client/authorship/src/authorship.rs @@ -49,6 +49,10 @@ use sc_proposer_metrics::{EndProposingReason, MetricsLink as PrometheusMetrics}; use sp_core::crypto::KeyTypeId; use sp_keystore::{Keystore, KeystorePtr}; use stbl_primitives_fee_compatible_api::CompatibleFeeApi; +use fp_rpc::EthereumRuntimeRPCApi; +use sp_runtime::Saturating; + + /// Default block size limit in bytes used by [`Proposer`]. /// @@ -259,6 +263,7 @@ where + 'static, C::Api: ApiExt> + BlockBuilderApi + + fp_rpc::EthereumRuntimeRPCApi + stbl_primitives_fee_compatible_api::CompatibleFeeApi + stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi, PR: ProofRecording, @@ -305,6 +310,7 @@ where + 'static, C::Api: ApiExt> + BlockBuilderApi + + fp_rpc::EthereumRuntimeRPCApi + stbl_primitives_fee_compatible_api::CompatibleFeeApi + stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi, PR: ProofRecording, @@ -367,6 +373,7 @@ where + 'static, C::Api: ApiExt> + BlockBuilderApi + + fp_rpc::EthereumRuntimeRPCApi + stbl_primitives_fee_compatible_api::CompatibleFeeApi + stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi, PR: ProofRecording, @@ -437,7 +444,6 @@ where let http_client = reqwest::Client::new(); let mut request = Box::pin(http_client.post(zero_gas_tx_pool).send().fuse()); let mut timeout = Box::pin(futures_timer::Delay::new(std::time::Duration::from_millis(500)).fuse()); - let result_response_raw_zero = select! { res = request => { @@ -475,14 +481,41 @@ where } else { None }; - - // If we pull successfully from the zero gas transaction pool, we will try to push them to the block - if let Some(raw_zero_gas_transactions) = raw_zero_gas_transactions_option { let mut pending_raw_zero_gas_transactions = raw_zero_gas_transactions.transactions.into_iter(); - + + let chain_id = self + .client + .runtime_api() + .chain_id(self.parent_hash).expect("Could not get chain id"); + + let current_block = self.parent_number.saturated_into::().saturating_add(1); + + let message: Vec = b"I consent to validate zero gas transactions in block " + .iter() + .chain(current_block.to_string().as_bytes().iter()) + .chain(b" on chain ") + .chain(chain_id.to_string().as_bytes().iter()) + .cloned() + .collect(); + + let keys = Keystore::ecdsa_public_keys( + &*self.keystore, + KeyTypeId::try_from("aura").unwrap_or_default(), + ); + let public = keys[0].clone().into(); + + let eip191_message = stbl_tools::eth::build_eip191_message_hash(message.clone()); + + let signed_hash = Keystore::ecdsa_sign_prehashed( + &*self.keystore, + KeyTypeId::try_from("aura").unwrap_or_default(), + &public, + &eip191_message.as_fixed_bytes(), + ).expect("Could not sign the Ethereum transaction hash").expect("Could not sign the Ethereum transaction hash"); + loop { let pending_hex_string_tx = if let Some(tx) = pending_raw_zero_gas_transactions.next() { tx @@ -498,7 +531,7 @@ where ); break EndProposingReason::HitDeadline; } - + let pending_raw_tx = if let Ok(pending_raw_tx) = hex::decode(pending_hex_string_tx) { pending_raw_tx } @@ -507,38 +540,6 @@ where }; let ethereum_transaction: ethereum::TransactionV2 = ethereum::EnvelopedDecodable::decode(&pending_raw_tx).unwrap(); - - - let keys = Keystore::ecdsa_public_keys( - &*self.keystore, - KeyTypeId::try_from("aura").unwrap_or_default(), - ); - - - let public = keys[0].clone().into(); - let hash = ethereum_transaction.hash(); - let hash_string = hex::encode(hash.as_bytes()); - - - let mut message: Vec = Vec::new(); - message.extend_from_slice(b"I consent to validate the transaction for free: 0x"); - message.extend_from_slice(hash_string.as_bytes()); - - let eip191_message = stbl_tools::eth::build_eip191_message_hash(message.clone()); - - let signed_hash_option = Keystore::ecdsa_sign_prehashed( - &*self.keystore, - KeyTypeId::try_from("aura").unwrap_or_default(), - &public, - &eip191_message.as_fixed_bytes(), - ).expect("Could not sign the Ethereum transaction hash"); - - let signed_hash = if let Some(signed_hash) = signed_hash_option { - signed_hash - } - else { - continue; - }; let pending_tx = if let Ok(pending_tx) = self .client @@ -549,9 +550,7 @@ where else { continue; }; - - - + let block_size = block_builder.estimate_block_size(self.include_proof_in_block_size_estimation); @@ -605,7 +604,7 @@ where } } }; - } + }; let mut unqueue_invalid = Vec::new(); let mut t1 = self.transaction_pool.ready_at(self.parent_number).fuse(); diff --git a/pallets/zero-gas-transactions/src/lib.rs b/pallets/zero-gas-transactions/src/lib.rs index b6e9a45..8076ec6 100644 --- a/pallets/zero-gas-transactions/src/lib.rs +++ b/pallets/zero-gas-transactions/src/lib.rs @@ -23,6 +23,7 @@ pub mod pallet { use sp_core::U256; use sp_std::vec; use sp_std::vec::Vec; + use parity_scale_codec::alloc::string::ToString; pub use fp_rpc::TransactionStatus; @@ -36,7 +37,7 @@ pub mod pallet { pub type SponsorNonce = StorageMap<_, Blake2_128Concat, H160, u64, ValueQuery>; #[pallet::config] - pub trait Config: frame_system::Config + pallet_evm::Config + pallet_ethereum::Config { + pub trait Config: frame_system::Config + pallet_evm::Config + pallet_ethereum::Config { type RuntimeCall: Parameter + GetDispatchInfo; type UserFeeTokenController: UserFeeTokenController; } @@ -64,7 +65,6 @@ pub mod pallet { let current_block_validator = >::find_author(); Self::ensure_zero_gas_transaction( - transaction.clone(), current_block_validator, validator_signature.clone(), ) @@ -118,7 +118,6 @@ pub mod pallet { let current_block_validator = >::find_author(); Self::ensure_zero_gas_transaction( - transaction.clone(), current_block_validator, validator_signature, ) @@ -196,12 +195,14 @@ pub mod pallet { } fn ensure_zero_gas_transaction( - transaction: pallet_ethereum::Transaction, expected_validator: H160, validator_signature: Vec, ) -> Result<(), ()> { + let chain_id = T::ChainId::get(); + let block_number = >::block_number(); + let zero_gas_trx_internal_message: Vec = - Self::get_zero_gas_transaction_signing_message(transaction.clone()); + Self::get_zero_gas_transaction_signing_message(block_number, chain_id); let eip191_message = stbl_tools::eth::build_eip191_message_hash(zero_gas_trx_internal_message); @@ -216,18 +217,16 @@ pub mod pallet { } pub fn get_zero_gas_transaction_signing_message( - trx: pallet_ethereum::Transaction, + block_number: u32, + chain_id: u64, ) -> Vec { - let mut message: Vec = Vec::new(); - - let trx_hash = trx.hash(); - let trx_bytes_hash = trx_hash.as_bytes(); - let trx_hash_string = hex::encode(trx_bytes_hash); - - message.extend_from_slice(b"I consent to validate the transaction for free: 0x"); - message.extend_from_slice(trx_hash_string.as_bytes()); - - return message; + b"I consent to validate zero gas transactions in block " + .iter() + .chain(block_number.to_string().as_bytes().iter()) + .chain(b" on chain ") + .chain(chain_id.to_string().as_bytes().iter()) + .cloned() + .collect() } fn get_zero_gas_trx_signer(signature: Vec, message: H256) -> Option { @@ -245,5 +244,6 @@ pub mod pallet { return Some(H160::from_slice(&result[12..32])); } + } } diff --git a/test-utils/stability-runtime/Cargo.toml b/test-utils/stability-runtime/Cargo.toml index bbe6e59..cd2cd6c 100644 --- a/test-utils/stability-runtime/Cargo.toml +++ b/test-utils/stability-runtime/Cargo.toml @@ -52,6 +52,8 @@ stbl-primitives-fee-compatible-api = { workspace = true } stbl-primitives-zero-gas-transactions-api = { workspace = true } stbl-core-primitives = { workspace = true } pallet-ethereum = { workspace = true } +fp-rpc = { workspace = true } +pallet-evm = { workspace = true } # 3rd party array-bytes = { workspace = true, optional = true} log = { workspace = true } diff --git a/test-utils/stability-runtime/src/lib.rs b/test-utils/stability-runtime/src/lib.rs index 6c9bbb7..9c77392 100644 --- a/test-utils/stability-runtime/src/lib.rs +++ b/test-utils/stability-runtime/src/lib.rs @@ -40,7 +40,11 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, CheckNonce, CheckWeight, }; +use sp_core::H160; +use sp_core::U256; +use pallet_ethereum::TransactionStatus; use scale_info::TypeInfo; +use sp_runtime::Permill; use sp_std::prelude::*; use pallet_ethereum::Transaction as EthereumTransaction; use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic}; @@ -725,6 +729,101 @@ impl_runtime_apis! { return Extrinsic::new_unsigned((substrate_test_pallet::pallet::Call::call_do_not_propagate {}).into()) } } + + impl fp_rpc::EthereumRuntimeRPCApi for Runtime { + fn chain_id() -> u64 { + panic!("Function not implemented") + } + + fn account_basic(_address: H160) -> pallet_evm::Account { + panic!("Function not implemented") + } + + fn gas_price() -> U256 { + panic!("Function not implemented") + } + + fn account_code_at(_address: H160) -> Vec { + panic!("Function not implemented") + } + + fn author() -> H160 { + panic!("Function not implemented") + } + + fn storage_at(_address: H160, _index: U256) -> H256 { + panic!("Function not implemented") + } + + fn call( + _from: H160, + _to: H160, + _data: Vec, + _value: U256, + _gas_limit: U256, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + _estimate: bool, + _access_list: Option)>>, + ) -> Result { + panic!("Function not implemented") + } + + fn create( + _from: H160, + _data: Vec, + _value: U256, + _gas_limit: U256, + _max_fee_per_gas: Option, + _max_priority_fee_per_gas: Option, + _nonce: Option, + _estimate: bool, + _access_list: Option)>>, + ) -> Result { + panic!("Function not implemented") + } + + fn current_transaction_statuses() -> Option> { + panic!("Function not implemented") + } + + fn current_block() -> Option { + panic!("Function not implemented") + } + + fn current_receipts() -> Option> { + panic!("Function not implemented") + } + + fn current_all() -> ( + Option, + Option>, + Option> + ) { + panic!("Function not implemented") + } + + fn extrinsic_filter( + _xts: Vec<::Extrinsic>, + ) -> Vec { + panic!("Function not implemented") + } + + fn elasticity() -> Option { + panic!("Function not implemented") + } + + fn gas_limit_multiplier_support() { + panic!("Function not implemented") + } + + fn pending_block( + _xts: Vec<::Extrinsic>, + ) -> (Option, Option>) { + panic!("Function not implemented") + } + } } fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) {