Current section
Files
Jump to
Current section
Files
lib/sutra/cardano/transaction.ex
defmodule Sutra.Cardano.Transaction do
@moduledoc ~S"""
Cardano Transaction
"""
alias __MODULE__.Output
alias Sutra.Blake2b
alias Sutra.Cardano.Transaction.Input
alias Sutra.Cardano.Transaction.OutputReference
alias Sutra.Cardano.Transaction.TxBody
alias Sutra.Cardano.Transaction.Witness
alias Sutra.Data.Cbor
use Sutra.Data
use TypedStruct
use Sutra.Data
@type input() :: %Input{
output_reference: OutputReference.t(),
output: Output.t()
}
defmodule Input do
@moduledoc """
Transaction Input
"""
defdata do
data(:output_reference, OutputReference)
data(:output, Output)
end
@doc """
converts cbor encoded hex to Input
"""
def from_hex(raw_hex) when is_binary(raw_hex) do
case Data.decode(raw_hex) do
{:ok, [ref, output]} ->
from_cbor([ref, output])
_ ->
{:error, :INVALID_INPUT_CBOR}
end
end
def to_hex(%Input{output_reference: output_reference, output: output}) do
Cbor.encode_hex([OutputReference.to_cbor(output_reference), Output.to_cbor(output)])
end
def from_cbor([out_ref, output]),
do: %__MODULE__{
output_reference: OutputReference.from_cbor(out_ref),
output: Output.from_cbor(output)
}
def sort_inputs(inputs) when is_list(inputs) do
Enum.sort_by(inputs, & &1.output_reference, {:asc, OutputReference})
end
def extract_ref(%__MODULE__{output_reference: %OutputReference{} = out_ref}),
do: "#{out_ref.transaction_id}##{out_ref.output_index}"
end
typedstruct do
field(:tx_body, TxBody.t())
field(:witnesses, Witness.t())
field(:is_valid, boolean())
field(:metadata, any())
end
@doc """
Generate transaction from hex encoded cbor
iex> from_hex("valid-hex-transaction")
{:ok, %Sutra.Cardano.Transaction{}}
iex> from_hex("some-invalid-hex-transaction")
{:error, :invalid_cbor}
"""
def from_hex(cbor_hex) when is_binary(cbor_hex) do
decoded_cbor =
cbor_hex
|> Base.decode16!(case: :mixed)
|> CBOR.decode()
case decoded_cbor do
{:ok, cbor, _} -> from_cbor(cbor)
{:error, err} -> {:error, err}
end
end
@doc """
Generate transaction from cbor
iex> from_cbor([valid_tx_body, valid_witness_cbor, true, metadata])
%Sutra.Cardano.Transaction{}
"""
def from_cbor([tx_body, witness_cbor, is_valid, metadata])
when is_boolean(is_valid) do
%__MODULE__{
tx_body: TxBody.decode(tx_body),
witnesses: Witness.from_cbor(Cbor.extract_value!(witness_cbor)),
is_valid: is_valid,
metadata: metadata
}
end
def from_cbor(_) do
raise """
Only Conway era transaction supported. Todo: support other eras.
"""
end
@doc """
Convert transaction to hex encoded cbor
iex> to_hex(%Sutra.Cardano.Transaction{})
"some-valid-hex-encoded-cbor"
"""
def to_cbor(%__MODULE__{} = tx) do
tx_body_cbor = TxBody.to_cbor(tx.tx_body)
[tx_body_cbor, Witness.to_cbor(tx.witnesses), tx.is_valid, tx.metadata]
end
@doc """
Get transaction id
iex> tx_id(%Sutra.Cardano.Transaction{})
"88350824a9557e16a8f18b9b3cc4ab7cc0c282c178132083babde3cdb33393ee"
"""
@spec tx_id(__MODULE__.t()) :: Blake2b.blake2b_256()
def tx_id(%__MODULE__{} = tx) do
tx.tx_body
|> TxBody.to_cbor()
|> CBOR.encode()
|> Blake2b.blake2b_256()
end
end