Files
blackcoin-more/src/cashaddrenc.cpp
lateminer 323a6750c2 Add CashAddr Address Format
Ported from Bitcoin Unlimited, Bitcoin ABC
2018-01-14 22:32:08 +03:00

197 lines
4.9 KiB
C++

// Copyright (c) 2017 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "cashaddrenc.h"
#include "cashaddr.h"
#include "chainparams.h"
#include "pubkey.h"
#include "script/script.h"
#include "utilstrencodings.h"
#include <boost/variant/static_visitor.hpp>
#include <algorithm>
namespace
{
// Convert the data part to a 5 bit representation.
template <class T>
std::vector<uint8_t> PackAddrData(const T &id, uint8_t type)
{
uint8_t version_byte(type << 3);
size_t size = id.size();
uint8_t encoded_size = 0;
switch (size * 8)
{
case 160:
encoded_size = 0;
break;
case 192:
encoded_size = 1;
break;
case 224:
encoded_size = 2;
break;
case 256:
encoded_size = 3;
break;
case 320:
encoded_size = 4;
break;
case 384:
encoded_size = 5;
break;
case 448:
encoded_size = 6;
break;
case 512:
encoded_size = 7;
break;
default:
throw std::runtime_error("Error packing cashaddr: invalid address length");
}
version_byte |= encoded_size;
std::vector<uint8_t> data = {version_byte};
data.insert(data.end(), std::begin(id), std::end(id));
std::vector<uint8_t> converted;
// Reserve the number of bytes required for a 5-bit packed version of a
// hash, with version byte. Add half a byte(4) so integer math provides
// the next multiple-of-5 that would fit all the data.
converted.reserve(((size + 1) * 8 + 4) / 5);
ConvertBits<8, 5, true>(converted, std::begin(data), std::end(data));
return converted;
}
// Implements encoding of CTxDestination using cashaddr.
class CashAddrEncoder : public boost::static_visitor<std::string>
{
public:
CashAddrEncoder(const CChainParams &p) : params(p) {}
std::string operator()(const CKeyID &id) const
{
std::vector<uint8_t> data = PackAddrData(id, PUBKEY_TYPE);
return cashaddr::Encode(params.CashAddrPrefix(), data);
}
std::string operator()(const CScriptID &id) const
{
std::vector<uint8_t> data = PackAddrData(id, SCRIPT_TYPE);
return cashaddr::Encode(params.CashAddrPrefix(), data);
}
std::string operator()(const CNoDestination &) const { return ""; }
private:
const CChainParams &params;
};
} // anon ns
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
{
return boost::apply_visitor(CashAddrEncoder(params), dst);
}
CTxDestination DecodeCashAddr(const std::string &addr, const CChainParams &params)
{
CashAddrContent content = DecodeCashAddrContent(addr, params);
if (content.hash.size() == 0)
{
return CNoDestination{};
}
return DecodeCashAddrDestination(content);
}
CashAddrContent DecodeCashAddrContent(const std::string &addr, const CChainParams &params)
{
std::string prefix;
std::vector<uint8_t> payload;
std::tie(prefix, payload) = cashaddr::Decode(addr, params.CashAddrPrefix());
if (prefix != params.CashAddrPrefix())
{
return {};
}
if (payload.empty())
{
return {};
}
// Check that the padding is zero.
size_t extrabits = payload.size() * 5 % 8;
if (extrabits >= 5)
{
// We have more padding than allowed.
return {};
}
uint8_t last = payload.back();
uint8_t mask = (1 << extrabits) - 1;
if (last & mask)
{
// We have non zero bits as padding.
return {};
}
std::vector<uint8_t> data;
data.reserve(payload.size() * 5 / 8);
ConvertBits<5, 8, false>(data, begin(payload), end(payload));
// Decode type and size from the version.
uint8_t version = data[0];
if (version & 0x80)
{
// First bit is reserved.
return {};
}
auto type = CashAddrType((version >> 3) & 0x1f);
uint32_t hash_size = 20 + 4 * (version & 0x03);
if (version & 0x04)
{
hash_size *= 2;
}
// Check that we decoded the exact number of bytes we expected.
if (data.size() != hash_size + 1)
{
return {};
}
// Pop the version.
data.erase(data.begin());
return {type, std::move(data)};
}
CTxDestination DecodeCashAddrDestination(const CashAddrContent &content)
{
if (content.hash.size() != 20)
{
// Only 20 bytes hash are supported now.
return CNoDestination{};
}
uint160 hash;
std::copy(begin(content.hash), end(content.hash), hash.begin());
switch (content.type)
{
case PUBKEY_TYPE:
return CKeyID(hash);
case SCRIPT_TYPE:
return CScriptID(hash);
default:
return CNoDestination{};
}
}
// PackCashAddrContent allows for testing PackAddrData in unittests due to
// template definitions.
std::vector<uint8_t> PackCashAddrContent(const CashAddrContent &content)
{
return PackAddrData(content.hash, content.type);
}