Skip to content

Commit

Permalink
[ETCM-355] Send fork id in the Status message
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasz-golebiewski committed Jun 29, 2021
1 parent e72902b commit 8c9fbe9
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
override val blockchain: Blockchain = CommonFakePeer.this.bl
override val blockchainReader: BlockchainReader = CommonFakePeer.this.blockchainReader
override val appStateStorage: AppStateStorage = storagesInstance.storages.appStateStorage
override val blockchainConfig: BlockchainConfig = Config.blockchains.blockchainConfig
override val capabilities: List[Capability] = blockchainConfig.capabilities
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
import io.iohk.ethereum.network.{ForkResolver, PeerEventBusActor, PeerManagerActor}
import io.iohk.ethereum.nodebuilder.{AuthHandshakerBuilder, NodeKeyBuilder}
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus}
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus, BlockchainConfig}
import org.bouncycastle.util.encoders.Hex
import org.scalamock.scalatest.MockFactory

Expand Down Expand Up @@ -99,6 +99,7 @@ object DumpChainApp
override val blockchain: Blockchain = DumpChainApp.blockchain
override val blockchainReader: BlockchainReader = DumpChainApp.blockchainReader
override val appStateStorage: AppStateStorage = storagesInstance.storages.appStateStorage
override val blockchainConfig: BlockchainConfig = Config.blockchains.blockchainConfig
override val capabilities: List[Capability] = blockchainConfig.capabilities
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import io.iohk.ethereum.network.handshaker.Handshaker.HandshakeResult
import io.iohk.ethereum.network.p2p.messages.ETH62.{BlockHeaders, GetBlockHeaders, NewBlockHashes}
import io.iohk.ethereum.network.p2p.messages.ETC64.NewBlock
import io.iohk.ethereum.network.p2p.messages.WireProtocol.Disconnect
import io.iohk.ethereum.network.p2p.messages.{Codes, BaseETH6XMessages, ETC64}
import io.iohk.ethereum.network.p2p.messages.{Codes, BaseETH6XMessages, ETC64, ETH64}
import io.iohk.ethereum.network.p2p.{Message, MessageSerializable}
import io.iohk.ethereum.utils.ByteStringUtils

Expand Down Expand Up @@ -259,6 +259,15 @@ object EtcPeerManagerActor {
}

