-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Private packets verification and queue refactoring #8715
Conversation
#[derive(Debug)] | ||
pub struct PrivateScorying; | ||
|
||
impl txpool::Scoring<VerifiedPrivateTransaction> for PrivateScorying { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tomusdrw I reworked my queue's implementation in order to use txpool directly for storing transaction in it. The main problem, that I'm facing, is scoring implementation. Actually I'd like to fully re-use miner's scoring, but I don't know, how to do it, because it's bound with miner's VerifiedTransaction trait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So as result I had to copy-past scoring code from miner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make the two (Scoring
and Ready
) generic then.
@grbIzl we should update the documentation for that one right? |
@Tbaut i don't think so. It's mostly the internal improvements and refactorings |
63f4402
to
6436745
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of grumbles and suggestions.
ethcore/private-tx/src/error.rs
Outdated
@@ -167,6 +168,12 @@ error_chain! { | |||
description("General ethcore error."), | |||
display("General ethcore error {}", err), | |||
} | |||
|
|||
#[doc = "Tx pool error."] | |||
Txpool(err: TxPoolError) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be just foreign_link
(and From
implementation wouldn't be needed in such case)
ethcore/private-tx/src/lib.rs
Outdated
} | ||
Ok(()) | ||
} | ||
|
||
/// Add signed private transaction into the store /// Creates corresponding public transaction if last required signature collected and sends it to the chain |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split into two lines
ethcore/private-tx/src/lib.rs
Outdated
} | ||
let account = desc.validator_account; | ||
if let Action::Call(contract) = transaction.signed().action { | ||
let signed_tx = transaction.transaction.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need to clone the entire transaction? It seems that we only need a reference in :283
ethcore/private-tx/src/lib.rs
Outdated
None => { | ||
trace!("Propagating transaction further"); | ||
self.broadcast_private_transaction(private_hash, transaction.private_transaction.rlp_bytes().into_vec()); | ||
return Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect that error handling in this block might be incorrect:
- We
drain
all transactions for verification - But we stop at first incorrect transaction
That means that every other transaction will just be ignored. Most likely we should run all the transactions and only return error after that (or even return a Vec<Result<>
).
Please review all return
, bail!
and ?
statements in that loop.
ethcore/private-tx/src/lib.rs
Outdated
let mut signed_transactions = self.imported_signed_transactions.lock(); | ||
for tx in signed_transactions.drain(..) { | ||
let private_hash = tx.private_transaction_hash(); | ||
let desc = match self.transactions_for_signing.lock().get(&private_hash) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We acquire a second lock here (after acquiring imported_signed_transactions
).
Couple of issues with this:
- Seems that
RwLock
would do (you sometimes only needread
access, notwrite
) - Multiple, dependent locks are always error-prone, is it really necessary to have the two? Can't we put it into separate structs?
- Calling private functions
self.*
after acquiring a lock is always super error-prone, we have to be sure that the lock is not re-acquired in that private function. - Last, but not least: the locking happens in opposite order than the declaration - to avoid deadlocks we should always acquire locks in the same order as the fields are declared in the struct.
// Use pool's verifying pipeline for original transaction's verification | ||
let verifier = pool::verifier::Verifier::new(client, options, Default::default()); | ||
let _verified_tx = verifier.verify_transaction(pool::verifier::Transaction::Unverified(transaction.clone()))?; | ||
let signed_tx = SignedTransaction::new(transaction)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should take SignedTransaction
from _verified_tx
, otherwise you are recovering the signature twice
pub fn drain<C: pool::client::NonceClient>(&self, client: C) -> Vec<Arc<VerifiedPrivateTransaction>> { | ||
let ready = PrivateReadyState::new(client); | ||
let mut hashes: Vec<H256> = Vec::new(); | ||
let res: Vec<Arc<VerifiedPrivateTransaction>> = self.verification_pool.read().pending(ready).collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let transactions: Vec<_> = self.verification_pool.read().pending(ready).collect()
let mut pool = self.verification_pool.write();
for tx in &transactions {
pool.remove(tx.hash(), true);
}
transactions
doesn't make sense to allocate the vec twice.
ethcore/sync/src/chain/mod.rs
Outdated
@@ -329,6 +329,10 @@ pub struct PeerInfo { | |||
ask_time: Instant, | |||
/// Holds a set of transactions recently sent to this peer to avoid spamming. | |||
last_sent_transactions: HashSet<H256>, | |||
/// Holds a set of private transactions recently sent to this peer to avoid spamming. | |||
last_sent_private_transactions: HashSet<H256>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to separate the two? The collision is not possibly anyway, for simplicity let's have a single set.
ethcore/sync/src/chain/mod.rs
Outdated
@@ -1083,6 +1117,9 @@ impl ChainSync { | |||
peer_info.last_sent_transactions.clear() | |||
); | |||
} | |||
|
|||
// reset stats for private transaction packets | |||
self.clear_private_stats(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So it seems that we always re-broadcast after the the block, right? Why can't we use the same logic as for regular transactions?
Also this statements should be guarded by similar ifs as the above code - we don't need to clear it when we import uncle block or we are syncing.
ethcore/sync/src/chain/mod.rs
Outdated
@@ -1121,13 +1158,13 @@ impl ChainSync { | |||
} | |||
|
|||
/// Broadcast private transaction message to peers. | |||
pub fn propagate_private_transaction(&mut self, io: &mut SyncIo, packet: Bytes) { | |||
SyncPropagator::propagate_private_transaction(self, io, packet); | |||
pub fn propagate_private_transaction(&mut self, io: &mut SyncIo, transaction_hash: H256, packet: Bytes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth merging the two methods (and the methods they are calling into one) and just discriminate using enum
to avoid duplication.
Marking as stale @grbIzl. 🐥 |
Will reopen once you continue working on it. 👍 |
ethcore/private-tx/src/lib.rs
Outdated
} | ||
Ok(()) | ||
} | ||
|
||
/// Add signed private transaction into the store | ||
/// Creates corresponding public transaction if last required signature collected and sends it to the chain | ||
pub fn process_signature(&self, signed_tx: SignedPrivateTransaction) -> Result<(), Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
take signed_tx
by reference instead and get rid of a clone()
: https://github.com/paritytech/parity-ethereum/blob/2778d4e99bb25c8e2df3f15617e57b16156fbc10/ethcore/private-tx/src/lib.rs#L678
ethcore/private-tx/src/lib.rs
Outdated
} | ||
} | ||
verification_queue.remove_private_transaction(&transaction_hash); | ||
return Ok(()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needless return
i.e, enough with Ok(())
ethcore/private-tx/src/lib.rs
Outdated
warn!("Failed to remove transaction from signing store, error: {:?}", err); | ||
bail!(err); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace single-pattern match
with:
if let Err(err) = self.transactions_for_signing.write().remove(&private_hash) { ..... }
fn broadcast_private_transaction(&self, message: Bytes) { | ||
self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(message.clone()))); | ||
fn broadcast_private_transaction(&self, transaction_hash: H256, message: Bytes) { | ||
self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(transaction_hash, message.clone()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks suspect to cloning
the vector here instead of moving into the closure! Any particular reason why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not possible to move captured outer variable (message)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note: in the future we may consider to change type ChainMessageType
to only take reference of [u8]
. In that case this should solve this message clone issue.
use error::{Error, ErrorKind}; | ||
|
||
type Pool = txpool::Pool<VerifiedPrivateTransaction, pool::scoring::NonceAndGasPrice>; | ||
|
||
/// Maximum length for private transactions queues. | ||
const MAX_QUEUE_LEN: usize = 8312; | ||
|
||
/// Desriptor for private transaction stored in queue for verification |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in Descriptor
Rephrase this, something like Private transaction type
!
/// Address that should be used for verification | ||
pub validator_account: Address, | ||
pub validator_account: Option<Address>, | ||
/// Resulted verified |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verified result
pub validator_account: Option<Address>, | ||
/// Resulted verified | ||
pub transaction: SignedTransaction, | ||
/// Original transaction's hash |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Original transaction hash
pub transaction: SignedTransaction, | ||
/// Original transaction's hash | ||
pub transaction_hash: H256, | ||
/// Original transaction's sender |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Orginal transaction sender or Transaction origin address
let options = self.verification_options.clone(); | ||
// Use pool's verifying pipeline for original transaction's verification | ||
let verifier = pool::verifier::Verifier::new(client, options, Default::default(), None); | ||
let verified_tx = verifier.verify_transaction(pool::verifier::Transaction::Unverified(transaction.clone()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needless clone()
call
You can either take the transaction (UnverifiedTransaction)
by reference and clone it or take it by value and move it!
} | ||
} | ||
} | ||
|
||
impl VerificationStore { | ||
/// Adds private transaction for verification into the store | ||
pub fn add_transaction<C: pool::client::Client>( | ||
&mut self, | ||
&self, | ||
transaction: UnverifiedTransaction, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment below!
ethcore/sync/src/private_tx.rs
Outdated
|
||
/// Trait which should be implemented by a private transaction handler. | ||
pub trait PrivateTxHandler: Send + Sync + 'static { | ||
/// Function called on new private transaction received. | ||
fn import_private_transaction(&self, rlp: &[u8]) -> Result<(), String>; | ||
/// Returns hash of the imported transaction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the hash
ethcore/sync/src/private_tx.rs
Outdated
|
||
/// Function called on new signed private transaction received. | ||
fn import_signed_private_transaction(&self, rlp: &[u8]) -> Result<(), String>; | ||
/// Returns hash of the imported transaction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the hash
miner/src/pool/scoring.rs
Outdated
insertion_id: i as u64, | ||
transaction: Arc::new(verified), | ||
} | ||
let txs = vec![tx1, tx2, tx3, tx4].into_iter().enumerate().map(|(_, tx)| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove enumerate()
because it is not used!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey,
Looks overall good, I have left a few comments that can improve that code a bit!
I have not commented on the logging but ideally, all these logs should have a target i.e., trace!(target: "foo", "some very important message
. Not required for this PR
But, this PR needs to rebased/merged to master before merging (that's why I used the Request changes
option!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
fn broadcast_private_transaction(&self, message: Bytes) { | ||
self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(message.clone()))); | ||
fn broadcast_private_transaction(&self, transaction_hash: H256, message: Bytes) { | ||
self.notify(|notify| notify.broadcast(ChainMessageType::PrivateTransaction(transaction_hash, message.clone()))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note: in the future we may consider to change type ChainMessageType
to only take reference of [u8]
. In that case this should solve this message clone issue.
* master: evmbin: Fix gas_used issue in state root mismatch and handle output better (#9418) Update hardcoded sync (#9421) Add block reward contract config to ethash and allow off-chain contracts (#9312) Private packets verification and queue refactoring (#8715) Update tobalaba.json (#9419) docs: add parity ethereum logo to readme (#9415) build: update rocksdb crate (#9414) Updating the CI system (#8765) Better support for eth_getLogs in light mode (#9186) Add update docs script to CI (#9219) `gasleft` extern implemented for WASM runtime (kip-6) (#9357) block view! removal in progress (#9397) Prevent sync restart if import queue full (#9381) nonroot CentOS Docker image (#9280) ethcore: kovan: delay activation of strict score validation (#9406)
This PR addresses two major TODOs from private transactions PR