Skip to content

Commit

Permalink
Improvement zero gas transaction performance (#152)
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrielMartinezRodriguez authored Nov 20, 2023
1 parent a4d6194 commit e1731f2
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 58 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/authorship/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
83 changes: 41 additions & 42 deletions client/authorship/src/authorship.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
///
Expand Down Expand Up @@ -259,6 +263,7 @@ where
+ 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>
+ fp_rpc::EthereumRuntimeRPCApi<Block>
+ stbl_primitives_fee_compatible_api::CompatibleFeeApi<Block, AccountId>
+ stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi<Block>,
PR: ProofRecording,
Expand Down Expand Up @@ -305,6 +310,7 @@ where
+ 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>
+ fp_rpc::EthereumRuntimeRPCApi<Block>
+ stbl_primitives_fee_compatible_api::CompatibleFeeApi<Block, AccountId>
+ stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi<Block>,
PR: ProofRecording,
Expand Down Expand Up @@ -367,6 +373,7 @@ where
+ 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>
+ fp_rpc::EthereumRuntimeRPCApi<Block>
+ stbl_primitives_fee_compatible_api::CompatibleFeeApi<Block, AccountId>
+ stbl_primitives_zero_gas_transactions_api::ZeroGasTransactionApi<Block>,
PR: ProofRecording,
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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::<u32>().saturating_add(1);

let message: Vec<u8> = 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
Expand All @@ -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
}
Expand All @@ -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<u8> = 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
Expand All @@ -549,9 +550,7 @@ where
else {
continue;
};




let block_size =
block_builder.estimate_block_size(self.include_proof_in_block_size_estimation);

Expand Down Expand Up @@ -605,7 +604,7 @@ where
}
}
};
}
};

let mut unqueue_invalid = Vec::new();
let mut t1 = self.transaction_pool.ready_at(self.parent_number).fuse();
Expand Down
32 changes: 16 additions & 16 deletions pallets/zero-gas-transactions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -36,7 +37,7 @@ pub mod pallet {
pub type SponsorNonce<T: Config> = 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<BlockNumber = u32> + pallet_evm::Config + pallet_ethereum::Config {
type RuntimeCall: Parameter + GetDispatchInfo;
type UserFeeTokenController: UserFeeTokenController;
}
Expand Down Expand Up @@ -64,7 +65,6 @@ pub mod pallet {
let current_block_validator = <pallet_evm::Pallet<T>>::find_author();

Self::ensure_zero_gas_transaction(
transaction.clone(),
current_block_validator,
validator_signature.clone(),
)
Expand Down Expand Up @@ -118,7 +118,6 @@ pub mod pallet {
let current_block_validator = <pallet_evm::Pallet<T>>::find_author();

Self::ensure_zero_gas_transaction(
transaction.clone(),
current_block_validator,
validator_signature,
)
Expand Down Expand Up @@ -196,12 +195,14 @@ pub mod pallet {
}

fn ensure_zero_gas_transaction(
transaction: pallet_ethereum::Transaction,
expected_validator: H160,
validator_signature: Vec<u8>,
) -> Result<(), ()> {
let chain_id = T::ChainId::get();
let block_number = <frame_system::Pallet<T>>::block_number();

let zero_gas_trx_internal_message: Vec<u8> =
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);
Expand All @@ -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<u8> {
let mut message: Vec<u8> = 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<u8>, message: H256) -> Option<H160> {
Expand All @@ -245,5 +244,6 @@ pub mod pallet {

return Some(H160::from_slice(&result[12..32]));
}

}
}
2 changes: 2 additions & 0 deletions test-utils/stability-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
99 changes: 99 additions & 0 deletions test-utils/stability-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<Block> 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<u8> {
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<u8>,
_value: U256,
_gas_limit: U256,
_max_fee_per_gas: Option<U256>,
_max_priority_fee_per_gas: Option<U256>,
_nonce: Option<U256>,
_estimate: bool,
_access_list: Option<Vec<(H160, Vec<H256>)>>,
) -> Result<pallet_evm::CallInfo, sp_runtime::DispatchError> {
panic!("Function not implemented")
}

fn create(
_from: H160,
_data: Vec<u8>,
_value: U256,
_gas_limit: U256,
_max_fee_per_gas: Option<U256>,
_max_priority_fee_per_gas: Option<U256>,
_nonce: Option<U256>,
_estimate: bool,
_access_list: Option<Vec<(H160, Vec<H256>)>>,
) -> Result<pallet_evm::CreateInfo, sp_runtime::DispatchError> {
panic!("Function not implemented")
}

fn current_transaction_statuses() -> Option<Vec<TransactionStatus>> {
panic!("Function not implemented")
}

fn current_block() -> Option<pallet_ethereum::Block> {
panic!("Function not implemented")
}

fn current_receipts() -> Option<Vec<pallet_ethereum::Receipt>> {
panic!("Function not implemented")
}

fn current_all() -> (
Option<pallet_ethereum::Block>,
Option<Vec<pallet_ethereum::Receipt>>,
Option<Vec<TransactionStatus>>
) {
panic!("Function not implemented")
}

fn extrinsic_filter(
_xts: Vec<<Block as BlockT>::Extrinsic>,
) -> Vec<EthereumTransaction> {
panic!("Function not implemented")
}

fn elasticity() -> Option<Permill> {
panic!("Function not implemented")
}

fn gas_limit_multiplier_support() {
panic!("Function not implemented")
}

fn pending_block(
_xts: Vec<<Block as BlockT>::Extrinsic>,
) -> (Option<pallet_ethereum::Block>, Option<Vec<TransactionStatus>>) {
panic!("Function not implemented")
}
}
}

fn test_ed25519_crypto() -> (ed25519::AppSignature, ed25519::AppPublic) {
Expand Down

0 comments on commit e1731f2

Please sign in to comment.