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

Commit

Permalink
feat: verify inputs (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored Oct 17, 2024
1 parent 25eaa7e commit c02037e
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 78 deletions.
3 changes: 2 additions & 1 deletion lib/cashubrew/NUTs/NUT-03/impl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ defmodule Cashubrew.Nuts.Nut03.Impl 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(blinded_messages) do
case Mint.create_blinded_signatures(repo, blinded_messages) do
{:ok, signatures} -> signatures
{:error, reason} -> raise reason
end
Expand Down
6 changes: 2 additions & 4 deletions lib/cashubrew/NUTs/NUT-04/impl.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,12 @@ defmodule Cashubrew.Nuts.Nut04.Impl do
mint_quote = MintQuoteMutex.check_and_acquire!(repo, quote_id)

try do
{keyset_id, total_amount} = Mint.Verification.Outputs.verify!(repo, blinded_messages)
{keyset, total_amount} = Mint.Verification.Outputs.verify!(repo, blinded_messages)

if total_amount != mint_quote.amount do
raise "TotalOutputAmountDoesNotEqualMintQuoteAmount"
end

keyset = repo.get!(Schema.Keyset, keyset_id)

if !keyset.active do
raise "KeysetIsNotActive"
end
Expand All @@ -80,7 +78,7 @@ defmodule Cashubrew.Nuts.Nut04.Impl do
raise "QuoteExpired"
end

promises = Mint.generate_promises(repo, keyset_id, blinded_messages)
promises = Mint.generate_promises(repo, keyset.id, blinded_messages)
repo.insert_all(Schema.Promises, promises)
after
MintQuoteMutex.release!(repo, quote_id)
Expand Down
74 changes: 1 addition & 73 deletions lib/cashubrew/core/mint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,7 @@ defmodule Cashubrew.Mint do
end
end

def create_blinded_signatures(blinded_messages) do
repo = Application.get_env(:cashubrew, :repo)
create_blinded_signatures(repo, blinded_messages)
end

defp create_blinded_signatures(repo, blinded_messages) do
def create_blinded_signatures(repo, blinded_messages) do
signatures =
Enum.map(blinded_messages, fn bm ->
# Get key from database
Expand Down Expand Up @@ -273,71 +268,4 @@ defmodule Cashubrew.Mint do
]
end)
end

defmodule Verification do
@moduledoc """
Logic to verify user-provided data
"""
defmodule Outputs do
@moduledoc """
Logic to verify protocol "outputs" (blinded messages)
"""
alias Cashubrew.Nuts.Nut02

# Will perform all the check required upon some user provided 'output'
def verify!(repo, outputs) do
inner_verify!(repo, outputs, nil, [], 0)
end

# Empty list
defp inner_verify!(_repo, [], nil, _seen_B, _total_amount) do
raise "EmptyListOfBlindMessages"
end

# First elem
defp inner_verify!(repo, [head | tail], nil, seen_B, total_amount) do
verify_blind_message!(repo, head)
inner_verify!(repo, tail, head.id, [head."B_" | seen_B], total_amount + head.amount)
end

# End of list
defp inner_verify!(repo, [], id, _seen_B, total_amount) do
keyset = repo.get!(Schema.Keyset, id)

if !keyset.active do
raise "InactiveKeyset"
end

{id, total_amount}
end

# Middle elems
defp inner_verify!(repo, [head | tail], id, seen_B, total_amount) do
if head.id != id do
raise "BlindMessagesBelongsToDifferentKeysetIds"
end

if Enum.member?(seen_B, head."B_") do
"DuplicateBlindMessageInList"
end

verify_blind_message!(repo, head)
inner_verify!(repo, tail, id, [head."B_" | seen_B], total_amount + head.amount)
end

defp verify_blind_message!(repo, blind_message) do
if blind_message.amount < 0 do
raise "BlindMessageAmountShoulBePositive"
end

if blind_message.amount > 2 ** Nut02.Keyset.max_order() do
raise "BlindMessageAmountNotExceed2^MaxOrder"
end

if repo.exists?(Schema.Promises, blind_message."B_") do
raise "BlindedSignatureAlreadyEmittedForThisMessage"
end
end
end
end
end
172 changes: 172 additions & 0 deletions lib/cashubrew/core/mint_verification.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
defmodule Cashubrew.Mint.Verification.Amount do
@moduledoc """
Logic to verify user-provided amount
"""
alias Cashubrew.Nuts.Nut02

