Added cashu functionality.
This commit is contained in:
@@ -393,3 +393,429 @@ int cashu_mint_check_proofs_state(const char* mint_url,
|
||||
free(body);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cashu_mint_get_active_keyset(const cashu_mint_info_t* info,
|
||||
const char* optional_unit,
|
||||
cashu_keyset_t* keyset_out) {
|
||||
if (!info || !keyset_out || !info->keysets || info->keyset_count <= 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < info->keyset_count; i++) {
|
||||
const cashu_keyset_t* ks = &info->keysets[i];
|
||||
if (!ks->active) continue;
|
||||
if (optional_unit && optional_unit[0] != '\0' && strcmp(ks->unit, optional_unit) != 0) continue;
|
||||
memset(keyset_out, 0, sizeof(*keyset_out));
|
||||
memcpy(keyset_out, ks, sizeof(*keyset_out));
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
if (optional_unit && optional_unit[0] != '\0') {
|
||||
for (int i = 0; i < info->keyset_count; i++) {
|
||||
const cashu_keyset_t* ks = &info->keysets[i];
|
||||
if (strcmp(ks->unit, optional_unit) != 0) continue;
|
||||
memset(keyset_out, 0, sizeof(*keyset_out));
|
||||
memcpy(keyset_out, ks, sizeof(*keyset_out));
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return NOSTR_ERROR_CASHU_INVALID_KEYSET;
|
||||
}
|
||||
|
||||
int cashu_mint_select_proofs_for_amount(const nostr_cashu_proof_t* proofs,
|
||||
int proof_count,
|
||||
uint64_t target_amount,
|
||||
int* selected_indices_out,
|
||||
int selected_indices_cap,
|
||||
uint64_t* selected_total_out) {
|
||||
if (!proofs || proof_count <= 0 || !selected_indices_out || selected_indices_cap <= 0 || target_amount == 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
int* used = (int*)calloc((size_t)proof_count, sizeof(int));
|
||||
if (!used) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
int selected_count = 0;
|
||||
uint64_t total = 0;
|
||||
|
||||
while (total < target_amount) {
|
||||
int best_idx = -1;
|
||||
uint64_t best_amt = 0;
|
||||
|
||||
for (int i = 0; i < proof_count; i++) {
|
||||
if (used[i]) continue;
|
||||
if (proofs[i].amount > best_amt) {
|
||||
best_amt = proofs[i].amount;
|
||||
best_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_idx < 0 || best_amt == 0) {
|
||||
free(used);
|
||||
return NOSTR_ERROR_NIP60_INSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
if (selected_count >= selected_indices_cap) {
|
||||
free(used);
|
||||
return NOSTR_ERROR_NIP60_INSUFFICIENT_FUNDS;
|
||||
}
|
||||
|
||||
used[best_idx] = 1;
|
||||
selected_indices_out[selected_count++] = best_idx;
|
||||
total += proofs[best_idx].amount;
|
||||
}
|
||||
|
||||
free(used);
|
||||
|
||||
if (selected_total_out) {
|
||||
*selected_total_out = total;
|
||||
}
|
||||
|
||||
return selected_count;
|
||||
}
|
||||
|
||||
int cashu_mint_plan_split_amounts(uint64_t amount,
|
||||
uint64_t* amounts_out,
|
||||
int max_amounts,
|
||||
int* amount_count_out) {
|
||||
if (!amounts_out || max_amounts <= 0 || !amount_count_out || amount == 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
while (amount > 0) {
|
||||
uint64_t part = 1;
|
||||
while ((part << 1) > part && (part << 1) <= amount) {
|
||||
part <<= 1;
|
||||
}
|
||||
|
||||
if (count >= max_amounts) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
amounts_out[count++] = part;
|
||||
amount -= part;
|
||||
}
|
||||
|
||||
*amount_count_out = count;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
static int cashu_build_outputs_json(const cashu_blinded_output_t* outputs,
|
||||
int output_count,
|
||||
cJSON** outputs_out) {
|
||||
if (!outputs || output_count <= 0 || !outputs_out) return NOSTR_ERROR_INVALID_INPUT;
|
||||
|
||||
cJSON* arr = cJSON_CreateArray();
|
||||
if (!arr) return NOSTR_ERROR_MEMORY_FAILED;
|
||||
|
||||
for (int i = 0; i < output_count; i++) {
|
||||
cJSON* o = cJSON_CreateObject();
|
||||
if (!o) {
|
||||
cJSON_Delete(arr);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
cJSON_AddStringToObject(o, "id", outputs[i].id);
|
||||
cJSON_AddNumberToObject(o, "amount", (double)outputs[i].amount);
|
||||
cJSON_AddStringToObject(o, "B_", outputs[i].B_);
|
||||
cJSON_AddItemToArray(arr, o);
|
||||
}
|
||||
|
||||
*outputs_out = arr;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
static int cashu_parse_signatures_array(cJSON* signatures,
|
||||
cashu_blinded_signature_t** sigs_out,
|
||||
int* sig_count_out) {
|
||||
if (!sigs_out || !sig_count_out) return NOSTR_ERROR_INVALID_INPUT;
|
||||
|
||||
*sigs_out = NULL;
|
||||
*sig_count_out = 0;
|
||||
|
||||
if (!signatures || !cJSON_IsArray(signatures)) {
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
int n = cJSON_GetArraySize(signatures);
|
||||
if (n <= 0) return NOSTR_SUCCESS;
|
||||
|
||||
cashu_blinded_signature_t* sigs =
|
||||
(cashu_blinded_signature_t*)calloc((size_t)n, sizeof(cashu_blinded_signature_t));
|
||||
if (!sigs) return NOSTR_ERROR_MEMORY_FAILED;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON* item = cJSON_GetArrayItem(signatures, i);
|
||||
if (!item || !cJSON_IsObject(item)) {
|
||||
free(sigs);
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
cJSON* id = cJSON_GetObjectItemCaseSensitive(item, "id");
|
||||
cJSON* amount = cJSON_GetObjectItemCaseSensitive(item, "amount");
|
||||
cJSON* C_ = cJSON_GetObjectItemCaseSensitive(item, "C_");
|
||||
|
||||
if (!id || !cJSON_IsString(id) || !id->valuestring ||
|
||||
!amount || !cJSON_IsNumber(amount) ||
|
||||
!C_ || !cJSON_IsString(C_) || !C_->valuestring) {
|
||||
free(sigs);
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
snprintf(sigs[i].id, sizeof(sigs[i].id), "%s", id->valuestring);
|
||||
sigs[i].amount = (uint64_t)cJSON_GetNumberValue(amount);
|
||||
snprintf(sigs[i].C_, sizeof(sigs[i].C_), "%s", C_->valuestring);
|
||||
}
|
||||
|
||||
*sigs_out = sigs;
|
||||
*sig_count_out = n;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int cashu_build_mint_tokens_request(const cashu_mint_tokens_request_t* req,
|
||||
cJSON** request_body_out) {
|
||||
if (!req || !request_body_out || req->quote_id[0] == '\0' || req->output_count <= 0 || !req->outputs) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (!root) return NOSTR_ERROR_MEMORY_FAILED;
|
||||
|
||||
cJSON_AddStringToObject(root, "quote", req->quote_id);
|
||||
if (req->unit[0] != '\0') {
|
||||
cJSON_AddStringToObject(root, "unit", req->unit);
|
||||
}
|
||||
|
||||
cJSON* outputs = NULL;
|
||||
int rc = cashu_build_outputs_json(req->outputs, req->output_count, &outputs);
|
||||
if (rc != NOSTR_SUCCESS) {
|
||||
cJSON_Delete(root);
|
||||
return rc;
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "outputs", outputs);
|
||||
*request_body_out = root;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int cashu_parse_mint_tokens_response(cJSON* response_body,
|
||||
cashu_mint_tokens_response_t* response_out) {
|
||||
if (!response_body || !response_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
memset(response_out, 0, sizeof(*response_out));
|
||||
|
||||
cJSON* signatures = cJSON_GetObjectItemCaseSensitive(response_body, "signatures");
|
||||
return cashu_parse_signatures_array(signatures,
|
||||
&response_out->signatures,
|
||||
&response_out->signature_count);
|
||||
}
|
||||
|
||||
void cashu_mint_free_mint_tokens_response(cashu_mint_tokens_response_t* response) {
|
||||
if (!response) return;
|
||||
free(response->signatures);
|
||||
memset(response, 0, sizeof(*response));
|
||||
}
|
||||
|
||||
int cashu_build_swap_request(const cashu_swap_request_t* req,
|
||||
cJSON** request_body_out) {
|
||||
if (!req || !request_body_out || !req->inputs || req->input_count <= 0 || !req->outputs || req->output_count <= 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (!root) return NOSTR_ERROR_MEMORY_FAILED;
|
||||
|
||||
cJSON* inputs = nostr_nip60_proofs_to_json(req->inputs, req->input_count);
|
||||
if (!inputs) {
|
||||
cJSON_Delete(root);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
cJSON_AddItemToObject(root, "inputs", inputs);
|
||||
|
||||
cJSON* outputs = NULL;
|
||||
int rc = cashu_build_outputs_json(req->outputs, req->output_count, &outputs);
|
||||
if (rc != NOSTR_SUCCESS) {
|
||||
cJSON_Delete(root);
|
||||
return rc;
|
||||
}
|
||||
cJSON_AddItemToObject(root, "outputs", outputs);
|
||||
|
||||
*request_body_out = root;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int cashu_parse_swap_response(cJSON* response_body,
|
||||
cashu_swap_response_t* response_out) {
|
||||
if (!response_body || !response_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
memset(response_out, 0, sizeof(*response_out));
|
||||
|
||||
cJSON* signatures = cJSON_GetObjectItemCaseSensitive(response_body, "signatures");
|
||||
return cashu_parse_signatures_array(signatures,
|
||||
&response_out->signatures,
|
||||
&response_out->signature_count);
|
||||
}
|
||||
|
||||
void cashu_mint_free_swap_response(cashu_swap_response_t* response) {
|
||||
if (!response) return;
|
||||
free(response->signatures);
|
||||
memset(response, 0, sizeof(*response));
|
||||
}
|
||||
|
||||
int cashu_build_melt_tokens_request(const cashu_melt_tokens_request_t* req,
|
||||
cJSON** request_body_out) {
|
||||
if (!req || !request_body_out || req->quote_id[0] == '\0' || !req->inputs || req->input_count <= 0) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
if (!root) return NOSTR_ERROR_MEMORY_FAILED;
|
||||
|
||||
cJSON_AddStringToObject(root, "quote", req->quote_id);
|
||||
|
||||
cJSON* inputs = nostr_nip60_proofs_to_json(req->inputs, req->input_count);
|
||||
if (!inputs) {
|
||||
cJSON_Delete(root);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "inputs", inputs);
|
||||
*request_body_out = root;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int cashu_parse_melt_tokens_response(cJSON* response_body,
|
||||
cashu_melt_tokens_response_t* response_out) {
|
||||
if (!response_body || !response_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
memset(response_out, 0, sizeof(*response_out));
|
||||
|
||||
cJSON* paid = cJSON_GetObjectItemCaseSensitive(response_body, "paid");
|
||||
cJSON* preimage = cJSON_GetObjectItemCaseSensitive(response_body, "payment_preimage");
|
||||
cJSON* change = cJSON_GetObjectItemCaseSensitive(response_body, "change");
|
||||
|
||||
response_out->paid = (paid && cJSON_IsBool(paid) && cJSON_IsTrue(paid)) ? 1 : 0;
|
||||
|
||||
if (preimage && cJSON_IsString(preimage) && preimage->valuestring) {
|
||||
response_out->payment_preimage = cashu_strdup(preimage->valuestring);
|
||||
if (!response_out->payment_preimage) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
if (change) {
|
||||
response_out->change = cJSON_Duplicate(change, 1);
|
||||
if (!response_out->change) {
|
||||
free(response_out->payment_preimage);
|
||||
memset(response_out, 0, sizeof(*response_out));
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
void cashu_mint_free_melt_tokens_response(cashu_melt_tokens_response_t* response) {
|
||||
if (!response) return;
|
||||
free(response->payment_preimage);
|
||||
cJSON_Delete(response->change);
|
||||
memset(response, 0, sizeof(*response));
|
||||
}
|
||||
|
||||
int cashu_build_checkstate_request(const char** Ys,
|
||||
int y_count,
|
||||
cJSON** request_body_out) {
|
||||
if (!Ys || y_count <= 0 || !request_body_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
cJSON* root = cJSON_CreateObject();
|
||||
cJSON* ys = cJSON_CreateArray();
|
||||
if (!root || !ys) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(ys);
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
for (int i = 0; i < y_count; i++) {
|
||||
if (!Ys[i] || Ys[i][0] == '\0') {
|
||||
cJSON_Delete(root);
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
cJSON_AddItemToArray(ys, cJSON_CreateString(Ys[i]));
|
||||
}
|
||||
|
||||
cJSON_AddItemToObject(root, "Ys", ys);
|
||||
*request_body_out = root;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
int cashu_parse_checkstate_response(cJSON* response_body,
|
||||
cashu_checkstate_response_t* response_out) {
|
||||
if (!response_body || !response_out) {
|
||||
return NOSTR_ERROR_INVALID_INPUT;
|
||||
}
|
||||
|
||||
memset(response_out, 0, sizeof(*response_out));
|
||||
|
||||
cJSON* states = cJSON_GetObjectItemCaseSensitive(response_body, "states");
|
||||
if (!states || !cJSON_IsArray(states)) {
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
int n = cJSON_GetArraySize(states);
|
||||
if (n <= 0) {
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
response_out->states = (cashu_proof_state_t*)calloc((size_t)n, sizeof(cashu_proof_state_t));
|
||||
if (!response_out->states) {
|
||||
return NOSTR_ERROR_MEMORY_FAILED;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
cJSON* item = cJSON_GetArrayItem(states, i);
|
||||
if (!item || !cJSON_IsObject(item)) {
|
||||
cashu_mint_free_checkstate_response(response_out);
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
|
||||
cJSON* Y = cJSON_GetObjectItemCaseSensitive(item, "Y");
|
||||
cJSON* state = cJSON_GetObjectItemCaseSensitive(item, "state");
|
||||
cJSON* witness = cJSON_GetObjectItemCaseSensitive(item, "witness");
|
||||
|
||||
if (Y && cJSON_IsString(Y) && Y->valuestring) {
|
||||
snprintf(response_out->states[i].Y, sizeof(response_out->states[i].Y), "%s", Y->valuestring);
|
||||
}
|
||||
if (state && cJSON_IsString(state) && state->valuestring) {
|
||||
snprintf(response_out->states[i].state, sizeof(response_out->states[i].state), "%s", state->valuestring);
|
||||
}
|
||||
if (witness && cJSON_IsString(witness) && witness->valuestring) {
|
||||
snprintf(response_out->states[i].witness, sizeof(response_out->states[i].witness), "%s", witness->valuestring);
|
||||
}
|
||||
|
||||
if (response_out->states[i].state[0] == '\0') {
|
||||
cashu_mint_free_checkstate_response(response_out);
|
||||
return NOSTR_ERROR_CASHU_JSON_PARSE_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
response_out->state_count = n;
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
void cashu_mint_free_checkstate_response(cashu_checkstate_response_t* response) {
|
||||
if (!response) return;
|
||||
free(response->states);
|
||||
memset(response, 0, sizeof(*response));
|
||||
}
|
||||
|
||||
@@ -47,6 +47,65 @@ typedef struct {
|
||||
time_t expiry;
|
||||
} cashu_melt_quote_t;
|
||||
|
||||
typedef struct {
|
||||
char id[NOSTR_CASHU_KEYSET_ID_HEX_SIZE];
|
||||
uint64_t amount;
|
||||
char B_[256];
|
||||
} cashu_blinded_output_t;
|
||||
|
||||
typedef struct {
|
||||
char id[NOSTR_CASHU_KEYSET_ID_HEX_SIZE];
|
||||
uint64_t amount;
|
||||
char C_[256];
|
||||
} cashu_blinded_signature_t;
|
||||
|
||||
typedef struct {
|
||||
char quote_id[128];
|
||||
char unit[16];
|
||||
cashu_blinded_output_t* outputs;
|
||||
int output_count;
|
||||
} cashu_mint_tokens_request_t;
|
||||
|
||||
typedef struct {
|
||||
cashu_blinded_signature_t* signatures;
|
||||
int signature_count;
|
||||
} cashu_mint_tokens_response_t;
|
||||
|
||||
typedef struct {
|
||||
nostr_cashu_proof_t* inputs;
|
||||
int input_count;
|
||||
cashu_blinded_output_t* outputs;
|
||||
int output_count;
|
||||
} cashu_swap_request_t;
|
||||
|
||||
typedef struct {
|
||||
cashu_blinded_signature_t* signatures;
|
||||
int signature_count;
|
||||
} cashu_swap_response_t;
|
||||
|
||||
typedef struct {
|
||||
char quote_id[128];
|
||||
nostr_cashu_proof_t* inputs;
|
||||
int input_count;
|
||||
} cashu_melt_tokens_request_t;
|
||||
|
||||
typedef struct {
|
||||
int paid;
|
||||
char* payment_preimage;
|
||||
cJSON* change;
|
||||
} cashu_melt_tokens_response_t;
|
||||
|
||||
typedef struct {
|
||||
char Y[256];
|
||||
char state[32];
|
||||
char witness[512];
|
||||
} cashu_proof_state_t;
|
||||
|
||||
typedef struct {
|
||||
cashu_proof_state_t* states;
|
||||
int state_count;
|
||||
} cashu_checkstate_response_t;
|
||||
|
||||
int cashu_mint_get_info(const char* mint_url,
|
||||
cashu_mint_info_t* info_out,
|
||||
int timeout_seconds);
|
||||
@@ -95,6 +154,55 @@ int cashu_mint_check_proofs_state(const char* mint_url,
|
||||
cJSON** response_out,
|
||||
int timeout_seconds);
|
||||
|
||||
int cashu_mint_get_active_keyset(const cashu_mint_info_t* info,
|
||||
const char* optional_unit,
|
||||
cashu_keyset_t* keyset_out);
|
||||
|
||||
int cashu_mint_select_proofs_for_amount(const nostr_cashu_proof_t* proofs,
|
||||
int proof_count,
|
||||
uint64_t target_amount,
|
||||
int* selected_indices_out,
|
||||
int selected_indices_cap,
|
||||
uint64_t* selected_total_out);
|
||||
|
||||
int cashu_mint_plan_split_amounts(uint64_t amount,
|
||||
uint64_t* amounts_out,
|
||||
int max_amounts,
|
||||
int* amount_count_out);
|
||||
|
||||
int cashu_build_mint_tokens_request(const cashu_mint_tokens_request_t* req,
|
||||
cJSON** request_body_out);
|
||||
|
||||
int cashu_parse_mint_tokens_response(cJSON* response_body,
|
||||
cashu_mint_tokens_response_t* response_out);
|
||||
|
||||
void cashu_mint_free_mint_tokens_response(cashu_mint_tokens_response_t* response);
|
||||
|
||||
int cashu_build_swap_request(const cashu_swap_request_t* req,
|
||||
cJSON** request_body_out);
|
||||
|
||||
int cashu_parse_swap_response(cJSON* response_body,
|
||||
cashu_swap_response_t* response_out);
|
||||
|
||||
void cashu_mint_free_swap_response(cashu_swap_response_t* response);
|
||||
|
||||
int cashu_build_melt_tokens_request(const cashu_melt_tokens_request_t* req,
|
||||
cJSON** request_body_out);
|
||||
|
||||
int cashu_parse_melt_tokens_response(cJSON* response_body,
|
||||
cashu_melt_tokens_response_t* response_out);
|
||||
|
||||
void cashu_mint_free_melt_tokens_response(cashu_melt_tokens_response_t* response);
|
||||
|
||||
int cashu_build_checkstate_request(const char** Ys,
|
||||
int y_count,
|
||||
cJSON** request_body_out);
|
||||
|
||||
int cashu_parse_checkstate_response(cJSON* response_body,
|
||||
cashu_checkstate_response_t* response_out);
|
||||
|
||||
void cashu_mint_free_checkstate_response(cashu_checkstate_response_t* response);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define NOSTR_CORE_H
|
||||
|
||||
// Version information (auto-updated by increment_and_push.sh)
|
||||
#define VERSION "v0.5.0"
|
||||
#define VERSION "v0.5.1"
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 5
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_PATCH 1
|
||||
|
||||
/*
|
||||
* NOSTR Core Library - Complete API Reference
|
||||
|
||||
Reference in New Issue
Block a user