mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 13:42:10 +02:00
`LocateErrors()` returns multiple useful positions for character and checksum errors, but an overlength string has one structural error. Every character from the limit onward is outside the permitted address, so listing each position adds no diagnostic value. `validateaddress` converts every returned position into a `UniValue` number before serializing the response. An authenticated request below the HTTP body limit can therefore require several gigabytes of memory. Return only the first position beyond the length limit, which identifies where the violation begins. Character and checksum errors continue to report multiple useful positions when they can be determined, and the existing unit and functional tests cover both behaviors.
162 lines
5.9 KiB
C++
162 lines
5.9 KiB
C++
// Copyright (c) 2017 Pieter Wuille
|
|
// Copyright (c) 2021-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <bech32.h>
|
|
#include <util/strencodings.h>
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <string>
|
|
|
|
BOOST_AUTO_TEST_SUITE(bech32_tests)
|
|
|
|
BOOST_AUTO_TEST_CASE(bech32_testvectors_valid)
|
|
{
|
|
static const std::string CASES[] = {
|
|
"A12UEL5L",
|
|
"a12uel5l",
|
|
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
|
|
"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
|
|
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
|
|
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
|
|
"?1ezyfcl",
|
|
};
|
|
for (const std::string& str : CASES) {
|
|
const auto dec = bech32::Decode(str);
|
|
BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32);
|
|
std::string recode = bech32::Encode(bech32::Encoding::BECH32, dec.hrp, dec.data);
|
|
BOOST_CHECK(!recode.empty());
|
|
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bech32m_testvectors_valid)
|
|
{
|
|
static const std::string CASES[] = {
|
|
"A1LQFN3A",
|
|
"a1lqfn3a",
|
|
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
|
|
"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
|
|
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
|
|
"split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
|
|
"?1v759aa"
|
|
};
|
|
for (const std::string& str : CASES) {
|
|
const auto dec = bech32::Decode(str);
|
|
BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32M);
|
|
std::string recode = bech32::Encode(bech32::Encoding::BECH32M, dec.hrp, dec.data);
|
|
BOOST_CHECK(!recode.empty());
|
|
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
|
|
{
|
|
static const std::string CASES[] = {
|
|
" 1nwldj5",
|
|
"\x7f""1axkwrx",
|
|
"\x80""1eym55h",
|
|
"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
|
|
"pzry9x0s0muk",
|
|
"1pzry9x0s0muk",
|
|
"x1b4n0q5v",
|
|
"li1dgmt3",
|
|
"de1lg7wt\xff",
|
|
"A1G7SGD8",
|
|
"10a06t8",
|
|
"1qzzfhee",
|
|
"a12UEL5L",
|
|
"A12uEL5L",
|
|
"abcdef1qpzrz9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
|
|
"test1zg69w7y6hn0aqy352euf40x77qddq3dc",
|
|
std::string(100, 'q'),
|
|
};
|
|
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}},
|
|
{"Bech32 string too long", {90}},
|
|
};
|
|
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);
|
|
auto [error, error_locations] = bech32::LocateErrors(str);
|
|
BOOST_CHECK_EQUAL(err.first, error);
|
|
BOOST_CHECK(err.second == error_locations);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bech32m_testvectors_invalid)
|
|
{
|
|
static const std::string CASES[] = {
|
|
" 1xj0phk",
|
|
"\x7f""1g6xzxy",
|
|
"\x80""1vctc34",
|
|
"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4",
|
|
"qyrz8wqd2c9m",
|
|
"1qyrz8wqd2c9m",
|
|
"y1b0jsk6g",
|
|
"lt1igcx5c0",
|
|
"in1muywd",
|
|
"mm1crxm3i",
|
|
"au1s5cgom",
|
|
"M1VUXWEZ",
|
|
"16plkw9",
|
|
"1p2gdwpf",
|
|
"abcdef1l7aum6echk45nj2s0wdvt2fg8x9yrzpqzd3ryx",
|
|
"test1zg69v7y60n00qy352euf40x77qcusag6",
|
|
};
|
|
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);
|
|
auto [error, error_locations] = bech32::LocateErrors(str);
|
|
BOOST_CHECK_EQUAL(err.first, error);
|
|
BOOST_CHECK(err.second == error_locations);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|