Skip to content
This repository has been archived by the owner on Nov 21, 2024. It is now read-only.

Commit

Permalink
feat: Mint is no longer a GenServer (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored Oct 25, 2024
1 parent 1621f4b commit 5514ff4
Show file tree
Hide file tree
Showing 8 changed files with 16 additions and 109 deletions.
2 changes: 0 additions & 2 deletions lib/cashubrew/NUTs/NUT-01/serde.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/cashubrew/NUTs/NUT-02/impl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 7 additions & 41 deletions lib/cashubrew/NUTs/NUT-03/impl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion lib/cashubrew/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
65 changes: 4 additions & 61 deletions lib/cashubrew/core/mint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -13,7 +12,6 @@ defmodule Cashubrew.Mint do
alias Cashubrew.Schema.{
Key,
MintConfiguration,
MintQuote,
UsedProof
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/cashubrew/schema/keyset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion test/nut01_test.exs
Original file line number Diff line number Diff line change
@@ -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")
Expand Down
3 changes: 2 additions & 1 deletion test/nut02_test.exs
Original file line number Diff line number Diff line change
@@ -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)
Expand Down

0 comments on commit 5514ff4

Please sign in to comment.