diff --git a/bitcoin/short_channel_id.c b/bitcoin/short_channel_id.c index 98fe4748b714..3cfab21ae9bd 100644 --- a/bitcoin/short_channel_id.c +++ b/bitcoin/short_channel_id.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -40,17 +41,27 @@ bool mk_short_channel_id(struct short_channel_id *scid, bool short_channel_id_from_str(const char *str, size_t strlen, struct short_channel_id *dst) { - u32 blocknum, txnum; - u16 outnum; - int matches; + const char *x1, *x2; + u64 blocknum, txnum, outnum; - char buf[strlen + 1]; - memcpy(buf, str, strlen); - buf[strlen] = 0; + x1 = memchr(str, 'x', strlen); + if (!x1) + return false; + + x2 = memchr(x1 + 1, 'x', strlen - (x1 + 1 - str)); + if (!x2) + return false; + + if (!str_to_u64(str, x1 - str, &blocknum)) + return false; + + if (!str_to_u64(x1 + 1, x2 - (x1 + 1), &txnum)) + return false; + + if (!str_to_u64(x2 + 1, (str + strlen) - (x2 + 1), &outnum)) + return false; - matches = sscanf(buf, "%ux%ux%hu", &blocknum, &txnum, &outnum); - return matches == 3 - && mk_short_channel_id(dst, blocknum, txnum, outnum); + return mk_short_channel_id(dst, blocknum, txnum, outnum); } char *fmt_short_channel_id(const tal_t *ctx, struct short_channel_id scid) diff --git a/bitcoin/test/run-short_channel_id.c b/bitcoin/test/run-short_channel_id.c new file mode 100644 index 000000000000..af947f40db7e --- /dev/null +++ b/bitcoin/test/run-short_channel_id.c @@ -0,0 +1,109 @@ +#include "config.h" +#include "../short_channel_id.c" +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for randbytes_ */ +void randbytes_(void *bytes UNNEEDED, size_t num_bytes UNNEEDED, u64 *offset UNNEEDED) +{ fprintf(stderr, "randbytes_ called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + struct short_channel_id scid, exp; + struct short_channel_id_dir scidd; + + common_setup(argv[0]); + + /* Valid canonical short_channel_id strings */ + assert(short_channel_id_from_str("539268x845x1", strlen("539268x845x1"), &scid)); + assert(mk_short_channel_id(&exp, 539268, 845, 1)); + assert(scid.u64 == exp.u64); + assert(short_channel_id_blocknum(scid) == 539268); + assert(short_channel_id_txnum(scid) == 845); + assert(short_channel_id_outnum(scid) == 1); + + /* Boundary valid values */ + assert(short_channel_id_from_str("0x0x0", strlen("0x0x0"), &scid)); + assert(mk_short_channel_id(&exp, 0, 0, 0)); + assert(scid.u64 == exp.u64); + + assert(short_channel_id_from_str("16777215x16777215x65535", + strlen("16777215x16777215x65535"), &scid)); + assert(mk_short_channel_id(&exp, 16777215, 16777215, 65535)); + assert(scid.u64 == exp.u64); + + /* Formatting */ + char *str = fmt_short_channel_id(tmpctx, scid); + assert(streq(str, "16777215x16777215x65535")); + + /* Valid short_channel_id_dir strings */ + assert(mk_short_channel_id(&exp, 539268, 845, 1)); + assert(short_channel_id_dir_from_str("539268x845x1/0", strlen("539268x845x1/0"), &scidd)); + assert(scidd.scid.u64 == exp.u64); + assert(scidd.dir == 0); + + assert(short_channel_id_dir_from_str("539268x845x1/1", strlen("539268x845x1/1"), &scidd)); + assert(scidd.dir == 1); + + /* Invalid trailing garbage */ + assert(!short_channel_id_from_str("1x2x3garbage", strlen("1x2x3garbage"), &scid)); + assert(!short_channel_id_from_str("1x2x3/0", strlen("1x2x3/0"), &scid)); + assert(!short_channel_id_from_str("1x2x3x4", strlen("1x2x3x4"), &scid)); + assert(!short_channel_id_from_str("1x2x3 4", strlen("1x2x3 4"), &scid)); + + /* Invalid signs and negative components */ + assert(!short_channel_id_from_str("1x2x-1", strlen("1x2x-1"), &scid)); + assert(!short_channel_id_from_str("-1x2x3", strlen("-1x2x3"), &scid)); + assert(!short_channel_id_from_str("1x-2x3", strlen("1x-2x3"), &scid)); + assert(!short_channel_id_from_str("+1x2x3", strlen("+1x2x3"), &scid)); + assert(!short_channel_id_from_str("1x+2x3", strlen("1x+2x3"), &scid)); + assert(!short_channel_id_from_str("1x2x+3", strlen("1x2x+3"), &scid)); + + /* Invalid whitespace */ + assert(!short_channel_id_from_str(" 1x2x3", strlen(" 1x2x3"), &scid)); + assert(!short_channel_id_from_str("1x2x3 ", strlen("1x2x3 "), &scid)); + assert(!short_channel_id_from_str("1x 2x3", strlen("1x 2x3"), &scid)); + assert(!short_channel_id_from_str("1x2x 3", strlen("1x2x 3"), &scid)); + + /* Incomplete or empty */ + assert(!short_channel_id_from_str("", 0, &scid)); + assert(!short_channel_id_from_str("1", 1, &scid)); + assert(!short_channel_id_from_str("1x", 2, &scid)); + assert(!short_channel_id_from_str("1x2", 3, &scid)); + assert(!short_channel_id_from_str("1x2x", 4, &scid)); + assert(!short_channel_id_from_str("x2x3", 4, &scid)); + assert(!short_channel_id_from_str("1xx3", 4, &scid)); + + /* Out of range components (including > 2^16, > 2^32, > 2^64) */ + assert(!short_channel_id_from_str("16777216x1x1", strlen("16777216x1x1"), &scid)); + assert(!short_channel_id_from_str("1x16777216x1", strlen("1x16777216x1"), &scid)); + assert(!short_channel_id_from_str("1x1x65536", strlen("1x1x65536"), &scid)); + assert(!short_channel_id_from_str("1x1x65537", strlen("1x1x65537"), &scid)); + assert(!short_channel_id_from_str("4294967296x1x1", strlen("4294967296x1x1"), &scid)); + assert(!short_channel_id_from_str("4294967297x1x1", strlen("4294967297x1x1"), &scid)); + assert(!short_channel_id_from_str("1x4294967296x1", strlen("1x4294967296x1"), &scid)); + assert(!short_channel_id_from_str("1x4294967297x1", strlen("1x4294967297x1"), &scid)); + assert(!short_channel_id_from_str("1x1x4294967296", strlen("1x1x4294967296"), &scid)); + assert(!short_channel_id_from_str("18446744073709551616x1x1", strlen("18446744073709551616x1x1"), &scid)); + assert(!short_channel_id_from_str("1x18446744073709551616x1", strlen("1x18446744073709551616x1"), &scid)); + assert(!short_channel_id_from_str("1x1x18446744073709551616", strlen("1x1x18446744073709551616"), &scid)); + assert(!short_channel_id_from_str("99999999999999999999x1x1", strlen("99999999999999999999x1x1"), &scid)); + + /* Invalid short_channel_id_dir */ + assert(!short_channel_id_dir_from_str("1x2x3garbage/0", strlen("1x2x3garbage/0"), &scidd)); + assert(!short_channel_id_dir_from_str("1x2x3/2", strlen("1x2x3/2"), &scidd)); + assert(!short_channel_id_dir_from_str("1x2x3/", strlen("1x2x3/"), &scidd)); + assert(!short_channel_id_dir_from_str("1x2x3/00", strlen("1x2x3/00"), &scidd)); + + common_shutdown(); + return 0; +}