Bitcoin ABC 0.33.10
P2P Digital Currency
cashaddr.cpp
Go to the documentation of this file.
1// Copyright (c) 2017 Pieter Wuille
2// Copyright (c) 2017-2019 The Bitcoin developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include <cashaddr.h>
7#include <util/vector.h>
8
9namespace {
10
11typedef std::vector<uint8_t> data;
12
13constexpr size_t CHECKSUM_SIZE = 8;
14
18const char *CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
19
23const int8_t CHARSET_REV[128] = {
24 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
25 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
26 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7,
27 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22,
28 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1,
29 -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0,
30 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1};
31
37uint64_t PolyMod(const data &v) {
71 uint64_t c = 1;
72 for (uint8_t d : v) {
96 // First, determine the value of c0:
97 uint8_t c0 = c >> 35;
98
99 // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + d:
100 c = ((c & 0x07ffffffff) << 5) ^ d;
101
102 // Finally, for each set bit n in c0, conditionally add {2^n}k(x):
103 if (c0 & 0x01) {
104 // k(x) = {19}*x^7 + {3}*x^6 + {25}*x^5 + {11}*x^4 + {25}*x^3 +
105 // {3}*x^2 + {19}*x + {1}
106 c ^= 0x98f2bc8e61;
107 }
108
109 if (c0 & 0x02) {
110 // {2}k(x) = {15}*x^7 + {6}*x^6 + {27}*x^5 + {22}*x^4 + {27}*x^3 +
111 // {6}*x^2 + {15}*x + {2}
112 c ^= 0x79b76d99e2;
113 }
114
115 if (c0 & 0x04) {
116 // {4}k(x) = {30}*x^7 + {12}*x^6 + {31}*x^5 + {5}*x^4 + {31}*x^3 +
117 // {12}*x^2 + {30}*x + {4}
118 c ^= 0xf33e5fb3c4;
119 }
120
121 if (c0 & 0x08) {
122 // {8}k(x) = {21}*x^7 + {24}*x^6 + {23}*x^5 + {10}*x^4 + {23}*x^3 +
123 // {24}*x^2 + {21}*x + {8}
124 c ^= 0xae2eabe2a8;
125 }
126
127 if (c0 & 0x10) {
128 // {16}k(x) = {3}*x^7 + {25}*x^6 + {7}*x^5 + {20}*x^4 + {7}*x^3 +
129 // {25}*x^2 + {3}*x + {16}
130 c ^= 0x1e4f43e470;
131 }
132 }
133
141 return c ^ 1;
142}
143
149inline uint8_t LowerCase(uint8_t c) {
150 // ASCII black magic.
151 return c | 0x20;
152}
153
157data ExpandPrefix(const std::string &prefix) {
158 data ret;
159 ret.resize(prefix.size() + 1);
160 for (size_t i = 0; i < prefix.size(); ++i) {
161 ret[i] = prefix[i] & 0x1f;
162 }
163
164 ret[prefix.size()] = 0;
165 return ret;
166}
167
171bool VerifyChecksum(const std::string &prefix, const data &payload) {
172 return PolyMod(Cat(ExpandPrefix(prefix), payload)) == 0;
173}
174
178data CreateChecksum(const std::string &prefix, const data &payload) {
179 data enc = Cat(ExpandPrefix(prefix), payload);
180 // Append 8 zeroes.
181 enc.resize(enc.size() + CHECKSUM_SIZE);
182 // Determine what to XOR into those 8 zeroes.
183 uint64_t mod = PolyMod(enc);
184 data ret(CHECKSUM_SIZE);
185 for (size_t i = 0; i < CHECKSUM_SIZE; ++i) {
186 // Convert the 5-bit groups in mod to checksum values.
187 ret[i] = (mod >> (5 * (7 - i))) & 0x1f;
188 }
189
190 return ret;
191}
192
193} // namespace
194
195namespace cashaddr {
196
200std::string Encode(const std::string &prefix, const data &payload) {
201 data checksum = CreateChecksum(prefix, payload);
202 data combined = Cat(payload, checksum);
203 std::string ret = prefix + ':';
204
205 ret.reserve(ret.size() + combined.size());
206 for (uint8_t c : combined) {
207 ret += CHARSET[c];
208 }
209
210 return ret;
211}
212
216std::pair<std::string, data> Decode(const std::string &str,
217 const std::string &default_prefix) {
218 // Go over the string and do some sanity checks.
219 bool lower = false, upper = false, hasNumber = false;
220 size_t prefixSize = 0;
221 for (size_t i = 0; i < str.size(); ++i) {
222 uint8_t c = str[i];
223 if (c >= 'a' && c <= 'z') {
224 lower = true;
225 continue;
226 }
227
228 if (c >= 'A' && c <= 'Z') {
229 upper = true;
230 continue;
231 }
232
233 if (c >= '0' && c <= '9') {
234 // We cannot have numbers in the prefix.
235 hasNumber = true;
236 continue;
237 }
238
239 if (c == ':') {
240 // The separator cannot be the first character, cannot have number
241 // and there must not be 2 separators.
242 if (hasNumber || i == 0 || prefixSize != 0) {
243 return {};
244 }
245
246 prefixSize = i;
247 continue;
248 }
249
250 // We have an unexpected character.
251 return {};
252 }
253
254 // We can't have both upper case and lowercase.
255 if (upper && lower) {
256 return {};
257 }
258
259 // Get the prefix.
260 std::string prefix;
261 if (prefixSize == 0) {
262 prefix = default_prefix;
263 } else {
264 prefix.reserve(prefixSize);
265 for (size_t i = 0; i < prefixSize; ++i) {
266 prefix += LowerCase(str[i]);
267 }
268
269 // Now add the ':' in the size.
270 prefixSize++;
271 }
272
273 // Decode values.
274 const size_t valuesSize = str.size() - prefixSize;
275 if (valuesSize < CHECKSUM_SIZE) {
276 return {};
277 }
278
279 data values(valuesSize);
280 for (size_t i = 0; i < valuesSize; ++i) {
281 uint8_t c = str[i + prefixSize];
282 // We have an invalid char in there.
283 if (c > 127 || CHARSET_REV[c] == -1) {
284 return {};
285 }
286
287 values[i] = CHARSET_REV[c];
288 }
289
290 // Verify the checksum.
291 if (!VerifyChecksum(prefix, values)) {
292 return {};
293 }
294
295 return {std::move(prefix),
296 data(values.begin(), values.end() - CHECKSUM_SIZE)};
297}
298
299} // namespace cashaddr
std::pair< std::string, data > Decode(const std::string &str, const std::string &default_prefix)
Decode a cashaddr string.
Definition: cashaddr.cpp:216
std::string Encode(const std::string &prefix, const data &payload)
Encode a cashaddr string.
Definition: cashaddr.cpp:200
const char * prefix
Definition: rest.cpp:813
V Cat(V v1, V &&v2)
Concatenate two vectors, moving elements.
Definition: vector.h:34