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

[BE2] migrate to Scala 3 #1111

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,6 @@ jobs:
- name: Report coverage
run: sbt coverageReport

- name: Run static analysis (ScapeGoat)
run: sbt scapegoat

- name: Run Sonar scanner
env:
SONAR_HOST_URL: https://sonarcloud.io
Expand Down
46 changes: 8 additions & 38 deletions be2-scala/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,21 @@ import sbtsonar.SonarPlugin.autoImport.sonarProperties

name := "pop"

scalaVersion := "2.13.7"
scalaVersion := "3.2.0"

// Recommended 2.13 Scala flags (https://nathankleyn.com/2019/05/13/recommended-scalac-flags-for-2-13) slightly adapted for PoP
scalacOptions ++= Seq(
"-deprecation", // Emit warning and location for usages of deprecated APIs.
"-explaintypes", // Explain type errors in more detail.
"-explain-types", // Explain type errors in more detail.
"-feature", // Emit warning and location for usages of features that should be imported explicitly.
"-language:existentials", // Existential types (besides wildcard types) can be written and inferred
"-language:experimental.macros", // Allow macro definition (besides implementation and application)
"-language:higherKinds", // Allow higher-kinded types
"-language:implicitConversions", // Allow definition of implicit functions called views
"-unchecked", // Enable additional warnings where generated code depends on assumptions.
"-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access.
"-Xfatal-warnings", // Fail the compilation if there are any warnings.
"-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver.
"-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error.
"-Xlint:delayedinit-select", // Selecting member of DelayedInit.
"-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element.
"-Xlint:inaccessible", // Warn about inaccessible types in method signatures.
"-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`.
"-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id.
"-Xlint:nullary-unit", // Warn when nullary methods return Unit.
"-Xlint:option-implicit", // Option.apply used implicit view.
"-Xlint:package-object-classes", // Class or object defined in package object.
"-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds.
"-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field.
"-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component.
"-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope.
"-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined.
"-Ywarn-numeric-widen", // Warn when numerics are widened.
"-Ycache-plugin-class-loader:last-modified", // Enables caching of classloaders for compiler plugins
"-Ycache-macro-class-loader:last-modified", // and macro definitions. This can lead to performance improvements.
)

Scapegoat/ scalacOptions -= "-Xfatal-warnings"
// Temporarily report scapegoat errors as warnings, to avoid broken builds
Scapegoat/ scalacOptions += "-P:scapegoat:overrideLevels:all=Warning"

