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

[ETCM-280] Use capabilities when sending checkpoint related network messages #793

Merged
merged 1 commit into from
Nov 26, 2020

Conversation

pslaski
Copy link
Contributor

@pslaski pslaski commented Nov 13, 2020

Description

The version of the message to send should be determined by peer capabilities

Proposed Solution

During the handshake, the peer is introducing its capabilities to supported protocols. Base on that mantis is deciding which version of message use.

Important Changes Introduced

  • new protocol version 64 with new NewBlock and Status messages (can be configured by network.protocol-version)
  • decouple NewBlock and Status messages from our services

Testing

Enable PV64 and try to sync with PV63 client and PV64 client

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch from 3135e5a to 928d2b3 Compare November 13, 2020 15:15
Copy link

@ntallar ntallar left a comment

Choose a reason for hiding this comment

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

I like how this is all looking! I still have pending taking a look at the test changes, some comments before that:

@@ -24,10 +26,14 @@ case class EtcHelloExchangeState(handshakerConfiguration: EtcHandshakerConfigura

override def applyResponseMessage: PartialFunction[Message, HandshakerState[PeerInfo]] = { case hello: Hello =>
log.debug("Protocol handshake finished with peer ({})", hello)
if (hello.capabilities.contains(Capability("eth", Versions.PV63.toByte)))
EtcNodeStatusExchangeState(handshakerConfiguration)
if (handshakerConfiguration.protocolVersion == Versions.PV64 && hello.capabilities.contains(Eth64Capability))
Copy link

Choose a reason for hiding this comment

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

Something that I'm thinking about this now... this will be all fine and well for a testnet with Mantis only nodes, but when we open it up for any other client to implement checkpointing/treasury and connect to it, this will sound like a hack very difficult for them to do so.

And I don't see any way of working our way around it... if you agree with what I'm saying I'll share this with Dragan and Juli so that they know about it and maybe give higher priority to implementing eth64 and eth65

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 - current PV64 version should be temporary, only for our testnet usage and ultimately it should be PV65/PV66 (I'm not sure what is last ETC PV)

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch 3 times, most recently from 0c09056 to 5a36702 Compare November 17, 2020 10:26
chainWeight.totalDifficulty,
bestHash,
genesisHash,
chainWeight.lastCheckpointNumber
Copy link
Contributor

Choose a reason for hiding this comment

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

This is really minor, but I see you keep insisting on having this as last field in the RLP. I really don't see the point in this. This is not an extension, all the fields could be totally different. It would make much more sense to me to keep relevant fields close together.

requestSender.expectMsg(PeerInfoResponse(Some(peer1Info.withChainWeight(ChainWeight.totalDifficultyOnly(newBlock.totalDifficulty)))))
}

it should "update the peer total difficulty when receiving a PV64.NewBlock" in new TestSetup {
Copy link
Contributor

Choose a reason for hiding this comment

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

total diff -> chain weight ?

case (PV62 | PV63, pv62.BlockHeaders.code) => payload.toBlockHeaders
case (PV62 | PV63, pv62.GetBlockBodies.code) => payload.toGetBlockBodies
case (PV62 | PV63, pv62.BlockBodies.code) => payload.toBlockBodies
case (PV62 | PV63, Codes.NewBlockHashesCode) => payload.toNewBlockHashes
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't these messages be handled for PV64 as well? This code is getting a bit out of control so I would suggest to refactor as follows:

def fromBytes(protocolVersion, msgCode, payload) = {
  protocolVersion match {
     case PV64 => handlePV64(msgCode, payload)
     case PV63 => handlePV63(msgCode, payload)
     ...
     case PV60 => handlePV60(msgCode, payload)
     case _ => fail()
  }
}

private def handlePV64(msgCode, payload) = msgCode match {
  case pv64msg1 => decode(payload)
  case pv64msg2 => decode(payload)
  case msgObsoletedByPV64 => fail() // not sure if we need this case for any PV**
  ...
  case _ => handlePV63(msg, payload)
}

private def handlePV63(msgCode, payload) = msgCode match {
  case pv63msg1 => decode(payload)
  case pv63msg2 => decode(payload)
  ...
  case _ => handlePV62(msg, payload)
}

...

private def handlePV60(msgCode, payload) = msgCode match {
  case pv60msg1 => decode(payload)
  case pv60msg2 => decode(payload)
  ...
  case _ => fail()
}

This introduces PV60 as the version for common messages. Perhaps we should even rename CommonMessages to PV60 as this would give us more consistent naming?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In my initial version, Status and NewBlock were part of PV63, but I decided later to move them to CommonMessages again. The true is PV61 Status is different than other ones, but we have not supported it and just use that common Status everywhere besides new PV. I don't know if we want to clean it up or not? @ntallar ? If yes, we can do this in the different PR, as this one is already big one.

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch 3 times, most recently from c33def8 to d88ac8f Compare November 18, 2020 19:12
@@ -38,45 +35,87 @@ object NetworkMessageDecoder extends MessageDecoder {
// scalastyle:off
object EthereumMessageDecoder extends MessageDecoder {

override def fromBytes(msgCode: Int, payload: Array[Byte], protocolVersion: Version): Message =
private type MessageHandler = PartialFunction[(Int, Array[Byte]), Message]
Copy link
Contributor

Choose a reason for hiding this comment

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

How is this approach better/cleaner than what I proposed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is far more composable - with your approach I can't just do case _ => handlePV63(msg, payload) because it isn't true, we are handling NewBlock/Status differently than PV63, so it ends up with copying all those PV63 and PV62 messages or doing similar partition as I did and instead of orElse use case _ => nextHandler(msg, payload) where nextHandler will have another call with wildcard etc. For me it's just the perfect fit for PartialFunction

Copy link
Contributor

Choose a reason for hiding this comment

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

I can't just do case _ => handlePV63(msg, payload) because it isn't true

What isn't true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok you right, I can do this with simple matching, I forgot that those new messages would be matched firstly and the bad path is not reachable

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch 3 times, most recently from 1591596 to ec3b952 Compare November 20, 2020 15:18
Copy link

@ntallar ntallar left a comment

Choose a reason for hiding this comment

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

Only major comment regarding the block broadcasting compatibility issue

Apart from this comments, LGTM!

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch 2 times, most recently from eb04693 to 80d17db Compare November 24, 2020 14:09
Copy link

@ntallar ntallar left a comment

Choose a reason for hiding this comment

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

Apart from the two comments above, LGTM!

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch 2 times, most recently from b1307b7 to fa3d0d2 Compare November 25, 2020 09:35
Copy link

@ntallar ntallar left a comment

Choose a reason for hiding this comment

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

LGTM!

 .\\            //.
. \ \          / /.
.\  ,\     /` /,.-
 -.   \  /'/ /  .
 ` -   `-'  \  -
   '.       /.\`
      -    .-
      :`//.'
      .`.'
      .' BP 

@pslaski pslaski force-pushed the etcm-280-peer-checkpoint-capability branch from fa3d0d2 to 298934b Compare November 26, 2020 10:23
@ntallar ntallar requested a review from mirkoAlic November 26, 2020 13:46
@mirkoAlic
Copy link
Contributor

mirkoAlic commented Nov 26, 2020

I reviewed this in the following one(#810), where i wrote some comments...But in general, the code LGTM!

case (PV62 | PV63, pv62.BlockHeaders.code) => payload.toBlockHeaders
case (PV62 | PV63, pv62.GetBlockBodies.code) => payload.toGetBlockBodies
case (PV62 | PV63, pv62.BlockBodies.code) => payload.toBlockBodies
private def handleCommonMessages(msgCode: Int, payload: Array[Byte]): Message = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we not want to call them PV60?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think CommonMessages may be misleading. Since PV64 overrides Status and NewBlock, they are not common anymore

Copy link
Contributor Author

Choose a reason for hiding this comment

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

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 can refactor it in ETCM-402

case PV61 => handlePV61(msgCode, payload)
case PV62 => handlePV62(msgCode, payload)
case PV63 => handlePV63(msgCode, payload)
case PV64 => handlePV64(msgCode, payload)
Copy link
Contributor

@rtkaczyk rtkaczyk Nov 26, 2020

Choose a reason for hiding this comment

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

Very minor optimization: higher PVs will be matched more often, so they could be matched first

case (_, NewBlock.code63 | NewBlock.code64) => payload.toNewBlock(msgCode)

case (PV61, t) => handlePV61(t, payload)
override def fromBytes(msgCode: Int, payload: Array[Byte], protocolVersion: Version): Message = {
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks very tidy now <3

case Disconnect.code => payload.toDisconnect
case Ping.code => payload.toPing
case Pong.code => payload.toPong
case Hello.code => payload.toHello
Copy link
Contributor

Choose a reason for hiding this comment

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

<3

@ntallar ntallar merged commit 302e2dc into develop Nov 26, 2020
@ntallar ntallar deleted the etcm-280-peer-checkpoint-capability branch November 26, 2020 19:16
@ntallar ntallar added the BREAKS API Affects services that interacts with APIs label Nov 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BREAKS API Affects services that interacts with APIs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants