lib/signing/psbt/global/unsigned_tx.ex

defmodule BitcoinLib.Signing.Psbt.Global.UnsignedTx do
  alias BitcoinLib.Transaction

  # TODO: document
  def parse(keypair) do
    keypair
    |> validate_keypair()
    |> decode_transaction()
    |> validate_witness()
  end

  defp validate_keypair(keypair) do
    case keypair.key.data do
      "" ->
        {:ok, keypair}

      value ->
        {:error, "PSBT_GLOBAL_UNSIGNED_TX key shouldn't contain a value (#{inspect(value)})"}
    end
  end

  defp decode_transaction({:error, message}) do
    {:error, message}
  end

  defp decode_transaction({:ok, keypair}) do
    case Transaction.decode(keypair.value.data) do
      {:error, message} ->
        {:error, message}

      {:ok, unsigned_tx, <<>>} ->
        case Transaction.check_if_unsigned(unsigned_tx) do
          true -> {:ok, unsigned_tx}
          false -> {:error, "the supposedly unsigned transaction has already been signed"}
        end
    end
  end

  defp validate_witness({:error, message}), do: {:error, message}

  defp validate_witness({:ok, %Transaction{segwit?: true, witness: witness} = transaction}) do
    case get_non_empty_witness_count(witness) do
      0 -> {:error, "unsigned tx serialized with witness serialization format"}
      _ -> {:ok, transaction}
    end
  end

  defp validate_witness({:ok, transaction}), do: {:ok, transaction}

  defp get_non_empty_witness_count(witness) do
    witness
    |> Enum.concat()
    |> Enum.count()
  end
end