// Reload changes automatically
Global / onChangedBuildSource := ReloadOnSourceChanges
Global / cancelable := true
Expand Down Expand Up @@ -89,15 +66,9 @@ copyProtocolTask := {
Compile/ run/ mainClass := Some("ch.epfl.pop.Server")
Compile/ packageBin/ mainClass := Some("ch.epfl.pop.Server")

lazy val scoverageSettings = Seq(
Compile/ coverageEnabled := true,
Test/ coverageEnabled := true,
packageBin/ coverageEnabled := false,
)

ThisBuild/ scapegoatVersion := "1.4.11"

scapegoatReports := Seq("xml", "html")
// https://github.com/ckipp01/dotty/blob/2fc33a36ea19752920d17596b5a6194c815a1873/docs/_docs/usage/coverage.md
Compile/ compile/ scalacOptions +=
s"-coverage-out:target/scala-${scalaVersion.value}/scoverage-data"

// Configure Sonar
sonarProperties := Map(
Expand All @@ -108,10 +79,9 @@ sonarProperties := Map(
"sonar.tests" -> "src/test/scala",

"sonar.sourceEncoding" -> "UTF-8",
"sonar.scala.version" -> "2.13.7",
"sonar.scala.version" -> scalaVersion.value,
// Paths to the test and coverage reports
"sonar.scala.coverage.reportPaths" -> "./target/scala-2.13/scoverage-report/scoverage.xml",
"sonar.scala.scapegoat.reportPaths" -> "./target/scala-2.13/scapegoat-report/scapegoat.xml"
"sonar.scala.coverage.reportPaths" -> "./target/scala-3/scoverage-report/scoverage.xml",
)

assembly/ assemblyMergeStrategy := {
Expand All @@ -131,7 +101,7 @@ val AkkaHttpVersion = "10.2.9"

libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-stream-typed" % AkkaVersion, // Akka streams (Graph)
"com.typesafe.akka" %% "akka-http" % AkkaHttpVersion, // Akka http (WebSockets)
("com.typesafe.akka" %% "akka-http" % AkkaHttpVersion).cross(CrossVersion.for3Use2_13), // Akka http (WebSockets)
"com.typesafe.akka" %% "akka-cluster-tools" % AkkaVersion, // Akka distributed publish/subscribe cluster

"ch.qos.logback" % "logback-classic" % "1.1.3" % Runtime, // Akka logging library
Expand Down
4 changes: 1 addition & 3 deletions be2-scala/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
addSbtPlugin("com.sksamuel.scapegoat" % "sbt-scapegoat" % "1.1.1")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.0-M4")
addSbtPlugin("com.sonar-scala" % "sbt-sonar" % "2.3.0")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.1.0")
addSbtPlugin("com.rallyhealth.sbt" % "sbt-git-versioning" % "1.6.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("ch.epfl.scala" % "sbt-scala3-migrate" % "0.5.1")
2 changes: 1 addition & 1 deletion be2-scala/src/main/scala/ch/epfl/pop/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ object Server {
val bindingFuture = Http().newServerAt(config.interface, config.port).bindFlow(publishSubscribeRoute)

bindingFuture.onComplete {
case Success(_) => println(f"ch.epfl.pop.Server online at ws://${config.interface}:${config.port}/${config.path}")
case Success(_) => println(s"ch.epfl.pop.Server online at ws://${config.interface}:${config.port}/${config.path}")
case Failure(_) =>
logger.error(
"ch.epfl.pop.Server failed to start. Terminating actor system"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object ElectionChannel {
*/
def extractMessages[T: reflect.ClassTag](dbActor: AskableActorRef = DbActor.getInstance): Future[List[(Message, T)]] = {
bbjubjub2494 marked this conversation as resolved.
Show resolved Hide resolved
for {
DbActor.DbActorCatchupAck(messages) <- dbActor ? DbActor.Catchup(channel)
case DbActor.DbActorCatchupAck(messages) <- dbActor ? DbActor.Catchup(channel)
result <- Future.traverse(messages.flatMap(message =>
message.decodedData match {
case Some(t: T) => Some((message, t))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ final case class Transaction(
}

private def signaturePayload =
Base64Data.encode(inputs.map { txin => f"${txin.txOutHash}${txin.txOutIndex}" }.reduce(_ + _) +
outputs.map { txout => f"${txout.value}${txout.script.`type`}${txout.script.pubkeyHash.base64Data}" }.reduce(_ + _))
Base64Data.encode(inputs.map { txin => s"${txin.txOutHash}${txin.txOutIndex}" }.reduce(_ + _) +
outputs.map { txout => s"${txout.value}${txout.script.`type`}${txout.script.pubkeyHash.base64Data}" }.reduce(_ + _))

/** This ensures the validity of the signatures, not that the funds are unspent.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ object Uint53 {
*/
def safeSum(seq: IterableOnce[Uint53]): Either[ArithmeticOverflowError, Uint53] = {
var acc = 0L
for (v <- seq.iterator) {
val it = seq.iterator
while (it.hasNext) {
val v = it.next
require(inRange(v), s"value $v out of range for uint53")
acc += v
if (acc > MaxValue)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class ElectionHandler(dbRef: => AskableActorRef) extends MessageHandler {
castsVotesElections <- electionChannel.getLastVotes(dbActor)
setupMessage <- electionChannel.getSetupMessage(dbActor)
questionToBallots = setupMessage.questions.map(question => question.id -> question.ballot_options).toMap
DbActorReadElectionDataAck(electionData) <- dbActor ? DbActor.ReadElectionData(setupMessage.id)
case DbActorReadElectionDataAck(electionData) <- dbActor ? DbActor.ReadElectionData(setupMessage.id)
} yield {
val resultsTable = mutable.HashMap.from(for {
(question, ballots) <- questionToBallots
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ case object LaoHandler extends MessageHandler {
val reactionChannel: Channel = Channel(s"$laoChannel${Channel.REACTIONS_CHANNEL_PREFIX}")
// we get access to the canonical address of the server
val config = ServerConf(appConf)
val address: Option[String] = Some(f"ws://${config.interface}:${config.port}/${config.path}")
val address: Option[String] = Some(s"ws://${config.interface}:${config.port}/${config.path}")

val combined = for {
// check whether the lao already exists in db
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ trait MessageHandler extends AskPatternConstants {
* the database answer wrapped in a [[scala.concurrent.Future]]
*/
def dbAskWrite(rpcRequest: JsonRpcRequest): Future[GraphMessage] = {
val m: Message = rpcRequest.getParamsMessage.getOrElse(
return Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWrite failed : retrieve empty rpcRequest message", rpcRequest.id))
}
)

val askWrite = dbActor ? DbActor.Write(rpcRequest.getParamsChannel, m)
askWrite.transformWith {
case Success(_) => Future(Left(rpcRequest))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWrite failed : could not write message $m", rpcRequest.id)))
rpcRequest.getParamsMessage match {
case None =>
Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWrite failed : retrieve empty rpcRequest message", rpcRequest.id))
}
case Some(m) =>
val askWrite = dbActor ? DbActor.Write(rpcRequest.getParamsChannel, m)
askWrite.transformWith {
case Success(_) => Future(Left(rpcRequest))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWrite failed : could not write message $m", rpcRequest.id)))
}
}
}

Expand All @@ -48,16 +49,17 @@ trait MessageHandler extends AskPatternConstants {
* the database answer wrapped in a [[scala.concurrent.Future]]
*/
def dbAskWritePropagate(rpcRequest: JsonRpcRequest): Future[GraphMessage] = {
val m: Message = rpcRequest.getParamsMessage.getOrElse(
return Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : retrieve empty rpcRequest message", rpcRequest.id))
}
)

val askWritePropagate = dbActor ? DbActor.WriteAndPropagate(rpcRequest.getParamsChannel, m)
askWritePropagate.transformWith {
case Success(_) => Future(Left(rpcRequest))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : could not write & propagate message $m", rpcRequest.id)))
rpcRequest.getParamsMessage match {
case None =>
Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : retrieve empty rpcRequest message", rpcRequest.id))
}
case Some(m) =>
val askWritePropagate = dbActor ? DbActor.WriteAndPropagate(rpcRequest.getParamsChannel, m)
askWritePropagate.transformWith {
case Success(_) => Future(Left(rpcRequest))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : could not write & propagate message $m", rpcRequest.id)))
}
}
}

Expand All @@ -75,23 +77,24 @@ trait MessageHandler extends AskPatternConstants {
* the database answer wrapped in a [[scala.concurrent.Future]]
*/
def dbBroadcast(rpcMessage: JsonRpcRequest, channel: Channel, broadcastData: Base64Data, broadcastChannel: Channel): Future[GraphMessage] = {
val m: Message = rpcMessage.getParamsMessage.getOrElse(
return Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : retrieve empty rpcRequest message", rpcMessage.id))
}
)

val combined = for {
DbActorReadLaoDataAck(laoData) <- dbActor ? DbActor.ReadLaoData(channel)
broadcastSignature: Signature = laoData.privateKey.signData(broadcastData)
broadcastId: Hash = Hash.fromStrings(broadcastData.toString, broadcastSignature.toString)
broadcastMessage: Message = Message(broadcastData, laoData.publicKey, broadcastSignature, broadcastId, List.empty)
_ <- dbActor ? DbActor.WriteAndPropagate(broadcastChannel, broadcastMessage)
} yield ()
rpcMessage.getParamsMessage match {
case None =>
Future {
Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbAskWritePropagate failed : retrieve empty rpcRequest message", rpcMessage.id))
}
case Some(m) =>
val combined = for {
case DbActorReadLaoDataAck(laoData) <- dbActor ? DbActor.ReadLaoData(channel)
broadcastSignature: Signature = laoData.privateKey.signData(broadcastData)
broadcastId: Hash = Hash.fromStrings(broadcastData.toString, broadcastSignature.toString)
broadcastMessage: Message = Message(broadcastData, laoData.publicKey, broadcastSignature, broadcastId, List.empty)
_ <- dbActor ? DbActor.WriteAndPropagate(broadcastChannel, broadcastMessage)
} yield ()

combined.transformWith {
case Success(_) => Future(Left(rpcMessage))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbBroadcast failed : could not read and broadcast message $m", rpcMessage.id)))
combined.transformWith {
case Success(_) => Future(Left(rpcMessage))
case _ => Future(Right(PipelineError(ErrorCodes.SERVER_ERROR.id, s"dbBroadcast failed : could not read and broadcast message $m", rpcMessage.id)))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class WitnessHandler(dbRef: => AskableActorRef) extends MessageHandler {
case Some(_) =>
val combined = for {
// add new witness signature to existing ones
DbActorAddWitnessSignatureAck(witnessMessage) <- dbActor ? DbActor.AddWitnessSignature(channel, messageId, signature)
case DbActorAddWitnessSignatureAck(witnessMessage) <- dbActor ? DbActor.AddWitnessSignature(channel, messageId, signature)
// overwrites the message containing now the witness signature in the db
_ <- dbActor ? DbActor.WriteAndPropagate(channel, witnessMessage)
} yield ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PublicKeySuite extends FunSuite with Matchers {

test("pubkey hash matches example data") {
for (suffix <- Seq("", "2")) {
val Seq(JsString(pubkeyData), JsString(pubkeyHash)) = data.asJsObject.getFields(s"publicKey$suffix", s"publicKeyHash$suffix")
val Seq(JsString(pubkeyData), JsString(pubkeyHash)) = data.asJsObject.getFields(s"publicKey$suffix", s"publicKeyHash$suffix"): @unchecked
val pubkey = PublicKey(Base64Data(pubkeyData))
pubkey.hash.base64Data should equal(Base64Data(pubkeyHash))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object TestKeyPairs {

val keypairs: Vector[KeyPairWithHash] = for (suffix <- Vector("", "2")) yield {
val Seq(JsString(privkeyData), JsString(pubkeyData), JsString(pubkeyHash)) =
raw.getFields(s"privateKey$suffix", s"publicKey$suffix", s"publicKeyHash$suffix")
raw.getFields(s"privateKey$suffix", s"publicKey$suffix", s"publicKeyHash$suffix"): @unchecked
val fixedPrivkeyData = Base64Data(privkeyData).decode().take(32)
KeyPairWithHash(KeyPair(PrivateKey(Base64Data.encode(fixedPrivkeyData)), PublicKey(Base64Data(pubkeyData))), Base64Data(pubkeyHash))
}
Expand Down