<commit_before>package org.bitcoins.testkit.core.gen

import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
import org.bitcoins.core.number.{Int32, Int64, UInt32}
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{
  TransactionInput,
  TransactionOutPoint,
  TransactionOutput,
  _
}
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.core.script.locktime.LockTimeInterpreter
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.testkit.Implicits._
import org.scalacheck.Gen

import scala.annotation.tailrec

object TransactionGenerators extends BitcoinSLogger {

  /** Responsible for generating [[org.bitcoins.core.protocol.transaction.TransactionOutPoint TransactionOutPoint]] */
  def outPoint: Gen[TransactionOutPoint] =
    for {
      txId <- CryptoGenerators.doubleSha256Digest
      vout <- NumberGenerator.uInt32s
    } yield TransactionOutPoint(txId, vout)

  /** Generates a random TX output paying to the given SPK */
  def outputTo(spk: ScriptPubKey): Gen[TransactionOutput] =
    for {
      satoshis <- CurrencyUnitGenerator.positiveRealistic
    } yield TransactionOutput(satoshis, spk)

  /** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionOutput TransactionOutput]] */
  def output: Gen[TransactionOutput] =
    for {
      satoshis <- CurrencyUnitGenerator.satoshis
      (scriptPubKey, _) <- ScriptGenerators.scriptPubKey
    } yield TransactionOutput(satoshis, scriptPubKey)

  def outputs = Gen.listOf(output)

  /**
    * Outputs that only have a positive amount of satoshis, techinically the bitcoin protocol allows you
    * to have negative value outputs
    */
  def realisticOutput: Gen[TransactionOutput] =
    CurrencyUnitGenerator.positiveRealistic.flatMap { amt =>
      ScriptGenerators.scriptPubKey.map(spk => TransactionOutput(amt, spk._1))
    }

  def realisticOutputs: Gen[Seq[TransactionOutput]] =
    Gen.choose(0, 5).flatMap(n => Gen.listOfN(n, realisticOutput))

