diff --git a/sim/src/main/scala/agent.scala b/sim/src/main/scala/agent.scala index 8b3d650c..e4badc3c 100644 --- a/sim/src/main/scala/agent.scala +++ b/sim/src/main/scala/agent.scala @@ -1,3 +1,5 @@ +package sim + import cats.Monad import cats.effect.kernel.{Async, Clock} import cats.effect.std.{AtomicCell, Queue} diff --git a/sim/src/main/scala/blockchain.scala b/sim/src/main/scala/blockchain.scala index 698cf59f..3033fc74 100644 --- a/sim/src/main/scala/blockchain.scala +++ b/sim/src/main/scala/blockchain.scala @@ -1,3 +1,5 @@ +package sim + import cats.Monad import cats.effect.std.Random import cats.effect.{Clock, Sync, Temporal} @@ -9,10 +11,7 @@ import fs2.Stream import scala.concurrent.duration.* -case class Account(value: String) - -case class Transaction(id: Long, account: Account, change: Long) -case class Block(txs: Vector[Transaction], seqNum: Int) +import sim.env.* /** L1 robust ledger system. */ diff --git a/sim/src/main/scala/env/ledger.scala b/sim/src/main/scala/env/ledger.scala index 44a0ea71..32ed01ab 100644 --- a/sim/src/main/scala/env/ledger.scala +++ b/sim/src/main/scala/env/ledger.scala @@ -1,23 +1,39 @@ -package env +package sim.env +import cats.effect.{ExitCode, IO, IOApp} -case class Rejection() +case class Address(v: String) + +case class VrfPublicKey(value: String) // HexString +case class KesPublicKey(value: String) // HexString +case class DsigPublicKey(value: String) // HexString + +case class Account( + address: Address, + stake: Long, + vrfPk: VrfPublicKey, + kesPk: KesPublicKey, + dsigPk: DsigPublicKey +) + +case class Transaction(id: Long, account: Account, change: Long) case class BlockId(value: Long) -/** @type - * B - Block - * @type - * S - State - */ -trait Ledger[B, S, F[_]]: +case class Block(id: BlockId, seqNum: Int) + +case class State(verAccounts: Vector[Account]) // set of verification keys to store in the Ledger + +trait Ledger[F[_]]: /** Add a new block to the ledger. */ - def add(block: B): F[Either[Rejection, S]] + def add(block: Block): F[Unit] /** Get all blocks at the given height. */ - def getAll(height: Int): F[List[B]] + def getAll(height: Int): F[List[Block]] /** Get specific block. */ - def get(id: BlockId): F[Option[B]] + def get(id: BlockId): F[Option[Block]] + + def addParticipant(acc: Account): F[Unit] diff --git a/sim/src/main/scala/main.scala b/sim/src/main/scala/main.scala index fa7c42ae..63c99b92 100644 --- a/sim/src/main/scala/main.scala +++ b/sim/src/main/scala/main.scala @@ -3,6 +3,8 @@ import cats.syntax.show.* import scala.concurrent.duration.* +import sim.* + object Main extends IOApp { override def run(args: List[String]): IO[ExitCode] = for diff --git a/sim/src/main/scala/protocol/spectrum.scala b/sim/src/main/scala/protocol/spectrum.scala new file mode 100644 index 00000000..150c3638 --- /dev/null +++ b/sim/src/main/scala/protocol/spectrum.scala @@ -0,0 +1,57 @@ +package sim.spectrum + +import cats.Monad +import cats.effect.IO +import cats.effect.kernel.{Async, Clock} +import cats.effect.std.{AtomicCell, Queue} +import cats.syntax.applicative.* +import cats.syntax.flatMap.* +import cats.syntax.foldable.* +import cats.syntax.functor.* +import cats.syntax.traverse.* +import sim.* +import sim.env.* + +import scala.concurrent.duration.* +import scala.language.postfixOps + + +sealed trait SpectrumMsg +case class NewBlock(block: Block) extends SpectrumMsg +case class GetBlock(blockId: BlockId) extends SpectrumMsg +case class Register(newAcc: Account) extends SpectrumMsg + + +final class Spectrum[F[_]: Monad]( + selfAddr: Addr, + // abstract interface to Ledger, so you dont have to care about impl. + ledger: Ledger[F], + // queue of pending output commands waiting to be returned from poll(). + pendingOutputs: Queue[F, (Addr, AgentOut[SpectrumMsg])] + ) extends Agent[SpectrumMsg, F]: + def getAddr: F[Addr] = selfAddr.pure + + def injectMessage(srcAddr: Addr, m: SpectrumMsg): F[Unit] = + m match + case NewBlock(block) => ledger.add(block) + case Register(account) => ledger.addParticipant(account) + case GetBlock(blockId) => + for + maybeBlock <- ledger.get(blockId) + _ <- maybeBlock match + case None => + // do nothing + ().pure + case Some(block) => + // if the requested block is found locally we send it to the peer which requested it + pendingOutputs.offer(srcAddr -> SendMessage(NewBlock(block))) + yield () + + def injectConnReq(peerAddr: Addr, handshake: SpectrumMsg): F[Either[Reject, SpectrumMsg]] = ??? + + def injectConnLost(peerAddr: Addr): F[Unit] = ??? + + def injectConnEstablished(peerAddr: Addr, handshake: SpectrumMsg): F[Unit] = ??? + + def poll: F[Option[(Addr, AgentOut[SpectrumMsg])]] = + pendingOutputs.tryTake \ No newline at end of file diff --git a/sim/src/main/scala/swarm.scala b/sim/src/main/scala/swarm.scala index 08110b68..f9b5d0c6 100644 --- a/sim/src/main/scala/swarm.scala +++ b/sim/src/main/scala/swarm.scala @@ -1,3 +1,5 @@ +package sim + import cats.effect.kernel.Async import cats.effect.std.{AtomicCell, Random} import cats.effect.syntax.spawn.*