![JAR search and dependency download from the Maven repository](/logo.png)
io.iohk.metronome.checkpointing.models.RLPCodecs.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of metronome-checkpointing-models_2.12 Show documentation
Show all versions of metronome-checkpointing-models_2.12 Show documentation
Checkpointing domain models, including the checkpoint certificate and its validation logic.
The newest version!
package io.iohk.metronome.checkpointing.models
import cats.data.NonEmptyList
import io.iohk.ethereum.crypto.ECDSASignature
import io.iohk.ethereum.rlp.{RLPEncoder, RLPList}
import io.iohk.ethereum.rlp.RLPCodec
import io.iohk.ethereum.rlp.RLPCodec.Ops
import io.iohk.ethereum.rlp.RLPException
import io.iohk.ethereum.rlp.RLPImplicitDerivations._
import io.iohk.ethereum.rlp.RLPImplicits._
import io.iohk.metronome.checkpointing.CheckpointingAgreement
import io.iohk.metronome.crypto.hash.Hash
import io.iohk.metronome.hotstuff.consensus.ViewNumber
import io.iohk.metronome.hotstuff.consensus.basic.{
Phase,
VotingPhase,
QuorumCertificate
}
import scodec.bits.{BitVector, ByteVector}
import scala.reflect.ClassTag
object RLPCodecs {
implicit val rlpBitVector: RLPCodec[BitVector] =
implicitly[RLPCodec[Array[Byte]]].xmap(BitVector(_), _.toByteArray)
implicit val rlpByteVector: RLPCodec[ByteVector] =
implicitly[RLPCodec[Array[Byte]]].xmap(ByteVector(_), _.toArray)
implicit val hashRLPCodec: RLPCodec[Hash] =
implicitly[RLPCodec[ByteVector]].xmap(Hash(_), identity)
implicit val headerHashRLPCodec: RLPCodec[Block.Header.Hash] =
implicitly[RLPCodec[ByteVector]].xmap(Block.Header.Hash(_), identity)
implicit val bodyHashRLPCodec: RLPCodec[Block.Body.Hash] =
implicitly[RLPCodec[ByteVector]].xmap(Block.Body.Hash(_), identity)
implicit val ledgerHashRLPCodec: RLPCodec[Ledger.Hash] =
implicitly[RLPCodec[ByteVector]].xmap(Ledger.Hash(_), identity)
implicit val merkleHashRLPCodec: RLPCodec[MerkleTree.Hash] =
implicitly[RLPCodec[ByteVector]].xmap(MerkleTree.Hash(_), identity)
implicit val rlpProposerBlock: RLPCodec[Transaction.ProposerBlock] =
deriveLabelledGenericRLPCodec
implicit val rlpCheckpointCandidate
: RLPCodec[Transaction.CheckpointCandidate] =
deriveLabelledGenericRLPCodec
implicit def rlpIndexedSeq[T: RLPCodec]: RLPCodec[IndexedSeq[T]] =
seqEncDec[T]().xmap(_.toVector, _.toSeq)
implicit def rlpNonEmptyList[T: RLPCodec]: RLPCodec[NonEmptyList[T]] =
seqEncDec[T]().xmap(
xs =>
NonEmptyList.fromList(xs.toList).getOrElse {
RLPException.decodeError("NonEmptyList", "List cannot be empty.")
},
_.toList
)
implicit val rlpLedger: RLPCodec[Ledger] =
deriveLabelledGenericRLPCodec
implicit val rlpTransaction: RLPCodec[Transaction] = {
import Transaction._
val ProposerBlockTag: Short = 1
val CheckpointCandidateTag: Short = 2
def encodeWithTag[T: RLPEncoder](tag: Short, value: T) = {
val t = RLPEncoder.encode(tag)
val l = RLPEncoder.encode(value).asInstanceOf[RLPList]
t +: l
}
RLPCodec.instance[Transaction](
{
case tx: ProposerBlock =>
encodeWithTag(ProposerBlockTag, tx)
case tx: CheckpointCandidate =>
encodeWithTag(CheckpointCandidateTag, tx)
},
{ case RLPList(tag, items @ _*) =>
val rest = RLPList(items: _*)
tag.decodeAs[Short]("tag") match {
case ProposerBlockTag =>
rest.decodeAs[ProposerBlock]("transaction")
case CheckpointCandidateTag =>
rest.decodeAs[CheckpointCandidate]("transaction")
case unknown =>
RLPException.decodeError(
"Transaction",
s"Unknown tag: $unknown",
List(tag)
)
}
}
)
}
implicit val rlpBlockBody: RLPCodec[Block.Body] =
deriveLabelledGenericRLPCodec
implicit val rlpBlockHeader: RLPCodec[Block.Header] =
deriveLabelledGenericRLPCodec
// Cannot use derivation because Block is a sealed abstract case class,
// so it doesn't allow creation of an invalid block.
implicit val rlpBlock: RLPCodec[Block] =
RLPCodec.instance[Block](
block =>
RLPList(
RLPEncoder.encode(block.header),
RLPEncoder.encode(block.body)
),
{ case RLPList(header, body) =>
val h = header.decodeAs[Block.Header]("header")
val b = body.decodeAs[Block.Body]("body")
Block.makeUnsafe(h, b)
}
)
implicit val rlpMerkleProof: RLPCodec[MerkleTree.Proof] =
deriveLabelledGenericRLPCodec
implicit val rlpViewNumber: RLPCodec[ViewNumber] =
implicitly[RLPCodec[Long]].xmap(ViewNumber(_), identity)
implicit val rlpVotingPhase: RLPCodec[VotingPhase] =
RLPCodec.instance[VotingPhase](
phase => {
val tag: Short = phase match {
case Phase.Prepare => 1
case Phase.PreCommit => 2
case Phase.Commit => 3
}
RLPEncoder.encode(tag)
},
{ case tag =>
tag.decodeAs[Short]("phase") match {
case 1 => Phase.Prepare
case 2 => Phase.PreCommit
case 3 => Phase.Commit
case u =>
RLPException.decodeError(
"VotingPhase",
s"Unknown phase tag: $u",
List(tag)
)
}
}
)
implicit val rlpECDSASignature: RLPCodec[ECDSASignature] =
RLPCodec.instance[ECDSASignature](
sig => RLPEncoder.encode(sig.toBytes),
{ case enc =>
val bytes = enc.decodeAs[ByteVector]("signature")
ECDSASignature
.fromBytes(akka.util.ByteString.fromArrayUnsafe(bytes.toArray))
.getOrElse {
RLPException.decodeError(
"ECDSASignature",
"Invalid signature format.",
List(enc)
)
}
}
)
implicit val rlpGroupSignature
: RLPCodec[CheckpointingAgreement.GroupSignature] =
deriveLabelledGenericRLPCodec
// Derivation doesn't seem to work on generic case class.
implicit val rlpQuorumCertificate
: RLPCodec[QuorumCertificate[CheckpointingAgreement, VotingPhase]] =
RLPCodec.instance[QuorumCertificate[CheckpointingAgreement, VotingPhase]](
{ case QuorumCertificate(phase, viewNumber, blockHash, signature) =>
RLPList(
RLPEncoder.encode(phase),
RLPEncoder.encode(viewNumber),
RLPEncoder.encode(blockHash),
RLPEncoder.encode(signature)
)
},
{ case RLPList(phase, viewNumber, blockHash, signature) =>
QuorumCertificate[CheckpointingAgreement, VotingPhase](
phase.decodeAs[VotingPhase]("phase"),
viewNumber.decodeAs[ViewNumber]("viewNumber"),
blockHash.decodeAs[CheckpointingAgreement.Hash]("blockHash"),
signature.decodeAs[CheckpointingAgreement.GroupSignature]("signature")
)
}
)
def rlpQuorumCertificate[P <: VotingPhase: ClassTag]
: RLPCodec[QuorumCertificate[CheckpointingAgreement, P]] = {
val ct = implicitly[ClassTag[P]]
rlpQuorumCertificate.xmap[QuorumCertificate[CheckpointingAgreement, P]](
qc =>
ct.unapply(qc.phase) match {
case Some(_) => qc.coerce[P]
case None =>
RLPException.decodeError(
"QuorumCertificate",
s"expected phase ${ct.runtimeClass.getSimpleName}, got ${qc.phase}"
)
},
qc => qc.coerce[VotingPhase]
)
}
implicit val rlpQuorumCertificateCommit
: RLPCodec[QuorumCertificate[CheckpointingAgreement, Phase.Commit]] =
rlpQuorumCertificate[Phase.Commit]
implicit val rlpCheckpointCertificate: RLPCodec[CheckpointCertificate] =
deriveLabelledGenericRLPCodec
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy