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.message.payloads.IncomingMessageEncryptionConsumer.scala Maven / Gradle / Ivy
package sss.openstar.message.payloads
import sss.ancillary.FailWithException.fail
import sss.ancillary.Logging
import sss.openstar.{Currency, UniqueNodeIdentifier}
import sss.openstar.account.{NodeIdTag, NodeIdentity}
import sss.openstar.balanceledger.{StandardTx, TxInput, TxOutput}
import sss.openstar.chains.TxWriterActor.{InternalCommit, InternalTempNack, InternalTxResult}
import sss.openstar.contacts.ContactService
import sss.openstar.contract.SaleSecretDec
import sss.openstar.identityledger.IdentityServiceQuery
import sss.openstar.message.MessageProcessorUtils.mergeMessages
import sss.openstar.message.payloads.MessageEcryption.{BodyToBeEncrypted, EmbeddedBounty, EncryptedMessage, EncryptedSessionKey}
import sss.openstar.message.payloads.ThreadedMessage.Threadable
import sss.openstar.message.{Add, AsyncMessageProcessor, ConsumeResult, ExpandedComposite, ExpandedMessage, IncomingMessageConsumer, Message, MessageDecoder, TempRejected, UtxoWatch, ValidateBounty}
import sss.openstar.network.MessageEventBus
import sss.openstar.wallet.UnlockedWallet
import scala.annotation.tailrec
import scala.concurrent.{ExecutionContext, Future}
class IncomingMessageEncryptionConsumer(validateBounty: ValidateBounty,
userId: NodeIdentity,
userWallet: UnlockedWallet[Currency],
messageDecoder: MessageDecoder)(
implicit identityServiceQuery: IdentityServiceQuery,
events: MessageEventBus,
watchUtxo:UtxoWatch,
contactService: ContactService,
ec: ExecutionContext)
extends IncomingMessageConsumer with Logging {
override val incoming: AsyncMessageProcessor = {
case (ExpandedMessage(ExpandedComposite(msg: EncryptedMessage, guid, payload), None)) =>
msg.bountyOpt match {
case None if (msg.author.nodeId == userId.id) =>
Future.successful(Seq.empty[ConsumeResult])
case None =>
Future.successful(Seq(Add(Message(payload, guid, None))))
case Some(bounty) =>
decrypt(msg.author, msg) flatMap { body =>
processPayment(msg.author.nodeId, bounty, msg.receivers, body.secret).map {
case _: InternalCommit if (msg.author.nodeId == userId.id) =>
// if we are sending to ourselves, take the payment but it's already saved so don't do anything
Seq.empty
case _: InternalCommit =>
Seq(Add(Message(payload, guid, None)))
case _: InternalTempNack =>
Seq(TempRejected)
case e => fail(e.toString)
}
}
}
case ExpandedMessage(ExpandedComposite(msg: EncryptedMessage, guid, payload),
Some(ExpandedComposite(parentMsg: Threadable, parentGuid, _))) =>
msg.bountyOpt match {
case None if(msg.author.nodeId == userId.id) =>
Future.successful(Seq.empty[ConsumeResult])
case None =>
Future(mergeMessages(msg, guid, payload, parentMsg, parentGuid))
case Some(bounty) =>
decrypt(msg.author, msg).flatMap { body =>
processPayment(msg.author.nodeId, bounty, msg.receivers, body.secret).map {
case _: InternalCommit if (msg.author.nodeId == userId.id) =>
// if we are sending to ourselves, take the payment but it's already saved so don't do anything
Seq.empty
case _: InternalCommit =>
mergeMessages(msg, guid, payload, parentMsg, parentGuid)
case _: InternalTempNack =>
Seq(TempRejected)
case e => fail(e.toString)
// if we are sending to ourselves, take the payment but it's already saved to don't do anything
}
}
}
}
/**
* I wrote this using a fold at first, recursion is just clearer.
*/
@tailrec
private def findTxOffset(
recipients: Seq[UniqueNodeIdentifier],
id: UniqueNodeIdentifier,
acc: Int = 0): Option[Int] = {
recipients.headOption match {
case None => None
case Some(recipient) if (recipient == id) => Some(acc)
case _ => findTxOffset(recipients.tail, id, acc + 1)
}
}
private def decrypt(author: NodeIdTag, eMsg: EncryptedMessage): Future[BodyToBeEncrypted] = {
identityServiceQuery.accountOpt(author.nodeId, author.tag) match {
case None =>
fail(s"Message from unknown identity ${author}, cannot decrypt")
case Some(_) =>
eMsg.decrypt(userId, MessageEcryption.createLookupId(identityServiceQuery))
}
}
private def processPayment(
author: UniqueNodeIdentifier,
bounty: EmbeddedBounty,
receivers: Seq[EncryptedSessionKey],
secret:Array[Byte]): Future[InternalTxResult] = Future {
if (validateBounty(bounty.amountOfBounty, author)) {
val recipientIds = receivers.map(_.identity.tag)
val offset = findTxOffset(recipientIds, userId.id).getOrElse(fail(s"${userId.id} not in ${recipientIds}"))
val inIndex = bounty.addrOfBounty.copy(index = bounty.addrOfBounty.index + offset)
val in = TxInput(inIndex, bounty.amountOfBounty, SaleSecretDec)
val out = TxOutput(bounty.amountOfBounty, userWallet.w.encumberToIdentity())
StandardTx(Seq(in), Seq(out))
} else {
fail(s"Message bounty failed to validate ${bounty.amountOfBounty}")
}
}.flatMap {
userWallet.payAsync(_, Some(secret))
}
}