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

Implement eth/65 (EIP-2464) #11626

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft

Implement eth/65 (EIP-2464) #11626

wants to merge 1 commit into from

Conversation

vorot93
Copy link

@vorot93 vorot93 commented Apr 11, 2020

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

ethcore/client-traits/src/lib.rs Outdated Show resolved Hide resolved
ethcore/client-traits/src/lib.rs Outdated Show resolved Hide resolved
@@ -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)
Copy link
Collaborator

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"
Copy link
Collaborator

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 thatsync/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 },
Copy link
Collaborator

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.

Copy link
Collaborator

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?

Copy link
Author

@vorot93 vorot93 May 23, 2020

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 {
Copy link
Collaborator

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.

ethcore/sync/src/chain/propagator.rs Show resolved Hide resolved
@@ -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;
Copy link
Collaborator

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.

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))
Copy link
Collaborator

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 {
Copy link
Collaborator

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).

Copy link
Collaborator

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.

@vorot93 vorot93 added the A4-gotissues 💥 Pull request is reviewed and has significant issues which must be addressed. label Apr 15, 2020
Copy link
Collaborator

@dvdplm dvdplm left a 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
Copy link
Collaborator

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 {
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
/// 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>>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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,
Copy link
Collaborator

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.

@@ -676,6 +691,8 @@ pub struct ChainSync {
sync_start_time: Option<Instant>,
/// Transactions propagation statistics
transactions_stats: TransactionsStats,
/// Unfetched transactions
Copy link
Collaborator

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(),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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>,
Copy link
Collaborator

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?

@vorot93
Copy link
Author

vorot93 commented May 28, 2020

@tomusdrw Is it correct that unsent_pooled_hashes is an unnecessary addition in general and the better approach would be to compute the difference between last_sent_hashes and transaction queue?

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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement EIP-2464: eth/65
4 participants