Skip to content

Commit aa624b6

Browse files
committed
Merge #11167: Full BIP173 (Bech32) support
8213838 [Qt] tolerate BIP173/bech32 addresses during input validation (Jonas Schnelli) 06eaca6 [RPC] Wallet: test importing of native witness scripts (NicolasDorier) fd0041a Use BIP173 addresses in segwit.py test (Pieter Wuille) e278f12 Support BIP173 in addwitnessaddress (Pieter Wuille) c091b99 Implement BIP173 addresses and tests (Pieter Wuille) bd355b8 Add regtest testing to base58_tests (Pieter Wuille) 6565c55 Convert base58_tests from type/payload to scriptPubKey comparison (Pieter Wuille) 8fd2267 Import Bech32 C++ reference code & tests (Pieter Wuille) 1e46ebd Implement {Encode,Decode}Destination without CBitcoinAddress (Pieter Wuille) Pull request description: Builds on top of #11117. This adds support for: * Creating BIP173 addresses for testing (through `addwitnessaddress`, though by default it still produces P2SH versions) * Sending to BIP173 addresses (including non-v0 ones) * Analysing BIP173 addresses (through `validateaddress`) It includes a reformatted version of the [C++ Bech32 reference code](https://github.com/sipa/bech32/tree/master/ref/c%2B%2B) and an independent implementation of the address encoding/decoding logic (integrated with CTxDestination). All BIP173 test vectors are included. Not included (and intended for other PRs): * Full wallet support for SegWit (which would include automatically adding witness scripts to the wallet during automatic keypool topup, SegWit change outputs, ...) [see #11403] * Splitting base58.cpp and tests/base58_tests.cpp up into base58-specific code, and "address encoding"-code [see #11372] * Error locating in UI for BIP173 addresses. Tree-SHA512: 238031185fd07f3ac873c586043970cc2db91bf7735c3c168cb33a3db39a7bda81d4891b649685bb17ef90dc63af0328e7705d8cd3e8dafd6c4d3c08fb230341
2 parents a72003d + 8213838 commit aa624b6

30 files changed

+1290
-531
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ BITCOIN_CORE_H = \
7878
addrdb.h \
7979
addrman.h \
8080
base58.h \
81+
bech32.h \
8182
bloom.h \
8283
blockencodings.h \
8384
chain.h \
@@ -316,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
316317
libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
317318
libbitcoin_common_a_SOURCES = \
318319
base58.cpp \
320+
bech32.cpp \
319321
chainparams.cpp \
320322
coins.cpp \
321323
compressor.cpp \

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ BITCOIN_TESTS =\
3131
test/base32_tests.cpp \
3232
test/base58_tests.cpp \
3333
test/base64_tests.cpp \
34+
test/bech32_tests.cpp \
3435
test/bip32_tests.cpp \
3536
test/blockencodings_tests.cpp \
3637
test/bloom_tests.cpp \

src/base58.cpp

Lines changed: 106 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@
44

55
#include "base58.h"
66

7+
#include "bech32.h"
78
#include "hash.h"
9+
#include "script/script.h"
810
#include "uint256.h"
11+
#include "utilstrencodings.h"
912

10-
#include <assert.h>
11-
#include <stdint.h>
12-
#include <string.h>
13-
#include <vector>
14-
#include <string>
1513
#include <boost/variant/apply_visitor.hpp>
1614
#include <boost/variant/static_visitor.hpp>
1715

16+
#include <algorithm>
17+
#include <assert.h>
18+
#include <string.h>
19+
20+
1821
/** All alphanumeric characters except for "0", "I", "O", and "l" */
1922
static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2023

@@ -212,86 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const
212215

213216
namespace
214217
{
215-
/** base58-encoded Bitcoin addresses.
216-
* Public-key-hash-addresses have version 0 (or 111 testnet).
217-
* The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
218-
* Script-hash-addresses have version 5 (or 196 testnet).
219-
* The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
220-
*/
221-
class CBitcoinAddress : public CBase58Data {
222-
public:
223-
bool Set(const CKeyID &id);
224-
bool Set(const CScriptID &id);
225-
bool Set(const CTxDestination &dest);
226-
bool IsValid() const;
227-
bool IsValid(const CChainParams &params) const;
228-
229-
CBitcoinAddress() {}
230-
CBitcoinAddress(const CTxDestination &dest) { Set(dest); }
231-
CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); }
232-
CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); }
233-
234-
CTxDestination Get() const;
235-
};
236-
237-
class CBitcoinAddressVisitor : public boost::static_visitor<bool>
218+
class DestinationEncoder : public boost::static_visitor<std::string>
238219
{
239220
private:
240-
CBitcoinAddress* addr;
221+
const CChainParams& m_params;
241222

242223
public:
243-
explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {}
224+
DestinationEncoder(const CChainParams& params) : m_params(params) {}
244225

245-
bool operator()(const CKeyID& id) const { return addr->Set(id); }
246-
bool operator()(const CScriptID& id) const { return addr->Set(id); }
247-
bool operator()(const CNoDestination& no) const { return false; }
248-
};
249-
250-
} // namespace
226+
std::string operator()(const CKeyID& id) const
227+
{
228+
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
229+
data.insert(data.end(), id.begin(), id.end());
230+
return EncodeBase58Check(data);
231+
}
251232

