diff --git a/integration-tests/nut05.rs b/integration-tests/nut05.rs index f21f0df..07c64f9 100644 --- a/integration-tests/nut05.rs +++ b/integration-tests/nut05.rs @@ -23,11 +23,7 @@ pub async fn melt_quote_ok() { assert_matches!( melt_quote, MeltQuote { -<<<<<<< HEAD ref id, -======= - id, ->>>>>>> b96d0cf (test: add integration test for 05 mint quote) unit: _, amount, request: _, diff --git a/lib/cashubrew/NUTs/NUT-04/impl.ex b/lib/cashubrew/NUTs/NUT-04/impl.ex index 4acf47b..e3d62b4 100644 --- a/lib/cashubrew/NUTs/NUT-04/impl.ex +++ b/lib/cashubrew/NUTs/NUT-04/impl.ex @@ -8,15 +8,18 @@ defmodule Cashubrew.Nuts.Nut04.Impl do alias Cashubrew.Schema def create_mint_quote!(amount, unit, description) do + # Todo: sanitize earlier? + # amount = if is_integer(amount), do: amount, else: String.to_integer(amount) + repo = Application.get_env(:cashubrew, :repo) + lnd_client = Application.get_env(:cashubrew, :lnd_client) - {payment_request, _payment_hash} = - Cashubrew.LightingNetwork.Lnd.create_invoice!(amount, unit, description) + {:ok, payment_request} = lnd_client.create_invoice!(amount, unit, description) # Note: quote is a unique and random id generated by the mint to internally look up the payment state. # quote MUST remain a secret between user and mint and MUST NOT be derivable from the payment request. # A third party who knows the quote ID can front-run and steal the tokens that this operation mints. - quote_id = Ecto.UUID.bingenerate() + quote_id = :os.system_time(:millisecond) # 1 hour expiry expiry = :os.system_time(:second) + 3600 @@ -24,6 +27,8 @@ defmodule Cashubrew.Nuts.Nut04.Impl do Schema.MintQuote.create!(repo, %{ id: quote_id, payment_request: payment_request, + amount: amount, + unit: unit, expiry: expiry, # Unpaid state: <<0>> diff --git a/lib/cashubrew/NUTs/NUT-04/serde.ex b/lib/cashubrew/NUTs/NUT-04/serde.ex index cf4bdd1..bd62833 100644 --- a/lib/cashubrew/NUTs/NUT-04/serde.ex +++ b/lib/cashubrew/NUTs/NUT-04/serde.ex @@ -36,5 +36,6 @@ defmodule Cashubrew.Nuts.Nut04.Serde.PostMintBolt11Response do The body of the post mint response """ @enforce_keys [:signatures] + @derive [Jason.Encoder] defstruct [:signatures] end diff --git a/lib/cashubrew/schema/mint_quote.ex b/lib/cashubrew/schema/mint_quote.ex index 2ddfe09..fa113d3 100644 --- a/lib/cashubrew/schema/mint_quote.ex +++ b/lib/cashubrew/schema/mint_quote.ex @@ -5,7 +5,7 @@ defmodule Cashubrew.Schema.MintQuote do use Ecto.Schema import Ecto.Changeset - @primary_key {:id, :binary_id, autogenerate: false} + @primary_key {:id, :id, autogenerate: false} schema "mint_quotes" do field(:payment_request, :string) field(:amount, :integer) @@ -23,7 +23,7 @@ defmodule Cashubrew.Schema.MintQuote do def changeset(quote, attrs) do quote |> cast(attrs, [:id, :payment_request, :amount, :unit, :expiry, :state]) - |> validate_required([:id, :payment_request, :amout, :unit, :expiry, :state]) + |> validate_required([:id, :payment_request, :amount, :unit, :expiry, :state]) |> validate_inclusion(:state, [<<0>>, <<1>>, <<2>>, <<128>>, <<129>>, <<130>>]) end diff --git a/lib/cashubrew/web/controllers/mint_controller.ex b/lib/cashubrew/web/controllers/mint_controller.ex index 67a74c2..5b0f522 100644 --- a/lib/cashubrew/web/controllers/mint_controller.ex +++ b/lib/cashubrew/web/controllers/mint_controller.ex @@ -50,23 +50,30 @@ defmodule Cashubrew.Web.MintController do e in RuntimeError -> conn |> put_status(:bad_request) |> json(Nut00.Error.new_error(0, e)) end - def create_mint_quote(conn, params) do - method = params["method"] - + def create_mint_quote(conn, %{ + "method" => method, + "amount" => amount, + "unit" => unit, + "description" => description + }) do if method != "bolt11" do raise "UnsuportedMethod" end - %Nut04.Serde.PostMintQuoteBolt11Request{ - amount: amount, - unit: unit, - description: description - } = params["body"] - res = Nut04.Impl.create_mint_quote!(amount, unit, description) - json(conn, struct(Nut04.Serde.PostMintBolt11Response, Map.put(res, :state, "UNPAID"))) + + json( + conn, + struct( + Nut04.Serde.PostMintBolt11Response, + Map.merge(res, %{signatures: [], state: "UNPAID"}) + ) + ) rescue - e in RuntimeError -> conn |> put_status(:bad_request) |> json(Nut00.Error.new_error(0, e)) + e in RuntimeError -> + conn + |> put_status(:bad_request) + |> json(%{error: e.message}) end def get_mint_quote(conn, %{"quote_id" => quote_id, "method" => method}) do diff --git a/priv/repo/migrations/20240918113122_create_mint_quote.exs b/priv/repo/migrations/20240918113122_create_mint_quote.exs index d774884..f66606b 100644 --- a/priv/repo/migrations/20240918113122_create_mint_quote.exs +++ b/priv/repo/migrations/20240918113122_create_mint_quote.exs @@ -5,10 +5,11 @@ defmodule Cashubrew.Repo.Migrations.CreateMintQuote do create table(:mint_quotes) do add :amount, :integer, null: false add :payment_request, :text, null: false - add :state, :string, default: "UNPAID", null: false + add :state, :binary, null: false, default: fragment("decode('00', 'hex')") add :expiry, :integer, null: false add :description, :string add :payment_hash, :string + add :unit, :string timestamps() end