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

org.alephium.api.model.BuildTxCommon.scala Maven / Gradle / Ivy

The newest version!
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see .

package org.alephium.api.model

import akka.util.ByteString

import org.alephium.api.{badRequest, Try}
import org.alephium.crypto.BIP340SchnorrPublicKey
import org.alephium.protocol.PublicKey
import org.alephium.protocol.model.{Address, BlockHash, SchnorrAddress, TokenId, TransactionId}
import org.alephium.protocol.vm.{GasBox, GasPrice, LockupScript, UnlockScript}
import org.alephium.util.{AVector, Hex, U256}

trait BuildTxCommon {
  def gasAmount: Option[GasBox]

  def gasPrice: Option[GasPrice]

  def targetBlockHash: Option[BlockHash]
}

object BuildTxCommon {
  sealed trait PublicKeyType
  object Default       extends PublicKeyType // SecP256K1
  object BIP340Schnorr extends PublicKeyType

  trait FromPublicKey {
    def fromPublicKey: ByteString
    def fromPublicKeyType: Option[PublicKeyType]

    def getLockPair(): Try[(LockupScript.Asset, UnlockScript)] = fromPublicKeyType match {
      case Some(BuildTxCommon.BIP340Schnorr) => schnorrLockPair(fromPublicKey)
      case _                                 => p2pkhLockPair(fromPublicKey)
    }
  }

  def p2pkhLockPair(fromPublicKey: ByteString): Try[(LockupScript.Asset, UnlockScript)] = {
    PublicKey.from(fromPublicKey) match {
      case Some(publicKey) =>
        Right(LockupScript.p2pkh(publicKey) -> UnlockScript.p2pkh(publicKey))
      case None =>
        Left(badRequest(s"Invalid SecP256K1 public key: ${Hex.toHexString(fromPublicKey)}"))
    }
  }

  def schnorrLockPair(fromPublicKey: ByteString): Try[(LockupScript.Asset, UnlockScript)] = {
    BIP340SchnorrPublicKey.from(fromPublicKey) match {
      case Some(publicKey) =>
        val address = SchnorrAddress(publicKey)
        Right(address.lockupScript -> address.unlockScript)
      case None =>
        Left(badRequest(s"Invalid BIP340Schnorr public key: ${Hex.toHexString(fromPublicKey)}"))
    }
  }

  def getAlphAndTokenAmounts(
      attoAlphAmount: Option[Amount],
      tokensAmount: Option[AVector[Token]]
  ): Either[String, (Option[U256], AVector[(TokenId, U256)])] = {
    val alphAmountOpt = attoAlphAmount.map(_.value)
    tokensAmount match {
      case None => Right((alphAmountOpt, AVector.empty))
      case Some(tokens) =>
        val amounts = tokens.foldE((alphAmountOpt, Map.empty[TokenId, U256])) {
          case ((Some(alphAmount), tokenList), Token(TokenId.alph, tokenAmount)) =>
            alphAmount
              .add(tokenAmount)
              .toRight("ALPH amount overflow")
              .map(v => (Some(v), tokenList))
          case ((None, tokenList), Token(TokenId.alph, tokenAmount)) =>
            Right((Some(tokenAmount), tokenList))
          case ((alphOpt, tokenList), Token(tokenId, tokenAmount)) =>
            if (tokenList.contains(tokenId)) {
              tokenList(tokenId)
                .add(tokenAmount)
                .toRight(s"Token $tokenId amount overflow")
                .map(v => (alphOpt, tokenList + (tokenId -> v)))
            } else {
              Right((alphOpt, tokenList + (tokenId -> tokenAmount)))
            }
        }
        amounts.map(v => (v._1, AVector.from(v._2.view.filter(_._2.nonZero))))
    }
  }

  def getTokenIssuanceInfo(
      issueTokenAmount: Option[Amount],
      issueTokenTo: Option[Address.Asset]
  ): Either[String, Option[(U256, Option[Address.Asset])]] = {
    (issueTokenAmount, issueTokenTo) match {
      case (None, Some(_)) =>
        Left("`issueTokenTo` is specified, but `issueTokenAmount` is not specified")
      case _ =>
        Right(issueTokenAmount.map { amount =>
          (amount.value, issueTokenTo)

        })
    }
  }
}

trait GasInfo {
  def gasAmount: GasBox

  def gasPrice: GasPrice

  def gasFee: U256 = gasPrice * gasAmount
}

trait TransactionInfo {
  def txId: TransactionId

  def unsignedTx: String
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy