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

sss.openstar.test.MultiIdentityTestTransactionSenderInternalClaim.scala Maven / Gradle / Ivy

package sss.openstar.test

import akka.actor.Status.{Failure => FutureFailure}
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Cancellable, Props, ReceiveTimeout}
import akka.pattern.pipe
import sss.ancillary.FutureOps.AwaitResult
import sss.openstar.Currency
import sss.openstar.account.{NodeIdTag, NodeIdentity, NodeIdentityManager}
import sss.openstar.chains.TxWriterActor._
import sss.openstar.common.users.UserDirectory
import sss.openstar.contract.FindPublicKeyAccOpt
import sss.openstar.ledger.LedgerId
import sss.openstar.network.MessageEventBus
import sss.openstar.test.MultiIdentityTestTransactionSender.FireTxs
import sss.openstar.test.MultiIdentityTestTransactionSenderInternalClaim.{Claimed, Funded}
import sss.openstar.tools.SendTxSupport.SendTx
import sss.openstar.wallet.UnlockedWallet
import sss.openstar.wallet.UtxoTracker.TrackWallet
import sss.openstar.wallet.Wallet.Payment

import java.util.concurrent.atomic.AtomicLong
import scala.concurrent.duration._
import scala.util.{Failure, Random, Success, Try}

object MultiIdentityTestTransactionSenderInternalClaim {

  case object Register

  case class Claimed(newId: NodeIdentity)

  case class Funded(newId: String, amount: Long)


  def apply[C <: Currency](nodeIdentityManager: NodeIdentityManager,
                           accountOpt: FindPublicKeyAccOpt,
                           users: UserDirectory,
                           testUsersConfig: Int,
                           initialAmount: Long,
                           testBalanceLedgerId: LedgerId,
                           buildUnlockedWallet: NodeIdentity => UnlockedWallet[C],
                           maxInProgress: Int,
                           maxTotalTxs: Int,
                           toIdentityLedgerOwner: NodeIdentity => Option[NodeIdentity],
                           hostingNodeIdentity: NodeIdentity
                          )(implicit actorSystem: ActorContext,
                            sendTx: SendTx,
                            messageEventBus: MessageEventBus
                          ): ActorRef = {

    actorSystem.actorOf(
      Props(new MultiIdentityTestTransactionSenderInternalClaim[C](
        nodeIdentityManager: NodeIdentityManager,
        accountOpt: FindPublicKeyAccOpt,
        users: UserDirectory,
        testUsersConfig: Int,
        initialAmount: Long,
        testBalanceLedgerId: LedgerId,
        buildUnlockedWallet: NodeIdentity => UnlockedWallet[C],
        maxInProgress,
        maxTotalTxs,
        toIdentityLedgerOwner,
        hostingNodeIdentity)
      ), "MultiIdentityTestTransactionSenderInternalClaim")

  }

}

