From 5514ff4afcabbaa42c13655a893bd7a34d01ccaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Delabrouille?= <34384633+tdelabro@users.noreply.github.com> Date: Fri, 25 Oct 2024 11:46:10 +0200 Subject: [PATCH] feat: Mint is no longer a GenServer (#39) --- lib/cashubrew/NUTs/NUT-01/serde.ex | 2 - lib/cashubrew/NUTs/NUT-02/impl.ex | 2 +- lib/cashubrew/NUTs/NUT-03/impl.ex | 48 ++++------------------ lib/cashubrew/application.ex | 2 +- lib/cashubrew/core/mint.ex | 65 ++---------------------------- lib/cashubrew/schema/keyset.ex | 2 +- test/nut01_test.exs | 1 - test/nut02_test.exs | 3 +- 8 files changed, 16 insertions(+), 109 deletions(-) diff --git a/lib/cashubrew/NUTs/NUT-01/serde.ex b/lib/cashubrew/NUTs/NUT-01/serde.ex index 759d04f..efad75b 100644 --- a/lib/cashubrew/NUTs/NUT-01/serde.ex +++ b/lib/cashubrew/NUTs/NUT-01/serde.ex @@ -4,7 +4,6 @@ defmodule Cashubrew.Nuts.Nut01.Serde.GetKeysResponse do """ @derive Jason.Encoder @enforce_keys [:keysets] - @derive [Jason.Encoder] defstruct [:keysets] def from_keysets(keysets) do @@ -61,7 +60,6 @@ defmodule Cashubrew.Nuts.Nut01.Serde.Keyset do """ @derive Jason.Encoder @enforce_keys [:id, :unit, :keys] - @derive [Jason.Encoder] defstruct [:id, :unit, :keys] def from_keyset(id, unit, keys) do diff --git a/lib/cashubrew/NUTs/NUT-02/impl.ex b/lib/cashubrew/NUTs/NUT-02/impl.ex index 9e70ee8..4d6715e 100644 --- a/lib/cashubrew/NUTs/NUT-02/impl.ex +++ b/lib/cashubrew/NUTs/NUT-02/impl.ex @@ -6,7 +6,7 @@ defmodule Cashubrew.Nuts.Nut02.Impl do alias Cashubrew.Repo alias Cashubrew.Schema.Keyset - def keysets() do + def keysets do Repo.all(Keyset) end diff --git a/lib/cashubrew/NUTs/NUT-03/impl.ex b/lib/cashubrew/NUTs/NUT-03/impl.ex index 86b5b5e..e53ad0f 100644 --- a/lib/cashubrew/NUTs/NUT-03/impl.ex +++ b/lib/cashubrew/NUTs/NUT-03/impl.ex @@ -6,53 +6,19 @@ defmodule Cashubrew.Nuts.Nut03.Impl do alias Cashubrew.Nuts.Nut00 @spec swap!(Nut00.Proof, Nut00.BlindedMessage) :: Nut00.BlindSignature - def swap!(proofs, blinded_messages) do + def swap!(inputs, outputs) do repo = Application.get_env(:cashubrew, :repo) - if Mint.check_proofs_are_used?(repo, proofs) do - raise "SwapProofIsAlreadyUsed" - end + Mint.Verification.InputsAndOutputs.verify!(repo, inputs, outputs) - if ListHasDuplicates.check?(blinded_messages) do - raise "SwapHasDuplicateOutputs" - end - - {keyset_id, total_amount} = Mint.Verification.Outputs.verify!(repo, blinded_messages) - total_amount_proofs = Enum.reduce(proofs, 0, fn p, acc -> acc + p.amount end) - - signatures = - case Mint.create_blinded_signatures(repo, blinded_messages) do - {:ok, signatures} -> signatures + promises = + case Mint.create_blinded_signatures(repo, outputs) do + {:ok, promises} -> promises {:error, reason} -> raise reason end - total_amount_signatures = Enum.reduce(signatures, 0, fn bm, acc -> acc + bm.amount end) - - if total_amount_proofs != total_amount_signatures do - raise "SwapAmountMismatch" - end - - Mint.register_used_proofs(repo, proofs) - - signatures - end -end - -defmodule ListHasDuplicates do - @moduledoc """ - Util logic to check if list contains duplicates - """ - def check?(list), - do: check?(list, %{}) - - defp check?([], _) do - false - end + Mint.register_used_proofs(repo, inputs) - defp check?([head | tail], set) do - case set do - %{^head => true} -> true - _ -> check?(tail, Map.put(set, head, true)) - end + promises end end diff --git a/lib/cashubrew/application.ex b/lib/cashubrew/application.ex index c369928..fc247fa 100644 --- a/lib/cashubrew/application.ex +++ b/lib/cashubrew/application.ex @@ -20,7 +20,7 @@ defmodule Cashubrew.Application do end # Always add Cashubrew.Mint after the repo - children = children ++ [Cashubrew.Mint] + children = children ++ [{Task, fn -> Cashubrew.Mint.init() end}] opts = [strategy: :one_for_one, name: Cashubrew.Supervisor] Supervisor.start_link(children, opts) diff --git a/lib/cashubrew/core/mint.ex b/lib/cashubrew/core/mint.ex index 65f9c17..dbd3429 100644 --- a/lib/cashubrew/core/mint.ex +++ b/lib/cashubrew/core/mint.ex @@ -3,7 +3,6 @@ defmodule Cashubrew.Mint do Mint operations for the Cashubrew mint. """ - use GenServer require Logger alias Cashubrew.Nuts.Nut00.{BDHKE, BlindSignature} alias Cashubrew.Nuts.Nut02 @@ -13,7 +12,6 @@ defmodule Cashubrew.Mint do alias Cashubrew.Schema.{ Key, MintConfiguration, - MintQuote, UsedProof } @@ -43,22 +41,11 @@ defmodule Cashubrew.Mint do GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end - @impl true - def init(_) do - {:ok, %{mint_pubkey: nil}, {:continue, :load_keysets_and_mint_key}} - end - - @impl true - @spec handle_continue(:load_keysets_and_mint_key, %{ - :mint_pubkey => any(), - optional(any()) => any() - }) :: {:noreply, %{:mint_pubkey => binary(), optional(any()) => any()}} - def handle_continue(:load_keysets_and_mint_key, state) do + def init do repo = Application.get_env(:cashubrew, :repo) seed = get_or_create_seed(repo) _keysets = load_or_create_keysets(repo, seed) - {_, mint_pubkey} = get_or_create_mint_key(repo, seed) - {:noreply, %{state | mint_pubkey: mint_pubkey}} + {_mint_privkey, _mint_pubkey} = get_or_create_mint_key(repo, seed) end defp get_or_create_seed(repo) do @@ -137,50 +124,6 @@ defmodule Cashubrew.Mint do {private_key, public_key} end - @impl true - def handle_call(:get_keysets, _from, state) do - {:reply, state.keysets, state} - end - - @impl true - def handle_call(:get_mint_pubkey, _from, state) do - {:reply, state.mint_pubkey, state} - end - - def handle_call({:mint_tokens, quote_id, blinded_messages}, _from, state) do - repo = Application.get_env(:cashubrew, :repo) - # Get quote from database - quote = repo.get(MintQuote, quote_id) - - # Return error if quote does not exist - if quote do - result = - Ecto.Multi.new() - |> Ecto.Multi.run(:verify_quote, fn _, _ -> - # Implement actual Lightning payment verification - # For now, we mock the payment as if it's been received - {:ok, quote} - end) - |> Ecto.Multi.update(:update_quote, fn %{verify_quote: quote} -> - Ecto.Changeset.change(quote, state: "ISSUED") - end) - |> Ecto.Multi.run(:create_blinded_signatures, fn _, _ -> - create_blinded_signatures(repo, blinded_messages) - end) - |> repo.transaction() - - case result do - {:ok, %{create_blinded_signatures: signatures}} -> - {:reply, {:ok, signatures}, state} - - {:error, _, reason, _} -> - {:reply, {:error, reason}, state} - end - else - {:reply, {:error, :not_found}, state} - end - end - def create_blinded_signatures(repo, blinded_messages) do signatures = Enum.map(blinded_messages, fn bm -> @@ -216,8 +159,8 @@ defmodule Cashubrew.Mint do repo.get_by(Key, keyset_id: keyset_id, amount: amount) end - def get_pubkey do - GenServer.call(__MODULE__, :get_mint_pubkey) + def get_pubkey(repo) do + repo.get(Schema.MintConfiguration, mint_pubkey_key()) end def get_active_keysets(repo) do diff --git a/lib/cashubrew/schema/keyset.ex b/lib/cashubrew/schema/keyset.ex index 45ed0f5..b3d11e1 100644 --- a/lib/cashubrew/schema/keyset.ex +++ b/lib/cashubrew/schema/keyset.ex @@ -6,8 +6,8 @@ defmodule Cashubrew.Schema.Keyset do import Ecto.Changeset - alias Cashubrew.Schema alias Cashubrew.Nuts.Nut02 + alias Cashubrew.Schema @primary_key {:id, :string, autogenerate: false} schema "keysets" do diff --git a/test/nut01_test.exs b/test/nut01_test.exs index 363a233..2c00d4e 100644 --- a/test/nut01_test.exs +++ b/test/nut01_test.exs @@ -1,6 +1,5 @@ defmodule Cashubrew.Nuts.Nut01Test do use Cashubrew.Test.ConnCase - alias Cashubrew.Nuts.Nut01 test "active_keysets", %{conn: conn} do conn = get(conn, ~p"/api/v1/keys") diff --git a/test/nut02_test.exs b/test/nut02_test.exs index 09ee2f9..3577109 100644 --- a/test/nut02_test.exs +++ b/test/nut02_test.exs @@ -1,8 +1,9 @@ defmodule Cashubrew.Nuts.Nut02Test do use Cashubrew.Test.ConnCase + + alias Cashubrew.Repo alias Cashubrew.Schema.Key alias Cashubrew.Schema.Keyset - alias Cashubrew.Repo test "get all keysets", %{conn: conn} do data = conn |> get(~p"/api/v1/keysets") |> json_response(200)