Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 12 additions & 20 deletions common/json_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,29 +507,21 @@ const char *json_scan(const tal_t *ctx,
bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok,
uint64_t *satoshi)
{
char *end;
unsigned long btc, sat;
const char *start = buffer + tok->start;
size_t len = tok->end - tok->start;
const char *dot = memchr(start, '.', len);
u64 btc, sat = 0;

errno = 0;
btc = strtoul(buffer + tok->start, &end, 10);
if (btc == ULONG_MAX && errno == ERANGE)
return false;
if (end != buffer + tok->end) {
/* Expect always 8 decimal places. */
if (*end != '.' || buffer + tok->end - end != 9)
return false;
sat = strtoul(end+1, &end, 10);
if (sat == ULONG_MAX && errno == ERANGE)
return false;
if (end != buffer + tok->end)
if (dot) {
if (start + len - (dot + 1) != 8 || !str_to_u64(dot + 1, 8, &sat))
return false;
} else
sat = 0;

*satoshi = btc * (uint64_t)100000000 + sat;
if (*satoshi != btc * (uint64_t)100000000 + sat)
len = dot - start;
}
if (!str_to_u64(start, len, &btc))
return false;

if (mul_overflows_u64(btc, 100000000) || add_overflows_u64(btc * 100000000, sat))
return false;
*satoshi = btc * 100000000 + sat;
return true;
}

Expand Down
42 changes: 31 additions & 11 deletions common/test/run-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,32 +51,52 @@ void towire_tlv(u8 **pptr UNNEEDED,

// issue #577

static void do_json_tok_bitcoin_amount(const char* val, uint64_t expected)
static void do_json_tok_bitcoin_amount(const char *val, bool ok, uint64_t expected)
{
uint64_t amount;
jsmntok_t tok;

tok.start = 0;
tok.end = strlen(val);

fprintf(stderr, "do_json_tok_bitcoin_amount(\"%s\", %"PRIu64"): ", val, expected);
fprintf(stderr, "do_json_tok_bitcoin_amount(\"%s\", %s, %"PRIu64"): ",
val, ok ? "true" : "false", expected);

assert(json_to_bitcoin_amount(val, &tok, &amount) == true);
assert(amount == expected);
assert(json_to_bitcoin_amount(val, &tok, &amount) == ok);
if (ok)
assert(amount == expected);

fprintf(stderr, "ok\n");
}


static int test_json_tok_bitcoin_amount(void)
{
do_json_tok_bitcoin_amount("0.00000001", 1);
do_json_tok_bitcoin_amount("0.00000007", 7);
do_json_tok_bitcoin_amount("0.00000008", 8);
do_json_tok_bitcoin_amount("0.00000010", 10);
do_json_tok_bitcoin_amount("0.12345678", 12345678);
do_json_tok_bitcoin_amount("0.01234567", 1234567);
do_json_tok_bitcoin_amount("123.45678900", 12345678900);
do_json_tok_bitcoin_amount("0", true, 0);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add overflow path

Suggested change
do_json_tok_bitcoin_amount("0", true, 0);
do_json_tok_bitcoin_amount("184467440737.09551615", true, UINT64_MAX);
do_json_tok_bitcoin_amount("184467440737.09551616", false, 0); /* add overflows */
do_json_tok_bitcoin_amount("1.+0000001", false, 0);
do_json_tok_bitcoin_amount("1.-0000001", false, 0);
do_json_tok_bitcoin_amount(" 1.00000000", false, 0);

do_json_tok_bitcoin_amount("0.00000000", true, 0);
do_json_tok_bitcoin_amount("0.00000001", true, 1);
do_json_tok_bitcoin_amount("0.00000007", true, 7);
do_json_tok_bitcoin_amount("0.00000008", true, 8);
do_json_tok_bitcoin_amount("0.00000010", true, 10);
do_json_tok_bitcoin_amount("0.12345678", true, 12345678);
do_json_tok_bitcoin_amount("0.01234567", true, 1234567);
do_json_tok_bitcoin_amount("123.45678900", true, 12345678900);
do_json_tok_bitcoin_amount("21000000.00000000", true, 2100000000000000ULL);

do_json_tok_bitcoin_amount("184467440737.09551615", true, UINT64_MAX);

/* Invalid inputs */
do_json_tok_bitcoin_amount("", false, 0);
do_json_tok_bitcoin_amount("-1", false, 0);
do_json_tok_bitcoin_amount("-0.00000001", false, 0);
do_json_tok_bitcoin_amount("+1.00000000", false, 0);
do_json_tok_bitcoin_amount("1.0000000", false, 0); /* 7 decimals */
do_json_tok_bitcoin_amount("1.000000000", false, 0); /* 9 decimals */
do_json_tok_bitcoin_amount("184467440738.00000000", false, 0); /* overflows u64 */
do_json_tok_bitcoin_amount("184467440737.09551616", false, 0); /* add overflows */
do_json_tok_bitcoin_amount("1.+0000001", false, 0);
do_json_tok_bitcoin_amount("1.-0000001", false, 0);
do_json_tok_bitcoin_amount(" 1.00000000", false, 0);

return 0;
}
Expand Down