  /** Generates a small list of TX outputs paying to the given SPK */
  def smallOutputsTo(spk: ScriptPubKey): Gen[Seq[TransactionOutput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, outputTo(spk)))

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.TransactionOutput TransactionOutput]] */
  def smallOutputs: Gen[Seq[TransactionOutput]] =
    Gen.choose(0, 5).flatMap(i => Gen.listOfN(i, output))

  /** Creates a small sequence of outputs whose total sum is <= totalAmount */
  def smallOutputs(totalAmount: CurrencyUnit): Gen[Seq[TransactionOutput]] = {
    val numOutputs = Gen.choose(0, 5).sampleSome
    @tailrec
    def loop(
        remaining: Int,
        remainingAmount: CurrencyUnit,
        accum: Seq[CurrencyUnit]): Seq[CurrencyUnit] = {
      if (remaining <= 0) {
        accum
      } else {
        val amt = Gen
          .choose(100, remainingAmount.toBigDecimal.toLongExact)
          .map(n => Satoshis(Int64(n)))
          .sampleSome
        loop(remaining - 1, remainingAmount - amt, amt +: accum)
      }
    }
    val amts = loop(numOutputs, totalAmount, Nil)
    val spks = Gen.listOfN(numOutputs, ScriptGenerators.scriptPubKey.map(_._1))
    spks.flatMap { s =>
      s.zip(amts).map {
        case (spk, amt) =>
          TransactionOutput(amt, spk)
      }
    }
  }

  /** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def input: Gen[TransactionInput] =
    for {
      outPoint <- outPoint
      scriptSig <- ScriptGenerators.scriptSignature
      sequenceNumber <- NumberGenerator.uInt32s
      randomNum <- Gen.choose(0, 10)
    } yield {
      if (randomNum == 0) {
        //gives us a coinbase input
        CoinbaseInput(scriptSig, sequenceNumber)
      } else TransactionInput(outPoint, scriptSig, sequenceNumber)
    }

  def inputs: Gen[List[TransactionInput]] = Gen.nonEmptyListOf(input)

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def smallInputs: Gen[Seq[TransactionInput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, input))

  /** Generates a small non empty list of
    * [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def smallInputsNonEmpty: Gen[Seq[TransactionInput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, input))

  /**
    * Generates an arbitrary [[org.bitcoins.core.protocol.transaction.Transaction Transaction]]
    * This transaction's [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]]s
    * will not evaluate to true inside of the
    * [[org.bitcoins.core.script.interpreter.ScriptInterpreter ScriptInterpreter]]
    */
  def transactions: Gen[Seq[Transaction]] = Gen.listOf(transaction)

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.Transaction Transaction]] */
  def smallTransactions: Gen[Seq[Transaction]] =
    Gen.choose(0, 10).flatMap(i => Gen.listOfN(i, transaction))

  def transaction: Gen[Transaction] =
    Gen.oneOf(baseTransaction, witnessTransaction)

  /** Generates a transaction where at least one output pays to the given SPK */
  def transactionTo(spk: ScriptPubKey) =
    Gen.oneOf(baseTransactionTo(spk), witnessTransactionTo(spk))

  /** Generates a transaction with at least one output */
  def nonEmptyOutputTransaction: Gen[Transaction] =
    TransactionGenerators.transaction.suchThat(_.outputs.nonEmpty)

  def baseTransaction: Gen[BaseTransaction] =
    for {
      version <- NumberGenerator.int32s
      is <- smallInputs
      os <- smallOutputs
      lockTime <- NumberGenerator.uInt32s
    } yield BaseTransaction(version, is, os, lockTime)

  /** Generates a legacy transaction with at least one output paying to the given SPK */
  def baseTransactionTo(spk: ScriptPubKey) =
    for {
      version <- NumberGenerator.int32s
      is <- smallInputs
      os <- smallOutputsTo(spk)
      lockTime <- NumberGenerator.uInt32s
    } yield BaseTransaction(version, is, os, lockTime)

  /** To avoid duplicating logic */
  private def witnessTxHelper(
      outputs: Seq[TransactionOutput]): Gen[WitnessTransaction] = {<commit_msg>Add annotated type to public method<commit_after>package org.bitcoins.testkit.core.gen

import org.bitcoins.core.crypto._
import org.bitcoins.core.currency._
import org.bitcoins.core.number.{Int32, Int64, UInt32}
import org.bitcoins.core.policy.Policy
import org.bitcoins.core.protocol.script._
import org.bitcoins.core.protocol.transaction.{
  TransactionInput,
  TransactionOutPoint,
  TransactionOutput,
  _
}
import org.bitcoins.core.script.constant.ScriptNumber
import org.bitcoins.core.script.locktime.LockTimeInterpreter
import org.bitcoins.core.util.BitcoinSLogger
import org.bitcoins.testkit.Implicits._
import org.scalacheck.Gen

import scala.annotation.tailrec

object TransactionGenerators extends BitcoinSLogger {

  /** Responsible for generating [[org.bitcoins.core.protocol.transaction.TransactionOutPoint TransactionOutPoint]] */
  def outPoint: Gen[TransactionOutPoint] =
    for {
      txId <- CryptoGenerators.doubleSha256Digest
      vout <- NumberGenerator.uInt32s
    } yield TransactionOutPoint(txId, vout)

  /** Generates a random TX output paying to the given SPK */
  def outputTo(spk: ScriptPubKey): Gen[TransactionOutput] =
    for {
      satoshis <- CurrencyUnitGenerator.positiveRealistic
    } yield TransactionOutput(satoshis, spk)

  /** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionOutput TransactionOutput]] */
  def output: Gen[TransactionOutput] =
    for {
      satoshis <- CurrencyUnitGenerator.satoshis
      (scriptPubKey, _) <- ScriptGenerators.scriptPubKey
    } yield TransactionOutput(satoshis, scriptPubKey)

  def outputs = Gen.listOf(output)

  /**
    * Outputs that only have a positive amount of satoshis, techinically the bitcoin protocol allows you
    * to have negative value outputs
    */
  def realisticOutput: Gen[TransactionOutput] =
    CurrencyUnitGenerator.positiveRealistic.flatMap { amt =>
      ScriptGenerators.scriptPubKey.map(spk => TransactionOutput(amt, spk._1))
    }

  def realisticOutputs: Gen[Seq[TransactionOutput]] =
    Gen.choose(0, 5).flatMap(n => Gen.listOfN(n, realisticOutput))

  /** Generates a small list of TX outputs paying to the given SPK */
  def smallOutputsTo(spk: ScriptPubKey): Gen[Seq[TransactionOutput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, outputTo(spk)))

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.TransactionOutput TransactionOutput]] */
  def smallOutputs: Gen[Seq[TransactionOutput]] =
    Gen.choose(0, 5).flatMap(i => Gen.listOfN(i, output))

  /** Creates a small sequence of outputs whose total sum is <= totalAmount */
  def smallOutputs(totalAmount: CurrencyUnit): Gen[Seq[TransactionOutput]] = {
    val numOutputs = Gen.choose(0, 5).sampleSome
    @tailrec
    def loop(
        remaining: Int,
        remainingAmount: CurrencyUnit,
        accum: Seq[CurrencyUnit]): Seq[CurrencyUnit] = {
      if (remaining <= 0) {
        accum
      } else {
        val amt = Gen
          .choose(100, remainingAmount.toBigDecimal.toLongExact)
          .map(n => Satoshis(Int64(n)))
          .sampleSome
        loop(remaining - 1, remainingAmount - amt, amt +: accum)
      }
    }
    val amts = loop(numOutputs, totalAmount, Nil)
    val spks = Gen.listOfN(numOutputs, ScriptGenerators.scriptPubKey.map(_._1))
    spks.flatMap { s =>
      s.zip(amts).map {
        case (spk, amt) =>
          TransactionOutput(amt, spk)
      }
    }
  }

  /** Generates a random [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def input: Gen[TransactionInput] =
    for {
      outPoint <- outPoint
      scriptSig <- ScriptGenerators.scriptSignature
      sequenceNumber <- NumberGenerator.uInt32s
      randomNum <- Gen.choose(0, 10)
    } yield {
      if (randomNum == 0) {
        //gives us a coinbase input
        CoinbaseInput(scriptSig, sequenceNumber)
      } else TransactionInput(outPoint, scriptSig, sequenceNumber)
    }

  def inputs: Gen[List[TransactionInput]] = Gen.nonEmptyListOf(input)

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def smallInputs: Gen[Seq[TransactionInput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, input))

  /** Generates a small non empty list of
    * [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]] */
  def smallInputsNonEmpty: Gen[Seq[TransactionInput]] =
    Gen.choose(1, 5).flatMap(i => Gen.listOfN(i, input))

  /**
    * Generates an arbitrary [[org.bitcoins.core.protocol.transaction.Transaction Transaction]]
    * This transaction's [[org.bitcoins.core.protocol.transaction.TransactionInput TransactionInput]]s
    * will not evaluate to true inside of the
    * [[org.bitcoins.core.script.interpreter.ScriptInterpreter ScriptInterpreter]]
    */
  def transactions: Gen[Seq[Transaction]] = Gen.listOf(transaction)

  /** Generates a small list of [[org.bitcoins.core.protocol.transaction.Transaction Transaction]] */
  def smallTransactions: Gen[Seq[Transaction]] =
    Gen.choose(0, 10).flatMap(i => Gen.listOfN(i, transaction))

  def transaction: Gen[Transaction] =
    Gen.oneOf(baseTransaction, witnessTransaction)

  /** Generates a transaction where at least one output pays to the given SPK */
  def transactionTo(spk: ScriptPubKey) =
    Gen.oneOf(baseTransactionTo(spk), witnessTransactionTo(spk))

  /** Generates a transaction with at least one output */
  def nonEmptyOutputTransaction: Gen[Transaction] =
    TransactionGenerators.transaction.suchThat(_.outputs.nonEmpty)

  def baseTransaction: Gen[BaseTransaction] =
    for {
      version <- NumberGenerator.int32s
      is <- smallInputs
      os <- smallOutputs
      lockTime <- NumberGenerator.uInt32s
    } yield BaseTransaction(version, is, os, lockTime)

  /** Generates a legacy transaction with at least one output paying to the given SPK */
  def baseTransactionTo(spk: ScriptPubKey): Gen[BaseTransaction] =
    for {
      version <- NumberGenerator.int32s
      is <- smallInputs
      os <- smallOutputsTo(spk)
      lockTime <- NumberGenerator.uInt32s
    } yield BaseTransaction(version, is, os, lockTime)

  /** To avoid duplicating logic */
  private def witnessTxHelper(
      outputs: Seq[TransactionOutput]): Gen[WitnessTransaction] = {
{'fraction_non_alphanumeric': 0.10805422647527911, 'fraction_numerical': 0.00781499202551834, 'mean_word_length': 3.784814956123617, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4943606', 'n_tokens_mistral': 3855, 'n_tokens_neox': 3672, 'n_words': 848}