Skip to content

Commit

Permalink
feat: post mint bolt11 route
Browse files Browse the repository at this point in the history
  • Loading branch information
StringNick committed Sep 13, 2024
1 parent 0b11e38 commit 4bacaf1
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 1 deletion.
111 changes: 110 additions & 1 deletion src/core/mint/mint.zig
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ pub const Mint = struct {
// check if keyset already in
{
self.keysets.lock.lockShared();
defer self.keysets.lock.lockShared();
defer self.keysets.lock.unlockShared();

if (self.keysets.value.contains(id)) return;
}
Expand Down Expand Up @@ -587,6 +587,115 @@ pub const Mint = struct {

_ = try self.localstore.value.updateMintQuoteState(mint_quote.id, .paid);
}

/// Blind Sign
pub fn blindSign(
self: *Mint,
gpa: std.mem.Allocator,
blinded_message: core.nuts.BlindedMessage,
) !core.nuts.BlindSignature {
try self.ensureKeysetLoaded(blinded_message.keyset_id);

const keyset_info = try self
.localstore.value
.getKeysetInfo(gpa, blinded_message.keyset_id) orelse return error.UnknownKeySet;
errdefer keyset_info.deinit(gpa);

const active = self
.localstore
.value
.getActiveKeysetId(keyset_info.unit) orelse return error.InactiveKeyset;

// Check that the keyset is active and should be used to sign
if (!std.meta.eql(keyset_info.id, active)) {
return error.InactiveKeyset;
}

const keyset = v: {
self.keysets.lock.lock();
defer self.keysets.lock.unlock();

break :v self.keysets.value.get(blinded_message.keyset_id) orelse return error.UknownKeySet;
};

const key_pair = keyset.keys.inner.get(blinded_message.amount) orelse return error.AmountKey;

const c = try core.dhke.signMessage(
self.secp_ctx,
key_pair.secret_key,
blinded_message.blinded_secret,
);

const blinded_signature = try core.nuts.initBlindSignature(
self.secp_ctx,
blinded_message.amount,
c,
keyset_info.id,
blinded_message.blinded_secret,
key_pair.secret_key,
);

return blinded_signature;
}

/// Process mint request
pub fn processMintRequest(
self: *Mint,
gpa: std.mem.Allocator,
mint_request: core.nuts.nut04.MintBolt11Request,
) !core.nuts.nut04.MintBolt11Response {
const quote_id = try zul.UUID.parse(&mint_request.quote);
const state = try self.localstore.value.updateMintQuoteState(quote_id.bin, .pending);

std.log.debug("process_mitn_request: quote_id {s}", .{quote_id.toHex(.lower)});
switch (state) {
.unpaid => return error.UnpaidQuote,
.pending => return error.PendingQuote,
.issued => return error.IssuedQuote,
.paid => {},
}

var blinded_messages = try std.ArrayList(secp256k1.PublicKey).initCapacity(gpa, mint_request.outputs.len);
errdefer blinded_messages.deinit();

for (mint_request.outputs) |b| {
blinded_messages.appendAssumeCapacity(b.blinded_secret);
}

const _blind_signatures = try self.localstore.value.getBlindSignatures(gpa, blinded_messages.items);
defer _blind_signatures.deinit();

for (_blind_signatures.items) |bs| {
if (bs != null) {
std.log.debug("output has already been signed", .{});
std.log.debug("Mint {x} did not succeed returning quote to Paid state", .{mint_request.quote});

_ = try self.localstore
.value.updateMintQuoteState(quote_id.bin, .paid);
return error.BlindedMessageAlreadySigned;
}
}

var blind_signatures = try std.ArrayList(core.nuts.BlindSignature).initCapacity(gpa, mint_request.outputs.len);
errdefer blind_signatures.deinit();

for (mint_request.outputs) |blinded_message| {
const blind_signature = try self.blindSign(gpa, blinded_message);
blind_signatures.appendAssumeCapacity(blind_signature);
}

try self.localstore
.value
.addBlindSignatures(blinded_messages.items, blind_signatures.items);

_ = try self.localstore.value.updateMintQuoteState(quote_id.bin, .issued);

std.log.debug("process_mint_request: issued {s}", .{quote_id.toHex(.lower)});

return .{
.signatures = try blind_signatures.toOwnedSlice(),
};
}
};

/// Generate new [`MintKeySetInfo`] from path
Expand Down
10 changes: 10 additions & 0 deletions src/core/mint/types.zig
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,16 @@ pub const MintQuote = struct {
/// Value used by ln backend to look up state of request
request_lookup_id: []const u8,

/// formatting mint quote
pub fn format(
self: MintQuote,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
) !void {
try writer.print("{}", .{std.json.fmt(self, .{})});
}

/// Create new [`MintQuote`]
/// creating copy of arguments, so caller responsible on deinit resources
pub fn initAlloc(
Expand Down
16 changes: 16 additions & 0 deletions src/core/nuts/nut04/nut04.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const std = @import("std");
const zul = @import("zul");

const CurrencyUnit = @import("../nut00/nut00.zig").CurrencyUnit;
const BlindedMessage = @import("../nut00/nut00.zig").BlindedMessage;
const BlindSignature = @import("../nut00/nut00.zig").BlindSignature;
const Proof = @import("../nut00/nut00.zig").Proof;
const PaymentMethod = @import("../nut00/nut00.zig").PaymentMethod;
const MintQuote = @import("../../mint/types.zig").MintQuote;
Expand Down Expand Up @@ -191,3 +193,17 @@ pub const MintQuoteBolt11Response = struct {
};
}
};

/// Mint response [NUT-04]
pub const MintBolt11Response = struct {
/// Blinded Signatures
signatures: []const BlindSignature,
};

/// Mint request [NUT-04]
pub const MintBolt11Request = struct {
/// Quote id
quote: [36]u8,
/// Outputs
outputs: []const BlindedMessage,
};
1 change: 1 addition & 0 deletions src/router/router.zig
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ pub fn createMintServer(

router.post("/v1/mint/quote/bolt11", router_handlers.getMintBolt11Quote, .{});
router.post("/v1/melt/quote/bolt11", router_handlers.getMeltBolt11Quote, .{});
router.post("/v1/mint/bolt11", router_handlers.postMintBolt11, .{});
return srv;
}

Expand Down
20 changes: 20 additions & 0 deletions src/router/router_handlers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ pub fn getMintBolt11Quote(
);
}

pub fn postMintBolt11(
state: MintState,
req: *httpz.Request,
res: *httpz.Response,
) !void {
errdefer std.log.debug("{any}", .{@errorReturnTrace()});

const payload = try req.json(core.nuts.nut04.MintBolt11Request) orelse return error.WrongRequest;

const r = state.mint
.processMintRequest(res.arena, payload) catch |err| {
// TODO print self error
std.log.err("could not process mint {any}, err {any}", .{ payload, err });

return err;
};

return try res.json(r, .{});
}

pub fn getMeltBolt11Quote(
state: MintState,
req: *httpz.Request,
Expand Down

0 comments on commit 4bacaf1

Please sign in to comment.