defmodule BitcoinLib.Signing.Psbt.Global do
defstruct [:unsigned_tx, tx_version: 0, xpubs: [], unknowns: []]
alias BitcoinLib.Signing.Psbt.GenericProperties.{Proprietary}
alias BitcoinLib.Signing.Psbt.Global.{Xpub, UnsignedTx, Version}
alias BitcoinLib.Signing.Psbt.{Keypair, KeypairList, Global}
@unsigned_tx 0
@xpub 1
@tx_version 2
@version 0xFB
@proprietary 0xFC
# TODO: document
def from_keypair_list(nil) do
{:ok, %Global{}}
end
# TODO: document
def from_keypair_list(%KeypairList{} = keypair_list) do
data =
keypair_list.keypairs
|> Enum.reduce(%{xpubs: [], unknowns: []}, &dispatch_keypair/2)
case Map.get(data, :error) do
nil ->
{:ok,
%Global{
unsigned_tx: data |> Map.get(:unsigned_tx),
tx_version: data |> Map.get(:tx_version),
xpubs: data |> Map.get(:xpubs),
unknowns: data |> Map.get(:unknowns)
}}
message ->
{:error, message}
end
end
defp dispatch_keypair(%Keypair{key: key, value: value} = keypair, input) do
case key.type do
@unsigned_tx -> add_unsigned_tx(input, keypair)
@xpub -> add_xpub(input, value)
@tx_version -> add_tx_version(input, value)
@version -> add_version(input, value)
@proprietary -> add_proprietary(input, keypair)
_ -> add_unknown(input, key, value)
end
end
defp add_unsigned_tx(input, keypair) do
case UnsignedTx.parse(keypair) do
{:ok, unsigned_tx} ->
input
|> Map.put(:unsigned_tx, unsigned_tx)
{:error, message} ->
input
|> Map.put(:error, message)
end
end
defp add_xpub(%{xpubs: xpubs} = input, value) do
{new_xpub, _remaining} = Xpub.parse(value.data)
input
|> Map.put(:xpubs, [new_xpub | xpubs])
end
defp add_tx_version(input, value) do
input
|> Map.put(:tx_version, value.data)
end
defp add_version(input, value) do
version = Version.parse(value.data)
case Map.get(version, :error) do
nil ->
input
|> Map.put(:version, version)
message ->
input
|> Map.put(:error, message)
end
end
defp add_proprietary(%{error: _message} = output, _keypair), do: output
defp add_proprietary(input, keypair) do
input
|> Map.put(:proprietary, Proprietary.parse(keypair))
end
defp add_unknown(input, key, value) do
input
|> Map.put(:unknowns, [{key, value} | input.unknowns])
end
end