From 9f8f0e6d45c70ac956f22d8102f998135d846b0b Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sat, 25 Mar 2023 22:33:24 +0000 Subject: [PATCH] codex32: introduce Lagrange interpolation and derived shares Github-Pull: #27351 Rebased-From: 7eced20085abbfa9412b726ae0f2f4e9013f1148 --- src/codex32.cpp | 84 +++++++++++ src/codex32.h | 10 ++ src/test/codex32_tests.cpp | 293 +++++++++++++++++++++++++++++++++++++ 3 files changed, 387 insertions(+) diff --git a/src/codex32.cpp b/src/codex32.cpp index 06632f3e98..13b5361129 100644 --- a/src/codex32.cpp +++ b/src/codex32.cpp @@ -78,6 +78,14 @@ uint8_t gf32_mul(uint8_t x, uint8_t y) { return GF32_EXP[(GF32_LOG[x] + GF32_LOG[y]) % 31]; } +uint8_t gf32_div(uint8_t x, uint8_t y) { + assert(y != 0); + if (x == 0) { + return 0; + } + return GF32_EXP[(GF32_LOG[x] + 31 - GF32_LOG[y]) % 31]; +} + // The bech32 string "secretshare32" constexpr const std::array CODEX32_M = { 16, 25, 24, 3, 25, 11, 16, 23, 29, 3, 25, 17, 10 @@ -191,6 +199,24 @@ data CreateChecksum(const std::string& hrp, const data& values, const Residue& g return ret; } +// Given a set of share indices and a target index `idx`, which must be in the set, +// compute the Lagrange basis polynomial for `idx` evaluated at the point `eval`. +// +// All inputs are GF32 elements, rather than array indices or anything else. +uint8_t lagrange_coefficient(std::vector& indices, uint8_t idx, uint8_t eval) { + uint8_t num = 1; + uint8_t den = 1; + for (const auto idx_i : indices) { + if (idx_i != idx) { + num = gf32_mul(num, idx_i ^ eval); + den = gf32_mul(den, idx_i ^ idx); + } + } + + // return num / den + return gf32_div(num, den); +} + } // namespace /** Encode a codex32 string. */ @@ -299,6 +325,64 @@ Result::Result(std::string&& hrp, size_t k, const std::string& id, char share_id ConvertBits<8, 5, true>([&](unsigned char c) { m_data.push_back(c); }, data.begin(), data.end()); } +Result::Result(const std::vector& shares, char output_idx) { + m_valid = OK; + + int8_t oidx = bech32::internal::CHARSET_REV[(unsigned char) output_idx]; + if (oidx == -1) { + m_valid = INVALID_SHARE_IDX; + } + if (shares.empty()) { + m_valid = TOO_FEW_SHARES; + return; + } + size_t k = shares[0].GetK(); + if (k > shares.size()) { + m_valid = TOO_FEW_SHARES; + } + if (m_valid != OK) { + return; + } + + std::vector indices; + indices.reserve(shares.size()); + for (size_t i = 0; i < shares.size(); ++i) { + // Currently the only supported hrp is "ms" so it is impossible to violate this + assert (shares[0].m_hrp == shares[i].m_hrp); + if (shares[0].m_data[0] != shares[i].m_data[0]) { + m_valid = MISMATCH_K; + } + for (size_t j = 1; j < 5; ++j) { + if (shares[0].m_data[j] != shares[i].m_data[j]) { + m_valid = MISMATCH_ID; + } + } + if (shares[i].m_data.size() != shares[0].m_data.size()) { + m_valid = MISMATCH_LENGTH; + } + + indices.push_back(shares[i].m_data[5]); + for (size_t j = i + 1; j < shares.size(); ++j) { + if (shares[i].m_data[5] == shares[j].m_data[5]) { + m_valid = DUPLICATE_SHARE; + } + } + } + + m_hrp = shares[0].m_hrp; + m_data.reserve(shares[0].m_data.size()); + for (size_t j = 0; j < shares[0].m_data.size(); ++j) { + m_data.push_back(0); + } + + for (size_t i = 0; i < shares.size(); ++i) { + uint8_t lagrange_coeff = lagrange_coefficient(indices, shares[i].m_data[5], oidx); + for (size_t j = 0; j < m_data.size(); ++j) { + m_data[j] ^= gf32_mul(lagrange_coeff, shares[i].m_data[j]); + } + } +} + std::string Result::GetIdString() const { assert(IsValid()); diff --git a/src/codex32.h b/src/codex32.h index 7dd49c6fc8..6f2f9ba1bf 100644 --- a/src/codex32.h +++ b/src/codex32.h @@ -34,6 +34,11 @@ enum Error { INVALID_LENGTH, INVALID_K, INVALID_SHARE_IDX, + TOO_FEW_SHARES, + DUPLICATE_SHARE, + MISMATCH_K, + MISMATCH_ID, + MISMATCH_LENGTH, }; class Result @@ -48,6 +53,11 @@ public: * ignore the case of `id` and `share_idx`. */ Result(std::string&& hrp, size_t k, const std::string& id, char share_idx, const std::vector& data); + /** Construct a codex32 result by interpolating a set of input shares to obtain an output share + * + * Requires that all input shares have the same k and seed ID */ + Result(const std::vector& shares, char output_idx); + /** Boolean indicating whether the data was successfully parsed. * * If this returns false, most of the other methods on this class will assert. */ diff --git a/src/test/codex32_tests.cpp b/src/test/codex32_tests.cpp index 8df80ab390..17427add89 100644 --- a/src/test/codex32_tests.cpp +++ b/src/test/codex32_tests.cpp @@ -79,6 +79,299 @@ BOOST_AUTO_TEST_CASE(codex32_bip93_vector_2) BOOST_CHECK_EQUAL(dec_c.GetIdString(), "name"); BOOST_CHECK_EQUAL(dec_c.GetShareIndex(), 'c'); BOOST_CHECK(CaseInsensitiveEqual(input_c, dec_c.Encode())); + + const auto d = codex32::Result{{input_a, input_c}, 'd'}; + BOOST_CHECK(d.IsValid()); + BOOST_CHECK_EQUAL(d.GetHrp(), "ms"); + BOOST_CHECK_EQUAL(d.GetK(), 2); + BOOST_CHECK_EQUAL(d.GetIdString(), "name"); + BOOST_CHECK_EQUAL(d.GetShareIndex(), 'd'); + BOOST_CHECK(CaseInsensitiveEqual("MS12NAMEDLL4F8JLH4E5VDVULDLFXU2JHDNLSM97XVENRXEG", d.Encode())); + + const auto err1 = codex32::Result{{}, 's'}; + BOOST_CHECK_EQUAL(err1.error(), codex32::TOO_FEW_SHARES); + const auto err2 = codex32::Result{{input_c}, 's'}; + BOOST_CHECK_EQUAL(err2.error(), codex32::TOO_FEW_SHARES); + const auto err3 = codex32::Result{{input_a, input_c}, 'b'}; + BOOST_CHECK_EQUAL(err3.error(), codex32::INVALID_SHARE_IDX); + + const auto s = codex32::Result{{input_a, input_c}, 's'}; + BOOST_CHECK(s.IsValid()); + BOOST_CHECK_EQUAL(s.GetHrp(), "ms"); + BOOST_CHECK_EQUAL(s.GetK(), 2); + BOOST_CHECK_EQUAL(s.GetIdString(), "name"); + BOOST_CHECK_EQUAL(s.GetShareIndex(), 's'); + BOOST_CHECK(CaseInsensitiveEqual("MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW", s.Encode())); + + const auto seed = s.GetPayload(); + BOOST_CHECK_EQUAL(seed.size(), 16); + BOOST_CHECK_EQUAL(HexStr(seed), "d1808e096b35b209ca12132b264662a5"); +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_vector_3) +{ + const auto s = codex32::Result("ms", 3, "Cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")); + BOOST_CHECK(s.IsValid()); + BOOST_CHECK_EQUAL(s.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(s.GetShareIndex(), 's'); + BOOST_CHECK_EQUAL(s.GetK(), 3); + BOOST_CHECK_EQUAL(s.Encode(), "ms13cashsllhdmn9m42vcsamx24zrxgs3qqjzqud4m0d6nln"); + + const auto a = codex32::Result{"ms13casha320zyxwvutsrqpnmlkjhgfedca2a8d0zehn8a0t"}; + BOOST_CHECK(a.IsValid()); + BOOST_CHECK_EQUAL(a.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(a.GetShareIndex(), 'a'); + BOOST_CHECK_EQUAL(a.GetK(), 3); + + const auto c = codex32::Result{"ms13cashcacdefghjklmnpqrstuvwxyz023949xq35my48dr"}; + BOOST_CHECK(c.IsValid()); + BOOST_CHECK_EQUAL(c.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(c.GetShareIndex(), 'c'); + BOOST_CHECK_EQUAL(c.GetK(), 3); + + const auto err1 = codex32::Result{{}, 'd'}; + BOOST_CHECK_EQUAL(err1.error(), codex32::TOO_FEW_SHARES); + const auto err2 = codex32::Result{{a, c}, 'd'}; + BOOST_CHECK_EQUAL(err2.error(), codex32::TOO_FEW_SHARES); + const auto err3 = codex32::Result{{s, a}, 'd'}; + BOOST_CHECK_EQUAL(err3.error(), codex32::TOO_FEW_SHARES); + const auto err4 = codex32::Result{{s, s, a}, 'd'}; + BOOST_CHECK_EQUAL(err4.error(), codex32::DUPLICATE_SHARE); + + const auto d = codex32::Result{{s, a, c}, 'd'}; + BOOST_CHECK(d.IsValid()); + BOOST_CHECK_EQUAL(d.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(d.GetShareIndex(), 'd'); + BOOST_CHECK_EQUAL(d.GetK(), 3); + BOOST_CHECK_EQUAL(d.Encode(), "ms13cashd0wsedstcdcts64cd7wvy4m90lm28w4ffupqs7rm"); + + const auto e = codex32::Result{{a, c, d}, 'e'}; + BOOST_CHECK(e.IsValid()); + BOOST_CHECK_EQUAL(e.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(e.GetShareIndex(), 'e'); + BOOST_CHECK_EQUAL(e.GetK(), 3); + BOOST_CHECK_EQUAL(e.Encode(), "ms13casheekgpemxzshcrmqhaydlp6yhms3ws7320xyxsar9"); + + const auto f = codex32::Result{{a, s, d}, 'f'}; + BOOST_CHECK(f.IsValid()); + BOOST_CHECK_EQUAL(f.GetIdString(), "cash"); + BOOST_CHECK_EQUAL(f.GetShareIndex(), 'f'); + BOOST_CHECK_EQUAL(f.GetK(), 3); + BOOST_CHECK_EQUAL(f.Encode(), "ms13cashf8jh6sdrkpyrsp5ut94pj8ktehhw2hfvyrj48704"); + + // Mismatched data + const auto g1 = codex32::Result{"ms", 2, "cash", 'g', ParseHex("ffeeddccbbaa99887766554433221100")}; + BOOST_CHECK(g1.IsValid()); + const auto err_s1 = codex32::Result{{a, c, g1}, 's'}; + BOOST_CHECK_EQUAL(err_s1.error(), codex32::MISMATCH_K); + + const auto g2 = codex32::Result{"ms", 3, "leet", 'g', ParseHex("ffeeddccbbaa99887766554433221100")}; + BOOST_CHECK(g2.IsValid()); + const auto err_s2 = codex32::Result{{a, c, g2}, 's'}; + BOOST_CHECK_EQUAL(err_s2.error(), codex32::MISMATCH_ID); + + const auto g3 = codex32::Result{"ms", 3, "cash", 'g', ParseHex("ffeeddccbbaa99887766554433221100ab")}; + BOOST_CHECK(g3.IsValid()); + const auto err_s3 = codex32::Result{{a, c, g3}, 's'}; + BOOST_CHECK_EQUAL(err_s3.error(), codex32::MISMATCH_LENGTH); +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_vector_4) +{ + const std::string seed = "ffeeddccbbaa99887766554433221100ffeeddccbbaa99887766554433221100"; + const auto s = codex32::Result{"ms", 0, "leet", 's', ParseHex(seed)}; + BOOST_CHECK(s.IsValid()); + BOOST_CHECK_EQUAL(s.GetIdString(), "leet"); + BOOST_CHECK_EQUAL(s.GetShareIndex(), 's'); + BOOST_CHECK_EQUAL(s.GetK(), 0); + BOOST_CHECK_EQUAL(HexStr(s.GetPayload()), seed); + BOOST_CHECK_EQUAL(s.Encode(), "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma"); + + std::vector alternates = { + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqqtum9pgv99ycma", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqpj82dp34u6lqtd", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqzsrs4pnh7jmpj5", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqrfcpap2w8dqezy", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqy5tdvphn6znrf0", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq9dsuypw2ragmel", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqx05xupvgp4v6qx", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq8k0h5p43c2hzsk", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqgum7hplmjtr8ks", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqf9q0lpxzt5clxq", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq28y48pyqfuu7le", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqt7ly0paesr8x0f", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqvrvg7pqydv5uyz", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqd6hekpea5n0y5j", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyqwcnrwpmlkmt9dt", + "ms10leetsllhdmn9m42vcsamx24zrxgs3qrl7ahwvhw4fnzrhve25gvezzyq0pgjxpzx0ysaam", + }; + for (const auto& alt : alternates) { + const auto s_alt = codex32::Result{alt}; + BOOST_CHECK(s_alt.IsValid()); + BOOST_CHECK_EQUAL(HexStr(s_alt.GetPayload()), seed); + } +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_vector_5) +{ + const auto s = codex32::Result{"MS100C8VSM32ZXFGUHPCHTLUPZRY9X8GF2TVDW0S3JN54KHCE6MUA7LQPZYGSFJD6AN074RXVCEMLH8WU3TK925ACDEFGHJKLMNPQRSTUVWXY06FHPV80UNDVARHRAK"}; + BOOST_CHECK(s.IsValid()); + BOOST_CHECK_EQUAL(s.error(), codex32::OK); + BOOST_CHECK_EQUAL(s.GetIdString(), "0c8v"); + BOOST_CHECK_EQUAL(s.GetShareIndex(), 's'); + BOOST_CHECK_EQUAL(s.GetK(), 0); + BOOST_CHECK_EQUAL(HexStr(s.GetPayload()), "dc5423251cb87175ff8110c8531d0952d8d73e1194e95b5f19d6f9df7c01111104c9baecdfea8cccc677fb9ddc8aec5553b86e528bcadfdcc201c17c638c47e9"); +} + +BOOST_AUTO_TEST_CASE(codex32_errors) +{ + const std::vector errs = { + codex32::Result("ms", 3, "cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + // bad hrp + codex32::Result("bc", 3, "cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + // bad ID len + codex32::Result("ms", 3, "cas", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 3, "cashcashcash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 3, "", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + // bad id char + codex32::Result("ms", 3, "bash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + // bad k + codex32::Result("ms", 1, "cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 10, "cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 100000, "cash", 's', ParseHex("ffeeddccbbaa99887766554433221100")), + // bad share idx + codex32::Result("ms", 100000, "cash", 'b', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 100000, "cash", 'i', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 100000, "cash", '1', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 100000, "cash", 'o', ParseHex("ffeeddccbbaa99887766554433221100")), + codex32::Result("ms", 100000, "cash", ' ', ParseHex("ffeeddccbbaa99887766554433221100")), + }; + + BOOST_CHECK_EQUAL(errs[0].error(), codex32::OK); + BOOST_CHECK_EQUAL(errs[1].error(), codex32::INVALID_HRP); + BOOST_CHECK_EQUAL(errs[2].error(), codex32::INVALID_ID_LEN); + BOOST_CHECK_EQUAL(errs[3].error(), codex32::INVALID_ID_LEN); + BOOST_CHECK_EQUAL(errs[4].error(), codex32::INVALID_ID_LEN); + BOOST_CHECK_EQUAL(errs[5].error(), codex32::INVALID_ID_CHAR); + BOOST_CHECK_EQUAL(errs[6].error(), codex32::INVALID_K); + BOOST_CHECK_EQUAL(errs[7].error(), codex32::INVALID_K); + BOOST_CHECK_EQUAL(errs[8].error(), codex32::INVALID_K); + BOOST_CHECK_EQUAL(errs[9].error(), codex32::INVALID_SHARE_IDX); + BOOST_CHECK_EQUAL(errs[10].error(), codex32::INVALID_SHARE_IDX); + BOOST_CHECK_EQUAL(errs[11].error(), codex32::INVALID_SHARE_IDX); + BOOST_CHECK_EQUAL(errs[12].error(), codex32::INVALID_SHARE_IDX); + BOOST_CHECK_EQUAL(errs[13].error(), codex32::INVALID_SHARE_IDX); + +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_invalid_1) +{ + std::vector bad_checksum = { + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghq", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxve740yyge2ghp", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxlk3yepcstwr", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx6pgnv7jnpcsp", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxx0cpvr7n4geq", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxm5252y7d3lr", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxrd9sukzl05ej", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxc55srw5jrm0", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxgc7rwhtudwc", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx4gy22afwghvs", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pe0", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxme084q0vpht7pew", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxqyadsp3nywm8a", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxzvg7ar4hgaejk", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcznau0advgxqe", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxch3jrc6j5040j", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx52gxl6ppv40mcv", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx7g4g2nhhle8fk", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx63m45uj8ss4x8", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxy4r708q7kg65x", + }; + for (const auto& bad : bad_checksum) { + auto res = codex32::Result{bad}; + BOOST_CHECK_EQUAL(res.error(), codex32::BAD_CHECKSUM); + } +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_invalid_2) +{ + std::vector bad_checksum = { + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxurfvwmdcmymdufv", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxcsyppjkd8lz4hx3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxu6hwvl5p0l9xf3c", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwqey9rfs6smenxa", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxv70wkzrjr4ntqet", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx3hmlrmpa4zl0v", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxrfggf88znkaup", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpt7l4aycv9qzj", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxus27z9xtyxyw3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcwm4re8fs78vn", + }; + for (const auto& bad : bad_checksum) { + auto res = codex32::Result{bad}; + BOOST_CHECK(res.error() == codex32::BAD_CHECKSUM || res.error() == codex32::INVALID_LENGTH); + } +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_invalid_3) +{ + std::vector bad_checksum = { + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxw0a4c70rfefn4", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxk4pavy5n46nea", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxx9lrwar5zwng4w", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxr335l5tv88js3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxvu7q9nz8p7dj68v", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxpq6k542scdxndq3", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxkmfw6jm270mz6ej", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxzhddxw99w7xws", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxx42cux6um92rz", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxarja5kqukdhy9", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxky0ua3ha84qk8", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9eheesxadh2n2n9", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx9llwmgesfulcj2z", + "ms12fauxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx02ev7caq6n9fgkf", + }; + for (const auto& bad : bad_checksum) { + auto res = codex32::Result{bad}; + BOOST_CHECK_EQUAL(res.error(), codex32::INVALID_LENGTH); + } +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_invalid_hrp) +{ + std::vector bad_checksum = { + "0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "0fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws", + "10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxhkd4f70m8lgws", + "m10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxx8t28z74x8hs4l", + "s10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxh9d0fhnvfyx3x", + }; + for (const auto& bad : bad_checksum) { + auto res = codex32::Result{bad}; + BOOST_CHECK(res.error() == codex32::INVALID_HRP || res.error() == codex32::BECH32_DECODE); + } +} + +BOOST_AUTO_TEST_CASE(codex32_bip93_invalid_case) +{ + std::vector bad_checksum = { + "Ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "mS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "MS10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10FAUXsxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10fauxSxxxxxxxxxxxxxxxxxxxxxxxxxxuqxkk05lyf3x2", + "ms10fauxsXXXXXXXXXXXXXXXXXXXXXXXXXXuqxkk05lyf3x2", + "ms10fauxsxxxxxxxxxxxxxxxxxxxxxxxxxxUQXKK05LYF3X2", + }; + for (const auto& bad : bad_checksum) { + auto res = codex32::Result{bad}; + BOOST_CHECK_EQUAL(res.error(), codex32::BECH32_DECODE); + } } BOOST_AUTO_TEST_SUITE_END()