-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Support eth and consensus subprotocols #1129
Support eth and consensus subprotocols #1129
Conversation
Enable the eth service to support multiple subprotocols, e.g. "eth" AND "istanbul/100", instead of a single protocol, e.g. "eth" OR "istanbul/99". When istanbul consensus was originally added, the eth service was modified to override the eth subprotocol maintained by the eth service to a quorum specific consensus protocol, e.g. "istanbul/99" meaning all eth p2p messages would be sent over a consensus specific subprotocol in addition to any consensus protocol message. In the case of istanbul, the "eth" subprotocol would no long be included as a devp2p capability/ subprotocol for the node. This commit changes that, and enables the eth service to return multiple subprotocols, e.g. "eth" and "istanbul/100". "istanbul/100" has been added as a new istanbul subprotocol version, which supports the multple subprotocol design. peers running older versions of istanbul will continue to override the "eth" service and will communicate over a single subprotocol: old node: istanbul/99 <-> istanbul/99 new node: istanbul/100, eth <-> istanbul/100, eth To maintain backwards compatibility, when establishing a connection with a new peer that does not support mutliple protocols, but rather a "legacy" protocol, e.g. "istanbul/64" "istanbul/99" "clique" (v2.6), the nodes will communicate over the single "legacy" protcol and not "eth" + consensus subprotocol. When running two subprotcols, "eth" + "istanbul" (consensus), the "eth" subprotocol manages the lifecycle of the peer for the eth service, and the consensus subprotocol uses this peer for its communication, registering a separate protocol read writer on it. To support this, a channel is added to a p2p peer (EthPeerRegistered), which is used to communicate to the "consensus" / quorum subprotocols that the eth peer is ready and can be retrieved. When running a quorum specific subprotocol on the eth service, the eth peer is started and managed by the "eth" subprotocol. The quorum specific subprotocol uses this peer to send messages. This channel is necessary, as every protocol registerd in a p2p peer running map, the Run method is called inside a goroutine. When iterating through the running map, the order is not guaranteed and inside the goroutines we cannot count on execution time of the methods. Thus, we use the EthPeerRegistered channel to ensure that the eth protocol has register the peer before attempting to look it up. maintains backwards compatiblity with: istanbul, raft, clique.
When registering a peer check that the protocol is "eth" and version >= eth65 and not a legacy quorum protocol to determine if p.announceTransactions() can be called.
Following scenarios scenarios should be tested for this PR:
This can be done using
start minikube
copy the file in
check that the image was built in the minikube docker env
At this point there will be a local image in the minikube docker context that we can access when creating our local quorum networks. Create a Quorum network of new nodes
Create a Quorum network of new nodes and latest stable release
Add 1 or more stable release nodes to the minimal config
create and generate the network
once the network is deployedconnect to a node
open the logs in vi
search for 'Starting Protocol' to see the protocols that each nodes uses to connect. |
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.
is there a plan to add Acceptance Tests for this case?
// The Run method starts the protocol and is called by the p2p server. The quorum consensus subprotocol, | ||
// leverages the peer created and managed by the "eth" subprotocol. | ||
// The quorum consensus protocol requires that the "eth" protocol is running as well. | ||
func (pm *ProtocolManager) makeQuorumConsensusProtocol(ProtoName string, version uint, length uint64) p2p.Protocol { |
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 have unit tests for this new code or other tests that are covering this?
eth/handler.go
Outdated
|
||
// Quorum notify other subprotocols that the eth peer is ready, and has been added to the peerset. | ||
select { | ||
case p.EthPeerRegistered <- true: |
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 am not fan of notifying through a chan bool
A more standard option is to use a chan struct{}
and close it to broadcast notification.
In this case, since it is required to notify true
or false
I would instead use 2 channels
- EthPeerReady
- EthPeerDisconnected
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.
Changed EthPeerRegistered
to buffered chan struct{}{}
and added EthPeerDisconnected
instead of using chan bool and sending true
false
.
eth/quorum_protocol.go
Outdated
|
||
// istanbul/64, istanbul/99, clique/63, clique/64 all override the "eth" subprotocol. | ||
func isLegacyProtocol(name string, version uint) bool { | ||
legacyProtocols := map[string][]uint{"istanbul": {64, 99}, "clique": {63, 64}} |
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 we declare legacyProtocols
as a global variable?
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 can, but it is only used in the isLegacyProtocol
method atm.
protocolName = quorumProtocol.Name | ||
ProtocolVersions = quorumProtocol.Versions | ||
// set the quorum specific consensus devp2p subprotocol, eth subprotocol remains set to protocolName as in upstream geth. | ||
quorumConsensusProtocolName = quorumProtocol.Name |
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.
Is it necessary to use quorumConsensusProtocolName
, quorumConsensusProtocolVersions
, etc.
global variable?
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 are mirroring the way the eth protocolName
ProtocolVersions
is set for consistency:
eth/backend.go
func (s *Ethereum) Protocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(ProtocolVersions))
for i, vsn := range ProtocolVersions {
func (s *Ethereum) quorumConsensusProtocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(quorumConsensusProtocolVersions))
for i, vsn := range quorumConsensusProtocolVersions {
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.
Ok I get the point.
Still I see a difference
func (s *Ethereum) Protocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(ProtocolVersions))
for i, vsn := range ProtocolVersions {
uses ProtocolVersions
global variable which is constant while
func (s *Ethereum) quorumConsensusProtocols() []p2p.Protocol {
protos := make([]p2p.Protocol, len(quorumConsensusProtocolVersions))
for i, vsn := range quorumConsensusProtocolVersions {
uses quorumConsensusProtocolVersions
global variable which is not constant.
quorumConsensusProtocolVersions
is indirectly set by New(...) (*Ethereum, error)
which I think is not a good practice and maybe error-prone (e.g it creates coupling between Ethereum
structs through a global variable which is not constant).
I think it would be better to have a quorumConsensusProtocolVersions
attribute on the Ethereum
struct that is set on New(...)
then quorumConsensusProtocols()
could re-use the attribute or something like this.
What do you think?
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.
Before this change, the ProtocolVersions
and ProtocolName
vars would be overridden here to set them to the quorum specific subprototol obtained from the consensus engine. In upstream (same geth version), they would not be overridden. This change leaves ProtocolName
ProtocolVersions
always set to the eth
subprotocol.
With this initial dual subprotocol approach, keeping the changes as minimally invasive to the existing code as possible was the approach. In the (near) future, this will be refactored more (consensus may be a service with its own protocol, when we get up to date with the current geth, eth service will be refactored to support more than one subprotocol, e.g. snap
, eth
, quorumconsensus
. To the client, they won't see a difference once they are running the dual subprotocols, but the Quorum code can refactored to support a different subprotocol design.
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.
Ok
8563d93
to
5f6e74a
Compare
make EthPeerRegistered a buffered chan struct{} instead of a chan bool, add EthPeerDisconnected chan to listen for peer disconnect.
5f6e74a
to
49fedfc
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.
LGTM
Enable the eth service to support multiple subprtocols, e.g.
"eth" AND "istanbul/100", instead of a single protocol, e.g. "eth" OR "istanbul/99".
When istanbul consensus was originally added, the eth service was
modified to override the eth subprotocol maintained by the eth service
to an quorum specific consensus protocol, e.g. "istanbul/99" meaning all
eth p2p messages would be sent over a consensus specific protocol in
addition to any consensus protocol message. In the case of istanbul,
the "eth" subprotocol would no long be included as a devp2p capability/
subprotocol for the node.
This commit changes that, and enables the eth service to return multiple
subprotocols, e.g. "eth", "istanbul/100".
"istanbul/100" has been added as a new istanbul subprotocol version, which supports
the multple subprotocols design. peers running older versions of istanbul will
continue to override the "eth" service and will communicate over a single subprotocol:
istanbul/99 <-> istanbul/99
istanbul/100, eth <-> istanbul/100, eth
To maintain backwards compatibility, when establishing a connection with a
new peer that does not support multiple protocols, but rather a "legacy"
protocol, e.g. "istanbul/64" "istanbul/99" "clique" (v2.6), the nodes will communicate
over the single "legacy" protocol and not "eth" + consensus subprotocol.
When running two subprotocols, "eth" + "consensus", the eth subprotocol
manages the lifecycle of the peer for the eth service, and the consensus
subprotocol uses this peer for its communication, registering a separate
protocol read writer on it.
maintains backwards compatibility with: istanbul, raft, clique.