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

scorex.transaction.GenesisTransaction.scala Maven / Gradle / Ivy

The newest version!
package scorex.transaction

import com.google.common.primitives.{Bytes, Ints, Longs}
import play.api.libs.json.{JsObject, Json}
import scorex.account.Account
import scorex.crypto.encode.Base58
import scorex.crypto.hash.FastCryptographicHash._
import scorex.serialization.Deser
import scorex.transaction.TypedTransaction.TransactionType

import scala.util.{Failure, Try}


case class GenesisTransaction(override val recipient: Account,
                              override val amount: Long,
                              override val timestamp: Long)
  extends LagonakiTransaction(TransactionType.GenesisTransaction, recipient, amount, 0, timestamp,
    GenesisTransaction.generateSignature(recipient, amount, timestamp)) {

  import scorex.transaction.GenesisTransaction._
  import scorex.transaction.LagonakiTransaction._

  override lazy val creator: Option[Account] = None

  override lazy val json: JsObject =
    jsonBase() ++ Json.obj("recipient" -> recipient.address, "amount" -> amount)

  override lazy val bytes: Array[Byte] = {
    val typeBytes = Array(TransactionType.GenesisTransaction.id.toByte)

    val timestampBytes = Bytes.ensureCapacity(Longs.toByteArray(timestamp), TimestampLength, 0)

    val amountBytes = Bytes.ensureCapacity(Longs.toByteArray(amount), AmountLength, 0)

    val rcpBytes = recipient.bytes
    require(rcpBytes.length == Account.AddressLength)

    val res = Bytes.concat(typeBytes, timestampBytes, rcpBytes, amountBytes)
    require(res.length == dataLength)
    res
  }

  override lazy val dataLength = TypeLength + BASE_LENGTH

  override lazy val signatureValid: Boolean = {
    val typeBytes = Array(TransactionType.GenesisTransaction.id.toByte)
    val timestampBytes = Bytes.ensureCapacity(Longs.toByteArray(timestamp), TimestampLength, 0)
    val amountBytes = Bytes.ensureCapacity(Longs.toByteArray(amount), AmountLength, 0)
    val data = Bytes.concat(typeBytes, timestampBytes, recipient.bytes, amountBytes)

    val h = hash(data)
    Bytes.concat(h, h).sameElements(signature)
  }

  override def validate: ValidationResult.Value =
    if (amount < 0) {
      ValidationResult.NegativeAmount
    } else if (!Account.isValid(recipient)) {
      ValidationResult.InvalidAddress
    } else ValidationResult.ValidateOke

  override def involvedAmount(account: Account): Long = if (recipient.address.equals(account.address)) amount else 0

  override def balanceChanges(): Seq[BalanceChange] = Seq(BalanceChange(AssetAcc(recipient, None), amount))
}


object GenesisTransaction extends Deser[GenesisTransaction] {

  import scorex.transaction.LagonakiTransaction._

  private val RECIPIENT_LENGTH = Account.AddressLength
  private val BASE_LENGTH = TimestampLength + RECIPIENT_LENGTH + AmountLength

  def generateSignature(recipient: Account, amount: Long, timestamp: Long): Array[Byte] = {
    val typeBytes = Bytes.ensureCapacity(Ints.toByteArray(TransactionType.GenesisTransaction.id), TypeLength, 0)
    val timestampBytes = Bytes.ensureCapacity(Longs.toByteArray(timestamp), TimestampLength, 0)
    val amountBytes = Longs.toByteArray(amount)
    val amountFill = new Array[Byte](AmountLength - amountBytes.length)

    val data = Bytes.concat(typeBytes, timestampBytes, recipient.bytes, Bytes.concat(amountFill, amountBytes))

    val h = hash(data)
    Bytes.concat(h, h)
  }

  def parseBytes(data: Array[Byte]): Try[GenesisTransaction] = {
    data.head match {
      case transactionType: Byte if transactionType == TransactionType.GenesisTransaction.id =>
        parseTail(data.tail)
      case transactionType =>
        Failure(new Exception(s"Incorrect transaction type '$transactionType' in GenesisTransaction data"))
    }
  }

  def parseTail(data: Array[Byte]): Try[GenesisTransaction] = Try {
    require(data.length >= BASE_LENGTH, "Data does not match base length")

    var position = 0

    val timestampBytes = java.util.Arrays.copyOfRange(data, position, position + TimestampLength)
    val timestamp = Longs.fromByteArray(timestampBytes)
    position += TimestampLength

    val recipientBytes = java.util.Arrays.copyOfRange(data, position, position + RECIPIENT_LENGTH)
    val recipient = new Account(Base58.encode(recipientBytes))
    position += RECIPIENT_LENGTH

    val amountBytes = java.util.Arrays.copyOfRange(data, position, position + AmountLength)
    val amount = Longs.fromByteArray(amountBytes)

    GenesisTransaction(recipient, amount, timestamp)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy