Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(net): support eth/68 #1361

Merged
merged 37 commits into from
Feb 21, 2023
Merged

feat(net): support eth/68 #1361

merged 37 commits into from
Feb 21, 2023

Conversation

jinsankim
Copy link
Contributor

@jinsankim jinsankim commented Feb 15, 2023

@rkrasiuk rkrasiuk added C-enhancement New feature or request A-devp2p Related to the Ethereum P2P protocol labels Feb 15, 2023
@rkrasiuk rkrasiuk self-requested a review February 15, 2023 08:18
@jinsankim jinsankim marked this pull request as ready for review February 15, 2023 08:52
@codecov-commenter
Copy link

codecov-commenter commented Feb 15, 2023

Codecov Report

Merging #1361 (d43efa0) into main (5c2f96e) will decrease coverage by 28.93%.
The diff coverage is 42.24%.

📣 This organization is not using Codecov’s GitHub App Integration. We recommend you install it so Codecov can continue to function properly for your repositories. Learn more

@@             Coverage Diff             @@
##             main    #1361       +/-   ##
===========================================
- Coverage   76.19%   47.27%   -28.93%     
===========================================
  Files         357      358        +1     
  Lines       41040    42320     +1280     
===========================================
- Hits        31270    20005    -11265     
- Misses       9770    22315    +12545     
Flag Coverage Δ
integration-tests 21.96% <14.65%> (-0.40%) ⬇️
unit-tests 37.70% <41.37%> (-32.82%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
crates/net/eth-wire/src/errors/eth.rs 47.05% <ø> (-5.89%) ⬇️
crates/net/network/src/message.rs 40.77% <ø> (-3.89%) ⬇️
crates/net/network/src/network.rs 53.67% <0.00%> (-5.15%) ⬇️
crates/net/network/src/session/active.rs 33.28% <0.00%> (-53.12%) ⬇️
crates/net/network/src/transactions.rs 0.00% <0.00%> (-21.98%) ⬇️
crates/net/eth-wire/src/types/message.rs 35.24% <7.69%> (-14.76%) ⬇️
crates/net/eth-wire/src/types/version.rs 30.00% <25.00%> (-50.00%) ⬇️
crates/net/eth-wire/src/ethstream.rs 47.50% <52.94%> (-35.10%) ⬇️
crates/net/eth-wire/src/types/broadcast.rs 65.00% <66.66%> (-11.79%) ⬇️
crates/net/eth-wire/src/capability.rs 61.76% <96.77%> (+2.33%) ⬆️
... and 181 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

thanks for taking this on.
left some comments, but would like to hear @Rjected s opinion on some parts.

Comment on lines 157 to 158
NewPooledTransactionHashes(NewPooledTransactionHashes),
NewPooledTransactionHashes68(NewPooledTransactionHashes68),
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 a bit cursed -.- that the variant now is an enum

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think given we don't have support for multiple capabilities, which also has the need for new message types and encodings, it's up in the air how we should do this properly. But yeah, I agree this doesn't seem like the best way to do it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree it's too ugly and immature.

I adopted the convention from geth. (They uses the name like NewPooledTransactionHashesPacket66 and NewPooledTransactionHashesPacket68.) And I tried to minimize changes. This code is the result of this approach.

I'll think more what's better way.

Copy link
Collaborator

@mattsse mattsse Feb 17, 2023

Choose a reason for hiding this comment

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

I actually like that we do version-specific decoding on the ethstream level.

so, after some thinking this change seems to be fine to me, because we already have outdated variants like GetNodeData etc.
and because the stream does not emit them if they don't match the version this is ok.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to rename the existing variant to NewPooledTransactionHashes66 then

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd like to rename the existing variant to NewPooledTransactionHashes66 then

That's my first implementation but I've reverted it. I'll think again and share why I reverted it. Or will follow up the idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm learning for ActiveSession, PeerMessage::PooledTransactions and so on. The PR might have a bug.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After learning code more, I could remember why I choose to use NewPooledTransactionHashes than NewPooledTransactionHashes66. It's because NewPooledTransactionHashes is used internally as well for PeerMessage::PooledTransactions, NetworkHandleMessage::SendPooledTransactionHashes and NetworkTransactionEvent::IncomingPooledTransactionHashes.

Now I've realized that we should care of these as well. maybe need more 66, 68 convention and branches. T_T I'm working on it.

Copy link
Contributor Author

@jinsankim jinsankim Feb 18, 2023

Choose a reason for hiding this comment

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

I'd like to rename the existing variant to NewPooledTransactionHashes66 then

follow it up with a94b1da. I've changed this PR into Draft because the new change is little bit big. I need to add more tests and review myself first.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ready for review.

@@ -181,6 +181,19 @@ impl ActiveSession {
EthMessage::NewPooledTransactionHashes(msg) => {
self.try_emit_broadcast(PeerMessage::PooledTransactions(msg)).into()
}
EthMessage::NewPooledTransactionHashes68(msg) => {
if msg.hashes.len() != msg.types.len() || msg.hashes.len() != msg.sizes.len() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@Rjected perhaps we should apply this check in the stream directly? so the stream only yields well-formatted messages

Copy link
Member

Choose a reason for hiding this comment

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

yes, we should be doing these checks in the stream, and malformed messages should be considered invalid

message_type: EthMessageID,
buf: &mut &[u8],
) -> Result<Self, reth_rlp::DecodeError> {
pub fn decode_message(version: u8, buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

so now decode_message depends on the version? because NewPooledTransactionHashes format differs?

...
I feel like mixing this together with decode is a bit weird. for example decoding GetNodeData now fails with an rlp error even though it can still be valid rlp.

I really would like to do this in two steps:
decode then validate against version.
wdyt @Rjected

Copy link
Member

Choose a reason for hiding this comment

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

yes, decoding should not worry about version details, I think this is another consequence of not supporting multiple capabilities. We don't know what to decode into if we don't know the eth wire protocol version being used. It's ambiguous whether or not we should attempt to decode a eth/68 pooled hashes message, or a eth/67 pooled hashes message when we receive a NewPooledTransactionHashes message ID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For support multiple capabilities, we must have a branch at some level. It could be just if, match, traits, module or whatever. It's needed but I agree that it's not the best way.

capabilities: capabilities
.unwrap_or_else(|| vec![EthVersion::Eth66.into(), EthVersion::Eth67.into()]),
capabilities: capabilities.unwrap_or_else(|| {
vec![EthVersion::Eth66.into(), EthVersion::Eth67.into(), EthVersion::Eth68.into()]
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think we should enable this by default already?

Copy link
Member

Choose a reason for hiding this comment

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

agreed, this is a future upgrade, 67 is still considered the current version

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed. I thought it's safe because it's still including 66, 67, but I was too naive and aggressive. Definitely, I agree not to enable it yet and need to verify it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with 651fc4d

#[pin]
inner: S,
}

impl<S> EthStream<S> {
/// Creates a new unauthed [`EthStream`] from a provided stream. You will need
/// to manually handshake a peer.
pub fn new(inner: S) -> Self {
Self { inner }
pub fn new(version: u8, inner: S) -> Self {
Copy link
Collaborator

Choose a reason for hiding this comment

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

if we do this then all version: u8 should be version: EthVersion instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with ca74056

Copy link
Member

@Rjected Rjected left a comment

Choose a reason for hiding this comment

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

I'm really not sure that we should implement this without first figuring out the best way to support multiple capabilities. eth/66 and eth/67 are a convenient exception to the rule that most versions have entirely separate message encodings, that are ambiguous unless you know the version. New capabilities also have this problem, so if we push to support eth/68 without first figuring out how to support other capabilities, I think we might bury a lot of version-specific code / tech debt that should not be there long-term

Comment on lines 157 to 158
NewPooledTransactionHashes(NewPooledTransactionHashes),
NewPooledTransactionHashes68(NewPooledTransactionHashes68),
Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I think given we don't have support for multiple capabilities, which also has the need for new message types and encodings, it's up in the air how we should do this properly. But yeah, I agree this doesn't seem like the best way to do it

@@ -181,6 +181,19 @@ impl ActiveSession {
EthMessage::NewPooledTransactionHashes(msg) => {
self.try_emit_broadcast(PeerMessage::PooledTransactions(msg)).into()
}
EthMessage::NewPooledTransactionHashes68(msg) => {
if msg.hashes.len() != msg.types.len() || msg.hashes.len() != msg.sizes.len() {
Copy link
Member

Choose a reason for hiding this comment

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

yes, we should be doing these checks in the stream, and malformed messages should be considered invalid

message_type: EthMessageID,
buf: &mut &[u8],
) -> Result<Self, reth_rlp::DecodeError> {
pub fn decode_message(version: u8, buf: &mut &[u8]) -> Result<Self, reth_rlp::DecodeError> {
Copy link
Member

Choose a reason for hiding this comment

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

yes, decoding should not worry about version details, I think this is another consequence of not supporting multiple capabilities. We don't know what to decode into if we don't know the eth wire protocol version being used. It's ambiguous whether or not we should attempt to decode a eth/68 pooled hashes message, or a eth/67 pooled hashes message when we receive a NewPooledTransactionHashes message ID.

@jinsankim
Copy link
Contributor Author

@mattsse @Rjected

Thank you for comments! I'm much learning from your comments. And now I understand it could cause tech debt and we need to figure out what's the best way to support multiple protocol.

Please feel free to close it if this PR is not proper way. I just picked it up because the issue is marked as good-first-issue. It's enough for me to work it on and learn from it. Whether it could be merged or not, I'd like to revise it in order to learn more. I open to close it any time without any comment. :)

Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

alright, we actually need this urgently because this is now a blocker for hive tests.

This looks pretty good already, I guess biggest change is: move tx hash 68 validation into stream itself.

and separate decoding from version but the stream should validate the decoded message against the version, right @Rjected ?

@@ -115,7 +122,7 @@ impl Capabilities {
/// Whether the peer supports `eth` sub-protocol.
#[inline]
pub fn supports_eth(&self) -> bool {
self.eth_67 || self.eth_66
self.eth_67 || self.eth_66 || self.eth_68
Copy link
Collaborator

Choose a reason for hiding this comment

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

since we expect that client will update over time we can change the order here: 68 || 67 || 66

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will do

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with b7b4b14. nit but please note that I also changed the eth versions order as desc in HelloMessageBuilder.

Comment on lines 157 to 158
NewPooledTransactionHashes(NewPooledTransactionHashes),
NewPooledTransactionHashes68(NewPooledTransactionHashes68),
Copy link
Collaborator

@mattsse mattsse Feb 17, 2023

Choose a reason for hiding this comment

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

I actually like that we do version-specific decoding on the ethstream level.

so, after some thinking this change seems to be fine to me, because we already have outdated variants like GetNodeData etc.
and because the stream does not emit them if they don't match the version this is ok.

Comment on lines 157 to 158
NewPooledTransactionHashes(NewPooledTransactionHashes),
NewPooledTransactionHashes68(NewPooledTransactionHashes68),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to rename the existing variant to NewPooledTransactionHashes66 then

@@ -153,6 +158,7 @@ pub enum EthMessage {
NewBlock(Box<NewBlock>),
Transactions(Transactions),
NewPooledTransactionHashes(NewPooledTransactionHashes),
NewPooledTransactionHashes68(NewPooledTransactionHashes68),
Copy link
Collaborator

Choose a reason for hiding this comment

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

also need to update struct docs for EthMessage.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with 011eb73. Please suggest if docs is insufficient because I'm not a fluent English speaker.

@Rjected
Copy link
Member

Rjected commented Feb 17, 2023

and separate decoding from version but the stream should validate the decoded message against the version, right @Rjected ?

I guess it might be difficult to decode without using the version, because otherwise we don't know if we should attempt a NewPooledTransactionHashes68::decode, or a NewPooledTransactionHashes::decode

@jinsankim
Copy link
Contributor Author

merged upstream/main to resolve conflicts.

@jinsankim
Copy link
Contributor Author

I need to investigate why the test is failed because it seems not to be related with my change. Please note that the work might be delayed because it's weekend in my timezone.

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

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

Good work! I think final things missing might be some tests? Defer to @mattsse otherwise. I guess passing Hive will matter there, but worth configuring 2 clients with eth/68 and seeing that it works? Or one with 67 and one with 68?

Copy link
Collaborator

@mattsse mattsse left a comment

Choose a reason for hiding this comment

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

only a few suggestion for the eth-wire changes.

I have some questions regarding the network changes, mainly why we need this type with optional fields.

can we extract all of these and submit separately, so we can merge the eth68 changes asap?

EthHandshakeError(#[from] EthHandshakeError),
#[error("message size ({0}) exceeds max length (10MB)")]
MessageTooBig(usize),
#[error("TransactionHashes invalid len of fields: {0} {1} {2}")]
TransactionHashesInvalidLenOfFields(usize, usize, usize),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to use named fields here, otherwise it's harder to understand what the different usizes are

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with 231f460.

pub hashes: Vec<H256>,
}

impl TryFrom<(Option<Vec<u8>>, Option<Vec<usize>>, Vec<H256>)> for NewPooledTransactionHashes68 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

not a fan of this conversion.

why would we need this anyway?
I'd like to remove this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please give me an advice. T_T

  • It's used for converting from NewPooledTransactionHashes into NewPooledTransactionHashes68.
  • NewPooledTransactionHashes is defined in network so we could not define TryFrom<NewPooledTransactionHashes> for NewPooledTransactionHashes68.
  • The only alternative idea I have is that pub fn new(...) -> Result<Self, Error>.

HDYT?

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 this in this PR?

I still don't understand why we would need this.

in any case, a TryFrom a tuple with 3 items is not great.

Comment on lines 74 to 76
if version >= EthVersion::Eth67 {
return Err(reth_rlp::DecodeError::Custom("invalid message id"))
}
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 since this is no longer rlp only we need to change the error type we return as well and add a dedicated error variant for when this happens.

Comment on lines 41 to 44
/// Internal form of a `PooledTransactions` message
/// Same as [`NewPooledTransactionHashes68`] but types and sizes are Option.
#[derive(Debug, Clone)]
pub struct NewPooledTransactionHashes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need this, why do we need optional fields here?

}
}

impl From<(Vec<TxHash>, Vec<TxType>, Vec<usize>)> for NewPooledTransactionHashes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

From impls for tuples are not great so would abandon them entirely.

but not clear why we need this type at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with acb0d81.

@jinsankim
Copy link
Contributor Author

@gakonst

worth configuring 2 clients with eth/68 and seeing that it works? Or one with 67 and one with 68?

I've tested and succeeded it with enabling only eth/68 and connecting to mainnet but I think it's conducted only early stage. Because the PR is too old during reviewing, I'll test it again.

@jinsankim
Copy link
Contributor Author

@mattsse

I have some questions regarding the network changes, mainly why we need this type with optional fields.

Thanks for your comment! To my understanding, maybe you asked why does NewPooledTransactionHashes have optional types and sizes, right? If I understand correctly, because it's used for PeerMessage::PooledTransactions.

/// All Bi-directional eth-message variants that can be sent to a session or received from a
/// session.
#[derive(Debug)]
pub enum PeerMessage {
/// Announce new block hashes
NewBlockHashes(NewBlockHashes),
/// Broadcast new block.
NewBlock(NewBlockMessage),
/// Received transactions _from_ the peer
ReceivedTransaction(Transactions),
/// Broadcast transactions _from_ local _to_ a peer.
SendTransactions(SharedTransactions),
/// Send new pooled transactions
PooledTransactions(NewPooledTransactionHashes),
/// All `eth` request variants.
EthRequest(PeerRequest),
/// Other than eth namespace message
#[allow(unused)]
Other(RawCapabilityMessage),
}

PeerMessage::PooledTransactions is bi-directional eth-message variants so it could represent NewPooledTransactionHashes66 and NewPooledTransactionHashes68. As you know, NewPooledTransactionHashes66 has only hashes but NewPooledTransactionHashes68 has all types, sizes, and hashes.

My first approach was decomposing PooledTransactions into each PooledTransactions66 and PooledTransactions68. But after some trial, I've realized that it's better to use optional field to simplify handlers.

I wonder whether it's acceptable or not. Always welcome for any advice.

@mattsse
Copy link
Collaborator

mattsse commented Feb 20, 2023

I see, I'd like to add another variant for 68 here as well, but let's do that separately, we can leave incoming 68 unhandled in this PR

@mattsse
Copy link
Collaborator

mattsse commented Feb 20, 2023

@jinsankim can we please do the bare minimum for eth68 here and do the new message type in a separate PR?

@jinsankim
Copy link
Contributor Author

jinsankim commented Feb 20, 2023

@mattsse

can we please do the bare minimum for eth68 here and do the new message type in a separate PR?

little bit confusing. I need more guide about what's the bare minimum for eth68. I agree whatever we want for this PR, close, modify, revert only some part or whatever. But I can't catch what should I do for bare minimum.

Comment on lines 41 to 44
/// Internal form of a `PooledTransactions` message
/// Same as [`NewPooledTransactionHashes68`] but types and sizes are Option.
#[derive(Debug, Clone)]
pub struct NewPooledTransactionHashes {
Copy link
Collaborator

Choose a reason for hiding this comment

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

please remove this type and instead move to separate PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with f53e2a7.

pub hashes: Vec<H256>,
}

impl TryFrom<(Option<Vec<u8>>, Option<Vec<usize>>, Vec<H256>)> for NewPooledTransactionHashes68 {
Copy link
Collaborator

Choose a reason for hiding this comment

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

please remove this trait impl entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

follow it up with f53e2a7.

@jinsankim
Copy link
Contributor Author

@mattsse

What I've caught your direction from 2 todos,

  • minimize changes
  • only focusing incoming (even though it's adhoc)
  • remove outgoing

Am I understanding correctly?

@mattsse
Copy link
Collaborator

mattsse commented Feb 20, 2023

yeh, I want to merge this with just 68 support on the eth wire level to unblock hive tests.
and followup with the actual 68 tx handling (in network impl) separately

@jinsankim
Copy link
Contributor Author

I have no knowledge for hive tests. But I've just concerned that if we send NewPooledTransactionHashes66 though eth/68, maybe tests will be failed.

I agree to merge PR step by step!

* minimize changes
* remove `NewPooledTransactionHashes`
@jinsankim
Copy link
Contributor Author

Now, in this PR, it could handle incoming eth/68 message but couldn't handle outgoing eth/68 message. I left a TODO comments for it.

Copy link
Member

@gakonst gakonst left a comment

Choose a reason for hiding this comment

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

@mattsse calling this good, the logic for parsing msgs seems fine.

main 2 things are:

  1. We do try_emit_broadcast w/o types/sizes to NewPooledTransactionHashes68 msgs, is this correct? Or will we get penalized?
  2. The TODO at active.rs#Implement new transaction propagation #254

@gakonst gakonst merged commit c907592 into paradigmxyz:main Feb 21, 2023
@jinsankim jinsankim deleted the feat-eth68 branch February 21, 2023 04:39
@jinsankim
Copy link
Contributor Author

@gakonst

We do try_emit_broadcast w/o types/sizes to NewPooledTransactionHashes68 msgs, is this correct? Or will we get penalized?

I think the incoming handling will be working well. geth also uses types/sizes only to check validation. Internally we need only hashes at least now.

The TODO at active.rs##254

outgoing is removed from this PR because of unacceptable message structure. I hope it'll be followed up with #254 and also improve architecture for multi protocol in the future.

@jinsankim
Copy link
Contributor Author

Coverage 76.19% 47.27% -28.93%

I think it's too weird. :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-devp2p Related to the Ethereum P2P protocol C-enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support eth/68
6 participants