Bitcoin ABC 0.30.7
P2P Digital Currency
cashaddrenc.cpp
Go to the documentation of this file.
1// Copyright (c) 2017-2019 The Bitcoin developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4#include <cashaddrenc.h>
5
6#include <cashaddr.h>
7#include <chainparams.h>
8#include <pubkey.h>
9#include <script/script.h>
10#include <util/strencodings.h>
11
12#include <algorithm>
13#include <variant>
14
15namespace {
16
17// Convert the data part to a 5 bit representation.
18template <class T>
19std::vector<uint8_t> PackAddrData(const T &id, uint8_t type) {
20 uint8_t version_byte(type << 3);
21 size_t size = id.size();
22 uint8_t encoded_size = 0;
23 switch (size * 8) {
24 case 160:
25 encoded_size = 0;
26 break;
27 case 192:
28 encoded_size = 1;
29 break;
30 case 224:
31 encoded_size = 2;
32 break;
33 case 256:
34 encoded_size = 3;
35 break;
36 case 320:
37 encoded_size = 4;
38 break;
39 case 384:
40 encoded_size = 5;
41 break;
42 case 448:
43 encoded_size = 6;
44 break;
45 case 512:
46 encoded_size = 7;
47 break;
48 default:
49 throw std::runtime_error(
50 "Error packing cashaddr: invalid address length");
51 }
52 version_byte |= encoded_size;
53 std::vector<uint8_t> data = {version_byte};
54 data.insert(data.end(), std::begin(id), std::end(id));
55
56 std::vector<uint8_t> converted;
57 // Reserve the number of bytes required for a 5-bit packed version of a
58 // hash, with version byte. Add half a byte(4) so integer math provides
59 // the next multiple-of-5 that would fit all the data.
60 converted.reserve(((size + 1) * 8 + 4) / 5);
61 ConvertBits<8, 5, true>([&](uint8_t c) { converted.push_back(c); },
62 std::begin(data), std::end(data));
63
64 return converted;
65}
66
67// Implements encoding of CTxDestination using cashaddr.
68class CashAddrEncoder {
69public:
70 explicit CashAddrEncoder(const CChainParams &p) : params(p) {}
71
72 std::string operator()(const PKHash &id) const {
73 std::vector<uint8_t> data = PackAddrData(id, PUBKEY_TYPE);
74 return cashaddr::Encode(params.CashAddrPrefix(), data);
75 }
76
77 std::string operator()(const ScriptHash &id) const {
78 std::vector<uint8_t> data = PackAddrData(id, SCRIPT_TYPE);
79 return cashaddr::Encode(params.CashAddrPrefix(), data);
80 }
81
82 std::string operator()(const CNoDestination &) const { return ""; }
83
84private:
85 const CChainParams &params;
86};
87
88} // namespace
89
90std::string EncodeCashAddr(const CTxDestination &dst,
91 const CChainParams &params) {
92 return std::visit(CashAddrEncoder(params), dst);
93}
94
95std::string EncodeCashAddr(const std::string &prefix,
96 const CashAddrContent &content) {
97 std::vector<uint8_t> data = PackAddrData(content.hash, content.type);
98 return cashaddr::Encode(prefix, data);
99}
100
101CTxDestination DecodeCashAddr(const std::string &addr,
102 const CChainParams &params) {
103 CashAddrContent content =
105 if (content.hash.size() == 0) {
106 return CNoDestination{};
107 }
108
109 return DecodeCashAddrDestination(content);
110}
111
113 const std::string &expectedPrefix) {
114 auto [prefix, payload] = cashaddr::Decode(addr, expectedPrefix);
115
116 if (prefix != expectedPrefix) {
117 return {};
118 }
119
120 if (payload.empty()) {
121 return {};
122 }
123
124 std::vector<uint8_t> data;
125 data.reserve(payload.size() * 5 / 8);
126 if (!ConvertBits<5, 8, false>([&](uint8_t c) { data.push_back(c); },
127 begin(payload), end(payload))) {
128 return {};
129 }
130
131 // Decode type and size from the version.
132 uint8_t version = data[0];
133 if (version & 0x80) {
134 // First bit is reserved.
135 return {};
136 }
137
138 auto type = CashAddrType((version >> 3) & 0x1f);
139 uint32_t hash_size = 20 + 4 * (version & 0x03);
140 if (version & 0x04) {
141 hash_size *= 2;
142 }
143
144 // Check that we decoded the exact number of bytes we expected.
145 if (data.size() != hash_size + 1) {
146 return {};
147 }
148
149 // Pop the version.
150 data.erase(data.begin());
151 return {type, std::move(data)};
152}
153
155 if (content.hash.size() != 20) {
156 // Only 20 bytes hash are supported now.
157 return CNoDestination{};
158 }
159
160 uint160 hash;
161 std::copy(begin(content.hash), end(content.hash), hash.begin());
162
163 switch (content.type) {
164 case PUBKEY_TYPE:
165 return PKHash(hash);
166 case SCRIPT_TYPE:
167 return ScriptHash(hash);
168 default:
169 return CNoDestination{};
170 }
171}
172
173// PackCashAddrContent allows for testing PackAddrData in unittests due to
174// template definitions.
175std::vector<uint8_t> PackCashAddrContent(const CashAddrContent &content) {
176 return PackAddrData(content.hash, content.type);
177}
std::string EncodeCashAddr(const CTxDestination &dst, const CChainParams &params)
Definition: cashaddrenc.cpp:90
CashAddrContent DecodeCashAddrContent(const std::string &addr, const std::string &expectedPrefix)
CTxDestination DecodeCashAddrDestination(const CashAddrContent &content)
CTxDestination DecodeCashAddr(const std::string &addr, const CChainParams &params)
std::vector< uint8_t > PackCashAddrContent(const CashAddrContent &content)
CashAddrType
Definition: cashaddrenc.h:14
@ PUBKEY_TYPE
Definition: cashaddrenc.h:14
@ SCRIPT_TYPE
Definition: cashaddrenc.h:14
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:80
const std::string & CashAddrPrefix() const
Definition: chainparams.h:132
uint8_t * begin()
Definition: uint256.h:85
160-bit opaque blob.
Definition: uint256.h:117
std::pair< std::string, data > Decode(const std::string &str, const std::string &default_prefix)
Decode a cashaddr string.
Definition: cashaddr.cpp:214
std::string Encode(const std::string &prefix, const data &payload)
Encode a cashaddr string.
Definition: cashaddr.cpp:198
const char * prefix
Definition: rest.cpp:817
std::variant< CNoDestination, PKHash, ScriptHash > CTxDestination
A txout script template with a specific destination.
Definition: standard.h:85
CashAddrType type
Definition: cashaddrenc.h:17
std::vector< uint8_t > hash
Definition: cashaddrenc.h:18