diff --git a/build.zig.zon b/build.zig.zon index 3f3c83f..19c5cc7 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -24,8 +24,8 @@ // internet connectivity. .dependencies = .{ .zul = .{ - .url = "https://github.com/karlseguin/zul/archive/08c989bf6871e87807a4668232913ee245425863.zip", - .hash = "12206f5d1e5bd4793fe952bbae891b7424a19026e0d296a1381074c7d21d5d76c1a1", + .url = "https://github.com/StringNick/zul/archive/9f9acdff296918e0f81e8af39b269dad988c48f9.zip", + .hash = "122016552e83ec129489fd32d89b4122f737fcdacb22dbee34dca2fbc62981bb487d", }, .@"zig-cli" = .{ .url = "https://github.com/StringNick/zig-cli/archive/c9b9d17b14c524785a32a5b7c930b9584a331372.zip", diff --git a/cocomint_db.sqlite b/cocomint_db.sqlite new file mode 100644 index 0000000..d3cf648 Binary files /dev/null and b/cocomint_db.sqlite differ diff --git a/src/core/database/mint_sqlite.zig b/src/core/database/mint_sqlite.zig index 3c40eae..f6a482e 100644 --- a/src/core/database/mint_sqlite.zig +++ b/src/core/database/mint_sqlite.zig @@ -214,6 +214,8 @@ pub const Database = struct { defer row.deinit(); // must be called const quote_json = row.blob(0); + std.log.debug("quote-json: {s}", .{quote_json}); + const quote = try std.json.parseFromSlice(MintQuote, self.allocator, quote_json, .{}); defer quote.deinit(); diff --git a/src/core/mint/lightning/lnbits.zig b/src/core/mint/lightning/lnbits.zig index 22a287d..b57dce0 100644 --- a/src/core/mint/lightning/lnbits.zig +++ b/src/core/mint/lightning/lnbits.zig @@ -18,9 +18,7 @@ const FeeReserve = core.mint.FeeReserve; const Channel = @import("../../../channels/channels.zig").Channel; const MintLightning = core.lightning.MintLightning; -pub const HttpError = std.http.Client.RequestError || std.http.Client.Request.FinishError || std.http.Client.Request.WaitError || error{ ReadBodyError, WrongJson }; - -pub const LightningError = HttpError || std.Uri.ParseError || std.mem.Allocator.Error || error{ +pub const LightningError = error{ NotFound, Unauthorized, PaymentFailed, @@ -159,14 +157,12 @@ pub const LnBits = struct { const amnt = try core.lightning.toUnit(amount, unit, .sat); const expiry = unix_expiry - @abs(time_now); - _ = expiry; // autofix const create_invoice_response = try self.client.createInvoice(gpa, .{ .amount = amnt, .unit = unit.toString(), .memo = description, - // .expiry = expiry, - .expiry = null, + .expiry = expiry, .webhook = self.webhook_url, .internal = null, .out = false, @@ -226,7 +222,7 @@ pub const LNBitsClient = struct { admin_key: []const u8, invoice_api_key: []const u8, lnbits_url: []const u8, - client: std.http.Client, + client: zul.http.Client, pub fn init( allocator: std.mem.Allocator, @@ -234,9 +230,7 @@ pub const LNBitsClient = struct { invoice_api_key: []const u8, lnbits_url: []const u8, ) !LNBitsClient { - var client = std.http.Client{ - .allocator = allocator, - }; + var client = zul.http.Client.init(allocator); errdefer client.deinit(); return .{ @@ -256,40 +250,24 @@ pub const LNBitsClient = struct { self: *@This(), allocator: std.mem.Allocator, endpoint: []const u8, - ) LightningError![]const u8 { + ) ![]const u8 { const uri = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ self.lnbits_url, endpoint }); defer allocator.free(uri); - const header_buf = try allocator.alloc(u8, 1024 * 1024 * 4); - defer allocator.free(header_buf); - - var req = try self.client.open(.GET, try std.Uri.parse(uri), .{ - .server_header_buffer = header_buf, - .extra_headers = &.{ - .{ - .name = "X-Api-Key", - .value = self.admin_key, - }, - }, - }); + var req = try self.client.allocRequest(allocator, uri); defer req.deinit(); - try req.send(); - try req.finish(); - try req.wait(); + try req.header("X-Api-Key", self.admin_key); - if (req.response.status != .ok) { - if (req.response.status == .not_found) return LightningError.NotFound; + var res = try req.getResponse(.{}); + + if (res.status != 200) { + if (res.status == 404) return LightningError.NotFound; } - var rdr = req.reader(); - const body = rdr.readAllAlloc(allocator, 1024 * 1024 * 4) catch |err| { - std.log.debug("read body error: {any}", .{err}); - return error.ReadBodyError; - }; - errdefer allocator.free(body); + const res_body = try res.allocBody(allocator, .{}); - return body; + return res_body.buf; } pub fn post( @@ -297,51 +275,28 @@ pub const LNBitsClient = struct { allocator: std.mem.Allocator, endpoint: []const u8, req_body: []const u8, - ) LightningError![]const u8 { + ) !zul.StringBuilder { const uri = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ self.lnbits_url, endpoint }); defer allocator.free(uri); - const header_buf = try allocator.alloc(u8, 1024 * 1024 * 4); - defer allocator.free(header_buf); - - std.log.debug("uri: {s}", .{uri}); + var req = try self.client.allocRequest(allocator, uri); + defer req.deinit(); - var req = try self.client.open(.POST, try std.Uri.parse(uri), .{ - .server_header_buffer = header_buf, - .extra_headers = &.{ - .{ - .name = "X-Api-Key", - .value = self.admin_key, - }, + req.method = .POST; - .{ - .name = "accept", - .value = "*/*", - }, - }, - }); - defer req.deinit(); + req.body(req_body); - req.transfer_encoding = .{ .content_length = req_body.len }; + try req.header("X-Api-Key", self.admin_key); + try req.header("accept", "*/*"); - try req.send(); - try req.writeAll(req_body); - try req.finish(); - try req.wait(); + var res = try req.getResponse(.{}); - if (req.response.status != .ok) { - if (req.response.status == .not_found) return LightningError.NotFound; - if (req.response.status == .unauthorized) return LightningError.Unauthorized; + if (res.status != 200) { + if (res.status == 404) return LightningError.NotFound; + if (res.status == 401) return LightningError.Unauthorized; } - var rdr = req.reader(); - const body = rdr.readAllAlloc(allocator, 1024 * 1024 * 4) catch |err| { - std.log.debug("read post body error: {any}", .{err}); - return error.ReadBodyError; - }; - errdefer allocator.free(body); - - return body; + return try res.allocBody(allocator, .{}); } /// createInvoice - creating invoice @@ -351,37 +306,20 @@ pub const LNBitsClient = struct { .emit_null_optional_fields = false, }); - std.log.debug("request {s}", .{req_body}); - const res = try self.post(allocator, "api/v1/payments", req_body); + defer res.deinit(); - std.log.debug("create invoice, response : {s}", .{res}); - - const parsed = std.json.parseFromSlice(std.json.Value, allocator, res, .{ .allocate = .alloc_always }) catch |err| { - errdefer std.log.debug("{any}", .{err}); + const parsed = std.json.parseFromSlice(CreateInvoiceResponse, allocator, res.string(), .{ + .allocate = .alloc_always, + .ignore_unknown_fields = true, + }) catch |err| { + errdefer std.log.debug("Parse create invoice error: {any}, body:\n{s}", .{ err, res.string() }); return error.WrongJson; }; + defer parsed.deinit(); - const payment_request = parsed.value.object.get("payment_request") orelse unreachable; - const payment_hash = parsed.value.object.get("payment_hash") orelse unreachable; - - const pr = switch (payment_request) { - .string => |v| try allocator.dupe(u8, v), - else => unreachable, - }; - errdefer allocator.free(pr); - - const ph = switch (payment_hash) { - .string => |v| try allocator.dupe(u8, v), - else => unreachable, - }; - errdefer allocator.free(ph); - - return .{ - .payment_hash = ph, - .payment_request = pr, - }; + return try parsed.value.clone(allocator); } /// payInvoice - paying invoice @@ -390,8 +328,9 @@ pub const LNBitsClient = struct { const req_body = try std.json.stringifyAlloc(allocator, &.{ .out = true, .bolt11 = bolt11 }, .{}); const res = try self.post(allocator, "api/v1/payments", req_body); + defer res.deinit(); - const parsed = std.json.parseFromSlice(std.json.Value, allocator, res, .{ .allocate = .alloc_always }) catch return error.WrongJson; + const parsed = std.json.parseFromSlice(std.json.Value, allocator, res.string(), .{ .allocate = .alloc_always }) catch return error.WrongJson; const payment_hash = parsed.value.object.get("payment_hash") orelse unreachable; @@ -567,6 +506,15 @@ pub const CreateInvoiceResponse = struct { /// Payment request (bolt11) payment_request: []const u8, + pub fn clone(self: CreateInvoiceResponse, alloc: std.mem.Allocator) !CreateInvoiceResponse { + var s: CreateInvoiceResponse = undefined; + errdefer s.deinit(alloc); + + s.payment_hash = try alloc.dupe(u8, self.payment_hash); + s.payment_request = try alloc.dupe(u8, self.payment_request); + return s; + } + pub fn deinit(self: CreateInvoiceResponse, alloc: std.mem.Allocator) void { alloc.free(self.payment_hash); alloc.free(self.payment_request); diff --git a/src/core/nuts/nut04/nut04.zig b/src/core/nuts/nut04/nut04.zig index b0a5d87..9f30628 100644 --- a/src/core/nuts/nut04/nut04.zig +++ b/src/core/nuts/nut04/nut04.zig @@ -44,6 +44,12 @@ pub const QuoteState = enum { pub fn jsonStringify(self: *const QuoteState, out: anytype) !void { try out.write(self.toStr()); } + + pub fn jsonParse(allocator: std.mem.Allocator, source: anytype, options: std.json.ParseOptions) !QuoteState { + const state = try std.json.innerParse([]const u8, allocator, source, options); + + return QuoteState.fromStr(state) catch error.UnexpectedToken; + } }; pub const MintMethodSettings = struct { diff --git a/src/mint.zig b/src/mint.zig index d6bd727..9c2f863 100644 --- a/src/mint.zig +++ b/src/mint.zig @@ -100,8 +100,8 @@ pub fn main() !void { break :v try MintDatabase.initFrom(MintMemoryDatabase, gpa.allocator(), db); }, inline .sqlite => v: { - std.log.warn("hello", .{}); - var db = try core.mint_memory.MintSqliteDatabase.initFrom(gpa.allocator(), "./cdk-mintd.sqlite"); + // TODO custom path to database? + var db = try core.mint_memory.MintSqliteDatabase.initFrom(gpa.allocator(), "./cocomint_db.sqlite"); errdefer db.deinit(); break :v try MintDatabase.initFrom(core.mint_memory.MintSqliteDatabase, gpa.allocator(), db); diff --git a/src/router/router_handlers.zig b/src/router/router_handlers.zig index 8383271..63ec757 100644 --- a/src/router/router_handlers.zig +++ b/src/router/router_handlers.zig @@ -38,6 +38,8 @@ pub fn getCheckMintBolt11Quote( req: *httpz.Request, res: *httpz.Response, ) !void { + errdefer std.log.debug("{any}", .{@errorReturnTrace()}); + const quote_id_hex = req.param("quote_id") orelse return error.ExpectQuoteId; const quote_id = try zul.UUID.parse(quote_id_hex);