lib/transaction/validator.ex

defmodule BitcoinLib.Transaction.Validator do
  @moduledoc """
  Making sure transactions are valid
  """

  alias BitcoinLib.Console
  # alias BitcoinLib.Key.PublicKey
  alias BitcoinLib.Transaction
  # alias BitcoinLib.Transaction.{Input, Output}
  # alias BitcoinLib.Script
  alias BitcoinLib.Transaction.Validator.{Input, Preimage}
  alias BitcoinLib.Transaction.Validator.Input, as: ValidatorInput

  #  alias BitcoinLib.Console

  # ############ from signer, works right after creating the signature
  # valid? =
  #   signature
  #   |> PublicKey.validate_signature(hashed_preimage, public_key)
  #   |> IO.inspect(label: "is signature valid?")
  # ###########

  @doc """
  Validates a transaction

  ## Data from the signer's doctest
  # transaction before signing: <<0x0100000001b62ba991789fb1739e6a17b3891fd94cfebf09a61fedb203d619932a4326c2e4000000001976a914afc3e518577316386188af748a816cd14ce333f288acffffffff0110270000000000002d76a9283265393261373463333431393661303236653839653061643561633431386366393430613361663288ac00000000::1040>>
  # transaction after signing: <<0x0100000001b62ba991789fb1739e6a17b3891fd94cfebf09a61fedb203d619932a4326c2e4000000006a47304402207d2ff650acf4bd2f413dc04ded50fbbfc315bcb0aa97636b3c4caf55333d1c6a02207590f62363b2263b3d9b65dad3cd56e840e0d61dc0feab8f7e7956831c7e5103012102702ded1cca9816fa1a94787ffc6f3ace62cd3b63164f76d227d0935a33ee48c3ffffffff0110270000000000002d76a9283265393261373463333431393661303236653839653061643561633431386366393430613361663288ac00000000::1688>>
  # public_key: <<0x02702ded1cca9816fa1a94787ffc6f3ace62cd3b63164f76d227d0935a33ee48c3::264>>
  # signature: <<0x304402207d2ff650acf4bd2f413dc04ded50fbbfc315bcb0aa97636b3c4caf55333d1c6a02207590f62363b2263b3d9b65dad3cd56e840e0d61dc0feab8f7e7956831c7e5103::560>>
  # sighash_type: 1
  # preimage: <<0xee564327e1868240a2bbe3d3b445ac946a396de9802ede1ecaff0024f77eeca3::256>>

  #### STEPS
  # √ 1- decode transaction
  # √ 2- extract signature
  # √ 3- recreate preimage
  #   4- validate input

  ## Examples
      iex>  "0100000001b62ba991789fb1739e6a17b3891fd94cfebf09a61fedb203d619932a4326c2e4000000006a47304402207d2ff650acf4bd2f413dc04ded50fbbfc315bcb0aa97636b3c4caf55333d1c6a02207590f62363b2263b3d9b65dad3cd56e840e0d61dc0feab8f7e7956831c7e5103012102702ded1cca9816fa1a94787ffc6f3ace62cd3b63164f76d227d0935a33ee48c3ffffffff0110270000000000002d76a9283265393261373463333431393661303236653839653061643561633431386366393430613361663288ac00000000"
      ...>  |> BitcoinLib.Transaction.Validator.verify(prevouts, public_key)
      true
  """
  @spec verify(binary(), (binary() -> Transaction.t())) :: boolean()
  def verify(txid, get_transaction_by_id) do
    with {:ok, %Transaction{inputs: inputs} = transaction} <- get_transaction_by_id.(txid) do
      [{:ok, validator_input}] =
        inputs
        |> Enum.map(&Input.validate(&1, get_transaction_by_id))

      add_preimages(validator_input, transaction)

      #         {:ok, prevouts} <- Prevout.from_transaction(transaction, get_transaction_by_id) do
      ## {:ok, signatures} <- Signatures.from_transaction(transaction)
      ## [%{der_signature: der_signature, sighash_type: sighash_type}] = signatures
      ## preimage = Preimage.from_transaction(transaction, sighash_type)

      #      IO.inspect(prevouts, label: "prevouts")
      ## Console.print_hex(der_signature, "der_signature")
      ## Console.print_hex(preimage, "preimage")
    else
      {:error, message} -> {:error, message}
    end

    # signatures = get_signatures(transaction)

    # Enum.zip_with(signatures, prevouts, &merge_signatures_and_prevouts/2)
    # |> preimage(transaction)

    true
  end

  def add_preimages(
        %ValidatorInput{type: _type, der_signature: _der_signature, sighash_type: sighash_type},
        %Transaction{} = transaction
      ) do
    preimage = Preimage.from_transaction(transaction, sighash_type)

    Console.print_hex(preimage, "preimage")
  end
end