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:
Homu 2018-06-19 05:12:50 -07:00
commit 2ebde5860e
13 changed files with 303 additions and 249 deletions

View File

@ -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 \

View File

@ -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;

View File

@ -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());

View File

@ -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;

View File

@ -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();
}

View File

@ -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]);
}
}

View File

@ -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]);
}
}

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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. */