object RemoteStatus {
def apply(status: ETH64.Status): RemoteStatus = {
RemoteStatus(
status.protocolVersion,
status.networkId,
ChainWeight.totalDifficultyOnly(status.totalDifficulty),
status.bestHash,
status.genesisHash
)
}
def apply(status: ETC64.Status): RemoteStatus = {
RemoteStatus(status.protocolVersion, status.networkId, status.chainWeight, status.bestHash, status.genesisHash)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.iohk.ethereum.network.ForkResolver
import io.iohk.ethereum.network.PeerManagerActor.PeerConfiguration
import io.iohk.ethereum.network.p2p.messages.Capability
import io.iohk.ethereum.utils.NodeStatus
import io.iohk.ethereum.utils.BlockchainConfig

case class EtcHandshaker private (
handshakerState: HandshakerState[PeerInfo],
Expand Down Expand Up @@ -37,4 +38,5 @@ trait EtcHandshakerConfiguration {
val peerConfiguration: PeerConfiguration
val forkResolverOpt: Option[ForkResolver]
val capabilities: List[Capability]
val blockchainConfig: BlockchainConfig
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@ case class EtcHelloExchangeState(handshakerConfiguration: EtcHandshakerConfigura
Capability.negotiate(hello.capabilities.toList, handshakerConfiguration.capabilities) match {
case Some(ProtocolVersions.ETC64) => EtcNodeStatus64ExchangeState(handshakerConfiguration)
case Some(ProtocolVersions.ETH63) => EtcNodeStatus63ExchangeState(handshakerConfiguration)
case Some(ProtocolVersions.ETH64) => EthNodeStatus64ExchangeState(handshakerConfiguration)
case _ =>
log.debug(
s"Connected peer does not support {} / {} protocol. Disconnecting.",
s"Connected peer does not support {} / {} / {} protocol. Disconnecting.",
ProtocolVersions.ETH63,
ProtocolVersions.ETH64,
ProtocolVersions.ETC64
)
DisconnectedState(Disconnect.Reasons.IncompatibleP2pProtocolVersion)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.iohk.ethereum.network.handshaker

import io.iohk.ethereum.forkid.ForkId
import io.iohk.ethereum.network.EtcPeerManagerActor.{PeerInfo, RemoteStatus}
import io.iohk.ethereum.network.p2p.messages.{BaseETH6XMessages, ProtocolVersions, ETH64}
import io.iohk.ethereum.network.p2p.{Message, MessageSerializable}

case class EthNodeStatus64ExchangeState(
handshakerConfiguration: EtcHandshakerConfiguration
) extends EtcNodeStatusExchangeState[ETH64.Status] {

import handshakerConfiguration._

def applyResponseMessage: PartialFunction[Message, HandshakerState[PeerInfo]] = { case status: ETH64.Status =>
// TODO: validate fork id of the remote peer
applyRemoteStatusMessage(RemoteStatus(status))
}

override protected def createStatusMsg(): MessageSerializable = {
val bestBlockHeader = getBestBlockHeader()
val chainWeight = blockchain.getChainWeightByHash(bestBlockHeader.hash).get
val genesisHash = blockchain.genesisHeader.hash

val status = ETH64.Status(
protocolVersion = ProtocolVersions.ETH64.version,
networkId = peerConfiguration.networkId,
totalDifficulty = chainWeight.totalDifficulty,
bestHash = bestBlockHeader.hash,
genesisHash = genesisHash,
forkId = ForkId.create(genesisHash, blockchainConfig)(blockchain.getBestBlockNumber())
)

log.debug(s"Sending status $status")
status
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ trait HandshakerBuilder {
override val blockchainReader: BlockchainReader = self.blockchainReader
override val appStateStorage: AppStateStorage = self.storagesInstance.storages.appStateStorage
override val capabilities: List[Capability] = self.blockchainConfig.capabilities
override val blockchainConfig: BlockchainConfig = self.blockchainConfig
}

lazy val handshaker: Handshaker[PeerInfo] = EtcHandshaker(handshakerConfiguration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup
import io.iohk.ethereum.crypto.generateKeyPair
import io.iohk.ethereum.db.storage.AppStateStorage
import io.iohk.ethereum.domain._
import io.iohk.ethereum.forkid.ForkId
import io.iohk.ethereum.network.EtcPeerManagerActor.{PeerInfo, RemoteStatus}
import io.iohk.ethereum.network.ForkResolver
import io.iohk.ethereum.network.PeerManagerActor.PeerConfiguration
import io.iohk.ethereum.network.handshaker.Handshaker.HandshakeComplete.{HandshakeFailure, HandshakeSuccess}
import io.iohk.ethereum.network.p2p.messages.Capability.Capabilities._
import io.iohk.ethereum.network.p2p.messages.BaseETH6XMessages.Status.StatusEnc
import io.iohk.ethereum.network.p2p.messages.ETH64
import io.iohk.ethereum.network.p2p.messages.ETH62.GetBlockHeaders.GetBlockHeadersEnc
import io.iohk.ethereum.network.p2p.messages.ETH62.{BlockHeaders, GetBlockHeaders}
import io.iohk.ethereum.network.p2p.messages.WireProtocol.Hello.HelloEnc
Expand Down Expand Up @@ -181,6 +183,40 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
}
}

it should "send status with fork id when peer supports ETH64" in new LocalPeerETH64Setup
with RemotePeerETH64Setup {

val newChainWeight = ChainWeight.zero.increase(genesisBlock.header).increase(firstBlock.header)

blockchain.save(firstBlock, Nil, newChainWeight, saveAsBestBlock = true)

val newLocalStatusMsg =
localStatusMsg
.copy(
bestHash = firstBlock.header.hash,
totalDifficulty = newChainWeight.totalDifficulty,
forkId = ForkId(0xfc64ec04L, Some(1150000))
)

initHandshakerWithoutResolver.nextMessage.map(_.messageToSend) shouldBe Right(localHello: HelloEnc)

val handshakerAfterHelloOpt = initHandshakerWithoutResolver.applyMessage(remoteHello)
assert(handshakerAfterHelloOpt.isDefined)

handshakerAfterHelloOpt.get.nextMessage.map(_.messageToSend.underlyingMsg) shouldBe Right(newLocalStatusMsg)

val handshakerAfterStatusOpt = handshakerAfterHelloOpt.get.applyMessage(remoteStatusMsg)
assert(handshakerAfterStatusOpt.isDefined)

handshakerAfterStatusOpt.get.nextMessage match {
case Left(HandshakeSuccess(peerInfo)) =>
peerInfo.remoteStatus.protocolVersion shouldBe localStatus.protocolVersion

case other =>
fail(s"Invalid handshaker state: $other")
}
}

it should "fail if a timeout happened during hello exchange" in new TestSetup {
val handshakerAfterTimeout = initHandshakerWithoutResolver.processTimeout
handshakerAfterTimeout.nextMessage.map(_.messageToSend) shouldBe Left(
Expand Down Expand Up @@ -278,6 +314,7 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
override val appStateStorage: AppStateStorage = TestSetup.this.storagesInstance.storages.appStateStorage
override val capabilities: List[Capability] = pv
override val blockchainReader: BlockchainReader = TestSetup.this.blockchainReader
override val blockchainConfig: BlockchainConfig = TestSetup.this.blockchainConfig
}

val etcHandshakerConfigurationWithResolver = new MockEtcHandshakerConfiguration {
Expand All @@ -287,7 +324,7 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
}

val initHandshakerWithoutResolver = EtcHandshaker(
new MockEtcHandshakerConfiguration(List(ProtocolVersions.ETC64, ProtocolVersions.ETH63))
new MockEtcHandshakerConfiguration(List(ProtocolVersions.ETC64, ProtocolVersions.ETH63, ProtocolVersions.ETH64))
)

val initHandshakerWithResolver = EtcHandshaker(etcHandshakerConfigurationWithResolver)
Expand All @@ -300,7 +337,7 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
val localHello = Hello(
p2pVersion = EtcHelloExchangeState.P2pVersion,
clientId = Config.clientId,
capabilities = Seq(Etc64Capability, Eth63Capability),
capabilities = Seq(Etc64Capability, Eth63Capability, Eth64Capability),
listenPort = 0, //Local node not listening
nodeId = ByteString(nodeStatus.nodeId)
)
Expand All @@ -320,6 +357,18 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
val localStatus = RemoteStatus(localStatusMsg)
}

trait LocalPeerETH64Setup extends LocalPeerSetup {
val localStatusMsg = ETH64.Status(
protocolVersion = ProtocolVersions.ETH64.version,
networkId = Config.Network.peer.networkId,
totalDifficulty = genesisBlock.header.difficulty,
bestHash = genesisBlock.header.hash,
genesisHash = genesisBlock.header.hash,
forkId = ForkId(1L, None)
)
val localStatus = RemoteStatus(localStatusMsg)
}

trait LocalPeerETC64Setup extends LocalPeerSetup {
val localStatusMsg = ETC64.Status(
protocolVersion = ProtocolVersions.ETC64.version,
Expand Down Expand Up @@ -378,4 +427,25 @@ class EtcHandshakerSpec extends AnyFlatSpec with Matchers {
genesisHash = genesisBlock.header.hash
)
}

trait RemotePeerETH64Setup extends RemotePeerSetup {
val remoteHello = Hello(
p2pVersion = EtcHelloExchangeState.P2pVersion,
clientId = "remote-peer",
capabilities = Seq(Eth64Capability),
listenPort = remotePort,
nodeId = ByteString(remoteNodeStatus.nodeId)
)

val remoteStatusMsg = ETH64.Status(
protocolVersion = ProtocolVersions.ETH64.version,
networkId = Config.Network.peer.networkId,
totalDifficulty = 0,
bestHash = genesisBlock.header.hash,
genesisHash = genesisBlock.header.hash,
forkId = ForkId(2L, Some(3L))
)

val remoteStatus = RemoteStatus(remoteStatusMsg)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler
import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
import io.iohk.ethereum.network.{ForkResolver, PeerActor, PeerEventBusActor, _}
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus}
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus, BlockchainConfig}
import org.bouncycastle.crypto.AsymmetricCipherKeyPair
import org.bouncycastle.crypto.params.ECPublicKeyParameters
import org.bouncycastle.util.encoders.Hex
Expand Down Expand Up @@ -566,6 +566,7 @@ class PeerActorSpec
override val blockchainReader: BlockchainReader = self.blockchainReader
override val appStateStorage: AppStateStorage = self.storagesInstance.storages.appStateStorage
override val capabilities: List[Capability] = List(protocol)
override val blockchainConfig: BlockchainConfig = self.blockchainConfig
}

val handshaker = EtcHandshaker(handshakerConfiguration)
Expand Down

0 comments on commit 8c9fbe9

Please sign in to comment.