Browse Source
They're generally used pass-by-copy (unusual for C structs, but convenient they're basically u64) and all possibly problematic operations return WARN_UNUSED_RESULT bool to make you handle the over/underflow cases. The new #include in json.h means we bolt11.c sees the amount.h definition of MSAT_PER_BTC, so delete its local version. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>confirmed-only
Rusty Russell
6 years ago
25 changed files with 780 additions and 5 deletions
@ -0,0 +1,426 @@ |
|||
#include <assert.h> |
|||
#include <ccan/mem/mem.h> |
|||
#include <ccan/tal/str/str.h> |
|||
#include <common/amount.h> |
|||
#include <common/overflows.h> |
|||
#include <common/type_to_string.h> |
|||
#include <inttypes.h> |
|||
|
|||
bool amount_sat_to_msat(struct amount_msat *msat, |
|||
struct amount_sat sat) |
|||
{ |
|||
if (mul_overflows_u64(sat.satoshis, MSAT_PER_SAT)) |
|||
return false; |
|||
msat->millisatoshis = sat.satoshis * MSAT_PER_SAT; |
|||
return true; |
|||
} |
|||
|
|||
/* You can always truncate millisatoshis->satoshis. */ |
|||
struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat) |
|||
{ |
|||
struct amount_sat sat; |
|||
|
|||
sat.satoshis = msat.millisatoshis / MSAT_PER_SAT; |
|||
return sat; |
|||
} |
|||
|
|||
/* You may not be able to do this. */ |
|||
bool amount_msat_to_sat_exact(struct amount_sat *sat, |
|||
const struct amount_msat *msat) |
|||
{ |
|||
if (msat->millisatoshis % MSAT_PER_SAT != 0) |
|||
return false; |
|||
sat->satoshis = msat->millisatoshis / MSAT_PER_SAT; |
|||
return true; |
|||
} |
|||
|
|||
/* Different formatting by amounts: btc, sat and msat */ |
|||
const char *fmt_amount_msat_btc(const tal_t *ctx, |
|||
const struct amount_msat *msat, |
|||
bool append_unit) |
|||
{ |
|||
return tal_fmt(ctx, "%"PRIu64".%011"PRIu64"%s", |
|||
msat->millisatoshis / MSAT_PER_BTC, |
|||
msat->millisatoshis % MSAT_PER_BTC, |
|||
append_unit ? "btc" : ""); |
|||
} |
|||
|
|||
const char *fmt_amount_msat(const tal_t *ctx, const struct amount_msat *msat) |
|||
{ |
|||
return tal_fmt(ctx, "%"PRIu64"msat", msat->millisatoshis); |
|||
} |
|||
REGISTER_TYPE_TO_STRING(amount_msat, fmt_amount_msat); |
|||
|
|||
const char *fmt_amount_sat_btc(const tal_t *ctx, |
|||
const struct amount_sat *sat, |
|||
bool append_unit) |
|||
{ |
|||
return tal_fmt(ctx, "%"PRIu64".%08"PRIu64"%s", |
|||
sat->satoshis / SAT_PER_BTC, |
|||
sat->satoshis % SAT_PER_BTC, |
|||
append_unit ? "btc" : ""); |
|||
} |
|||
|
|||
const char *fmt_amount_sat(const tal_t *ctx, const struct amount_sat *sat) |
|||
{ |
|||
return tal_fmt(ctx, "%"PRIu64"sat", sat->satoshis); |
|||
} |
|||
REGISTER_TYPE_TO_STRING(amount_sat, fmt_amount_sat); |
|||
|
|||
static bool breakup(const char *str, size_t slen, |
|||
/* Length of first numeric part. */ |
|||
size_t *whole_number_len, |
|||
/* Pointer to post-decimal part, or NULL */ |
|||
const char **post_decimal_ptr, |
|||
size_t *post_decimal_len, |
|||
/* Pointer to suffix, or NULL */ |
|||
const char **suffix_ptr, |
|||
size_t *suffix_len) |
|||
{ |
|||
size_t i; |
|||
|
|||
*whole_number_len = 0; |
|||
*post_decimal_len = 0; |
|||
*post_decimal_ptr = NULL; |
|||
*suffix_ptr = NULL; |
|||
*suffix_len = 0; |
|||
|
|||
for (i = 0;; i++) { |
|||
if (i >= slen) |
|||
return i != 0; |
|||
if (cisdigit(str[i])) |
|||
(*whole_number_len)++; |
|||
else |
|||
break; |
|||
} |
|||
|
|||
if (str[i] == '.') { |
|||
i++; |
|||
*post_decimal_ptr = str + i; |
|||
for (;; i++) { |
|||
/* True if > 0 decimals. */ |
|||
if (i >= slen) |
|||
return str + i != *post_decimal_ptr; |
|||
if (cisdigit(str[i])) |
|||
(*post_decimal_len)++; |
|||
else |
|||
break; |
|||
} |
|||
} |
|||
|
|||
*suffix_ptr = str + i; |
|||
*suffix_len = slen - i; |
|||
return true; |
|||
} |
|||
|
|||
static bool from_number(u64 *res, const char *s, size_t len, u64 multipler) |
|||
{ |
|||
if (len == 0) |
|||
return false; |
|||
|
|||
*res = 0; |
|||
for (size_t i = 0; i < len; i++) { |
|||
if (mul_overflows_u64(*res, 10)) |
|||
return false; |
|||
*res *= 10; |
|||
assert(cisdigit(s[i])); |
|||
if (add_overflows_u64(*res, s[i] - '0')) |
|||
return false; |
|||
*res += s[i] - '0'; |
|||
} |
|||
if (mul_overflows_u64(*res, multipler)) |
|||
return false; |
|||
*res *= multipler; |
|||
return true; |
|||
} |
|||
|
|||
static bool from_numbers(u64 *res, |
|||
const char *s1, size_t len1, u64 multipler1, |
|||
const char *s2, size_t len2, u64 multipler2) |
|||
{ |
|||
u64 p1, p2; |
|||
if (!from_number(&p1, s1, len1, multipler1) |
|||
|| !from_number(&p2, s2, len2, multipler2)) |
|||
return false; |
|||
|
|||
if (add_overflows_u64(p1, p2)) |
|||
return false; |
|||
|
|||
*res = p1 + p2; |
|||
return true; |
|||
} |
|||
|
|||
/* Valid strings:
|
|||
* [0-9]+ => millisatoshi. |
|||
* [0-9]+msat => millisatoshi. |
|||
* [0-9]+sat => *1000 -> millisatopshi. |
|||
* [0-9]+.[0-9]{8}btc => *1000 -> millisatoshi. |
|||
* [0-9]+.[0-9]{11}btc => millisatoshi. |
|||
*/ |
|||
bool parse_amount_msat(struct amount_msat *msat, const char *s, size_t slen) |
|||
{ |
|||
size_t whole_number_len, post_decimal_len, suffix_len; |
|||
const char *post_decimal_ptr, *suffix_ptr; |
|||
|
|||
if (!breakup(s, slen, &whole_number_len, |
|||
&post_decimal_ptr, &post_decimal_len, |
|||
&suffix_ptr, &suffix_len)) |
|||
return false; |
|||
|
|||
if (!post_decimal_ptr && !suffix_ptr) |
|||
return from_number(&msat->millisatoshis, s, whole_number_len, 1); |
|||
if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "msat")) |
|||
return from_number(&msat->millisatoshis, s, whole_number_len, 1); |
|||
if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "sat")) |
|||
return from_number(&msat->millisatoshis, s, whole_number_len, |
|||
MSAT_PER_SAT); |
|||
if (post_decimal_ptr && post_decimal_len == 8 |
|||
&& memeqstr(suffix_ptr, suffix_len, "btc")) |
|||
return from_numbers(&msat->millisatoshis, |
|||
s, whole_number_len, |
|||
MSAT_PER_BTC, |
|||
post_decimal_ptr, post_decimal_len, |
|||
MSAT_PER_SAT); |
|||
if (post_decimal_ptr && post_decimal_len == 11 |
|||
&& memeqstr(suffix_ptr, suffix_len, "btc")) |
|||
return from_numbers(&msat->millisatoshis, |
|||
s, whole_number_len, |
|||
MSAT_PER_BTC, |
|||
post_decimal_ptr, post_decimal_len, 1); |
|||
return false; |
|||
} |
|||
|
|||
/* Valid strings:
|
|||
* [0-9]+ => satoshi. |
|||
* [0-9]+sat => satoshi. |
|||
* [0-9]+000msat => satoshi. |
|||
* [0-9]+.[0-9]{8}btc => satoshi. |
|||
*/ |
|||
bool parse_amount_sat(struct amount_sat *sat, const char *s, size_t slen) |
|||
{ |
|||
size_t whole_number_len, post_decimal_len, suffix_len; |
|||
const char *post_decimal_ptr, *suffix_ptr; |
|||
|
|||
if (!breakup(s, slen, &whole_number_len, |
|||
&post_decimal_ptr, &post_decimal_len, |
|||
&suffix_ptr, &suffix_len)) |
|||
return false; |
|||
|
|||
if (!post_decimal_ptr && !suffix_ptr) |
|||
return from_number(&sat->satoshis, s, whole_number_len, 1); |
|||
if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "sat")) |
|||
return from_number(&sat->satoshis, s, whole_number_len, 1); |
|||
if (!post_decimal_ptr && memeqstr(suffix_ptr, suffix_len, "msat")) { |
|||
if (!memends(s, whole_number_len, "000", strlen("000"))) |
|||
return false; |
|||
return from_number(&sat->satoshis, s, whole_number_len - 3, 1); |
|||
} |
|||
if (post_decimal_ptr && post_decimal_len == 8 |
|||
&& memeqstr(suffix_ptr, suffix_len, "btc")) |
|||
return from_numbers(&sat->satoshis, |
|||
s, whole_number_len, |
|||
SAT_PER_BTC, |
|||
post_decimal_ptr, post_decimal_len, |
|||
1); |
|||
|
|||
return false; |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_msat b) |
|||
{ |
|||
if (add_overflows_u64(a.millisatoshis, b.millisatoshis)) |
|||
return false; |
|||
|
|||
val->millisatoshis = a.millisatoshis + b.millisatoshis; |
|||
return true; |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_msat_sub(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_msat b) |
|||
{ |
|||
if (a.millisatoshis < b.millisatoshis) |
|||
return false; |
|||
|
|||
val->millisatoshis = a.millisatoshis - b.millisatoshis; |
|||
return true; |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_sat_add(struct amount_sat *val, |
|||
struct amount_sat a, |
|||
struct amount_sat b) |
|||
{ |
|||
if (add_overflows_u64(a.satoshis, b.satoshis)) |
|||
return false; |
|||
|
|||
val->satoshis = a.satoshis + b.satoshis; |
|||
return true; |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_sat_sub(struct amount_sat *val, |
|||
struct amount_sat a, |
|||
struct amount_sat b) |
|||
{ |
|||
if (a.satoshis < b.satoshis) |
|||
return false; |
|||
|
|||
val->satoshis = a.satoshis - b.satoshis; |
|||
return true; |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_msat_sub_sat(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_sat b) |
|||
{ |
|||
struct amount_msat msatb; |
|||
|
|||
if (!amount_sat_to_msat(&msatb, b)) |
|||
return false; |
|||
|
|||
return amount_msat_sub(val, a, msatb); |
|||
} |
|||
|
|||
WARN_UNUSED_RESULT bool amount_sat_sub_msat(struct amount_msat *val, |
|||
struct amount_sat a, |
|||
struct amount_msat b) |
|||
{ |
|||
struct amount_msat msata; |
|||
|
|||
if (!amount_sat_to_msat(&msata, a)) |
|||
return false; |
|||
|
|||
return amount_msat_sub(val, msata, b); |
|||
} |
|||
|
|||
bool amount_sat_eq(struct amount_sat a, struct amount_sat b) |
|||
{ |
|||
return a.satoshis == b.satoshis; |
|||
} |
|||
|
|||
bool amount_msat_eq(struct amount_msat a, struct amount_msat b) |
|||
{ |
|||
return a.millisatoshis == b.millisatoshis; |
|||
} |
|||
|
|||
bool amount_sat_greater(struct amount_sat a, struct amount_sat b) |
|||
{ |
|||
return a.satoshis > b.satoshis; |
|||
} |
|||
|
|||
bool amount_msat_greater(struct amount_msat a, struct amount_msat b) |
|||
{ |
|||
return a.millisatoshis > b.millisatoshis; |
|||
} |
|||
|
|||
bool amount_sat_greater_eq(struct amount_sat a, struct amount_sat b) |
|||
{ |
|||
return a.satoshis >= b.satoshis; |
|||
} |
|||
|
|||
bool amount_msat_greater_eq(struct amount_msat a, struct amount_msat b) |
|||
{ |
|||
return a.millisatoshis >= b.millisatoshis; |
|||
} |
|||
|
|||
bool amount_sat_less(struct amount_sat a, struct amount_sat b) |
|||
{ |
|||
return a.satoshis < b.satoshis; |
|||
} |
|||
|
|||
bool amount_msat_less(struct amount_msat a, struct amount_msat b) |
|||
{ |
|||
return a.millisatoshis < b.millisatoshis; |
|||
} |
|||
|
|||
bool amount_sat_less_eq(struct amount_sat a, struct amount_sat b) |
|||
{ |
|||
return a.satoshis <= b.satoshis; |
|||
} |
|||
|
|||
bool amount_msat_less_eq(struct amount_msat a, struct amount_msat b) |
|||
{ |
|||
return a.millisatoshis <= b.millisatoshis; |
|||
} |
|||
|
|||
bool amount_msat_greater_sat(struct amount_msat msat, struct amount_sat sat) |
|||
{ |
|||
struct amount_msat msat_from_sat; |
|||
|
|||
if (!amount_sat_to_msat(&msat_from_sat, sat)) |
|||
return false; |
|||
return msat.millisatoshis > msat_from_sat.millisatoshis; |
|||
} |
|||
|
|||
bool amount_msat_greater_eq_sat(struct amount_msat msat, struct amount_sat sat) |
|||
{ |
|||
struct amount_msat msat_from_sat; |
|||
|
|||
if (!amount_sat_to_msat(&msat_from_sat, sat)) |
|||
return false; |
|||
return msat.millisatoshis >= msat_from_sat.millisatoshis; |
|||
} |
|||
|
|||
bool amount_msat_less_sat(struct amount_msat msat, struct amount_sat sat) |
|||
{ |
|||
struct amount_msat msat_from_sat; |
|||
|
|||
if (!amount_sat_to_msat(&msat_from_sat, sat)) |
|||
return false; |
|||
return msat.millisatoshis < msat_from_sat.millisatoshis; |
|||
} |
|||
|
|||
bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat) |
|||
{ |
|||
struct amount_msat msat_from_sat; |
|||
|
|||
if (!amount_sat_to_msat(&msat_from_sat, sat)) |
|||
return false; |
|||
return msat.millisatoshis <= msat_from_sat.millisatoshis; |
|||
} |
|||
|
|||
bool amount_msat_fee(struct amount_msat *fee, |
|||
struct amount_msat amt, |
|||
u32 fee_base_msat, |
|||
u32 fee_proportional_millionths) |
|||
{ |
|||
struct amount_msat fee_base, fee_prop; |
|||
|
|||
/* BOLT #7:
|
|||
* |
|||
* - SHOULD accept HTLCs that pay a fee equal to or greater than: |
|||
* - fee_base_msat + ( amount_to_forward * fee_proportional_millionths / 1000000 ) |
|||
*/ |
|||
fee_base.millisatoshis = fee_base_msat; |
|||
|
|||
if (mul_overflows_u64(amt.millisatoshis, fee_proportional_millionths)) |
|||
return false; |
|||
fee_prop.millisatoshis = amt.millisatoshis * fee_proportional_millionths |
|||
/ 1000000; |
|||
|
|||
return amount_msat_add(fee, fee_base, fee_prop); |
|||
} |
|||
|
|||
bool amount_msat_add_fee(struct amount_msat *amt, |
|||
u32 fee_base_msat, |
|||
u32 fee_proportional_millionths) |
|||
{ |
|||
struct amount_msat fee; |
|||
|
|||
if (!amount_msat_fee(&fee, *amt, |
|||
fee_base_msat, fee_proportional_millionths)) |
|||
return false; |
|||
return amount_msat_add(amt, *amt, fee); |
|||
} |
|||
|
|||
struct amount_sat amount_tx_fee(u32 fee_per_kw, size_t weight) |
|||
{ |
|||
struct amount_sat fee; |
|||
|
|||
/* If this overflows, weight must be > 2^32, which is not a real tx */ |
|||
assert(!mul_overflows_u64(fee_per_kw, weight)); |
|||
fee.satoshis = fee_per_kw * weight / 1000; |
|||
|
|||
return fee; |
|||
} |
@ -0,0 +1,142 @@ |
|||
#ifndef LIGHTNING_COMMON_AMOUNT_H |
|||
#define LIGHTNING_COMMON_AMOUNT_H |
|||
#include "config.h" |
|||
#include <ccan/build_assert/build_assert.h> |
|||
#include <ccan/short_types/short_types.h> |
|||
#include <ccan/tal/tal.h> |
|||
#include <stdbool.h> |
|||
|
|||
#define MSAT_PER_SAT ((u64)1000) |
|||
#define SAT_PER_BTC ((u64)100000000) |
|||
#define MSAT_PER_BTC (MSAT_PER_SAT * SAT_PER_BTC) |
|||
|
|||
/* Use these to wrap amounts, for typesafety. Please use ops where possible,
|
|||
* rather than accessing the members directly. */ |
|||
struct amount_sat { |
|||
/* Amount in satoshis. */ |
|||
u64 satoshis; |
|||
}; |
|||
|
|||
struct amount_msat { |
|||
/* Amount in millisatoshis. */ |
|||
u64 millisatoshis; |
|||
}; |
|||
|
|||
/* For constants only: others must be built from primitives! */ |
|||
#if HAVE_BUILTIN_CONSTANT_P |
|||
#define AMOUNT_MUST_BE_CONST(c) BUILD_ASSERT_OR_ZERO(IS_COMPILE_CONSTANT(c)) |
|||
#else |
|||
#define AMOUNT_MUST_BE_CONST(c) 0 |
|||
#endif |
|||
|
|||
#define AMOUNT_MSAT(constant) \ |
|||
((struct amount_msat){(constant) + AMOUNT_MUST_BE_CONST(constant)}) |
|||
|
|||
#define AMOUNT_SAT(constant) \ |
|||
((struct amount_sat){(constant) + AMOUNT_MUST_BE_CONST(constant)}) |
|||
|
|||
/* You may not always be able to convert satoshis->millisatoshis. */ |
|||
WARN_UNUSED_RESULT bool amount_sat_to_msat(struct amount_msat *msat, |
|||
struct amount_sat sat); |
|||
|
|||
/* This may require rounding. */ |
|||
WARN_UNUSED_RESULT bool amount_msat_to_sat_exact(struct amount_sat *, |
|||
const struct amount_msat *); |
|||
|
|||
/* You can always truncate millisatoshis->satoshis. */ |
|||
struct amount_sat amount_msat_to_sat_round_down(struct amount_msat msat); |
|||
|
|||
/* Simple operations: val = a + b, val = a - b. */ |
|||
WARN_UNUSED_RESULT bool amount_msat_add(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_msat b); |
|||
WARN_UNUSED_RESULT bool amount_msat_sub(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_msat b); |
|||
WARN_UNUSED_RESULT bool amount_sat_add(struct amount_sat *val, |
|||
struct amount_sat a, |
|||
struct amount_sat b); |
|||
WARN_UNUSED_RESULT bool amount_sat_sub(struct amount_sat *val, |
|||
struct amount_sat a, |
|||
struct amount_sat b); |
|||
WARN_UNUSED_RESULT bool amount_msat_sub_sat(struct amount_msat *val, |
|||
struct amount_msat a, |
|||
struct amount_sat b); |
|||
WARN_UNUSED_RESULT bool amount_sat_sub_msat(struct amount_msat *val, |
|||
struct amount_sat a, |
|||
struct amount_msat b); |
|||
|
|||
/* Is a == b? */ |
|||
bool amount_sat_eq(struct amount_sat a, struct amount_sat b); |
|||
bool amount_msat_eq(struct amount_msat a, struct amount_msat b); |
|||
|
|||
/* Is a > b? */ |
|||
bool amount_sat_greater(struct amount_sat a, struct amount_sat b); |
|||
bool amount_msat_greater(struct amount_msat a, struct amount_msat b); |
|||
|
|||
/* Is a >= b */ |
|||
bool amount_sat_greater_eq(struct amount_sat a, struct amount_sat b); |
|||
bool amount_msat_greater_eq(struct amount_msat a, struct amount_msat b); |
|||
|
|||
/* Is a < b? */ |
|||
bool amount_sat_less(struct amount_sat a, struct amount_sat b); |
|||
bool amount_msat_less(struct amount_msat a, struct amount_msat b); |
|||
|
|||
/* Is a <= b? */ |
|||
bool amount_sat_less_eq(struct amount_sat a, struct amount_sat b); |
|||
bool amount_msat_less_eq(struct amount_msat a, struct amount_msat b); |
|||
|
|||
/* Is msat > sat? */ |
|||
bool amount_msat_greater_sat(struct amount_msat msat, struct amount_sat sat); |
|||
/* Is msat >= sat? */ |
|||
bool amount_msat_greater_eq_sat(struct amount_msat msat, struct amount_sat sat); |
|||
/* Is msat < sat? */ |
|||
bool amount_msat_less_sat(struct amount_msat msat, struct amount_sat sat); |
|||
/* Is msat <= sat? */ |
|||
bool amount_msat_less_eq_sat(struct amount_msat msat, struct amount_sat sat); |
|||
|
|||
/* Common operation: what is the HTLC fee for given feerate? Can overflow! */ |
|||
WARN_UNUSED_RESULT bool amount_msat_fee(struct amount_msat *fee, |
|||
struct amount_msat amt, |
|||
u32 fee_base_msat, |
|||
u32 fee_proportional_millionths); |
|||
|
|||
/* Same, but add into amt. */ |
|||
WARN_UNUSED_RESULT bool amount_msat_add_fee(struct amount_msat *amt, |
|||
u32 fee_base_msat, |
|||
u32 fee_proportional_millionths); |
|||
|
|||
/* What is the fee for this tx weight? */ |
|||
struct amount_sat amount_tx_fee(u32 fee_per_kw, size_t weight); |
|||
|
|||
/* Different formatting by amounts: btc, sat and msat */ |
|||
const char *fmt_amount_msat_btc(const tal_t *ctx, |
|||
const struct amount_msat *msat, |
|||
bool append_unit); |
|||
/* 1234msat */ |
|||
const char *fmt_amount_msat(const tal_t *ctx, const struct amount_msat *msat); |
|||
|
|||
const char *fmt_amount_sat_btc(const tal_t *ctx, |
|||
const struct amount_sat *sat, |
|||
bool append_unit); |
|||
/* 1234sat */ |
|||
const char *fmt_amount_sat(const tal_t *ctx, const struct amount_sat *sat); |
|||
|
|||
/* Valid strings:
|
|||
* [0-9]+ => millisatoshi. |
|||
* [0-9]+msat => millisatoshi. |
|||
* [0-9]+sat => *1000 -> millisatopshi. |
|||
* [0-9]+.[0-9]{8}btc => *1000 -> millisatoshi. |
|||
* [0-9]+.[0-9]{11}btc => millisatoshi. |
|||
*/ |
|||
bool parse_amount_msat(struct amount_msat *msat, const char *s, size_t slen); |
|||
|
|||
/* Valid strings:
|
|||
* [0-9]+ => satoshi. |
|||
* [0-9]+sat => satoshi. |
|||
* [0-9]+000msat => satoshi. |
|||
* [0-9]+.[0-9]{8}btc => satoshi. |
|||
*/ |
|||
bool parse_amount_sat(struct amount_sat *sat, const char *s, size_t slen); |
|||
|
|||
#endif /* LIGHTNING_COMMON_AMOUNT_H */ |
@ -0,0 +1,152 @@ |
|||
#include "../amount.c" |
|||
#include <common/utils.h> |
|||
|
|||
#define FAIL_MSAT(msatp, str) \ |
|||
assert(!parse_amount_msat((msatp), (str), strlen(str))) |
|||
#define PASS_MSAT(msatp, str, val) \ |
|||
do { \ |
|||
assert(parse_amount_msat((msatp), (str), strlen(str))); \ |
|||
assert((msatp)->millisatoshis == val); \ |
|||
} while (0) |
|||
|
|||
#define FAIL_SAT(satp, str) \ |
|||
assert(!parse_amount_sat((satp), (str), strlen(str))) |
|||
#define PASS_SAT(satp, str, val) \ |
|||
do { \ |
|||
assert(parse_amount_sat((satp), (str), strlen(str))); \ |
|||
assert((satp)->satoshis == val); \ |
|||
} while (0) |
|||
|
|||
int main(void) |
|||
{ |
|||
struct amount_msat msat; |
|||
struct amount_sat sat; |
|||
|
|||
setup_locale(); |
|||
setup_tmpctx(); |
|||
|
|||
/* Grossly malformed */ |
|||
FAIL_MSAT(&msat, "x"); |
|||
FAIL_MSAT(&msat, "x100"); |
|||
|
|||
PASS_MSAT(&msat, "0", 0); |
|||
PASS_MSAT(&msat, "1", 1); |
|||
PASS_MSAT(&msat, "2100000000000000000", 2100000000000000000ULL); |
|||
FAIL_MSAT(&msat, "0.0"); |
|||
FAIL_MSAT(&msat, "0.00000000"); |
|||
FAIL_MSAT(&msat, "0.00000000000"); |
|||
FAIL_MSAT(&msat, "0.00000000msat"); |
|||
FAIL_MSAT(&msat, "0.00000000000msat"); |
|||
|
|||
PASS_MSAT(&msat, "0msat", 0); |
|||
PASS_MSAT(&msat, "1msat", 1); |
|||
PASS_MSAT(&msat, "2100000000000000000msat", 2100000000000000000ULL); |
|||
|
|||
PASS_MSAT(&msat, "0sat", 0); |
|||
PASS_MSAT(&msat, "1sat", 1000); |
|||
PASS_MSAT(&msat, "2100000000000000sat", 2100000000000000000ULL); |
|||
|
|||
PASS_MSAT(&msat, "0.00000000btc", 0); |
|||
PASS_MSAT(&msat, "0.00000000000btc", 0); |
|||
PASS_MSAT(&msat, "0.00000001btc", 1000); |
|||
PASS_MSAT(&msat, "0.00000000001btc", 1); |
|||
PASS_MSAT(&msat, "1.23456789btc", 123456789000); |
|||
PASS_MSAT(&msat, "1.23456789012btc", 123456789012); |
|||
FAIL_MSAT(&msat, "1btc"); |
|||
FAIL_MSAT(&msat, "1.0000000btc"); |
|||
FAIL_MSAT(&msat, "1.000000000btc"); |
|||
|
|||
/* Overflowingly big. */ |
|||
FAIL_MSAT(&msat, "21000000000000000000000000.00000000btc"); |
|||
|
|||
/* Grossly malformed */ |
|||
FAIL_SAT(&sat, "x"); |
|||
FAIL_SAT(&sat, "x100"); |
|||
|
|||
PASS_SAT(&sat, "0", 0); |
|||
PASS_SAT(&sat, "1", 1); |
|||
PASS_SAT(&sat, "2100000000000000", 2100000000000000ULL); |
|||
FAIL_SAT(&sat, "0.0"); |
|||
FAIL_SAT(&sat, "0.00000000"); |
|||
FAIL_SAT(&sat, "0.00000000000"); |
|||
FAIL_SAT(&sat, "0.00000000sat"); |
|||
FAIL_SAT(&sat, "0.00000000000msat"); |
|||
|
|||
PASS_SAT(&sat, "0sat", 0); |
|||
PASS_SAT(&sat, "1sat", 1); |
|||
PASS_SAT(&sat, "2100000000000000sat", 2100000000000000ULL); |
|||
|
|||
PASS_SAT(&sat, "1000msat", 1); |
|||
PASS_SAT(&sat, "1000000msat", 1000); |
|||
PASS_SAT(&sat, "2100000000000000000msat", 2100000000000000ULL); |
|||
FAIL_SAT(&sat, "0msat"); |
|||
FAIL_SAT(&sat, "100msat"); |
|||
FAIL_SAT(&sat, "2000000000000000999msat"); |
|||
|
|||
PASS_SAT(&sat, "0.00000000btc", 0); |
|||
FAIL_SAT(&sat, "0.00000000000btc"); |
|||
PASS_SAT(&sat, "0.00000001btc", 1); |
|||
FAIL_SAT(&sat, "0.00000000001btc"); |
|||
PASS_SAT(&sat, "1.23456789btc", 123456789); |
|||
FAIL_SAT(&sat, "1.23456789012btc"); |
|||
FAIL_SAT(&sat, "1btc"); |
|||
FAIL_SAT(&sat, "1.0000000btc"); |
|||
FAIL_SAT(&sat, "1.000000000btc"); |
|||
|
|||
/* Overflowingly big. */ |
|||
FAIL_SAT(&sat, "21000000000000000000000000.00000000btc"); |
|||
|
|||
/* Test fmt_amount_msat_btc, fmt_amount_msat */ |
|||
for (u64 i = 0; i <= UINT64_MAX / 10; i = i ? i * 10 : 1) { |
|||
const char *with, *without; |
|||
|
|||
msat.millisatoshis = i; |
|||
with = fmt_amount_msat_btc(tmpctx, &msat, true); |
|||
without = fmt_amount_msat_btc(tmpctx, &msat, false); |
|||
assert(strends(with, "btc")); |
|||
assert(strlen(with) == strlen(without) + 3); |
|||
assert(strncmp(with, without, strlen(without)) == 0); |
|||
/* Make sure it overwrites. */ |
|||
msat.millisatoshis++; |
|||
assert(parse_amount_msat(&msat, with, strlen(with))); |
|||
assert(msat.millisatoshis == i); |
|||
|
|||
with = fmt_amount_msat(tmpctx, &msat); |
|||
without = tal_fmt(tmpctx, "%"PRIu64, msat.millisatoshis); |
|||
assert(strends(with, "msat")); |
|||
assert(strlen(with) == strlen(without) + 4); |
|||
assert(strncmp(with, without, strlen(without)) == 0); |
|||
/* Make sure it overwrites. */ |
|||
msat.millisatoshis++; |
|||
assert(parse_amount_msat(&msat, with, strlen(with))); |
|||
assert(msat.millisatoshis == i); |
|||
} |
|||
|
|||
/* Test fmt_amount_sat_btc, fmt_amount_sat */ |
|||
for (u64 i = 0; i <= UINT64_MAX / 10; i = i ? i * 10 : 1) { |
|||
const char *with, *without; |
|||
|
|||
sat.satoshis = i; |
|||
with = fmt_amount_sat_btc(tmpctx, &sat, true); |
|||
without = fmt_amount_sat_btc(tmpctx, &sat, false); |
|||
assert(strends(with, "btc")); |
|||
assert(strlen(with) == strlen(without) + 3); |
|||
assert(strncmp(with, without, strlen(without)) == 0); |
|||
/* Make sure it overwrites. */ |
|||
sat.satoshis++; |
|||
assert(parse_amount_sat(&sat, with, strlen(with))); |
|||
assert(sat.satoshis == i); |
|||
|
|||
with = fmt_amount_sat(tmpctx, &sat); |
|||
without = tal_fmt(tmpctx, "%"PRIu64, sat.satoshis); |
|||
assert(strends(with, "sat")); |
|||
assert(strlen(with) == strlen(without) + 3); |
|||
assert(strncmp(with, without, strlen(without)) == 0); |
|||
/* Make sure it overwrites. */ |
|||
sat.satoshis++; |
|||
assert(parse_amount_sat(&sat, with, strlen(with))); |
|||
assert(sat.satoshis == i); |
|||
} |
|||
|
|||
tal_free(tmpctx); |
|||
} |
Loading…
Reference in new issue