class MultiIdentityTestTransactionSenderInternalClaim[C <: Currency](nodeIdentityManager: NodeIdentityManager,
                                                                     accountOpt: FindPublicKeyAccOpt,
                                                                     users: UserDirectory,
                                                                     testUsersConfig: Int,
                                                                     initialAmount: Long,
                                                                     testBalanceLedgerId: LedgerId,
                                                                     buildUnlockedWallet: NodeIdentity => UnlockedWallet[C],
                                                                     maxInProgress: Int,
                                                                     maxTotalTxs: Int,
                                                                     toIdentityLedgerOwner: NodeIdentity => Option[NodeIdentity],
                                                                     hostingNodeIdentity: NodeIdentity
                                                                    )(implicit
                                                                      sendTx: SendTx,
                                                                      messageEventBus: MessageEventBus)
  extends Actor with ActorLogging {


  private val defaultPassword = "Password10!"
  import context.dispatcher

  context.setReceiveTimeout(120.seconds)

  private var testUsersWallets: Seq[UnlockedWallet[C]] = {
    users.listUsers() map { user =>
      val ni = nodeIdentityManager(user.identity, NodeIdTag.defaultTag, defaultPassword).await()
      buildUnlockedWallet(ni)
    }
  }

  private var schedFire: Option[Cancellable] = None

  private def scheduleFireTxs(delayInSeconds: Int) = {

    schedFire = schedFire match {
      case None =>
        Option(context.system.scheduler.scheduleOnce(delayInSeconds.seconds, self, FireTxs))
      case x => x
    }

  }


  case class AddWallet(wallet: UnlockedWallet[C])

  //private var testUsersWallets: Seq[UnlockedWallet[C]] = Seq.empty

  private def randomWallet(notIn: String*): UnlockedWallet[C] = {

    val candidate = testUsersWallets(Random.nextInt(testUsersWallets.size))
    if(notIn.contains(candidate.w.walletOwner)) randomWallet(notIn: _*)
    else candidate
  }

  private def randomWallet(): UnlockedWallet[C] = {
    testUsersWallets(Random.nextInt(testUsersWallets.size))
  }

  private var inProgress = 0
  private var totalTxsProcessed: Int = 0
  private val totalTimeProcessing = new AtomicLong(0)


  private def calculateLatency(): Double = {
    val time = totalTimeProcessing.get()
    val totalTxs = totalTxsProcessed
    if (time > 0 && totalTxs > 0) {
      time.toDouble / totalTxs.toDouble
    } else {
      0
    }
  }


  {
    context.actorOf(Props(
      new ClaimUsersActor(testUsersConfig, users, defaultPassword, nodeIdentityManager, accountOpt, toIdentityLedgerOwner, hostingNodeIdentity)
    ), "ClaimUsersActor")
  }


  scheduleFireTxs(1)

  override def receive: Receive = {

    case FutureFailure(e) =>
      log.warning("Failed, retry, " + e.toString)
      inProgress -= 1
      scheduleFireTxs(2)


    case ReceiveTimeout =>
      log.info("All quiet, print out wallet balances")
      var balances:Long = 0
      testUsersWallets.foreach(w => {
        balances = balances + w.w.balance().get.value
        log.info(s"${w.w.walletOwner} balance ${w.w.balance()}")
      })
      log.info(s"Total balances $balances")
      val lat = calculateLatency()
      log.info(s"Latency $lat, total ${totalTxsProcessed}")
    //self ! PoisonPill

    case com: InternalCommit =>
      log.info(s"Got commit: $com, going again")
      inProgress -= 1
      self ! FireTxs

    case ack: InternalAck =>
    //log.info(s"Got ack: $ack")

    case r: InternalTempNack =>
      inProgress -= 1
      if (inProgress % 20 == 0) {
        log.info(r.toString + s" TxResult $r rescheule")
      }
      scheduleFireTxs(2)

    case r: InternalTxResult =>
      inProgress -= 1
      self ! FireTxs

    case FireTxs if inProgress < maxInProgress && totalTxsProcessed < maxTotalTxs && testUsersWallets.size > 1 =>
      schedFire = None
      var fired = false
      (0 until (maxInProgress - inProgress)) foreach {
        _ =>
          val wallet = randomWallet()
          val wallet2 = randomWallet(wallet.w.walletOwner)

          val bal = wallet.w.balance().get.value
          log.info(s"User: ${wallet.w.walletOwner} -> balance: ${bal} ${wallet.w.ledgerId} ${wallet.w.clientBalanceTotal()}")
          if (bal > 0) {
            Try(wallet.payAsync(Payment(wallet2.w.walletOwner, 1, 5))) match {
              case Success(f) =>
                inProgress += 1
                val now = System.currentTimeMillis()
                f andThen { case Success(_) =>
                  totalTxsProcessed += 1
                  totalTimeProcessing.addAndGet(System.currentTimeMillis() - now)
                }
                f pipeTo self
              case Failure(e) =>
                log.debug(e.toString)
                log.info("Problem, try again")
                if (!fired) {
                  fired = true
                  scheduleFireTxs(1)
                }
            }
          } else if (!fired) {
            fired = true
            scheduleFireTxs(1)
          }
      }

    case FireTxs if totalTxsProcessed < maxTotalTxs=>
      schedFire.map(_.cancel())
      schedFire = None
      if (inProgress % 2 == 0) {
        log.info(s"$inProgress in progress, wait a few.... (total ${totalTxsProcessed}) average latency = ${calculateLatency()} ms")
      }
      scheduleFireTxs(1)


    case Claimed(newNodeIdentity) =>
      buildUnlockedWallet(newNodeIdentity).w.balance() map { amount =>

        val isFunded= amount.value > 0
        if(isFunded) {
          self ! Funded(newNodeIdentity.id, amount.value)
        } else {
          context.actorOf(Props(
            new FundWithTestCoinbase(newNodeIdentity.id, initialAmount, testBalanceLedgerId)
          ))
        }
      } recover {
        case e => log.warning(s"Failed to build wallet for ${newNodeIdentity.id} {}", e)
      }

    case AddWallet(wallet) =>
      testUsersWallets = testUsersWallets :+ wallet
      if(testUsersWallets.size % 5000 == 0) {
        log.info(s"TEST USERS NOW ${testUsersWallets.size}")
      }

    case Funded(newId, amount) =>
      Try {
        if(testUsersWallets.exists(_.w.walletOwner == newId)) {
          log.warning(s"Skipping add of ${newId} already exists.")
        } else {
          val nodeIdentity = nodeIdentityManager(newId, NodeIdTag.defaultTag, defaultPassword).await()
          val wallet = buildUnlockedWallet(nodeIdentity)
          log.info(s"Add wallet for ${newId} : ${amount} ${wallet.w.balance()}")
          messageEventBus publish TrackWallet[C](wallet.w, wallet.w.amount(amount))
          context.system.scheduler.scheduleOnce(5.seconds, self, AddWallet(wallet))

        }
        scheduleFireTxs(2)
      } recover {
        case e => log.warning(s"Failed to create fund!", e)
      }

  }


}









© 2015 - 2024 Weber Informatics LLC | Privacy Policy