Auto merge of #3326 - str4d:3058-sapling-addresses, r=str4d
Sapling address encodings This PR enables Sapling keys and addresses to be passed in anywhere Sprout keys and addresses are used. Doing so will cause crashes until those places are updated with Sapling support. Includes code cherry-picked from the following upstream PRs: - bitcoin/bitcoin#11167 - Only the `ConvertBits()` function. - bitcoin/bitcoin#11630 Closes #3058.
This commit is contained in:
commit
2ebde5860e
|
@ -62,6 +62,7 @@ BITCOIN_TESTS =\
|
|||
test/Checkpoints_tests.cpp \
|
||||
test/coins_tests.cpp \
|
||||
test/compress_tests.cpp \
|
||||
test/convertbits_tests.cpp \
|
||||
test/crypto_tests.cpp \
|
||||
test/DoS_tests.cpp \
|
||||
test/equihash_tests.cpp \
|
||||
|
|
|
@ -150,6 +150,9 @@ std::string Encode(const std::string& hrp, const data& values) {
|
|||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (auto c : combined) {
|
||||
if (c >= 32) {
|
||||
return "";
|
||||
}
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -153,6 +153,11 @@ public:
|
|||
// guarantees the first 2 characters, when base58 encoded, are "SK"
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAB,0x36};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zs";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviews";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivks";
|
||||
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-main";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main));
|
||||
|
||||
fMiningRequiresPeers = true;
|
||||
|
@ -310,6 +315,11 @@ public:
|
|||
// guarantees the first 2 characters, when base58 encoded, are "ST"
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "ztestsapling";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivktestsapling";
|
||||
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-test";
|
||||
|
||||
vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test));
|
||||
|
||||
fMiningRequiresPeers = true;
|
||||
|
@ -429,6 +439,11 @@ public:
|
|||
base58Prefixes[ZCVIEWING_KEY] = {0xA8,0xAC,0x0C};
|
||||
base58Prefixes[ZCSPENDING_KEY] = {0xAC,0x08};
|
||||
|
||||
bech32HRPs[SAPLING_PAYMENT_ADDRESS] = "zregtestsapling";
|
||||
bech32HRPs[SAPLING_FULL_VIEWING_KEY] = "zviewregtestsapling";
|
||||
bech32HRPs[SAPLING_INCOMING_VIEWING_KEY] = "zivkregtestsapling";
|
||||
bech32HRPs[SAPLING_SPENDING_KEY] = "secret-spending-key-regtest";
|
||||
|
||||
// Founders reward script expects a vector of 2-of-3 multisig addresses
|
||||
vFoundersRewardAddress = { "t2FwcEhFdNXuFMv1tcYwaBJtYVtMj8b1uTg" };
|
||||
assert(vFoundersRewardAddress.size() <= consensus.GetLastFoundersRewardBlockHeight());
|
||||
|
|
|
@ -56,6 +56,15 @@ public:
|
|||
MAX_BASE58_TYPES
|
||||
};
|
||||
|
||||
enum Bech32Type {
|
||||
SAPLING_PAYMENT_ADDRESS,
|
||||
SAPLING_FULL_VIEWING_KEY,
|
||||
SAPLING_INCOMING_VIEWING_KEY,
|
||||
SAPLING_SPENDING_KEY,
|
||||
|
||||
MAX_BECH32_TYPES
|
||||
};
|
||||
|
||||
const Consensus::Params& GetConsensus() const { return consensus; }
|
||||
const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; }
|
||||
const std::vector<unsigned char>& AlertKey() const { return vAlertPubKey; }
|
||||
|
@ -81,6 +90,7 @@ public:
|
|||
std::string NetworkIDString() const { return strNetworkID; }
|
||||
const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; }
|
||||
const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; }
|
||||
const std::string& Bech32HRP(Bech32Type type) const { return bech32HRPs[type]; }
|
||||
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
|
||||
const CCheckpointData& Checkpoints() const { return checkpointData; }
|
||||
/** Return the founder's reward address and script for a given block height */
|
||||
|
@ -103,6 +113,7 @@ protected:
|
|||
unsigned int nEquihashK = 0;
|
||||
std::vector<CDNSSeedData> vSeeds;
|
||||
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
||||
std::string bech32HRPs[MAX_BECH32_TYPES];
|
||||
std::string strNetworkID;
|
||||
std::string strCurrencyUnits;
|
||||
CBlock genesis;
|
||||
|
|
|
@ -85,6 +85,19 @@ public:
|
|||
return EncodeBase58Check(data);
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::SaplingPaymentAddress& zaddr) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zaddr;
|
||||
// ConvertBits requires unsigned char, but CDataStream uses char
|
||||
std::vector<unsigned char> seraddr(ss.begin(), ss.end());
|
||||
std::vector<unsigned char> data;
|
||||
// See calculation comment below
|
||||
data.reserve((seraddr.size() * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, seraddr.begin(), seraddr.end());
|
||||
return bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS), data);
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
|
@ -129,8 +142,31 @@ public:
|
|||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::SaplingSpendingKey& zkey) const
|
||||
{
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ss << zkey;
|
||||
// ConvertBits requires unsigned char, but CDataStream uses char
|
||||
std::vector<unsigned char> serkey(ss.begin(), ss.end());
|
||||
std::vector<unsigned char> data;
|
||||
// See calculation comment below
|
||||
data.reserve((serkey.size() * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, serkey.begin(), serkey.end());
|
||||
std::string ret = bech32::Encode(m_params.Bech32HRP(CChainParams::SAPLING_SPENDING_KEY), data);
|
||||
memory_cleanse(serkey.data(), serkey.size());
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string operator()(const libzcash::InvalidEncoding& no) const { return {}; }
|
||||
};
|
||||
|
||||
// Sizes of SaplingPaymentAddress and SaplingSpendingKey after
|
||||
// ConvertBits<8, 5, true>(). The calculations below take the
|
||||
// regular serialized size in bytes, convert to bits, and then
|
||||
// perform ceiling division to get the number of 5-bit clusters.
|
||||
const size_t ConvertedSaplingPaymentAddressSize = ((32 + 11) * 8 + 4) / 5;
|
||||
const size_t ConvertedSaplingSpendingKeySize = (32 * 8 + 4) / 5;
|
||||
} // namespace
|
||||
|
||||
CKey DecodeSecret(const std::string& str)
|
||||
|
@ -248,6 +284,19 @@ libzcash::PaymentAddress DecodePaymentAddress(const std::string& str)
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS) &&
|
||||
bech.second.size() == ConvertedSaplingPaymentAddressSize) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SaplingPaymentAddress ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return libzcash::InvalidEncoding();
|
||||
}
|
||||
|
||||
|
@ -301,6 +350,20 @@ libzcash::SpendingKey DecodeSpendingKey(const std::string& str)
|
|||
return ret;
|
||||
}
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
if (bech.first == Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY) &&
|
||||
bech.second.size() == ConvertedSaplingSpendingKeySize) {
|
||||
// Bech32 decoding
|
||||
data.reserve((bech.second.size() * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin(), bech.second.end())) {
|
||||
CDataStream ss(data, SER_NETWORK, PROTOCOL_VERSION);
|
||||
libzcash::SaplingSpendingKey ret;
|
||||
ss >> ret;
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
memory_cleanse(data.data(), data.size());
|
||||
return libzcash::InvalidEncoding();
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base32_testvectors)
|
|||
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
|
||||
{
|
||||
std::string strEnc = EncodeBase32(vstrIn[i]);
|
||||
BOOST_CHECK(strEnc == vstrOut[i]);
|
||||
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
|
||||
std::string strDec = DecodeBase32(vstrOut[i]);
|
||||
BOOST_CHECK(strDec == vstrIn[i]);
|
||||
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,9 +16,9 @@ BOOST_AUTO_TEST_CASE(base64_testvectors)
|
|||
for (unsigned int i=0; i<sizeof(vstrIn)/sizeof(vstrIn[0]); i++)
|
||||
{
|
||||
std::string strEnc = EncodeBase64(vstrIn[i]);
|
||||
BOOST_CHECK(strEnc == vstrOut[i]);
|
||||
BOOST_CHECK_EQUAL(strEnc, vstrOut[i]);
|
||||
std::string strDec = DecodeBase64(strEnc);
|
||||
BOOST_CHECK(strDec == vstrIn[i]);
|
||||
BOOST_CHECK_EQUAL(strDec, vstrIn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -64,4 +64,37 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bech32_deterministic_valid)
|
||||
{
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
std::vector<unsigned char> input(32, i);
|
||||
auto encoded = bech32::Encode("a", input);
|
||||
if (i < 32) {
|
||||
// Valid input
|
||||
BOOST_CHECK(!encoded.empty());
|
||||
auto ret = bech32::Decode(encoded);
|
||||
BOOST_CHECK(ret.first == "a");
|
||||
BOOST_CHECK(ret.second == input);
|
||||
} else {
|
||||
// Invalid input
|
||||
BOOST_CHECK(encoded.empty());
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
std::vector<unsigned char> input(43, i);
|
||||
auto encoded = bech32::Encode("a", input);
|
||||
if (i < 32) {
|
||||
// Valid input
|
||||
BOOST_CHECK(!encoded.empty());
|
||||
auto ret = bech32::Decode(encoded);
|
||||
BOOST_CHECK(ret.first == "a");
|
||||
BOOST_CHECK(ret.second == input);
|
||||
} else {
|
||||
// Invalid input
|
||||
BOOST_CHECK(encoded.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) 2018 The Zcash developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <utilstrencodings.h>
|
||||
#include <test/test_bitcoin.h>
|
||||
#include <zcash/NoteEncryption.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(convertbits_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(convertbits_deterministic)
|
||||
{
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
std::vector<unsigned char> input(32, i);
|
||||
std::vector<unsigned char> data;
|
||||
std::vector<unsigned char> output;
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, input.begin(), input.end());
|
||||
ConvertBits<5, 8, false>([&](unsigned char c) { output.push_back(c); }, data.begin(), data.end());
|
||||
BOOST_CHECK_EQUAL(data.size(), 52);
|
||||
BOOST_CHECK_EQUAL(output.size(), 32);
|
||||
BOOST_CHECK(input == output);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
std::vector<unsigned char> input(43, i);
|
||||
std::vector<unsigned char> data;
|
||||
std::vector<unsigned char> output;
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, input.begin(), input.end());
|
||||
ConvertBits<5, 8, false>([&](unsigned char c) { output.push_back(c); }, data.begin(), data.end());
|
||||
BOOST_CHECK_EQUAL(data.size(), 69);
|
||||
BOOST_CHECK_EQUAL(output.size(), 43);
|
||||
BOOST_CHECK(input == output);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(convertbits_random)
|
||||
{
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
auto input = libzcash::random_uint256();
|
||||
std::vector<unsigned char> data;
|
||||
std::vector<unsigned char> output;
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, input.begin(), input.end());
|
||||
ConvertBits<5, 8, false>([&](unsigned char c) { output.push_back(c); }, data.begin(), data.end());
|
||||
BOOST_CHECK_EQUAL(data.size(), 52);
|
||||
BOOST_CHECK_EQUAL(output.size(), 32);
|
||||
BOOST_CHECK(input == uint256(output));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "key.h"
|
||||
|
||||
#include "chainparams.h"
|
||||
#include "key_io.h"
|
||||
#include "script/script.h"
|
||||
#include "uint256.h"
|
||||
|
@ -220,4 +221,35 @@ BOOST_AUTO_TEST_CASE(zc_address_test)
|
|||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(zs_address_test)
|
||||
{
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
auto sk = SaplingSpendingKey::random();
|
||||
{
|
||||
std::string sk_string = EncodeSpendingKey(sk);
|
||||
BOOST_CHECK(sk_string.compare(0, 24, Params().Bech32HRP(CChainParams::SAPLING_SPENDING_KEY)) == 0);
|
||||
|
||||
auto spendingkey2 = DecodeSpendingKey(sk_string);
|
||||
BOOST_CHECK(IsValidSpendingKey(spendingkey2));
|
||||
|
||||
BOOST_ASSERT(boost::get<SaplingSpendingKey>(&spendingkey2) != nullptr);
|
||||
auto sk2 = boost::get<SaplingSpendingKey>(spendingkey2);
|
||||
BOOST_CHECK(sk == sk2);
|
||||
}
|
||||
{
|
||||
auto addr = sk.default_address();
|
||||
|
||||
std::string addr_string = EncodePaymentAddress(*addr);
|
||||
BOOST_CHECK(addr_string.compare(0, 2, Params().Bech32HRP(CChainParams::SAPLING_PAYMENT_ADDRESS)) == 0);
|
||||
|
||||
auto paymentaddr2 = DecodePaymentAddress(addr_string);
|
||||
BOOST_CHECK(IsValidPaymentAddress(paymentaddr2));
|
||||
|
||||
BOOST_ASSERT(boost::get<SaplingPaymentAddress>(&paymentaddr2) != nullptr);
|
||||
auto addr2 = boost::get<SaplingPaymentAddress>(paymentaddr2);
|
||||
BOOST_CHECK(addr == addr2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
|
|
@ -127,46 +127,11 @@ string EncodeBase64(const unsigned char* pch, size_t len)
|
|||
{
|
||||
static const char *pbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
string strRet="";
|
||||
strRet.reserve((len+2)/3*4);
|
||||
|
||||
int mode=0, left=0;
|
||||
const unsigned char *pchEnd = pch+len;
|
||||
|
||||
while (pch<pchEnd)
|
||||
{
|
||||
int enc = *(pch++);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // we have no bits
|
||||
strRet += pbase64[enc >> 2];
|
||||
left = (enc & 3) << 4;
|
||||
mode = 1;
|
||||
break;
|
||||
|
||||
case 1: // we have two bits
|
||||
strRet += pbase64[left | (enc >> 4)];
|
||||
left = (enc & 15) << 2;
|
||||
mode = 2;
|
||||
break;
|
||||
|
||||
case 2: // we have four bits
|
||||
strRet += pbase64[left | (enc >> 6)];
|
||||
strRet += pbase64[enc & 63];
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode)
|
||||
{
|
||||
strRet += pbase64[left];
|
||||
strRet += '=';
|
||||
if (mode == 1)
|
||||
strRet += '=';
|
||||
}
|
||||
|
||||
return strRet;
|
||||
std::string str;
|
||||
str.reserve(((len + 2) / 3) * 4);
|
||||
ConvertBits<8, 6, true>([&](int v) { str += pbase64[v]; }, pch, pch + len);
|
||||
while (str.size() % 4) str += '=';
|
||||
return str;
|
||||
}
|
||||
|
||||
string EncodeBase64(const string& str)
|
||||
|
@ -193,68 +158,32 @@ vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid)
|
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
if (pfInvalid)
|
||||
*pfInvalid = false;
|
||||
|
||||
vector<unsigned char> vchRet;
|
||||
vchRet.reserve(strlen(p)*3/4);
|
||||
|
||||
int mode = 0;
|
||||
int left = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int dec = decode64_table[(unsigned char)*p];
|
||||
if (dec == -1) break;
|
||||
p++;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // we have no bits and get 6
|
||||
left = dec;
|
||||
mode = 1;
|
||||
break;
|
||||
|
||||
case 1: // we have 6 bits and keep 4
|
||||
vchRet.push_back((left<<2) | (dec>>4));
|
||||
left = dec & 15;
|
||||
mode = 2;
|
||||
break;
|
||||
|
||||
case 2: // we have 4 bits and get 6, we keep 2
|
||||
vchRet.push_back((left<<4) | (dec>>2));
|
||||
left = dec & 3;
|
||||
mode = 3;
|
||||
break;
|
||||
|
||||
case 3: // we have 2 bits and get 6
|
||||
vchRet.push_back((left<<6) | dec);
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
const char* e = p;
|
||||
std::vector<uint8_t> val;
|
||||
val.reserve(strlen(p));
|
||||
while (*p != 0) {
|
||||
int x = decode64_table[(unsigned char)*p];
|
||||
if (x == -1) break;
|
||||
val.push_back(x);
|
||||
++p;
|
||||
}
|
||||
|
||||
if (pfInvalid)
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // 4n base64 characters processed: ok
|
||||
break;
|
||||
std::vector<unsigned char> ret;
|
||||
ret.reserve((val.size() * 3) / 4);
|
||||
bool valid = ConvertBits<6, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
|
||||
|
||||
case 1: // 4n+1 base64 character processed: impossible
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 2: // 4n+2 base64 characters processed: require '=='
|
||||
if (left || p[0] != '=' || p[1] != '=' || decode64_table[(unsigned char)p[2]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 3: // 4n+3 base64 characters processed: require '='
|
||||
if (left || p[0] != '=' || decode64_table[(unsigned char)p[1]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
const char* q = p;
|
||||
while (valid && *p != 0) {
|
||||
if (*p != '=') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
valid = valid && (p - e) % 4 == 0 && p - q < 4;
|
||||
if (pfInvalid) *pfInvalid = !valid;
|
||||
|
||||
return vchRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
string DecodeBase64(const string& str)
|
||||
|
@ -267,59 +196,11 @@ string EncodeBase32(const unsigned char* pch, size_t len)
|
|||
{
|
||||
static const char *pbase32 = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
string strRet="";
|
||||
strRet.reserve((len+4)/5*8);
|
||||
|
||||
int mode=0, left=0;
|
||||
const unsigned char *pchEnd = pch+len;
|
||||
|
||||
while (pch<pchEnd)
|
||||
{
|
||||
int enc = *(pch++);
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // we have no bits
|
||||
strRet += pbase32[enc >> 3];
|
||||
left = (enc & 7) << 2;
|
||||
mode = 1;
|
||||
break;
|
||||
|
||||
case 1: // we have three bits
|
||||
strRet += pbase32[left | (enc >> 6)];
|
||||
strRet += pbase32[(enc >> 1) & 31];
|
||||
left = (enc & 1) << 4;
|
||||
mode = 2;
|
||||
break;
|
||||
|
||||
case 2: // we have one bit
|
||||
strRet += pbase32[left | (enc >> 4)];
|
||||
left = (enc & 15) << 1;
|
||||
mode = 3;
|
||||
break;
|
||||
|
||||
case 3: // we have four bits
|
||||
strRet += pbase32[left | (enc >> 7)];
|
||||
strRet += pbase32[(enc >> 2) & 31];
|
||||
left = (enc & 3) << 3;
|
||||
mode = 4;
|
||||
break;
|
||||
|
||||
case 4: // we have two bits
|
||||
strRet += pbase32[left | (enc >> 5)];
|
||||
strRet += pbase32[enc & 31];
|
||||
mode = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const int nPadding[5] = {0, 6, 4, 3, 1};
|
||||
if (mode)
|
||||
{
|
||||
strRet += pbase32[left];
|
||||
for (int n=0; n<nPadding[mode]; n++)
|
||||
strRet += '=';
|
||||
}
|
||||
|
||||
return strRet;
|
||||
std::string str;
|
||||
str.reserve(((len + 4) / 5) * 8);
|
||||
ConvertBits<8, 5, true>([&](int v) { str += pbase32[v]; }, pch, pch + len);
|
||||
while (str.size() % 8) str += '=';
|
||||
return str;
|
||||
}
|
||||
|
||||
string EncodeBase32(const string& str)
|
||||
|
@ -346,102 +227,32 @@ vector<unsigned char> DecodeBase32(const char* p, bool* pfInvalid)
|
|||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
if (pfInvalid)
|
||||
*pfInvalid = false;
|
||||
|
||||
vector<unsigned char> vchRet;
|
||||
vchRet.reserve((strlen(p))*5/8);
|
||||
|
||||
int mode = 0;
|
||||
int left = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int dec = decode32_table[(unsigned char)*p];
|
||||
if (dec == -1) break;
|
||||
p++;
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // we have no bits and get 5
|
||||
left = dec;
|
||||
mode = 1;
|
||||
break;
|
||||
|
||||
case 1: // we have 5 bits and keep 2
|
||||
vchRet.push_back((left<<3) | (dec>>2));
|
||||
left = dec & 3;
|
||||
mode = 2;
|
||||
break;
|
||||
|
||||
case 2: // we have 2 bits and keep 7
|
||||
left = left << 5 | dec;
|
||||
mode = 3;
|
||||
break;
|
||||
|
||||
case 3: // we have 7 bits and keep 4
|
||||
vchRet.push_back((left<<1) | (dec>>4));
|
||||
left = dec & 15;
|
||||
mode = 4;
|
||||
break;
|
||||
|
||||
case 4: // we have 4 bits, and keep 1
|
||||
vchRet.push_back((left<<4) | (dec>>1));
|
||||
left = dec & 1;
|
||||
mode = 5;
|
||||
break;
|
||||
|
||||
case 5: // we have 1 bit, and keep 6
|
||||
left = left << 5 | dec;
|
||||
mode = 6;
|
||||
break;
|
||||
|
||||
case 6: // we have 6 bits, and keep 3
|
||||
vchRet.push_back((left<<2) | (dec>>3));
|
||||
left = dec & 7;
|
||||
mode = 7;
|
||||
break;
|
||||
|
||||
case 7: // we have 3 bits, and keep 0
|
||||
vchRet.push_back((left<<5) | dec);
|
||||
mode = 0;
|
||||
break;
|
||||
}
|
||||
const char* e = p;
|
||||
std::vector<uint8_t> val;
|
||||
val.reserve(strlen(p));
|
||||
while (*p != 0) {
|
||||
int x = decode32_table[(unsigned char)*p];
|
||||
if (x == -1) break;
|
||||
val.push_back(x);
|
||||
++p;
|
||||
}
|
||||
|
||||
if (pfInvalid)
|
||||
switch (mode)
|
||||
{
|
||||
case 0: // 8n base32 characters processed: ok
|
||||
break;
|
||||
std::vector<unsigned char> ret;
|
||||
ret.reserve((val.size() * 5) / 8);
|
||||
bool valid = ConvertBits<5, 8, false>([&](unsigned char c) { ret.push_back(c); }, val.begin(), val.end());
|
||||
|
||||
case 1: // 8n+1 base32 characters processed: impossible
|
||||
case 3: // +3
|
||||
case 6: // +6
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 2: // 8n+2 base32 characters processed: require '======'
|
||||
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || p[4] != '=' || p[5] != '=' || decode32_table[(unsigned char)p[6]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 4: // 8n+4 base32 characters processed: require '===='
|
||||
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || p[3] != '=' || decode32_table[(unsigned char)p[4]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 5: // 8n+5 base32 characters processed: require '==='
|
||||
if (left || p[0] != '=' || p[1] != '=' || p[2] != '=' || decode32_table[(unsigned char)p[3]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
|
||||
case 7: // 8n+7 base32 characters processed: require '='
|
||||
if (left || p[0] != '=' || decode32_table[(unsigned char)p[1]] != -1)
|
||||
*pfInvalid = true;
|
||||
break;
|
||||
const char* q = p;
|
||||
while (valid && *p != 0) {
|
||||
if (*p != '=') {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
valid = valid && (p - e) % 8 == 0 && p - q < 8;
|
||||
if (pfInvalid) *pfInvalid = !valid;
|
||||
|
||||
return vchRet;
|
||||
return ret;
|
||||
}
|
||||
|
||||
string DecodeBase32(const string& str)
|
||||
|
|
|
@ -133,4 +133,37 @@ bool TimingResistantEqual(const T& a, const T& b)
|
|||
*/
|
||||
bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out);
|
||||
|
||||
/**
|
||||
* Convert from one power-of-2 number base to another.
|
||||
*
|
||||
* Examples using ConvertBits<8, 5, true>():
|
||||
* 000000 -> 0000000000
|
||||
* 202020 -> 0400100200
|
||||
* 757575 -> 0e151a170a
|
||||
* abcdef -> 150f061e1e
|
||||
* ffffff -> 1f1f1f1f1e
|
||||
*/
|
||||
template<int frombits, int tobits, bool pad, typename O, typename I>
|
||||
bool ConvertBits(const O& outfn, I it, I end) {
|
||||
size_t acc = 0;
|
||||
size_t bits = 0;
|
||||
constexpr size_t maxv = (1 << tobits) - 1;
|
||||
constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
while (it != end) {
|
||||
acc = ((acc << frombits) | *it) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
outfn((acc >> bits) & maxv);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (pad) {
|
||||
if (bits) outfn((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // BITCOIN_UTILSTRENCODINGS_H
|
||||
|
|
|
@ -95,10 +95,6 @@ public:
|
|||
SproutPaymentAddress address() const;
|
||||
};
|
||||
|
||||
typedef boost::variant<InvalidEncoding, SproutPaymentAddress> PaymentAddress;
|
||||
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
|
||||
typedef boost::variant<InvalidEncoding, SproutSpendingKey> SpendingKey;
|
||||
|
||||
//! Sapling functions.
|
||||
class SaplingPaymentAddress {
|
||||
public:
|
||||
|
@ -209,6 +205,10 @@ public:
|
|||
boost::optional<SaplingPaymentAddress> default_address() const;
|
||||
};
|
||||
|
||||
typedef boost::variant<InvalidEncoding, SproutPaymentAddress, SaplingPaymentAddress> PaymentAddress;
|
||||
typedef boost::variant<InvalidEncoding, SproutViewingKey> ViewingKey;
|
||||
typedef boost::variant<InvalidEncoding, SproutSpendingKey, SaplingSpendingKey> SpendingKey;
|
||||
|
||||
}
|
||||
|
||||
/** Check whether a PaymentAddress is not an InvalidEncoding. */
|
||||
|
|
Loading…
Reference in New Issue