defmodule BitcoinLib.Signing.Psbt.Output do @moduledoc """ An output in a partially signed bitcoin transaction """ defstruct unknowns: [] alias BitcoinLib.Signing.Psbt.{Keypair, KeypairList, Output} alias BitcoinLib.Signing.Psbt.GenericProperties.{ Bip32Derivation, Proprietary, RedeemScript, WitnessScript } @redeem_script 0x0 @witness_script 0x1 @bip32_derivation 0x2 @proprietary 0xFC # TODO: document def from_keypair_list(nil) do nil end # TODO: document def from_keypair_list(%KeypairList{} = keypair_list) do keypair_list.keypairs |> validate_keys |> dispatch_keypairs end defp validate_keys(keypairs) do keys = keypairs |> Enum.map(& &1.key) duplicate_keys = keys -- Enum.uniq(keys) case duplicate_keys do [] -> {:ok, keypairs} duplicates -> {:error, "duplicate keys: #{inspect(duplicates)}"} end end defp dispatch_keypairs({:error, message}) do %{error: message} end defp dispatch_keypairs({:ok, keypairs}) do keypairs |> Enum.reduce(%Output{}, &dispatch_keypair/2) end defp dispatch_keypair(%Keypair{key: key} = keypair, output) do case key.type do @redeem_script -> add_redeem_script(output, keypair) @witness_script -> add_witness_script(output, keypair) @bip32_derivation -> add_bip32_derivation(output, keypair) @proprietary -> add_proprietary(output, keypair) _ -> add_unknown(output, keypair) end end defp add_redeem_script(output, keypair) do case RedeemScript.parse(keypair) do {:ok, redeem_script} -> output |> Map.put(:redeem_script, redeem_script) {:error, message} -> output |> Map.put(:error, message) end end defp add_witness_script(output, keypair) do case WitnessScript.parse(keypair) do {:ok, witness_script} -> output |> Map.put(:witness_script, witness_script.script) {:error, message} -> output |> Map.put(:error, message) end end defp add_bip32_derivation(output, keypair) do bip32_derivation = Bip32Derivation.parse(keypair) case Map.get(bip32_derivation, :error) do nil -> output |> Map.put(:bip32_derivation, bip32_derivation) message -> output |> Map.put(:error, message) end end defp add_proprietary(%{error: _message} = output, _keypair), do: output defp add_proprietary(output, keypair) do output |> Map.put(:proprietary, Proprietary.parse(keypair)) end defp add_unknown(output, keypair) do output |> Map.put(:unknowns, [keypair | output.unknowns]) end end