Skip to content

Commit 0bc2292

Browse files
[ETCM-355] Send fork id to peers (#1030)
* [ETCM-355] Introduce ETH64 message format * [ETCM-355] Send fork id in the Status message * [ETCM-355] Remove capabilities from EtcHandshakerConfiguration * [ETCM-355] Rename EtcNodeStatus63ExchangeState -> EthNodeStatus63ExchangeState * [ETCM-355] Use eth/64 in the nomad test env * fixup! [ETCM-355] Send fork id in the Status message * [ETCM-355] scalafix * [ETCM-355] Log protocol name upon unknown message type * [ETCM-355] Log negotiated protocol version * [ETCM-355] Log received messages in PeerActor * [ETCM-355] Post merge fix * [ETCM-355] Expand MessageDecoderSpec with eth/64 tests * [ETCM-355] Introduce ProtocolFamily This also fixes a bug in NewBlock message creation, where only protocol version was taken into account
1 parent f97c6bf commit 0bc2292

32 files changed

+359
-40
lines changed

src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ import io.iohk.ethereum.network.discovery.PeerDiscoveryManager.DiscoveredNodesIn
6161
import io.iohk.ethereum.network.handshaker.EtcHandshaker
6262
import io.iohk.ethereum.network.handshaker.EtcHandshakerConfiguration
6363
import io.iohk.ethereum.network.handshaker.Handshaker
64-
import io.iohk.ethereum.network.p2p.messages.Capability
6564
import io.iohk.ethereum.network.rlpx.AuthHandshaker
6665
import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
6766
import io.iohk.ethereum.nodebuilder.PruningConfigBuilder
@@ -199,7 +198,7 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
199198
override val blockchain: Blockchain = CommonFakePeer.this.bl
200199
override val blockchainReader: BlockchainReader = CommonFakePeer.this.blockchainReader
201200
override val appStateStorage: AppStateStorage = storagesInstance.storages.appStateStorage
202-
override val capabilities: List[Capability] = blockchainConfig.capabilities
201+
override val blockchainConfig: BlockchainConfig = Config.blockchains.blockchainConfig
203202
}
204203

205204
lazy val handshaker: Handshaker[PeerInfo] = EtcHandshaker(handshakerConfiguration)

src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ import io.iohk.ethereum.network.discovery.DiscoveryConfig
4242
import io.iohk.ethereum.network.handshaker.EtcHandshaker
4343
import io.iohk.ethereum.network.handshaker.EtcHandshakerConfiguration
4444
import io.iohk.ethereum.network.handshaker.Handshaker
45-
import io.iohk.ethereum.network.p2p.messages.Capability
4645
import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
4746
import io.iohk.ethereum.nodebuilder.AuthHandshakerBuilder
4847
import io.iohk.ethereum.nodebuilder.NodeKeyBuilder
4948
import io.iohk.ethereum.security.SecureRandomBuilder
49+
import io.iohk.ethereum.utils.BlockchainConfig
5050
import io.iohk.ethereum.utils.Config
5151
import io.iohk.ethereum.utils.NodeStatus
5252
import io.iohk.ethereum.utils.ServerStatus
@@ -119,7 +119,7 @@ object DumpChainApp
119119
override val blockchain: Blockchain = DumpChainApp.blockchain
120120
override val blockchainReader: BlockchainReader = DumpChainApp.blockchainReader
121121
override val appStateStorage: AppStateStorage = storagesInstance.storages.appStateStorage
122-
override val capabilities: List[Capability] = blockchainConfig.capabilities
122+
override val blockchainConfig: BlockchainConfig = Config.blockchains.blockchainConfig
123123
}
124124

125125
lazy val handshaker: Handshaker[PeerInfo] = EtcHandshaker(handshakerConfiguration)

src/main/resources/conf/chains/testnet-internal-nomad-chain.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# 1 - mainnet, 3 - ropsten, 7 - mordor
44
network-id = 42
55

6-
capabilities = ["etc/64"]
6+
capabilities = ["eth/64"]
77

88
# Possibility to set Proof of Work target time for testing purposes.
99
# null means that the standard difficulty calculation rules are used

src/main/scala/io/iohk/ethereum/blockchain/sync/regular/BlockBroadcast.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import io.iohk.ethereum.network.p2p.messages.BaseETH6XMessages
1717
import io.iohk.ethereum.network.p2p.messages.ETC64
1818
import io.iohk.ethereum.network.p2p.messages.ETH62
1919
import io.iohk.ethereum.network.p2p.messages.ETH62.BlockHash
20-
import io.iohk.ethereum.network.p2p.messages.ProtocolVersions
20+
import io.iohk.ethereum.network.p2p.messages.ProtocolFamily._
2121

