All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sss.openstar.wallet.UtxoTracker.scala Maven / Gradle / Ivy

package sss.openstar.wallet

import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props}
import sss.openstar.balanceledger.{NewUtxo, UtxoChanges}
import sss.openstar.chains.Chains.GlobalChainIdMask
import sss.openstar.eventbus.EventPublish
import sss.openstar.ledger.LedgerId
import sss.openstar.network.MessageEventBus
import sss.openstar.network.MessageEventBus.EventSubscriptions
import sss.openstar.util.{ActorTryAndLog, Amount, IOExecutionContext}
import sss.openstar.wallet.UtxoTracker.{NewBalance, TrackWallet}
import sss.openstar.{BusEvent, Currency, UniqueNodeIdentifier}


object UtxoTracker {

  case class NewBalance(ledgerId: LedgerId, nodeId: UniqueNodeIdentifier, balance: Amount) extends BusEvent

  case class TrackWallet[C <: Currency](walletTracking: Wallet[C], initialBalance: Amount) extends BusEvent

  def apply[C <: Currency](walletTracking: Seq[Wallet[C]],
                           ledgerId: LedgerId)
           (implicit actorSystem: ActorContext,
            messageEventBus: MessageEventBus,
            chainId: GlobalChainIdMask,
            ioExecutionContext: IOExecutionContext
           ): ActorRef = {

    actorSystem.actorOf(
      Props(classOf[UtxoTracker[C]],
        walletTracking,
        ledgerId: LedgerId,
        messageEventBus,
        chainId,
        ioExecutionContext)
        .withDispatcher("blocking-dispatcher"), s"UtxoTracker_${ledgerId.id}")
  }
}

private class UtxoTracker[C <: Currency](walletTracking: Seq[Wallet[C]], ledgerId: LedgerId
                         )(
                           implicit messageEventBus: EventPublish with EventSubscriptions,
                           chainId: GlobalChainIdMask,
                           ioExecutionContext: IOExecutionContext
                         ) extends Actor with ActorLogging with ActorTryAndLog {


  log.info(s"UtxoTracker started $ledgerId")

  override def preStart(): Unit = {
    super.preStart()
    messageEventBus.subscribe(classOf[UtxoChanges])
    messageEventBus.subscribe(classOf[TrackWallet[C]])
    walletTracking foreach (w => self ! TrackWallet(w, w.balance().get))
  }

  override def postStop(): Unit = {
    log.warning(s"UtxoTracker down $ledgerId !!!!")
    super.postStop()
  }

  override def receive: Receive = withWallets(Map.empty)


  private def withWallets(wallets: Map[Wallet[C], Amount]): Receive = {


    case t: TrackWallet[C @unchecked] if t.walletTracking.ledgerId == ledgerId =>

      val newWallets = wallets + (t.walletTracking -> t.initialBalance)
      context become withWallets(newWallets)
      messageEventBus publish NewBalance(t.walletTracking.ledgerId, t.walletTracking.walletOwner, t.initialBalance)

    case UtxoChanges(`ledgerId`, _, _, consumedSeq, emmittedSeq) => tryAndLog {

      wallets foreach { case (wallet, _) =>
        //log.debug(s"Checking Wallet ${wallet.ledgerId} ${wallet.walletOwner} ")
        val walletsForUpdate = consumedSeq.map { consumed =>
          wallet.confirmSpent(consumed.txIndx)
        } collect {
          case Some(_) => wallet
        }
        val allWalletsForUpdate = (
          emmittedSeq
            .collect {
              case out: NewUtxo => out
            }.map { emmitted =>
            val r = wallet(emmitted.txIndx, emmitted.out)
            r.foreach(l => log.info(s"Processing TX: ${wallet.walletOwner} applying ${emmitted.txIndx} ${l}"))
            r
          } collect {
            case Some(_) => wallet
          }).filterNot(walletsForUpdate.contains) ++ walletsForUpdate

        allWalletsForUpdate.foreach { wallet =>
          val newBalance = wallet.balance().get
          val newWallets = wallets + (wallet -> newBalance)
          context become withWallets(newWallets)

          messageEventBus publish NewBalance(wallet.ledgerId, wallet.walletOwner, newBalance)
        }
      }
    }

  }



}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy