Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

fn queue_transactions accepts UnverifiedTransactions #10887

Closed
wants to merge 1 commit into from

Conversation

debris
Copy link
Collaborator

@debris debris commented Jul 15, 2019

importing transactions on a client layer is a mess, this pr tries too simplify it a bit and reduce the number of redundant transaction encoding and decoding. example scenario before this pr and after this pr

before

light client, relay:

  • ethcore/light/src/net/mod.rs decodes transaction
  • ethcore/sync/src/api.rs encodes transaction
  • ethcore/src/client/client.rs decodes transaction
  • miner/src/pool/verifier.rs encodes transaction
  • ethcore/src/machine.rs decodes transaction

now

light client, relay

  • ethcore/light/src/net/mod.rs decodes transaction
  • miner/src/pool/verifier.rs encodes transaction

it's still not perfect, but definitely better than it was before

@debris debris added A0-pleasereview 🤓 Pull request needs code review. M4-core ⛓ Core client code / Rust. labels Jul 15, 2019
@debris debris requested a review from dvdplm July 15, 2019 11:42

let txs: Vec<UnverifiedTransaction> = transactions
.iter()
.filter_map(|bytes| client.engine.decode_transaction(bytes).ok())
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this verification is performed by a miner in import_external_transactions

Copy link
Collaborator

@ordian ordian Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we previous ignored invalid rlps and now we return an err in on_peer_transactions? This seems like an improvement to me. But maybe it was done on purpose?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we previous ignored invalid rlps and now we return an err in on_peer_transactions?

Yes

This seems like an improvement to me. But maybe it was done on purpose?

No, it was a regression introduced very recently

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This closure is invoked in a IoWorker thread pool and the whole thing was done to prevent blocking networking task for useless stuff like decoding transactions.

client.notify(|notify| {
notify.transactions_received(&txs, peer_id);
notify.transactions_received(&hashes, peer_id);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should move this notification after miner imports the transactions? cause those transactions may be invalid

Copy link
Collaborator

@ordian ordian Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIR this notification only updates last_sent_transactions of a peer, so that we don't propagate the same transaction that the peer sent us

self.machine().decode_transaction(transaction)
/// Verifies len of transaction RLP
fn verify_transaction_len(&self, transaction: &[u8]) -> Result<(), transaction::Error> {
if transaction.len() > self.params().max_transaction_size {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

max_transaction_size is configurable since #8417 but I don't know if this is really needed... maybe we could just remove it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we can't just remove it. We need a way to tell users that their transaction is not being accepted to the pool and will never be propagated. We can obviously hardcode that into a miner, but it feels like a regression.

@debris debris requested review from tomusdrw and ordian July 16, 2019 09:45
Copy link
Collaborator

@ordian ordian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

});

client.importer.miner.import_external_transactions(client, txs);
// TODO: queue_transactions.queue, should accept FnOnce, so it can capture outer variables
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you open an issue?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do it initially, but it requires reworking IoWorkers. Currently every IoMessage is passed to every handler. We just have one handler for ClientIoMessage, I guess we could maybe hack around it with some internal mutability though.


let txs: Vec<UnverifiedTransaction> = transactions
.iter()
.filter_map(|bytes| client.engine.decode_transaction(bytes).ok())
Copy link
Collaborator

@ordian ordian Jul 16, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we previous ignored invalid rlps and now we return an err in on_peer_transactions? This seems like an improvement to me. But maybe it was done on purpose?

@ordian ordian added this to the 2.7 milestone Jul 16, 2019
@ordian ordian added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Jul 16, 2019
Copy link
Collaborator

@tomusdrw tomusdrw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't accept this PR in it's current form it basically reverts a lot of work that has been done to optimize networking and transaction importing pipeline.

@@ -246,7 +246,7 @@ pub trait Handler: Send + Sync {
/// Called when a peer makes an announcement.
fn on_announcement(&self, _ctx: &dyn EventContext, _announcement: &Announcement) { }
/// Called when a peer requests relay of some transactions.
fn on_transactions(&self, _ctx: &dyn EventContext, _relay: &[UnverifiedTransaction]) { }
fn on_transactions(&self, _ctx: &dyn EventContext, _relay: Vec<UnverifiedTransaction>) { }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a regression. Luckily it's only a light client, so we shouldn't have many transactions but you may end up cloning a lot of data.

});

client.importer.miner.import_external_transactions(client, txs);
// TODO: queue_transactions.queue, should accept FnOnce, so it can capture outer variables
for res in client.importer.miner.import_external_transactions(client, transactions.clone()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unacceptable, it means you clone ~4MB a second.

});

client.importer.miner.import_external_transactions(client, txs);
// TODO: queue_transactions.queue, should accept FnOnce, so it can capture outer variables
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do it initially, but it requires reworking IoWorkers. Currently every IoMessage is passed to every handler. We just have one handler for ClientIoMessage, I guess we could maybe hack around it with some internal mutability though.

self.machine().decode_transaction(transaction)
/// Verifies len of transaction RLP
fn verify_transaction_len(&self, transaction: &[u8]) -> Result<(), transaction::Error> {
if transaction.len() > self.params().max_transaction_size {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we can't just remove it. We need a way to tell users that their transaction is not being accepted to the pool and will never be propagated. We can obviously hardcode that into a miner, but it feels like a regression.

@@ -673,8 +673,7 @@ impl SyncHandler {
trace!(target: "sync", "{:02} -> Transactions ({} entries)", peer_id, item_count);
let mut transactions = Vec::with_capacity(item_count);
for i in 0 .. item_count {
let rlp = r.at(i)?;
let tx = rlp.as_raw().to_vec();
let tx = r.val_at(i)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole point of passing Bytes was to avoid doing any intensive work (like hashing transactions) synchronously in the network IoHandlers.
The whole refactoring was done after a careful profiling of issues that happens on the mainnet where network lock could be acquired for a very long time.


let txs: Vec<UnverifiedTransaction> = transactions
.iter()
.filter_map(|bytes| client.engine.decode_transaction(bytes).ok())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This closure is invoked in a IoWorker thread pool and the whole thing was done to prevent blocking networking task for useless stuff like decoding transactions.

@tomusdrw tomusdrw added A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. and removed A8-looksgood 🦄 Pull request is reviewed well. labels Jul 16, 2019
@tomusdrw
Copy link
Collaborator

tomusdrw commented Jul 16, 2019

Regarding light client: note that all transactions always come from the RPC and we expect there is only few of them. Additional decoding-encoding is way lower priority than full node pipeline on the mainnet where you can have 16MBs packets of transactions received many times a second.

@debris
Copy link
Collaborator Author

debris commented Jul 30, 2019

I'll rework this code and open a new pr

@debris debris closed this Jul 30, 2019
@denisgranha denisgranha deleted the queue_transactions branch March 17, 2020 11:38
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants