Skip to content

Commit 4c166e1

Browse files
Merge #6531: backport: finish merging 25001
e714607 Merge bitcoin#25001: Modernize util/strencodings and util/string (pasta) f6bd506 rename: ValidAsCString -> ContainsNoNUL (pasta) 0ce624f Use std::string_view throughout util strencodings/string (Pieter Wuille) 5271cfa fix: DecodeBase64 changes in dash code (pasta) b6122ea Make DecodeBase{32,64} take string_view arguments (Pieter Wuille) 7e5c0b5 Generalize ConvertBits to permit transforming the input (Pieter Wuille) 9519ace Make DecodeBase{32,64} return optional instead of taking bool* (Pieter Wuille) 765ec3f Make DecodeBase{32,64} always return vector, not string (Pieter Wuille) Pull request description: ## Issue being fixed or feature implemented This was partially done earlier; do the remaining commits ## What was done? see commits ## How Has This Been Tested? built locally ## Breaking Changes none ## Checklist: _Go over all the following points, and put an `x` in all the boxes that apply._ - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK e714607 Tree-SHA512: e65c347a551c30b04f398ecd2d45388cbc04374a035726be35138dbd39d4921162ecee440056acd13d83aeec1b15ba6db6744c0de62a725859de0177f7c5f3ef
2 parents 80cd18e + e714607 commit 4c166e1

26 files changed

+198
-234
lines changed

src/base58.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ std::string EncodeBase58(Span<const unsigned char> input)
126126

