diff --git a/channeld/Makefile b/channeld/Makefile index f15bca3af..f96c23d17 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -33,8 +33,9 @@ ALL_GEN_HEADERS += $(LIGHTNINGD_CHANNEL_HEADERS_GEN) # Common source we use. CHANNELD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ - common/bip32.o \ + common/bip32.o \ common/channel_config.o \ common/crypto_state.o \ common/crypto_sync.o \ diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index 038a52d51..443551c61 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -12,6 +12,7 @@ static bool print_superverbose; #include #include #include +#include #include #include diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index d9062e2bf..b5d7baad9 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -7,11 +7,15 @@ #include #include #include +#include #include #include #include #include +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + void status_fmt(enum log_level level UNUSED, const char *fmt, ...) { va_list ap; diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c index cd85dbf68..bedf3d537 100644 --- a/cli/test/run-large-input.c +++ b/cli/test/run-large-input.c @@ -1,4 +1,5 @@ #include +#include #include #include #include diff --git a/closingd/Makefile b/closingd/Makefile index 639225f8a..d9e5768ef 100644 --- a/closingd/Makefile +++ b/closingd/Makefile @@ -42,6 +42,7 @@ $(LIGHTNINGD_CLOSING_OBJS): $(LIGHTNINGD_HEADERS) # Common source we use. CLOSINGD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bip32.o \ common/close_tx.o \ diff --git a/common/Makefile b/common/Makefile index a8a682210..cb4b4133c 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,4 +1,5 @@ COMMON_SRC_NOGEN := \ + common/amount.c \ common/base32.c \ common/bech32.c \ common/bech32_util.c \ diff --git a/common/amount.c b/common/amount.c new file mode 100644 index 000000000..e1b2a8b5f --- /dev/null +++ b/common/amount.c @@ -0,0 +1,426 @@ +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/common/amount.h b/common/amount.h new file mode 100644 index 000000000..e43c7b7ad --- /dev/null +++ b/common/amount.h @@ -0,0 +1,142 @@ +#ifndef LIGHTNING_COMMON_AMOUNT_H +#define LIGHTNING_COMMON_AMOUNT_H +#include "config.h" +#include +#include +#include +#include + +#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 */ diff --git a/common/bolt11.c b/common/bolt11.c index 7c0f4c4b6..4eea8078d 100644 --- a/common/bolt11.c +++ b/common/bolt11.c @@ -22,9 +22,6 @@ #include #include -/* 1000 * 10^8 millisatoshi == 1 bitcoin */ -#define MSAT_PER_BTC 100000000000ULL - struct multiplier { const char letter; /* We can't represent p postfix to msat, so we multiply this by 10 */ diff --git a/common/test/run-amount.c b/common/test/run-amount.c new file mode 100644 index 000000000..e12fb7eee --- /dev/null +++ b/common/test/run-amount.c @@ -0,0 +1,152 @@ +#include "../amount.c" +#include + +#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); +} diff --git a/common/type_to_string.h b/common/type_to_string.h index 45a5e125b..7c749f54c 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -30,6 +30,8 @@ union printable_types { const secp256k1_ecdsa_signature *secp256k1_ecdsa_signature; const struct bitcoin_signature *bitcoin_signature; const struct channel *channel; + const struct amount_msat *amount_msat; + const struct amount_sat *amount_sat; const char *charp_; }; diff --git a/connectd/Makefile b/connectd/Makefile index 3da35dfa2..de7a11fa9 100644 --- a/connectd/Makefile +++ b/connectd/Makefile @@ -37,6 +37,7 @@ LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_CONNECT_HEADERS) # Common source we use. CONNECTD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/devtools/Makefile b/devtools/Makefile index 4ea586a75..8018804b8 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -5,6 +5,7 @@ DEVTOOLS_TOOL_SRC := $(DEVTOOLS:=.c) DEVTOOLS_TOOL_OBJS := $(DEVTOOLS_TOOL_SRC:.c=.o) DEVTOOLS_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/gossipd/Makefile b/gossipd/Makefile index b8c96bcab..abe604f29 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -36,6 +36,7 @@ LIGHTNINGD_HEADERS_GEN += $(LIGHTNINGD_GOSSIP_HEADERS) # Common source we use. GOSSIPD_COMMON_OBJS := \ bitcoin/chainparams.o \ + common/amount.o \ common/base32.o \ common/bech32.o \ common/bech32_util.o \ diff --git a/hsmd/Makefile b/hsmd/Makefile index f13a6e5b7..432b05e56 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -13,6 +13,7 @@ LIGHTNINGD_HSM_OBJS := $(LIGHTNINGD_HSM_SRC:.c=.o) # Common source we use. HSMD_COMMON_OBJS := \ + common/amount.o \ common/bip32.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c index 42dbe5372..0a09c6452 100644 --- a/lightningd/test/run-find_my_abspath.c +++ b/lightningd/test/run-find_my_abspath.c @@ -4,6 +4,7 @@ int unused_main(int argc, char *argv[]); #include "../../common/wireaddr.c" #include "../lightningd.c" #include "../subd.c" +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for activate_peers */ diff --git a/onchaind/Makefile b/onchaind/Makefile index d9146f394..aa102f1d5 100644 --- a/onchaind/Makefile +++ b/onchaind/Makefile @@ -47,6 +47,7 @@ $(LIGHTNINGD_ONCHAIN_OBJS): $(LIGHTNINGD_HEADERS) # Common source we use. ONCHAIND_COMMON_OBJS := \ + common/amount.o \ common/bip32.o \ common/daemon.o \ common/daemon_conn.o \ diff --git a/openingd/Makefile b/openingd/Makefile index 20b69c52f..01847d90a 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -35,6 +35,7 @@ LIGHTNINGD_HEADERS_NOGEN += $(LIGHTNINGD_OPENING_HEADERS_NOGEN) # Common source we use. OPENINGD_COMMON_OBJS := \ + common/amount.o \ common/base32.o \ common/bip32.o \ common/channel_config.o \ diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 46a519e4d..424a1b7ac 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -45,7 +45,7 @@ class FieldType(object): self.name = name def is_assignable(self): - return self.name in ['u8', 'u16', 'u32', 'u64', 'bool'] or self.name.startswith('enum ') + return self.name in ['u8', 'u16', 'u32', 'u64', 'bool', 'struct amount_msat', 'struct amount_sat'] or self.name.startswith('enum ') # We only accelerate the u8 case: it's common and trivial. def has_array_helper(self): diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 86ece1bee..605c0348c 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -12,6 +12,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const c #include "test_utils.h" +#include #include #include #include diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index d9cf39a90..5f16016a6 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -19,6 +19,7 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const c #include #include +#include #include #include #include diff --git a/wire/fromwire.c b/wire/fromwire.c index d4962e33f..d542901b4 100644 --- a/wire/fromwire.c +++ b/wire/fromwire.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -270,3 +271,20 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max, { fromwire(cursor, max, seed, sizeof(*seed)); } + +struct amount_msat fromwire_amount_msat(const u8 **cursor, size_t *max) +{ + struct amount_msat msat; + + msat.millisatoshis = fromwire_u64(cursor, max); + return msat; +} + +struct amount_sat fromwire_amount_sat(const u8 **cursor, size_t *max) +{ + struct amount_sat sat; + + sat.satoshis = fromwire_u64(cursor, max); + return sat; +} + diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 5e156ed1f..6e9ba2676 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -13,10 +13,14 @@ void fromwire_pad_orig(const u8 **cursor, size_t *max, size_t num); #include #include +#include #include secp256k1_context *secp256k1_ctx; +/* AUTOGENERATED MOCKS START */ +/* AUTOGENERATED MOCKS END */ + /* We allow non-zero padding for testing. */ static const void *towire_pad_arr; void towire_pad(u8 **pptr, size_t num) diff --git a/wire/towire.c b/wire/towire.c index bec14906b..5de803e4a 100644 --- a/wire/towire.c +++ b/wire/towire.c @@ -8,6 +8,7 @@ #include #include #include +#include #include void towire(u8 **pptr, const void *data, size_t len) @@ -182,3 +183,13 @@ void towire_siphash_seed(u8 **pptr, const struct siphash_seed *seed) { towire(pptr, seed, sizeof(*seed)); } + +void towire_amount_msat(u8 **pptr, const struct amount_msat msat) +{ + towire_u64(pptr, msat.millisatoshis); +} + +void towire_amount_sat(u8 **pptr, const struct amount_sat sat) +{ + towire_u64(pptr, sat.satoshis); +} diff --git a/wire/wire.h b/wire/wire.h index 4b038dbe3..c615a8660 100644 --- a/wire/wire.h +++ b/wire/wire.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,8 @@ void towire_bitcoin_signature(u8 **pptr, const struct bitcoin_signature *sig); void towire_bitcoin_blkid(u8 **pptr, const struct bitcoin_blkid *blkid); void towire_preimage(u8 **pptr, const struct preimage *preimage); void towire_ripemd160(u8 **pptr, const struct ripemd160 *ripemd); +void towire_amount_msat(u8 **pptr, const struct amount_msat msat); +void towire_amount_sat(u8 **pptr, const struct amount_sat sat); void towire_u8(u8 **pptr, u8 v); void towire_u16(u8 **pptr, u16 v); void towire_u32(u8 **pptr, u32 v); @@ -102,6 +105,8 @@ void fromwire_bitcoin_blkid(const u8 **cursor, size_t *max, struct bitcoin_blkid *blkid); void fromwire_preimage(const u8 **cursor, size_t *max, struct preimage *preimage); void fromwire_ripemd160(const u8 **cursor, size_t *max, struct ripemd160 *ripemd); +struct amount_msat fromwire_amount_msat(const u8 **cursor, size_t *max); +struct amount_sat fromwire_amount_sat(const u8 **cursor, size_t *max); void fromwire_pad(const u8 **cursor, size_t *max, size_t num); void fromwire_u8_array(const u8 **cursor, size_t *max, u8 *arr, size_t num);