252-
bool CBitcoinAddress::Set(const CKeyID& id)
253-
{
254-
SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20);
255-
return true;
256-
}
233+
std::string operator()(const CScriptID& id) const
234+
{
235+
std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
236+
data.insert(data.end(), id.begin(), id.end());
237+
return EncodeBase58Check(data);
238+
}
257239

258-
bool CBitcoinAddress::Set(const CScriptID& id)
259-
{
260-
SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20);
261-
return true;
262-
}
240+
std::string operator()(const WitnessV0KeyHash& id) const
241+
{
242+
std::vector<unsigned char> data = {0};
243+
ConvertBits<8, 5, true>(data, id.begin(), id.end());
244+
return bech32::Encode(m_params.Bech32HRP(), data);
245+
}
263246

264-
bool CBitcoinAddress::Set(const CTxDestination& dest)
265-
{
266-
return boost::apply_visitor(CBitcoinAddressVisitor(this), dest);
267-
}
247+
std::string operator()(const WitnessV0ScriptHash& id) const
248+
{
249+
std::vector<unsigned char> data = {0};
250+
ConvertBits<8, 5, true>(data, id.begin(), id.end());
251+
return bech32::Encode(m_params.Bech32HRP(), data);
252+
}
268253

269-
bool CBitcoinAddress::IsValid() const
270-
{
271-
return IsValid(Params());
272-
}
254+
std::string operator()(const WitnessUnknown& id) const
255+
{
256+
if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) {
257+
return {};
258+
}
259+
std::vector<unsigned char> data = {(unsigned char)id.version};
260+
ConvertBits<8, 5, true>(data, id.program, id.program + id.length);
261+
return bech32::Encode(m_params.Bech32HRP(), data);
262+
}
273263

274-
bool CBitcoinAddress::IsValid(const CChainParams& params) const
275-
{
276-
bool fCorrectSize = vchData.size() == 20;
277-
bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) ||
278-
vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
279-
return fCorrectSize && fKnownVersion;
280-
}
264+
std::string operator()(const CNoDestination& no) const { return {}; }
265+
};
281266

282-
CTxDestination CBitcoinAddress::Get() const
267+
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params)
283268
{
284-
if (!IsValid())
285-
return CNoDestination();
286-
uint160 id;
287-
memcpy(&id, vchData.data(), 20);
288-
if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS))
289-
return CKeyID(id);
290-
else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS))
291-
return CScriptID(id);
292-
else
293-
return CNoDestination();
269+
std::vector<unsigned char> data;
270+
uint160 hash;
271+
if (DecodeBase58Check(str, data)) {
272+
// base58-encoded Bitcoin addresses.
273+
// Public-key-hash-addresses have version 0 (or 111 testnet).
274+
// The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key.
275+
const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS);
276+
if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) {
277+
std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin());
278+
return CKeyID(hash);
279+
}
280+
// Script-hash-addresses have version 5 (or 196 testnet).
281+
// The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script.
282+
const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS);
283+
if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) {
284+
std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin());
285+
return CScriptID(hash);
286+
}
287+
}
288+
data.clear();
289+
auto bech = bech32::Decode(str);
290+
if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) {
291+
// Bech32 decoding
292+
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
293+
// The rest of the symbols are converted witness program bytes.
294+
if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) {
295+
if (version == 0) {
296+
{
297+
WitnessV0KeyHash keyid;
298+
if (data.size() == keyid.size()) {
299+
std::copy(data.begin(), data.end(), keyid.begin());
300+
return keyid;
301+
}
302+
}
303+
{
304+
WitnessV0ScriptHash scriptid;
305+
if (data.size() == scriptid.size()) {
306+
std::copy(data.begin(), data.end(), scriptid.begin());
307+
return scriptid;
308+
}
309+
}
310+
return CNoDestination();
311+
}
312+
if (version > 16 || data.size() < 2 || data.size() > 40) {
313+
return CNoDestination();
314+
}
315+
WitnessUnknown unk;
316+
unk.version = version;
317+
std::copy(data.begin(), data.end(), unk.program);
318+
unk.length = data.size();
319+
return unk;
320+
}
321+
}
322+
return CNoDestination();
294323
}
324+
} // namespace
295325

296326
void CBitcoinSecret::SetKey(const CKey& vchSecret)
297327
{
@@ -328,22 +358,20 @@ bool CBitcoinSecret::SetString(const std::string& strSecret)
328358

329359
std::string EncodeDestination(const CTxDestination& dest)
330360
{
331-
CBitcoinAddress addr(dest);
332-
if (!addr.IsValid()) return "";
333-
return addr.ToString();
361+
return boost::apply_visitor(DestinationEncoder(Params()), dest);
334362
}
335363

336364
CTxDestination DecodeDestination(const std::string& str)
337365
{
338-
return CBitcoinAddress(str).Get();
366+
return DecodeDestination(str, Params());
339367
}
340368

341369
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
342370
{
343-
return CBitcoinAddress(str).IsValid(params);
371+
return IsValidDestination(DecodeDestination(str, params));
344372
}
345373

346374
bool IsValidDestinationString(const std::string& str)
347375
{
348-
return CBitcoinAddress(str).IsValid();
376+
return IsValidDestinationString(str, Params());
349377
}

src/base58.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include "chainparams.h"
1818
#include "key.h"
1919
#include "pubkey.h"
20-
#include "script/script.h"
2120
#include "script/standard.h"
2221
#include "support/allocators/zeroafterfree.h"
2322

0 commit comments

Comments
 (0)