2222
class BlockBroadcast(val etcPeerManager: ActorRef) {
2323

@@ -45,10 +45,12 @@ class BlockBroadcast(val etcPeerManager: ActorRef) {
4545

4646
private def broadcastNewBlock(blockToBroadcast: BlockToBroadcast, peers: Map[PeerId, PeerWithInfo]): Unit =
4747
obtainRandomPeerSubset(peers.values.map(_.peer).toSet).foreach { peer =>
48-
val message: MessageSerializable =
49-
if (peers(peer.id).peerInfo.remoteStatus.protocolVersion.toByte == ProtocolVersions.ETC64.version)
50-
blockToBroadcast.as64
51-
else blockToBroadcast.as63
48+
val remoteStatus = peers(peer.id).peerInfo.remoteStatus
49+
50+
val message: MessageSerializable = remoteStatus.protocolFamily match {
51+
case ETH => blockToBroadcast.as63
52+
case ETC => blockToBroadcast.as64
53+
}
5254
etcPeerManager ! EtcPeerManagerActor.SendMessage(message, peer.id)
5355
}
5456

src/main/scala/io/iohk/ethereum/network/EtcPeerManagerActor.scala

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ import io.iohk.ethereum.network.p2p.messages.ETC64.NewBlock
2626
import io.iohk.ethereum.network.p2p.messages.ETH62.BlockHeaders
2727
import io.iohk.ethereum.network.p2p.messages.ETH62.GetBlockHeaders
2828
import io.iohk.ethereum.network.p2p.messages.ETH62.NewBlockHashes
29+
import io.iohk.ethereum.network.p2p.messages.ETH64
30+
import io.iohk.ethereum.network.p2p.messages.ProtocolFamily
31+
import io.iohk.ethereum.network.p2p.messages.ProtocolFamily.ETC
32+
import io.iohk.ethereum.network.p2p.messages.ProtocolFamily.ETH
2933
import io.iohk.ethereum.network.p2p.messages.WireProtocol.Disconnect
3034
import io.iohk.ethereum.utils.ByteStringUtils
3135

@@ -239,6 +243,7 @@ object EtcPeerManagerActor {
239243
* (they are different versions of Status msg)
240244
*/
241245
case class RemoteStatus(
246+
protocolFamily: ProtocolFamily,
242247
protocolVersion: Int,
243248
networkId: Int,
244249
chainWeight: ChainWeight,
@@ -247,6 +252,7 @@ object EtcPeerManagerActor {
247252
) {
248253
override def toString: String =
249254
s"RemoteStatus { " +
255+
s"protocolFamily: $protocolFamily, " +
250256
s"protocolVersion: $protocolVersion, " +
251257
s"networkId: $networkId, " +
252258
s"chainWeight: $chainWeight, " +
@@ -256,11 +262,29 @@ object EtcPeerManagerActor {
256262
}
257263

258264
object RemoteStatus {
265+
def apply(status: ETH64.Status): RemoteStatus =
266+
RemoteStatus(
267+
ETH,
268+
status.protocolVersion,
269+
status.networkId,
270+
ChainWeight.totalDifficultyOnly(status.totalDifficulty),
271+
status.bestHash,
272+
status.genesisHash
273+
)
274+
259275
def apply(status: ETC64.Status): RemoteStatus =
260-
RemoteStatus(status.protocolVersion, status.networkId, status.chainWeight, status.bestHash, status.genesisHash)
276+
RemoteStatus(
277+
ETC,
278+
status.protocolVersion,
279+
status.networkId,
280+
status.chainWeight,
281+
status.bestHash,
282+
status.genesisHash
283+
)
261284

262285
def apply(status: BaseETH6XMessages.Status): RemoteStatus =
263286
RemoteStatus(
287+
ETH,
264288
status.protocolVersion,
265289
status.networkId,
266290
ChainWeight.totalDifficultyOnly(status.totalDifficulty),

src/main/scala/io/iohk/ethereum/network/PeerActor.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ class PeerActor[R <: HandshakeResult](
144144
case RLPxConnectionHandler.MessageReceived(msg) =>
145145
// Processes the received message, cancels the timeout and processes a new message but only if the handshaker
146146
// handles the received message
147+
log.debug("Message received: {} from peer {}", msg, peerAddress)
147148
handshaker.applyMessage(msg).foreach { newHandshaker =>
148149
timeout.cancel()
149150
processHandshakerNextMessage(newHandshaker, remoteNodeId, rlpxConnection, numRetries)

src/main/scala/io/iohk/ethereum/network/handshaker/EtcHandshaker.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import io.iohk.ethereum.domain.BlockchainReader
88
import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo
99
import io.iohk.ethereum.network.ForkResolver
1010
import io.iohk.ethereum.network.PeerManagerActor.PeerConfiguration
11-
import io.iohk.ethereum.network.p2p.messages.Capability
11+
import io.iohk.ethereum.utils.BlockchainConfig
1212
import io.iohk.ethereum.utils.NodeStatus
1313

1414
case class EtcHandshaker private (
@@ -37,5 +37,5 @@ trait EtcHandshakerConfiguration {
3737
val appStateStorage: AppStateStorage
3838
val peerConfiguration: PeerConfiguration
3939
val forkResolverOpt: Option[ForkResolver]
40-
val capabilities: List[Capability]
40+
val blockchainConfig: BlockchainConfig
4141
}

src/main/scala/io/iohk/ethereum/network/handshaker/EtcHelloExchangeState.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,21 @@ case class EtcHelloExchangeState(handshakerConfiguration: EtcHandshakerConfigura
3030
override def applyResponseMessage: PartialFunction[Message, HandshakerState[PeerInfo]] = { case hello: Hello =>
3131
log.debug("Protocol handshake finished with peer ({})", hello)
3232
// FIXME in principle this should be already negotiated
33-
Capability.negotiate(hello.capabilities.toList, handshakerConfiguration.capabilities) match {
34-
case Some(ProtocolVersions.ETC64) => EtcNodeStatus64ExchangeState(handshakerConfiguration)
35-
case Some(ProtocolVersions.ETH63) => EtcNodeStatus63ExchangeState(handshakerConfiguration)
33+
Capability.negotiate(hello.capabilities.toList, handshakerConfiguration.blockchainConfig.capabilities) match {
34+
case Some(ProtocolVersions.ETC64) =>
35+
log.debug("Negotiated protocol version with client {} is etc/64", hello.clientId)
36+
EtcNodeStatus64ExchangeState(handshakerConfiguration)
37+
case Some(ProtocolVersions.ETH63) =>
38+
log.debug("Negotiated protocol version with client {} is eth/63", hello.clientId)
39+
EthNodeStatus63ExchangeState(handshakerConfiguration)
40+
case Some(ProtocolVersions.ETH64) =>
41+
log.debug("Negotiated protocol version with client {} is eth/64", hello.clientId)
42+
EthNodeStatus64ExchangeState(handshakerConfiguration)
3643
case _ =>
3744
log.debug(
38-
s"Connected peer does not support {} / {} protocol. Disconnecting.",
45+
s"Connected peer does not support {} / {} / {} protocol. Disconnecting.",
3946
ProtocolVersions.ETH63,
47+
ProtocolVersions.ETH64,
4048
ProtocolVersions.ETC64
4149
)
4250
DisconnectedState(Disconnect.Reasons.IncompatibleP2pProtocolVersion)
@@ -57,7 +65,7 @@ case class EtcHelloExchangeState(handshakerConfiguration: EtcHandshakerConfigura
5765
Hello(
5866
p2pVersion = EtcHelloExchangeState.P2pVersion,
5967
clientId = Config.clientId,
60-
capabilities = handshakerConfiguration.capabilities,
68+
capabilities = handshakerConfiguration.blockchainConfig.capabilities,
6169
listenPort = listenPort,
6270
nodeId = ByteString(nodeStatus.nodeId)
6371
)

src/main/scala/io/iohk/ethereum/network/handshaker/EtcNodeStatus63ExchangeState.scala renamed to src/main/scala/io/iohk/ethereum/network/handshaker/EthNodeStatus63ExchangeState.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import io.iohk.ethereum.network.p2p.MessageSerializable
77
import io.iohk.ethereum.network.p2p.messages.BaseETH6XMessages
88
import io.iohk.ethereum.network.p2p.messages.ProtocolVersions
99

10-
case class EtcNodeStatus63ExchangeState(
10+
case class EthNodeStatus63ExchangeState(
1111
handshakerConfiguration: EtcHandshakerConfiguration
1212
) extends EtcNodeStatusExchangeState[BaseETH6XMessages.Status] {
1313

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.iohk.ethereum.network.handshaker
2+
3+
import io.iohk.ethereum.forkid.ForkId
4+
import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo
5+
import io.iohk.ethereum.network.EtcPeerManagerActor.RemoteStatus
6+
import io.iohk.ethereum.network.p2p.Message
7+
import io.iohk.ethereum.network.p2p.MessageSerializable
8+
import io.iohk.ethereum.network.p2p.messages.ETH64
9+
import io.iohk.ethereum.network.p2p.messages.ProtocolVersions
10+
11+
case class EthNodeStatus64ExchangeState(
12+
handshakerConfiguration: EtcHandshakerConfiguration
13+
) extends EtcNodeStatusExchangeState[ETH64.Status] {
14+
15+
import handshakerConfiguration._
16+
17+
def applyResponseMessage: PartialFunction[Message, HandshakerState[PeerInfo]] = { case status: ETH64.Status =>
18+
// TODO: validate fork id of the remote peer
19+
applyRemoteStatusMessage(RemoteStatus(status))
20+
}
21+
22+
override protected def createStatusMsg(): MessageSerializable = {
23+
val bestBlockHeader = getBestBlockHeader()
24+
val chainWeight = blockchain.getChainWeightByHash(bestBlockHeader.hash).get
25+
val genesisHash = blockchainReader.genesisHeader.hash
26+
27+
val status = ETH64.Status(
28+
protocolVersion = ProtocolVersions.ETH64.version,
29+
networkId = peerConfiguration.networkId,
30+
totalDifficulty = chainWeight.totalDifficulty,
31+
bestHash = bestBlockHeader.hash,
32+
genesisHash = genesisHash,
33+
forkId = ForkId.create(genesisHash, blockchainConfig)(blockchainReader.getBestBlockNumber())
34+
)
35+
36+
log.debug(s"Sending status $status")
37+
status
38+
}
39+
40+
}

src/main/scala/io/iohk/ethereum/network/p2p/MessageDecoders.scala

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object NetworkMessageDecoder extends MessageDecoder {
2727
case Ping.code => payload.toPing
2828
case Pong.code => payload.toPong
2929
case Hello.code => payload.toHello
30-
case _ => throw new RuntimeException(s"Unknown message type: $msgCode")
30+
case _ => throw new RuntimeException(s"Unknown network message type: $msgCode")
3131
}
3232

3333
}
@@ -51,7 +51,30 @@ object ETC64MessageDecoder extends MessageDecoder {
5151
case Codes.BlockBodiesCode => payload.toBlockBodies
5252
case Codes.BlockHashesFromNumberCode => payload.toBlockHashesFromNumber
5353
case Codes.SignedTransactionsCode => payload.toSignedTransactions
54-
case _ => throw new RuntimeException(s"Unknown message type: $msgCode")
54+
case _ => throw new RuntimeException(s"Unknown etc/64 message type: $msgCode")
55+
}
56+
}
57+
58+
object ETH64MessageDecoder extends MessageDecoder {
59+
import io.iohk.ethereum.network.p2p.messages.ETH64.Status._
60+
import io.iohk.ethereum.network.p2p.messages.BaseETH6XMessages.NewBlock._
61+
62+
def fromBytes(msgCode: Int, payload: Array[Byte]): Message =
63+
msgCode match {
64+
case Codes.GetNodeDataCode => payload.toGetNodeData
65+
case Codes.NodeDataCode => payload.toNodeData
66+
case Codes.GetReceiptsCode => payload.toGetReceipts
67+
case Codes.ReceiptsCode => payload.toReceipts
68+
case Codes.NewBlockHashesCode => payload.toNewBlockHashes
69+
case Codes.GetBlockHeadersCode => payload.toGetBlockHeaders
70+
case Codes.BlockHeadersCode => payload.toBlockHeaders
71+
case Codes.GetBlockBodiesCode => payload.toGetBlockBodies
72+
case Codes.BlockBodiesCode => payload.toBlockBodies
73+
case Codes.BlockHashesFromNumberCode => payload.toBlockHashesFromNumber
74+
case Codes.StatusCode => payload.toStatus
75+
case Codes.NewBlockCode => payload.toNewBlock
76+
case Codes.SignedTransactionsCode => payload.toSignedTransactions
77+
case _ => throw new RuntimeException(s"Unknown eth/64 message type: $msgCode")
5578
}
5679
}
5780

@@ -74,7 +97,7 @@ object ETH63MessageDecoder extends MessageDecoder {
7497
case Codes.StatusCode => payload.toStatus
7598
case Codes.NewBlockCode => payload.toNewBlock
7699
case Codes.SignedTransactionsCode => payload.toSignedTransactions
77-
case _ => throw new RuntimeException(s"Unknown message type: $msgCode")
100+
case _ => throw new RuntimeException(s"Unknown eth/63 message type: $msgCode")
78101
}
79102
}
80103

@@ -85,6 +108,7 @@ object EthereumMessageDecoder {
85108
protocolVersion match {
86109
case Capability.Capabilities.Etc64Capability => ETC64MessageDecoder.fromBytes
87110
case Capability.Capabilities.Eth63Capability => ETH63MessageDecoder.fromBytes
111+
case Capability.Capabilities.Eth64Capability => ETH64MessageDecoder.fromBytes
88112
case _ => throw new RuntimeException(s"Unsupported Protocol Version $protocolVersion")
89113
}
90114
}

src/main/scala/io/iohk/ethereum/network/p2p/messages/Capability.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ object Capability {
5151

5252
object Capabilities {
5353
val Eth63Capability: Capability = ProtocolVersions.ETH63
54+
val Eth64Capability: Capability = ProtocolVersions.ETH64
5455
val Etc64Capability: Capability = ProtocolVersions.ETC64
5556

56-
val All: Seq[Capability] = Seq(ProtocolVersions.ETC64, ProtocolVersions.ETH63)
57+
val All: Seq[Capability] = Seq(ProtocolVersions.ETC64, ProtocolVersions.ETH63, ProtocolVersions.ETH64)
5758
}
5859
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package io.iohk.ethereum.network.p2p.messages
2+
3+
import akka.util.ByteString
4+
5+
import org.bouncycastle.util.encoders.Hex
6+
7+
import io.iohk.ethereum.forkid.ForkId
8+
import io.iohk.ethereum.forkid.ForkId._
9+
import io.iohk.ethereum.network.p2p.Message
10+
import io.iohk.ethereum.network.p2p.MessageSerializableImplicit
11+
import io.iohk.ethereum.rlp.RLPImplicitConversions._
12+
import io.iohk.ethereum.rlp.RLPImplicits._
13+
import io.iohk.ethereum.rlp._
14+
15+
object ETH64 {
16+
17+
case class Status(
18+
protocolVersion: Int,
19+
networkId: Int,
20+
totalDifficulty: BigInt,
21+
bestHash: ByteString,
22+
genesisHash: ByteString,
23+
forkId: ForkId
24+
) extends Message {
25+
26+
override def toString: String =
27+
s"Status { " +
28+
s"code: $code, " +
29+
s"protocolVersion: $protocolVersion, " +
30+
s"networkId: $networkId, " +
31+
s"totalDifficulty: $totalDifficulty, " +
32+
s"bestHash: ${Hex.toHexString(bestHash.toArray[Byte])}, " +
33+
s"genesisHash: ${Hex.toHexString(genesisHash.toArray[Byte])}," +
34+
s"forkId: $forkId," +
35+
s"}"
36+
37+
override def toShortString: String = toString
38+
override def code: Int = Codes.StatusCode
39+
}
40+
41+
object Status {
42+
implicit class StatusEnc(val underlyingMsg: Status)
43+
extends MessageSerializableImplicit[Status](underlyingMsg)
44+
with RLPSerializable {
45+
override def code: Int = Codes.StatusCode
46+
47+
override def toRLPEncodable: RLPEncodeable = {
48+
import msg._
49+
RLPList(protocolVersion, networkId, totalDifficulty, bestHash, genesisHash, forkId.toRLPEncodable)
50+
}
51+
}
52+
53+
implicit class StatusDec(val bytes: Array[Byte]) extends AnyVal {
54+
def toStatus: Status = rawDecode(bytes) match {
55+
case RLPList(
56+
protocolVersion,
57+
networkId,
58+
totalDifficulty,
59+
bestHash,
60+
genesisHash,
61+
forkId
62+
) =>
63+
Status(
64+
protocolVersion,
65+
networkId,
66+
totalDifficulty,
67+
bestHash,
68+
genesisHash,
69+
decode[ForkId](forkId)
70+
)
71+
72+
case _ => throw new RuntimeException("Cannot decode Status")
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)