Merge bitcoin/bitcoin#23577: Follow-ups to Bech32 error detection

a4fe70171b Make Bech32 LocateErrors return error list rather than using out-arg (Samuel Dobson)
2fa4fd1961 Use std::iota instead of manually pushing range (Samuel Dobson)
405c96fc9f Use bounds-checked array lookups in Bech32 error detection code (Samuel Dobson)
28d9c2857f Simplify encoding of e in GF(1024) tables to (1,0) (Samuel Dobson)
14358a029d Replace GF1024 tables and syndrome constants with compile-time generated constexprs. (Samuel Dobson)
63f7b69779 Update release note for bech32 error detection (Samuel Dobson)
c8b9a224e7 Report encoding type in bech32 error message (Samuel Dobson)
92f0cafdca Improve Bech32 boost tests (Samuel Dobson)
bb4d3e9b97 Address review comments for Bech32 error validation (Samuel Dobson)

Pull request description:

  A number of follow-ups and improvements to the bech32 error location code, introduced in #16807.

  Notably, this removes the hardcoded GF1024 tables in favour of constexpr table generation.

ACKs for top commit:
  laanwj:
    Re-ACK a4fe70171b

Tree-SHA512: 6312373c20ebd6636f5797304876fa0d70fa777de2f6c507245f51a652b3d1224ebc55b236c9e11e6956c1e88e65faadab51d53587078efccb451455aa2e2276
This commit is contained in:
W. J. van der Laan
2021-12-06 10:37:53 +01:00
6 changed files with 231 additions and 321 deletions

View File

@@ -70,34 +70,36 @@ BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
"a12UEL5L",
"A12uEL5L",
"abcdef1qpzrz9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
"test1zg69w7y6hn0aqy352euf40x77qddq3dc",
};
static const std::pair<std::string, int> ERRORS[] = {
{"Invalid character or mixed case", 0},
{"Invalid character or mixed case", 0},
{"Invalid character or mixed case", 0},
{"Bech32 string too long", 90},
{"Missing separator", -1},
{"Invalid separator position", 0},
{"Invalid Base 32 character", 2},
{"Invalid separator position", 2},
{"Invalid character or mixed case", 8},
{"Invalid checksum", -1}, // The checksum is calculated using the uppercase form so the entire string is invalid, not just a few characters
{"Invalid separator position", 0},
{"Invalid separator position", 0},
{"Invalid character or mixed case", 3},
{"Invalid character or mixed case", 3},
{"Invalid checksum", 11}
static const std::pair<std::string, std::vector<int>> ERRORS[] = {
{"Invalid character or mixed case", {0}},
{"Invalid character or mixed case", {0}},
{"Invalid character or mixed case", {0}},
{"Bech32 string too long", {90}},
{"Missing separator", {}},
{"Invalid separator position", {0}},
{"Invalid Base 32 character", {2}},
{"Invalid separator position", {2}},
{"Invalid character or mixed case", {8}},
{"Invalid checksum", {}}, // The checksum is calculated using the uppercase form so the entire string is invalid, not just a few characters
{"Invalid separator position", {0}},
{"Invalid separator position", {0}},
{"Invalid character or mixed case", {3, 4, 5, 7}},
{"Invalid character or mixed case", {3}},
{"Invalid Bech32 checksum", {11}},
{"Invalid Bech32 checksum", {9, 16}},
};
static_assert(std::size(CASES) == std::size(ERRORS), "Bech32 CASES and ERRORS should have the same length");
int i = 0;
for (const std::string& str : CASES) {
const auto& err = ERRORS[i];
const auto dec = bech32::Decode(str);
BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
std::vector<int> error_locations;
std::string error = bech32::LocateErrors(str, error_locations);
auto [error, error_locations] = bech32::LocateErrors(str);
BOOST_CHECK_EQUAL(err.first, error);
if (err.second == -1) BOOST_CHECK(error_locations.empty());
else BOOST_CHECK_EQUAL(err.second, error_locations[0]);
BOOST_CHECK(err.second == error_locations);
i++;
}
}
@@ -120,34 +122,36 @@ BOOST_AUTO_TEST_CASE(bech32m_testvectors_invalid)
"16plkw9",
"1p2gdwpf",
"abcdef1l7aum6echk45nj2s0wdvt2fg8x9yrzpqzd3ryx",
"test1zg69v7y60n00qy352euf40x77qcusag6",
};
static const std::pair<std::string, int> ERRORS[] = {
{"Invalid character or mixed case", 0},
{"Invalid character or mixed case", 0},
{"Invalid character or mixed case", 0},
{"Bech32 string too long", 90},
{"Missing separator", -1},
{"Invalid separator position", 0},
{"Invalid Base 32 character", 2},
{"Invalid Base 32 character", 3},
{"Invalid separator position", 2},
{"Invalid Base 32 character", 8},
{"Invalid Base 32 character", 7},
{"Invalid checksum", -1},
{"Invalid separator position", 0},
{"Invalid separator position", 0},
{"Invalid checksum", 21},
static const std::pair<std::string, std::vector<int>> ERRORS[] = {
{"Invalid character or mixed case", {0}},
{"Invalid character or mixed case", {0}},
{"Invalid character or mixed case", {0}},
{"Bech32 string too long", {90}},
{"Missing separator", {}},
{"Invalid separator position", {0}},
{"Invalid Base 32 character", {2}},
{"Invalid Base 32 character", {3}},
{"Invalid separator position", {2}},
{"Invalid Base 32 character", {8}},
{"Invalid Base 32 character", {7}},
{"Invalid checksum", {}},
{"Invalid separator position", {0}},
{"Invalid separator position", {0}},
{"Invalid Bech32m checksum", {21}},
{"Invalid Bech32m checksum", {13, 32}},
};
static_assert(std::size(CASES) == std::size(ERRORS), "Bech32m CASES and ERRORS should have the same length");
int i = 0;
for (const std::string& str : CASES) {
const auto& err = ERRORS[i];
const auto dec = bech32::Decode(str);
BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
std::vector<int> error_locations;
std::string error = bech32::LocateErrors(str, error_locations);
auto [error, error_locations] = bech32::LocateErrors(str);
BOOST_CHECK_EQUAL(err.first, error);
if (err.second == -1) BOOST_CHECK(error_locations.empty());
else BOOST_CHECK_EQUAL(err.second, error_locations[0]);
BOOST_CHECK(err.second == error_locations);
i++;
}
}