127127
bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len)
128128
{
129-
if (!ValidAsCString(str)) {
129+
if (!ContainsNoNUL(str)) {
130130
return false;
131131
}
132132
return DecodeBase58(str.c_str(), vchRet, max_ret_len);
@@ -160,7 +160,7 @@ std::string EncodeBase58Check(Span<const unsigned char> input)
160160

161161
bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret)
162162
{
163-
if (!ValidAsCString(str)) {
163+
if (!ContainsNoNUL(str)) {
164164
return false;
165165
}
166166
return DecodeBase58Check(str.c_str(), vchRet, max_ret);

src/bitcoin-tx.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
223223
template <typename T>
224224
static T TrimAndParse(const std::string& int_str, const std::string& err)
225225
{
226-
const auto parsed{ToIntegral<T>(TrimString(int_str))};
226+
const auto parsed{ToIntegral<T>(TrimStringView(int_str))};
227227
if (!parsed.has_value()) {
228228
throw std::runtime_error(err + " '" + int_str + "'");
229229
}

src/httprpc.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,11 @@ static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUserna
171171
return false;
172172
if (strAuth.substr(0, 6) != "Basic ")
173173
return false;
174-
std::string strUserPass64 = TrimString(strAuth.substr(6));
175-
bool invalid;
176-
std::string strUserPass = DecodeBase64(strUserPass64, &invalid);
177-
if (invalid) return false;
174+
std::string_view strUserPass64 = TrimStringView(std::string_view{strAuth}.substr(6));
175+
auto userpass_data = DecodeBase64(strUserPass64);
176+
std::string strUserPass;
177+
if (!userpass_data) return false;
178+
strUserPass.assign(userpass_data->begin(), userpass_data->end());
178179

179180
if (strUserPass.find(':') != std::string::npos)
180181
strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));

src/httpserver.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,11 @@ static void http_request_cb(struct evhttp_request* req, void* arg)
272272
return false;
273273

274274
std::string strUserPass64 = TrimString(strAuth.substr(6));
275-
bool invalid;
276-
std::string strUserPass = DecodeBase64(strUserPass64, &invalid);
277-
if (invalid) return false;
278-
279-
if (strUserPass.find(':') == std::string::npos) return false;
280-
const std::string username{strUserPass.substr(0, strUserPass.find(':'))};
275+
auto opt_strUserPass = DecodeBase64(strUserPass64);
276+
if (!opt_strUserPass.has_value()) return false;
277+
auto it = std::find(opt_strUserPass->begin(), opt_strUserPass->end(), ':');
278+
if (it == opt_strUserPass->end()) return false;
279+
const std::string username = std::string(opt_strUserPass->begin(), it);
281280
return find(g_external_usernames.begin(), g_external_usernames.end(), username) != g_external_usernames.end();
282281
}();
283282

src/i2p.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,11 @@ static std::string SwapBase64(const std::string& from)
6969
static Binary DecodeI2PBase64(const std::string& i2p_b64)
7070
{
7171
const std::string& std_b64 = SwapBase64(i2p_b64);
72-
bool invalid;
73-
Binary decoded = DecodeBase64(std_b64.c_str(), &invalid);
74-
if (invalid) {
72+
auto decoded = DecodeBase64(std_b64);
73+
if (!decoded) {
7574
throw std::runtime_error(strprintf("Cannot decode Base64: \"%s\"", i2p_b64));
7675
}
77-
return decoded;
76+
return std::move(*decoded);
7877
}
7978

8079
/**

src/netaddress.cpp

+10-12
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ static void Checksum(Span<const uint8_t> addr_pubkey, uint8_t (&checksum)[CHECKS
211211

212212
bool CNetAddr::SetSpecial(const std::string& addr)
213213
{
214-
if (!ValidAsCString(addr)) {
214+
if (!ContainsNoNUL(addr)) {
215215
return false;
216216
}
217217

@@ -235,17 +235,16 @@ bool CNetAddr::SetTor(const std::string& addr)
235235
return false;
236236
}
237237

238-
bool invalid;
239-
const auto& input = DecodeBase32(addr.substr(0, addr.size() - suffix_len).c_str(), &invalid);
238+
auto input = DecodeBase32(std::string_view{addr}.substr(0, addr.size() - suffix_len));
240239

241-
if (invalid) {
240+
if (!input) {
242241
return false;
243242
}
244243

245-
if (input.size() == torv3::TOTAL_LEN) {
246-
Span<const uint8_t> input_pubkey{input.data(), ADDR_TORV3_SIZE};
247-
Span<const uint8_t> input_checksum{input.data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};
248-
Span<const uint8_t> input_version{input.data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};
244+
if (input->size() == torv3::TOTAL_LEN) {
245+
Span<const uint8_t> input_pubkey{input->data(), ADDR_TORV3_SIZE};
246+
Span<const uint8_t> input_checksum{input->data() + ADDR_TORV3_SIZE, torv3::CHECKSUM_LEN};
247+
Span<const uint8_t> input_version{input->data() + ADDR_TORV3_SIZE + torv3::CHECKSUM_LEN, sizeof(torv3::VERSION)};
249248

250249
if (input_version != torv3::VERSION) {
251250
return false;
@@ -281,15 +280,14 @@ bool CNetAddr::SetI2P(const std::string& addr)
281280
// can decode it.
282281
const std::string b32_padded = addr.substr(0, b32_len) + "====";
283282

284-
bool invalid;
285-
const auto& address_bytes = DecodeBase32(b32_padded.c_str(), &invalid);
283+
auto address_bytes = DecodeBase32(b32_padded);
286284

287-
if (invalid || address_bytes.size() != ADDR_I2P_SIZE) {
285+
if (!address_bytes || address_bytes->size() != ADDR_I2P_SIZE) {
288286
return false;
289287
}
290288

291289
m_net = NET_I2P;
292-
m_addr.assign(address_bytes.begin(), address_bytes.end());
290+
m_addr.assign(address_bytes->begin(), address_bytes->end());
293291

294292
return true;
295293
}

src/netbase.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ std::vector<std::string> GetNetworkNames(bool append_unroutable)
144144

145145
static std::vector<CNetAddr> LookupIntern(const std::string& name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
146146
{
147-
if (!ValidAsCString(name)) return {};
147+
if (!ContainsNoNUL(name)) return {};
148148
{
149149
CNetAddr addr;
150150
// From our perspective, onion addresses are not hostnames but rather
@@ -173,7 +173,7 @@ static std::vector<CNetAddr> LookupIntern(const std::string& name, unsigned int
173173

174174
std::vector<CNetAddr> LookupHost(const std::string& name, unsigned int nMaxSolutions, bool fAllowLookup, DNSLookupFn dns_lookup_function)
175175
{
176-
if (!ValidAsCString(name)) return {};
176+
if (!ContainsNoNUL(name)) return {};
177177
std::string strHost = name;
178178
if (strHost.empty()) return {};
179179
if (strHost.front() == '[' && strHost.back() == ']') {
@@ -191,7 +191,7 @@ std::optional<CNetAddr> LookupHost(const std::string& name, bool fAllowLookup, D
191191

192192
std::vector<CService> Lookup(const std::string& name, uint16_t portDefault, bool fAllowLookup, unsigned int nMaxSolutions, DNSLookupFn dns_lookup_function)
193193
{
194-
if (name.empty() || !ValidAsCString(name)) {
194+
if (name.empty() || !ContainsNoNUL(name)) {
195195
return {};
196196
}
197197
uint16_t port{portDefault};
@@ -216,7 +216,7 @@ std::optional<CService> Lookup(const std::string& name, uint16_t portDefault, bo
216216

217217
CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupFn dns_lookup_function)
218218
{
219-
if (!ValidAsCString(name)) {
219+
if (!ContainsNoNUL(name)) {
220220
return {};
221221
}
222222
// "1.2:345" will fail to resolve the ip, but will still set the port.
@@ -667,7 +667,7 @@ bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_
667667

668668
bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
669669
{
670-
if (!ValidAsCString(subnet_str)) {
670+
if (!ContainsNoNUL(subnet_str)) {
671671
return false;
672672
}
673673

src/psbt.cpp

+5-6
Original file line numberDiff line numberDiff line change
@@ -343,18 +343,17 @@ std::string PSBTRoleName(PSBTRole role) {
343343

344344
bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
345345
{
346-
bool invalid;
347-
std::string tx_data = DecodeBase64(base64_tx, &invalid);
348-
if (invalid) {
346+
auto tx_data = DecodeBase64(base64_tx);
347+
if (!tx_data) {
349348
error = "invalid base64";
350349
return false;
351350
}
352-
return DecodeRawPSBT(psbt, tx_data, error);
351+
return DecodeRawPSBT(psbt, MakeByteSpan(*tx_data), error);
353352
}
354353

355-
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
354+
bool DecodeRawPSBT(PartiallySignedTransaction& psbt, Span<const std::byte> tx_data, std::string& error)
356355
{
357-
CDataStream ss_data(MakeByteSpan(tx_data), SER_NETWORK, PROTOCOL_VERSION);
356+
CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
358357
try {
359358
ss_data >> psbt;
360359
if (!ss_data.empty()) {

src/psbt.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,6 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
914914
//! Decode a base64ed PSBT into a PartiallySignedTransaction
915915
[[nodiscard]] bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
916916
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
917-
[[nodiscard]] bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);
917+
[[nodiscard]] bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, Span<const std::byte> raw_psbt, std::string& error);
918918

919919
#endif // BITCOIN_PSBT_H

src/qt/walletframe.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,16 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
249249

250250
void WalletFrame::gotoLoadPSBT(bool from_clipboard)
251251
{
252-
std::string data;
252+
std::vector<unsigned char> data;
253253

254254
if (from_clipboard) {
255255
std::string raw = QApplication::clipboard()->text().toStdString();
256-
bool invalid;
257-
data = DecodeBase64(raw, &invalid);
258-
if (invalid) {
256+
auto result = DecodeBase64(raw);
257+
if (!result) {
259258
Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
260259
return;
261260
}
261+
data = std::move(*result);
262262
} else {
263263
QString filename = GUIUtil::getOpenFileName(this,
264264
tr("Load Transaction Data"), QString(),
@@ -269,12 +269,12 @@ void WalletFrame::gotoLoadPSBT(bool from_clipboard)
269269
return;
270270
}
271271
std::ifstream in{filename.toLocal8Bit().data(), std::ios::binary};
272-
data = std::string(std::istreambuf_iterator<char>{in}, {});
272+
data.assign(std::istream_iterator<unsigned char>{in}, {});
273273
}
274274

275275
std::string error;
276276
PartiallySignedTransaction psbtx;
277-
if (!DecodeRawPSBT(psbtx, data, error)) {
277+
if (!DecodeRawPSBT(psbtx, MakeByteSpan(data), error)) {
278278
Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
279279
return;
280280
}

src/rpc/evo.cpp

+6-6
Original file line numberDiff line numberDiff line change
@@ -815,9 +815,9 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request,
815815
} else if (err != SigningResult::OK){
816816
throw JSONRPCError(RPC_WALLET_ERROR, SigningResultString(err));
817817
}
818-
bool invalid = false;
819-
ptx.vchSig = DecodeBase64(signed_payload.c_str(), &invalid);
820-
if (invalid) throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to decode base64 ready signature for protx");
818+
auto opt_vchSig = DecodeBase64(signed_payload);
819+
if (!opt_vchSig.has_value()) throw JSONRPCError(RPC_INTERNAL_ERROR, "failed to decode base64 ready signature for protx");
820+
ptx.vchSig = opt_vchSig.value();
821821
} // cs_wallet
822822
SetTxPayload(tx, ptx);
823823
return SignAndSendSpecialTx(request, chain_helper, chainman, tx, fSubmit);
@@ -877,11 +877,11 @@ static RPCHelpMan protx_register_submit()
877877
throw JSONRPCError(RPC_INVALID_PARAMETER, "payload signature not empty");
878878
}
879879

880-
bool decode_fail{false};
881-
ptx.vchSig = DecodeBase64(request.params[1].get_str().c_str(), &decode_fail);
882-
if (decode_fail) {
880+
auto opt_vchSig= DecodeBase64(request.params[1].get_str());
881+
if (!opt_vchSig.has_value()) {
883882
throw JSONRPCError(RPC_INVALID_PARAMETER, "malformed base64 encoding");
884883
}
884+
ptx.vchSig = opt_vchSig.value();
885885

886886
SetTxPayload(tx, ptx);
887887
return SignAndSendSpecialTx(request, chain_helper, chainman, tx);

src/rpc/governance.cpp

+6-8
Original file line numberDiff line numberDiff line change
@@ -421,11 +421,10 @@ static bool SignVote(const CWallet& wallet, const CKeyID& keyID, CGovernanceVote
421421
LogPrintf("SignVote failed due to: %s\n", SigningResultString(err));
422422
return false;
423423
}
424-
bool failed = true;
425-
const auto decoded = DecodeBase64(signature, &failed);
426-
CHECK_NONFATAL(!failed); // DecodeBase64 should not fail
424+
const auto opt_decoded = DecodeBase64(signature);
425+
CHECK_NONFATAL(opt_decoded.has_value()); // DecodeBase64 should not fail
427426

428-
vote.SetSignature(std::vector<unsigned char>(decoded.data(), decoded.data() + decoded.size()));
427+
vote.SetSignature(std::vector<unsigned char>(opt_decoded->data(), opt_decoded->data() + opt_decoded->size()));
429428
return true;
430429
}
431430

@@ -959,10 +958,9 @@ static RPCHelpMan voteraw()
959958

960959
int64_t nTime = request.params[5].get_int64();
961960
std::string strSig = request.params[6].get_str();
962-
bool fInvalid = false;
963-
std::vector<unsigned char> vchSig = DecodeBase64(strSig.c_str(), &fInvalid);
961+
auto opt_vchSig = DecodeBase64(strSig);
964962

965-
if (fInvalid) {
963+
if (!opt_vchSig.has_value()) {
966964
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding");
967965
}
968966

@@ -975,7 +973,7 @@ static RPCHelpMan voteraw()
975973

976974
CGovernanceVote vote(outpoint, hashGovObj, eVoteSignal, eVoteOutcome);
977975
vote.SetTime(nTime);
978-
vote.SetSignature(vchSig);
976+
vote.SetSignature(opt_vchSig.value());
979977

980978
bool onlyVotingKeyAllowed = govObjType == GovernanceObject::PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING;
981979

src/stacktraces.cpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -426,13 +426,12 @@ std::string GetCrashInfoStrFromSerializedStr(const std::string& ciStr)
426426
{
427427
static uint64_t basePtr = GetBaseAddress();
428428

429-
bool dataInvalid = false;
430-
auto buf = DecodeBase32(ciStr.c_str(), &dataInvalid);
431-
if (buf.empty() || dataInvalid) {
429+
auto opt_buf = DecodeBase32(ciStr.c_str());
430+
if (!opt_buf.has_value() || opt_buf->empty()) {
432431
return "Error while deserializing crash info";
433432
}
434433

435-
CDataStream ds(buf, SER_DISK, 0);
434+
CDataStream ds(*opt_buf, SER_DISK, 0);
436435

437436
crash_info_header hdr;
438437
try {

src/test/base32_tests.cpp

+7-13
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,16 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
2222
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
2323
strEnc = EncodeBase32(vstrIn[i], false);
2424
BOOST_CHECK_EQUAL(strEnc, vstrOutNoPadding[i]);
25-
bool invalid;
26-
std::string strDec = DecodeBase32(vstrOut[i], &invalid);
27-
BOOST_CHECK(!invalid);
28-
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
25+
auto dec = DecodeBase32(vstrOut[i]);
26+
BOOST_REQUIRE(dec);
27+
BOOST_CHECK_MESSAGE(MakeByteSpan(*dec) == MakeByteSpan(vstrIn[i]), vstrOut[i]);
2928
}
3029

3130
// Decoding strings with embedded NUL characters should fail
32-
bool failure;
33-
(void)DecodeBase32("invalid\0"s, &failure); // correct size, invalid due to \0
34-
BOOST_CHECK(failure);
35-
(void)DecodeBase32("AWSX3VPP"s, &failure); // valid
36-
BOOST_CHECK(!failure);
37-
(void)DecodeBase32("AWSX3VPP\0invalid"s, &failure); // correct size, invalid due to \0
38-
BOOST_CHECK(failure);
39-
(void)DecodeBase32("AWSX3VPPinvalid"s, &failure); // invalid size
40-
BOOST_CHECK(failure);
31+
BOOST_CHECK(!DecodeBase32("invalid\0"s)); // correct size, invalid due to \0
32+
BOOST_CHECK(DecodeBase32("AWSX3VPP"s)); // valid
33+
BOOST_CHECK(!DecodeBase32("AWSX3VPP\0invalid"s)); // correct size, invalid due to \0
34+
BOOST_CHECK(!DecodeBase32("AWSX3VPPinvalid"s)); // invalid size
4135
}
4236

4337
BOOST_AUTO_TEST_SUITE_END()

src/test/base64_tests.cpp

+7-13
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
1919
{
2020
std::string strEnc = EncodeBase64(vstrIn[i]);
2121
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
22-
bool invalid;
23-
std::string strDec = DecodeBase64(strEnc, &invalid);
24-
BOOST_CHECK(!invalid);
25-
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
22+
auto dec = DecodeBase64(strEnc);
23+
BOOST_REQUIRE(dec);
24+
BOOST_CHECK_MESSAGE(MakeByteSpan(*dec) == MakeByteSpan(vstrIn[i]), vstrOut[i]);
2625
}
2726

2827
{
@@ -36,15 +35,10 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
3635
}
3736

3837
// Decoding strings with embedded NUL characters should fail
39-
bool failure;
40-
(void)DecodeBase64("invalid\0"s, &failure);
41-
BOOST_CHECK(failure);
42-
(void)DecodeBase64("nQB/pZw="s, &failure);
43-
BOOST_CHECK(!failure);
44-
(void)DecodeBase64("nQB/pZw=\0invalid"s, &failure);
45-
BOOST_CHECK(failure);
46-
(void)DecodeBase64("nQB/pZw=invalid\0"s, &failure);
47-
BOOST_CHECK(failure);
38+
BOOST_CHECK(!DecodeBase64("invalid\0"s));
39+
BOOST_CHECK(DecodeBase64("nQB/pZw="s));
40+
BOOST_CHECK(!DecodeBase64("nQB/pZw=\0invalid"s));
41+
BOOST_CHECK(!DecodeBase64("nQB/pZw=invalid\0"s));
4842
}
4943

5044
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)