Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
sss.openstar.controller.SendMessage.scala Maven / Gradle / Ivy
package sss.openstar.controller
import sss.ancillary.{Guid, Logging}
import sss.db.Db
import sss.openstar.balanceledger.{TxIndex, TxOutput}
import sss.openstar.chains.Chains.GlobalChainIdMask
import sss.openstar.chains.TxWriterActor.{InternalCommit, InternalTempNack}
import sss.openstar.contract.SaleOrReturnSecretEnc
import sss.openstar.identityledger.IdentityServiceQuery
import sss.openstar.message.MessageInBoxActor.SaveOutgoingMessage
import sss.openstar.message.payloads.PaywalledMessage
import sss.openstar.message.{PaywallCharges, ProviderStoreCharges, _}
import sss.openstar.network.MessageEventBus
import sss.openstar.wallet.UnlockedWallet
import sss.openstar.wallet.Wallet.CreditTooLow
import sss.openstar.{Currency, ExtraMessageKeys, UniqueNodeIdentifier}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Promise}
import scala.util.{Failure, Success, Try}
object SendMessage {
type SubmitMessage = (UniqueNodeIdentifier,
UnlockedWallet[Currency],
Int,
Set[UniqueNodeIdentifier],
MessageComposite,
Option[Guid],
Guid) => Future[Unit]
}
class SendMessage(currentBlockHeight: () => Long,
serviceProviderPayment: ProviderStoreCharges,
paywallCharges: PaywallCharges
)
(implicit
db: Db,
events: MessageEventBus,
send: Send,
identityServiceQuery: IdentityServiceQuery,
chainId: GlobalChainIdMask
) extends Logging {
type PayWallResult = Seq[(PaywallEnvelope, Set[String])]
private def processProviderPayments(numBlocksToLive: Int,
tos: Set[UniqueNodeIdentifier],
userWallet: UnlockedWallet[Currency],
msg: Message,
acc: PayWallResult,
): Future[PayWallResult] = {
if (tos.isEmpty) {
Future.successful(acc)
} else {
val to = tos.head
log.info(s"Balance now ${userWallet.w.balance()}")
Future.fromTry {
for {
chargePerMessage <- serviceProviderPayment(to)
userPaywallCharge <- paywallCharges(userWallet.w.walletOwner, to)
} yield(chargePerMessage, userPaywallCharge)
} flatMap { case (chargePerMessage, userPaywallCharge) =>
val totalCharges = (userWallet.w.amount(userPaywallCharge) + chargePerMessage).get
val providers = identityServiceQuery.messageStores(to) + to
if(totalCharges.value <= 0) {
val wrappedPayload = MessagePayloadDecoder
.toPayloadOpt(PaywalledMessage(Some(userWallet.w.walletOwner), None, msg.msgPayload))
.getOrElse(throw new Exception(s"Couildn't make payload from PaywalledMessage"))
val wrappedMessage = Message(wrappedPayload, msg.guid, msg.parentGuid)
processProviderPayments(numBlocksToLive, tos.tail, userWallet, msg, acc :+ (PaywallEnvelope(None, to, wrappedMessage), providers))
} else {
userWallet.w.createTx(totalCharges, numBlocksToLive) match {
case Success(baseTx) =>
val txPlusProviderCharge = if(chargePerMessage > 0) {
val paymentProvider = TxOutput(chargePerMessage,
SaleOrReturnSecretEnc(userWallet.w.walletOwner,
providers.toSeq,
None,
currentBlockHeight() + numBlocksToLive
)
)
userWallet.w.appendOutputs(baseTx, paymentProvider)
} else baseTx
val txPlusUserPaywallCharge = if(userPaywallCharge > 0) {
val paymentUserPaywall = TxOutput(userPaywallCharge,
SaleOrReturnSecretEnc(userWallet.w.walletOwner,
to,
None,
currentBlockHeight() + numBlocksToLive
)
)
userWallet.w.appendOutputs(txPlusProviderCharge, paymentUserPaywall)
} else txPlusProviderCharge
userWallet.payAsync(txPlusUserPaywallCharge) flatMap {
case _: InternalCommit =>
val providerPaymentIndex = if(chargePerMessage > 0) {
Some(TxIndex(txPlusProviderCharge.txId, txPlusUserPaywallCharge.outs.size - 1))
} else None
val paywallPaymentTxIndex = if(userPaywallCharge > 0) {
Some(TxIndex(txPlusProviderCharge.txId, txPlusProviderCharge.outs.size - 1))
} else None
val wrappedPayload = MessagePayloadDecoder
.toPayloadOpt(PaywalledMessage(Some(userWallet.w.walletOwner), paywallPaymentTxIndex, msg.msgPayload))
.getOrElse(throw new Exception(s"Couildn't make payload from PaywalledMessage"))
val wrappedMessage = Message(wrappedPayload, msg.guid, msg.parentGuid)
log.debug(s"Sending guid ${msg.guid} parent guid ${msg.parentGuid} to providers $providers}")
processProviderPayments(numBlocksToLive, tos.tail, userWallet, msg, acc :+ (PaywallEnvelope(providerPaymentIndex, to, wrappedMessage), providers))
case _: InternalTempNack =>
processProviderPayments(numBlocksToLive, tos.tail, userWallet, msg, acc)
case e =>
throw new Exception(s"payAsync fail ${e.toString}")
}
case Failure(e: CreditTooLow) =>
log.warn(s"CreditTooLow ${userWallet.w.walletOwner}", e)
Future.failed(e)
case Failure(e) =>
log.warn("send in order failed {}", e)
Future.failed(new Exception(s"Failed to create tx for delivery charge to $to"))
}
}
}
}
}
def sendToRecipients(
from: UniqueNodeIdentifier,
userWallet: UnlockedWallet[Currency],
numBlocksToLive: Int,
tos: Set[UniqueNodeIdentifier],
o: MessageComposite,
parentGuid: Option[Guid],
guid: Guid
): Future[Unit] = {
Try[Message] {
log.debug("Message To Send begins")
val payload = MessagePayloadDecoder.toPayload(o)
Message(payload, guid, parentGuid)
} match {
case Failure(e) =>
Future.failed(e)
case Success((msg)) =>
val tosMinusSelf = tos.filterNot(_ == from)
processProviderPayments(numBlocksToLive, tosMinusSelf, userWallet, msg, Seq.empty) flatMap { seqProviderPayments =>
saveMessage(from, tosMinusSelf.mkString(","), msg) map { _ =>
seqProviderPayments.map {
case (paywallEnv, providers) =>
send(ExtraMessageKeys.PaywallEnvelope, paywallEnv, providers)
}
}
}
}
}
private def saveMessage(from: UniqueNodeIdentifier, to: String, msg: Message): Future[Unit] = {
val p = Promise[Unit]()
events publish SaveOutgoingMessage(from, msg, p)
p.future
}
}