Skip to content
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## vNext

- [BREAKING][forge_operation]: error returns changed from `{:error, String.t()}` to `{:error, {:missing_keys, [String.t()]}}` (affects `validate_required_keys/3`, `operation/1`, `operation_group/1` and all per-operation builders)
- [BREAKING][fee]: `calculate_fee/3` error shape changed accordingly (propagated from `forge_operation`)
- [BREAKING][rpc]: unified all error returns to `{:error, {reason, detail}}` — transport errors are wrapped (`:transport`, `:http_status`, `:decode`); preapply returns `{:preapply_failed, errors}` / `{:unexpected_response, body}`; `fill_operation_fee/3` now returns `{:ok, operation} | {:error, _}` (previously crashed on fee errors); `get_counter_for_account/2` now returns `{:ok, integer()} | {:error, _}`; `get_balance/2` returns `{:error, {:invalid_balance, _}}` instead of raising on a non-integer body; `get_block_at_offset/2` returns `{:error, _}` instead of raising on a transport error, and now short-circuits to the head block for `offset <= 0`
- [BREAKING][crypto/bls]: drop `is_` prefix from predicates (`is_zero?` → `zero?`, `is_one?` → `one?`, `is_infinity?` → `infinity?`) across `Fq`, `Fr`, `Fq2`, `Fq12`, `FqP`, `G1`, `G2`
- [BREAKING][crypto/bls]: `Fq12.inv/1` and `FqP.inv/1` now return `{:ok, t} | {:error, :not_invertible}`
- [BREAKING][crypto/bls]: `Fr.from_bytes/1` rejects out-of-range scalars with `:out_of_range` instead of silently reducing
Expand Down
2 changes: 1 addition & 1 deletion lib/fee.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Tezex.Fee do
@spec calculate_fee(map(), pos_integer(),
extra_size: pos_integer(),
minimal_nanotez_per_gas_unit: pos_integer()
) :: {:ok, pos_integer()} | {:error, nonempty_binary()}
) :: {:ok, pos_integer()} | {:error, ForgeOperation.error_reason()}
def calculate_fee(content, consumed_gas, opts \\ []) do
extra_size = Keyword.get(opts, :extra_size, @extra_size)

Expand Down
37 changes: 19 additions & 18 deletions lib/forge_operation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ defmodule Tezex.ForgeOperation do

alias Tezex.Forge

@type error_reason() :: {:missing_keys, [String.t()]}

@operation_tags %{
"endorsement" => 0,
"endorsement_with_slot" => 10,
Expand Down Expand Up @@ -57,7 +59,7 @@ defmodule Tezex.ForgeOperation do
<<Map.fetch!(@operation_tags, kind)::size(8)>>
end

@spec operation(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec operation(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def operation(content) do
case content["kind"] do
"failing_noop" -> failing_noop(content)
Expand All @@ -77,7 +79,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(branch contents)
@spec operation_group(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec operation_group(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def operation_group(operation_group) do
with :ok <- validate_required_keys(operation_group, @keys),
operations = Enum.map(operation_group["contents"], &operation/1),
Expand All @@ -97,7 +99,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind pkh secret)
@spec activate_account(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec activate_account(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def activate_account(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -113,7 +115,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit public_key)
@spec reveal(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec reveal(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def reveal(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand Down Expand Up @@ -141,7 +143,7 @@ defmodule Tezex.ForgeOperation do

@keys ~w(kind source fee counter gas_limit storage_limit amount destination)
@params ~w(parameters parameters.entrypoint parameters.value)
@spec transaction(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec transaction(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def transaction(content) do
with :ok <- validate_required_keys(content, @keys),
:ok <-
Expand Down Expand Up @@ -178,7 +180,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit balance)
@spec origination(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec origination(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def origination(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand Down Expand Up @@ -206,7 +208,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit)
@spec delegation(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec delegation(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def delegation(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -232,7 +234,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind level)
@spec endorsement(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec endorsement(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def endorsement(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -247,7 +249,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(branch operations operations.kind operations.level signature)
@spec inline_endorsement(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec inline_endorsement(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def inline_endorsement(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -264,7 +266,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind endorsement slot)
@spec endorsement_with_slot(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec endorsement_with_slot(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def endorsement_with_slot(content) do
with :ok <- validate_required_keys(content, @keys),
{:ok, endorsement} <- inline_endorsement(content["endorsement"]) do
Expand All @@ -281,7 +283,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind arbitrary)
@spec failing_noop(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec failing_noop(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def failing_noop(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -296,7 +298,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit value)
@spec register_global_constant(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec register_global_constant(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def register_global_constant(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -316,7 +318,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit ticket_contents ticket_ty ticket_ticketer ticket_amount destination entrypoint)
@spec transfer_ticket(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec transfer_ticket(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def transfer_ticket(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -341,7 +343,7 @@ defmodule Tezex.ForgeOperation do
end

@keys ~w(kind source fee counter gas_limit storage_limit message)
@spec smart_rollup_add_messages(map()) :: {:ok, nonempty_binary()} | {:error, nonempty_binary()}
@spec smart_rollup_add_messages(map()) :: {:ok, nonempty_binary()} | {:error, error_reason()}
def smart_rollup_add_messages(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -367,7 +369,7 @@ defmodule Tezex.ForgeOperation do

@keys ~w(kind source fee counter gas_limit storage_limit rollup cemented_commitment output_proof)
@spec smart_rollup_execute_outbox_message(map()) ::
{:ok, nonempty_binary()} | {:error, nonempty_binary()}
{:ok, nonempty_binary()} | {:error, error_reason()}
def smart_rollup_execute_outbox_message(content) do
with :ok <- validate_required_keys(content, @keys) do
content =
Expand All @@ -388,7 +390,7 @@ defmodule Tezex.ForgeOperation do
end
end

@spec validate_required_keys(map(), list()) :: :ok | {:error, nonempty_binary()}
@spec validate_required_keys(map(), list()) :: :ok | {:error, error_reason()}
def validate_required_keys(map, required_keys, acc \\ "")
when is_map(map) and is_list(required_keys) do
required_keys = Enum.group_by(required_keys, &String.contains?(&1, "."))
Expand Down Expand Up @@ -421,8 +423,7 @@ defmodule Tezex.ForgeOperation do
end)
end
else
{:error,
"Operation content is missing required keys: #{acc}#{Enum.join(missing_keys, ", ")}"}
{:error, {:missing_keys, Enum.map(missing_keys, &(acc <> &1))}}
end
end
end
Loading
Loading