-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Implement eth/65 (EIP-2464) #11626
base: master
Are you sure you want to change the base?
Implement eth/65 (EIP-2464) #11626
Conversation
@@ -1889,6 +1889,10 @@ impl BlockChainClient for Client { | |||
self.transaction_address(id).and_then(|address| self.chain.read().transaction(&address)) | |||
} | |||
|
|||
fn pooled_transaction(&self, hash: H256) -> Option<Arc<VerifiedTransaction>> { | |||
self.importer.miner.transaction(&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.
The importer
separation was initially there to try to separate import pipeline from the client (which would become responsible only for queries). However I'm not sure how much it's still relevant, so I don't believe any change is required here, just giving some context.
@@ -34,6 +34,7 @@ rlp = "0.4.5" | |||
snapshot = { path = "../snapshot" } | |||
trace-time = "0.1" | |||
triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } | |||
transaction-pool = "2" |
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 think it might be worth to introduce some abstraction to avoid introducing this dependency. For instance client-traits
could have trait
QueuedTransactionthat would include all the things that
sync/network` needs.
@@ -590,9 +591,11 @@ impl SyncHandler { | |||
difficulty, | |||
latest_hash, | |||
genesis, | |||
unsent_pooled_hashes: if eth_protocol_version >= ETH_PROTOCOL_VERSION_65.0 { Some(io.chain().transactions_to_propagate().into_iter().map(|tx| *tx.hash()).collect()) } else { None }, |
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.
Using transactions_to_propagate
here is going to be extremely inefficient. This method creates an ordered iterator over all tranasctions in the pool, and it's quite costly - especially if the pool is large.
We support a more efficient way to query all hashes from the transaction pool (see Miner::pending_transaction_hashes
) and I think it's worth to use it here as well.
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 think there is also some recommended limit in the spec, isn't there?
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 I somehow expose dyn MinerService
here then to take advantage of pending_transaction_hashes
?
let mut affected = false; | ||
let mut packet = RlpStream::new(); | ||
packet.begin_unbounded_list(); | ||
if let Some(s) = &mut peer.unsent_pooled_hashes { |
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.
As I said earlier we should be using last_sent_hashes
here as well, we already have similar logic in transaction propagation.
Actually this whole method might not be needed at all. We should use current transaction propagation logic and this new ETH65 consolidation protocol should only be used to LIMIT the transactions we are sending to hashes that were actually requested (or not seen) by the other peer.
@@ -232,6 +219,24 @@ impl SyncSupplier { | |||
Ok(Some((BlockHeadersPacket.id(), rlp))) | |||
} | |||
|
|||
/// Respond to GetPooledTransactions request | |||
fn return_pooled_transactions(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { | |||
const LIMIT: usize = 256; |
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 already have a bunch of constants like this, we should keep them nearby with proper documentation.
ethcore/sync/src/chain/supplier.rs
Outdated
const LIMIT: usize = 256; | ||
|
||
let transactions = r.iter().take(LIMIT).filter_map(|v| { | ||
v.as_val::<H256>().ok().and_then(|hash| io.chain().pooled_transaction(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.
As mentioned earlier I think the method should support requesting a batch of transactions from the pool instead of going one-by-one.
|
||
let added = transactions.len(); | ||
let mut rlp = RlpStream::new_list(added); | ||
for tx in transactions { |
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 have methods to make sure the package does not end up oversized, please take a look how we construct transaction packages during propagation I think the same logic should apply here (instead of having new arbitrary limit of 256 transactions).
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 256 comes from the EIP and is a suggestion. Defntly should be combined with io.payload_soft_limit()
(4Mb I think) like we do in other places.
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.
Left a few comments, but I think it'd be best to address Tomeks concerns before digging in further; looks like there might be some significant changes coming.
@@ -232,6 +219,24 @@ impl SyncSupplier { | |||
Ok(Some((BlockHeadersPacket.id(), rlp))) | |||
} | |||
|
|||
/// Respond to GetPooledTransactions request |
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.
Can be improved.
|
||
let added = transactions.len(); | ||
let mut rlp = RlpStream::new_list(added); | ||
for tx in transactions { |
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 256 comes from the EIP and is a suggestion. Defntly should be combined with io.payload_soft_limit()
(4Mb I think) like we do in other places.
@@ -343,6 +349,8 @@ pub struct PeerInfo { | |||
asking_blocks: Vec<H256>, | |||
/// Holds requested header hash if currently requesting block header by hash | |||
asking_hash: Option<H256>, | |||
/// Holds requested transaction IDs |
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.
/// Holds requested transaction IDs | |
/// Hash of the transaction we're requesting from the peer |
?
@@ -343,6 +349,8 @@ pub struct PeerInfo { | |||
asking_blocks: Vec<H256>, | |||
/// Holds requested header hash if currently requesting block header by hash | |||
asking_hash: Option<H256>, | |||
/// Holds requested transaction IDs | |||
asking_pooled_transactions: Option<Vec<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.
asking_pooled_transactions: Option<Vec<H256>>, | |
asking_pooled_transaction: Option<Vec<H256>>, |
@@ -676,6 +691,8 @@ pub struct ChainSync { | |||
sync_start_time: Option<Instant>, | |||
/// Transactions propagation statistics | |||
transactions_stats: TransactionsStats, |
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 need changes here too to account for eth/65
traffic? Would be good to have good data on protocol usage.
ethcore/sync/src/chain/mod.rs
Outdated
@@ -676,6 +691,8 @@ pub struct ChainSync { | |||
sync_start_time: Option<Instant>, | |||
/// Transactions propagation statistics | |||
transactions_stats: TransactionsStats, | |||
/// Unfetched transactions |
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.
Are these txs that we have seen announced but not fetched yet? Are they txs that we know we do not have or?
@@ -717,6 +734,7 @@ impl ChainSync { | |||
snapshot: Snapshot::new(), | |||
sync_start_time: None, | |||
transactions_stats: TransactionsStats::default(), | |||
unfetched_pooled_transactions: Default::default(), |
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.
unfetched_pooled_transactions: Default::default(), | |
unfetched_pooled_transactions: H256FastMap::default(), |
(to me it's more readable to see the actual type, but maybe it's just me)
@@ -335,6 +339,8 @@ pub struct PeerInfo { | |||
network_id: u64, | |||
/// Peer best block hash | |||
latest_hash: H256, | |||
/// Unpropagated tx pool hashes | |||
unsent_pooled_hashes: Option<H256FastSet>, |
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.
What is the point of Option
here? Is None
different from the hash set being empty, semantically? I mean to say: isn't it unlikely we'll be running with this set None
during normal operation, and if so there's not much of a space saving?
@tomusdrw Is it correct that |
3374ed3
to
b4af110
Compare
This PR adds support for Ethereum protocol version 65 (eth/65) described in EIP-2464. This is a pretty rough draft and can definitely be improved, but I hope the overall direction is good.
Fixes #11500