def verify!(amount) do
if amount < 0 do
raise "BlindMessageAmountShouldBePositive"
end

if amount > 2 ** Nut02.Keyset.max_order() do
raise "BlindMessageAmountNotExceed2^MaxOrder"
end
end
end

defmodule Cashubrew.Mint.Verification.Inputs do
@moduledoc """
Logic to verify mint/melt user-provided payload
"""
alias Cashubrew.Mint
alias Cashubrew.Nuts.Nut00.BDHKE

defp max_secret_length do
512
end

def verify!(repo, inputs) do
inner_verify!(repo, inputs, nil, MapSet.new(), 0, 0)
end

# Empty list
defp inner_verify!(_repo, [], nil, _seen_secrets, _total_fee, _total_amount) do
raise "EmptyListOfProof"
end

# End of list
defp inner_verify!(_repo, [], unit, _seen_secrets, total_fee, total_amount) do
{total_fee, total_amount, unit}
end

# Elems
defp inner_verify!(repo, [head | tail], unit, seen_secrets, total_fee, total_amount) do
Cashubrew.Mint.Verification.Amount.verify!(head)

if head.secret == nil or head.secret == "" do
raise "NoSecretInProof"
end

if head.secret > max_secret_length() do
raise "SecretTooLong"
end

if Enum.member?(seen_secrets, head.secret) do
"DuplicateProofInList"
end

seen_secrets.put(head.secret)

keyset = Mint.get_keyset(repo, head.id)

if keyset == nil do
raise "UnkownKeyset"
end

# No check for first elem
if unit != nil and unit != keyset.unit do
raise "DifferentUnits"
end

key = Mint.get_key_for_amount(repo, head.id, head.amount)

if !BDHKE.verify(key, head."C", head.secret) do
raise "InvalidProof"
end

# TODO when NUT-11 and NUT-14 are implemented: check spending condition

inner_verify!(
repo,
tail,
keyset.unit,
seen_secrets,
total_fee + keyset.input_fee_ppk,
total_amount + head.amount
)
end
end

defmodule Cashubrew.Mint.Verification.Outputs do
@moduledoc """
Logic to verify protocol "outputs" (blinded messages)
"""

# Will perform all the check required upon some user provided 'output'
def verify!(repo, outputs) do
inner_verify!(repo, outputs, nil, MapSet.new(), 0)
end

# Empty list
defp inner_verify!(_repo, [], nil, _seen_Bs, _total_amount) do
raise "Empty"
end

# End of list
defp inner_verify!(repo, [], id, _seen_Bs, total_amount) do
keyset = repo.get!(Schema.Keyset, id)

if !keyset.active do
raise "InactiveKeyset"
end

{keyset, total_amount}
end

# Elems
defp inner_verify!(repo, [head | tail], id, seen_Bs, total_amount) do
# No check for first elem
if id != nil do
if head.id != id do
raise "DifferentKeysetIds"
end

if Enum.member?(seen_Bs, head."B_") do
"Duplicate"
end
end

seen_Bs.put(head."B_")

verify_blind_message!(repo, head)
inner_verify!(repo, tail, head.id, seen_Bs, total_amount + head.amount)
end

defp verify_blind_message!(repo, blind_message) do
Cashubrew.Mint.Verification.Amount.verify!(blind_message.amount)

if repo.exists?(Schema.Promises, blind_message."B_") do
raise "AlreadyEmitted"
end
end
end

defmodule Cashubrew.Mint.Verification.InputsAndOutputs do
@moduledoc """
Logic to verify protocol "inputs" and "outputs" together
"""

def verify!(repo, inputs, outputs) do
{total_fee, input_total_amount, unit} =
Cashubrew.Mint.Verification.Inputs.verify!(repo, inputs)

# Empty outputs means that we are melting
if outputs != [] do
{output_keyset, output_total_amount} =
Cashubrew.Mint.Verification.Outputs.verify!(repo, outputs)

if output_keyset.unit != unit do
raise "DifferentInputAndOutputUnit"
end

if output_total_amount + total_fee != input_total_amount do
"InvalidAmountAndFee"
end

# TODO when NUT-11 and NUT-14 are implemented: check output spending condition
end
end
end

# TODO: write unit tests

0 comments on commit c02037e

Please sign in to comment.