rippled
Loading...
Searching...
No Matches
tokens.cpp
1//
2/* The base58 encoding & decoding routines in the b58_ref namespace are taken
3 * from Bitcoin but have been modified from the original.
4 *
5 * Copyright (c) 2014 The Bitcoin Core developers
6 * Distributed under the MIT software license, see the accompanying
7 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 */
9
10#include <xrpl/basics/Expected.h>
11#include <xrpl/basics/safe_cast.h>
12#include <xrpl/beast/utility/instrumentation.h>
13#include <xrpl/protocol/detail/b58_utils.h>
14#include <xrpl/protocol/detail/token_errors.h>
15#include <xrpl/protocol/digest.h>
16#include <xrpl/protocol/tokens.h>
17
18#include <boost/container/small_vector.hpp>
19#include <boost/endian/conversion.hpp>
20
21#include <algorithm>
22#include <array>
23#include <cstdint>
24#include <cstring>
25#include <span>
26#include <string>
27#include <string_view>
28#include <type_traits>
29#include <vector>
30
31/*
32Converting between bases is straight forward. First, some background:
33
34Given the coefficients C[m], ... ,C[0] and base B, those coefficients represent
35the number C[m]*B^m + ... + C[0]*B^0; The following pseudo-code converts the
36coefficients to the (infinite precision) integer N:
37
38```
39N = 0;
40i = m ;; N.B. m is the index of the largest coefficient
41while (i>=0)
42 N = N + C[i]*B^i
43 i = i - 1
44```
45
46For example, in base 10, the number 437 represents the integer 4*10^2 + 3*10^1 +
477*10^0. In base 16, 437 is the same as 4*16^2 + 3*16^1 + 7*16^0.
48
49To find the coefficients that represent the integer N in base B, we start by
50computing the lowest order coefficients and work up to the highest order
51coefficients. The following pseudo-code converts the (infinite precision)
52integer N to the correct coefficients:
53
54```
55i = 0
56while(N)
57 C[i] = N mod B
58 N = floor(N/B)
59 i = i + 1
60```
61
62For example, to find the coefficients of the integer 437 in base 10:
63
64C[0] is 437 mod 10; C[0] = 7;
65N is floor(437/10); N = 43;
66C[1] is 43 mod 10; C[1] = 3;
67N is floor(43/10); N = 4;
68C[2] is 4 mod 10; C[2] = 4;
69N is floor(4/10); N = 0;
70Since N is 0, the algorithm stops.
71
72
73To convert between a number represented with coefficients from base B1 to that
74same number represented with coefficients from base B2, we can use the algorithm
75that converts coefficients from base B1 to an integer, and then use the
76algorithm that converts a number to coefficients from base B2.
77
78There is a useful shortcut that can be used if one of the bases is a power of
79the other base. If B1 == B2^G, then each coefficient from base B1 can be
80converted to base B2 independently to create a group of "G" B2 coefficient.
81These coefficients can be simply concatenated together. Since 16 == 2^4, this
82property is what makes base 16 useful when dealing with binary numbers. For
83example consider converting the base 16 number "93" to binary. The base 16
84coefficient 9 is represented in base 2 with the coefficients 1,0,0,1. The base
8516 coefficient 3 is represented in base 2 with the coefficients 0,0,1,1. To get
86the final answer, just concatenate those two independent conversions together.
87The base 16 number "93" is the binary number "10010011".
88
89The original (now reference) algorithm to convert from base 58 to a binary
90number used the
91
92```
93N = 0;
94for i in m to 0 inclusive
95 N = N + C[i]*B^i
96```
97
98algorithm.
99
100However, the algorithm above is pseudo-code. In particular, the variable "N" is
101an infinite precision integer in that pseudo-code. Real computers do
102computations on registers, and these registers have limited length. Modern
103computers use 64-bit general purpose registers, and can multiply two 64 bit
104numbers and obtain a 128 bit result (in two registers).
105
106The original algorithm in essence converted from base 58 to base 256 (base
1072^8). The new, faster algorithm converts from base 58 to base 58^10 (this is
108fast using the shortcut described above), then from base 58^10 to base 2^64
109(this is slow, and requires multi-precision arithmetic), and then from base 2^64
110to base 2^8 (this is fast, using the shortcut described above). Base 58^10 is
111chosen because it is the largest power of 58 that will fit into a 64-bit
112register.
113
114While it may seem counter-intuitive that converting from base 58 -> base 58^10
115-> base 2^64 -> base 2^8 is faster than directly converting from base 58 -> base
1162^8, it is actually 10x-15x faster. The reason for the speed increase is two of
117the conversions are trivial (converting between bases where one base is a power
118of another base), and doing the multi-precision computations with larger
119coefficients sizes greatly speeds up the multi-precision computations.
120*/
121
122namespace xrpl {
123
124static constexpr char const* alphabetForward = "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
125
126static constexpr std::array<int, 256> const alphabetReverse = []() {
128 for (auto& m : map)
129 m = -1;
130 for (int i = 0, j = 0; alphabetForward[i] != 0; ++i)
131 map[static_cast<unsigned char>(alphabetForward[i])] = j++;
132 return map;
133}();
134
135template <class Hasher>
136static typename Hasher::result_type
137digest(void const* data, std::size_t size) noexcept
138{
139 Hasher h;
140 h(data, size);
141 return static_cast<typename Hasher::result_type>(h);
142}
143
144template <class Hasher, class T, std::size_t N, class = std::enable_if_t<sizeof(T) == 1>>
145static typename Hasher::result_type
147{
148 return digest<Hasher>(v.data(), v.size());
149}
150
151// Computes a double digest (e.g. digest of the digest)
152template <class Hasher, class... Args>
153static typename Hasher::result_type
154digest2(Args const&... args)
155{
156 return digest<Hasher>(digest<Hasher>(args...));
157}
158
168static void
169checksum(void* out, void const* message, std::size_t size)
170{
171 auto const h = digest2<sha256_hasher>(message, size);
172 std::memcpy(out, h.data(), 4);
173}
174
175[[nodiscard]] std::string
176encodeBase58Token(TokenType type, void const* token, std::size_t size)
177{
178#ifndef _MSC_VER
179 return b58_fast::encodeBase58Token(type, token, size);
180#else
181 return b58_ref::encodeBase58Token(type, token, size);
182#endif
183}
184
185[[nodiscard]] std::string
187{
188#ifndef _MSC_VER
189 return b58_fast::decodeBase58Token(s, type);
190#else
191 return b58_ref::decodeBase58Token(s, type);
192#endif
193}
194
195namespace b58_ref {
196
197namespace detail {
198
200encodeBase58(void const* message, std::size_t size, void* temp, std::size_t temp_size)
201{
202 auto pbegin = reinterpret_cast<unsigned char const*>(message);
203 auto const pend = pbegin + size;
204
205 // Skip & count leading zeroes.
206 int zeroes = 0;
207 while (pbegin != pend && *pbegin == 0)
208 {
209 pbegin++;
210 zeroes++;
211 }
212
213 auto const b58begin = reinterpret_cast<unsigned char*>(temp);
214 auto const b58end = b58begin + temp_size;
215
216 std::fill(b58begin, b58end, 0);
217
218 while (pbegin != pend)
219 {
220 int carry = *pbegin;
221 // Apply "b58 = b58 * 256 + ch".
222 for (auto iter = b58end; iter != b58begin; --iter)
223 {
224 carry += 256 * (iter[-1]);
225 iter[-1] = carry % 58;
226 carry /= 58;
227 }
228 XRPL_ASSERT(carry == 0, "xrpl::b58_ref::detail::encodeBase58 : zero carry");
229 pbegin++;
230 }
231
232 // Skip leading zeroes in base58 result.
233 auto iter = b58begin;
234 while (iter != b58end && *iter == 0)
235 ++iter;
236
237 // Translate the result into a string.
238 std::string str;
239 str.reserve(zeroes + (b58end - iter));
240 str.assign(zeroes, alphabetForward[0]);
241 while (iter != b58end)
242 str += alphabetForward[*(iter++)];
243 return str;
244}
245
248{
249 auto psz = reinterpret_cast<unsigned char const*>(s.c_str());
250 auto remain = s.size();
251 // Skip and count leading zeroes
252 int zeroes = 0;
253 while (remain > 0 && alphabetReverse[*psz] == 0)
254 {
255 ++zeroes;
256 ++psz;
257 --remain;
258 }
259
260 if (remain > 64)
261 return {};
262
263 // Allocate enough space in big-endian base256 representation.
264 // log(58) / log(256), rounded up.
265 std::vector<unsigned char> b256(remain * 733 / 1000 + 1);
266 while (remain > 0)
267 {
268 auto carry = alphabetReverse[*psz];
269 if (carry == -1)
270 return {};
271 // Apply "b256 = b256 * 58 + carry".
272 for (auto iter = b256.rbegin(); iter != b256.rend(); ++iter)
273 {
274 carry += 58 * *iter;
275 *iter = carry % 256;
276 carry /= 256;
277 }
278 XRPL_ASSERT(carry == 0, "xrpl::b58_ref::detail::decodeBase58 : zero carry");
279 ++psz;
280 --remain;
281 }
282 // Skip leading zeroes in b256.
283 auto iter = std::find_if(b256.begin(), b256.end(), [](unsigned char c) { return c != 0; });
284 std::string result;
285 result.reserve(zeroes + (b256.end() - iter));
286 result.assign(zeroes, 0x00);
287 while (iter != b256.end())
288 result.push_back(*(iter++));
289 return result;
290}
291
292} // namespace detail
293
295encodeBase58Token(TokenType type, void const* token, std::size_t size)
296{
297 // expanded token includes type + 4 byte checksum
298 auto const expanded = 1 + size + 4;
299
300 // We need expanded + expanded * (log(256) / log(58)) which is
301 // bounded by expanded + expanded * (138 / 100 + 1) which works
302 // out to expanded * 3:
303 auto const bufsize = expanded * 3;
304
305 boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
306
307 // Lay the data out as
308 // <type><token><checksum>
309 buf[0] = safe_cast<std::underlying_type_t<TokenType>>(type);
310 if (size)
311 std::memcpy(buf.data() + 1, token, size);
312 checksum(buf.data() + 1 + size, buf.data(), 1 + size);
313
314 return detail::encodeBase58(buf.data(), expanded, buf.data() + expanded, bufsize - expanded);
315}
316
319{
320 std::string const ret = detail::decodeBase58(s);
321
322 // Reject zero length tokens
323 if (ret.size() < 6)
324 return {};
325
326 // The type must match.
327 if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
328 return {};
329
330 // And the checksum must as well.
332 checksum(guard.data(), ret.data(), ret.size() - guard.size());
333 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
334 return {};
335
336 // Skip the leading type byte and the trailing checksum.
337 return ret.substr(1, ret.size() - 1 - guard.size());
338}
339} // namespace b58_ref
340
341#ifndef _MSC_VER
342// The algorithms use gcc's int128 (fast MS version will have to wait, in the
343// meantime MS falls back to the slower reference implementation)
344namespace b58_fast {
345namespace detail {
346// Note: both the input and output will be BIG ENDIAN
347B58Result<std::span<std::uint8_t>>
349{
350 // Max valid input is 38 bytes:
351 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
352 if (input.size() > 38)
353 {
354 return Unexpected(TokenCodecErrc::inputTooLarge);
355 };
356
357 auto count_leading_zeros = [](std::span<std::uint8_t const> const& col) -> std::size_t {
358 std::size_t count = 0;
359 for (auto const& c : col)
360 {
361 if (c != 0)
362 {
363 return count;
364 }
365 count += 1;
366 }
367 return count;
368 };
369
370 auto const input_zeros = count_leading_zeros(input);
371 input = input.subspan(input_zeros);
372
373 // Allocate enough base 2^64 coeff for encoding 38 bytes
374 // log(2^(38*8),2^64)) ~= 4.75. So 5 coeff are enough
375 std::array<std::uint64_t, 5> base_2_64_coeff_buf{};
376 std::span<std::uint64_t> const base_2_64_coeff = [&]() -> std::span<std::uint64_t> {
377 // convert input from big endian to native u64, lowest coeff first
378 std::size_t num_coeff = 0;
379 for (int i = 0; i < base_2_64_coeff_buf.size(); ++i)
380 {
381 if (i * 8 >= input.size())
382 {
383 break;
384 }
385 auto const src_i_end = input.size() - i * 8;
386 if (src_i_end >= 8)
387 {
388 std::memcpy(&base_2_64_coeff_buf[num_coeff], &input[src_i_end - 8], 8);
389 boost::endian::big_to_native_inplace(base_2_64_coeff_buf[num_coeff]);
390 }
391 else
392 {
393 std::uint64_t be = 0;
394 for (int bi = 0; bi < src_i_end; ++bi)
395 {
396 be <<= 8;
397 be |= input[bi];
398 }
399 base_2_64_coeff_buf[num_coeff] = be;
400 };
401 num_coeff += 1;
402 }
403 return std::span(base_2_64_coeff_buf.data(), num_coeff);
404 }();
405
406 // Allocate enough base 58^10 coeff for encoding 38 bytes
407 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
408 std::array<std::uint64_t, 6> base_58_10_coeff{};
409 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
410 std::size_t num_58_10_coeffs = 0;
411 std::size_t cur_2_64_end = base_2_64_coeff.size();
412 // compute the base 58^10 coeffs
413 while (cur_2_64_end > 0)
414 {
415 base_58_10_coeff[num_58_10_coeffs] =
416 xrpl::b58_fast::detail::inplace_bigint_div_rem(base_2_64_coeff.subspan(0, cur_2_64_end), B_58_10);
417 num_58_10_coeffs += 1;
418 if (base_2_64_coeff[cur_2_64_end - 1] == 0)
419 {
420 cur_2_64_end -= 1;
421 }
422 }
423
424 // Translate the result into the alphabet
425 // Put all the zeros at the beginning, then all the values from the output
426 std::fill(out.begin(), out.begin() + input_zeros, ::xrpl::alphabetForward[0]);
427
428 // iterate through the base 58^10 coeff
429 // convert to base 58 big endian then
430 // convert to alphabet big endian
431 bool skip_zeros = true;
432 auto out_index = input_zeros;
433 for (int i = num_58_10_coeffs - 1; i >= 0; --i)
434 {
435 if (skip_zeros && base_58_10_coeff[i] == 0)
436 {
437 continue;
438 }
439 static constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
440 if (base_58_10_coeff[i] >= B_58_10)
441 {
442 return Unexpected(TokenCodecErrc::inputTooLarge);
443 }
444 std::array<std::uint8_t, 10> const b58_be = xrpl::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]);
445 std::size_t to_skip = 0;
446 std::span<std::uint8_t const> b58_be_s{b58_be.data(), b58_be.size()};
447 if (skip_zeros)
448 {
449 to_skip = count_leading_zeros(b58_be_s);
450 skip_zeros = false;
451 if (out.size() < (i + 1) * 10 - to_skip)
452 {
453 return Unexpected(TokenCodecErrc::outputTooSmall);
454 }
455 }
456 for (auto b58_coeff : b58_be_s.subspan(to_skip))
457 {
458 out[out_index] = ::xrpl::alphabetForward[b58_coeff];
459 out_index += 1;
460 }
461 }
462
463 return out.subspan(0, out_index);
464}
465
466// Note the input is BIG ENDIAN (some fn in this module use little endian)
467B58Result<std::span<std::uint8_t>>
468b58_to_b256_be(std::string_view input, std::span<std::uint8_t> out)
469{
470 // Convert from b58 to b 58^10
471
472 // Max encoded value is 38 bytes
473 // log(2^(38*8),58) ~= 51.9
474 if (input.size() > 52)
475 {
476 return Unexpected(TokenCodecErrc::inputTooLarge);
477 };
478 if (out.size() < 8)
479 {
480 return Unexpected(TokenCodecErrc::outputTooSmall);
481 }
482
483 auto count_leading_zeros = [&](auto const& col) -> std::size_t {
484 std::size_t count = 0;
485 for (auto const& c : col)
486 {
487 if (c != ::xrpl::alphabetForward[0])
488 {
489 return count;
490 }
491 count += 1;
492 }
493 return count;
494 };
495
496 auto const input_zeros = count_leading_zeros(input);
497
498 // Allocate enough base 58^10 coeff for encoding 38 bytes
499 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
500 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
501 std::array<std::uint64_t, 6> b_58_10_coeff{};
502 auto [num_full_coeffs, partial_coeff_len] = xrpl::b58_fast::detail::div_rem(input.size(), 10);
503 auto const num_partial_coeffs = partial_coeff_len ? 1 : 0;
504 auto const num_b_58_10_coeffs = num_full_coeffs + num_partial_coeffs;
505 XRPL_ASSERT(num_b_58_10_coeffs <= b_58_10_coeff.size(), "xrpl::b58_fast::detail::b58_to_b256_be : maximum coeff");
506 for (unsigned char c : input.substr(0, partial_coeff_len))
507 {
508 auto cur_val = ::xrpl::alphabetReverse[c];
509 if (cur_val < 0)
510 {
511 return Unexpected(TokenCodecErrc::invalidEncodingChar);
512 }
513 b_58_10_coeff[0] *= 58;
514 b_58_10_coeff[0] += cur_val;
515 }
516 for (int i = 0; i < 10; ++i)
517 {
518 for (int j = 0; j < num_full_coeffs; ++j)
519 {
520 unsigned char c = input[partial_coeff_len + j * 10 + i];
521 auto cur_val = ::xrpl::alphabetReverse[c];
522 if (cur_val < 0)
523 {
524 return Unexpected(TokenCodecErrc::invalidEncodingChar);
525 }
526 b_58_10_coeff[num_partial_coeffs + j] *= 58;
527 b_58_10_coeff[num_partial_coeffs + j] += cur_val;
528 }
529 }
530
531 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
532
533 // log(2^(38*8),2^64) ~= 4.75)
535 result[0] = b_58_10_coeff[0];
536 std::size_t cur_result_size = 1;
537 for (int i = 1; i < num_b_58_10_coeffs; ++i)
538 {
539 std::uint64_t const c = b_58_10_coeff[i];
540
541 {
542 auto code = xrpl::b58_fast::detail::inplace_bigint_mul(std::span(&result[0], cur_result_size + 1), B_58_10);
543 if (code != TokenCodecErrc::success)
544 {
545 return Unexpected(code);
546 }
547 }
548 {
549 auto code = xrpl::b58_fast::detail::inplace_bigint_add(std::span(&result[0], cur_result_size + 1), c);
550 if (code != TokenCodecErrc::success)
551 {
552 return Unexpected(code);
553 }
554 }
555 if (result[cur_result_size] != 0)
556 {
557 cur_result_size += 1;
558 }
559 }
560 std::fill(out.begin(), out.begin() + input_zeros, 0);
561 auto cur_out_i = input_zeros;
562 // Don't write leading zeros to the output for the most significant
563 // coeff
564 {
565 std::uint64_t const c = result[cur_result_size - 1];
566 auto skip_zero = true;
567 // start and end of output range
568 for (int i = 0; i < 8; ++i)
569 {
570 std::uint8_t const b = (c >> (8 * (7 - i))) & 0xff;
571 if (skip_zero)
572 {
573 if (b == 0)
574 {
575 continue;
576 }
577 skip_zero = false;
578 }
579 out[cur_out_i] = b;
580 cur_out_i += 1;
581 }
582 }
583 if ((cur_out_i + 8 * (cur_result_size - 1)) > out.size())
584 {
585 return Unexpected(TokenCodecErrc::outputTooSmall);
586 }
587
588 for (int i = cur_result_size - 2; i >= 0; --i)
589 {
590 auto c = result[i];
591 boost::endian::native_to_big_inplace(c);
592 memcpy(&out[cur_out_i], &c, 8);
593 cur_out_i += 8;
594 }
595
596 return out.subspan(0, cur_out_i);
597}
598} // namespace detail
599
600B58Result<std::span<std::uint8_t>>
602{
603 constexpr std::size_t tmpBufSize = 128;
605 if (input.size() > tmpBufSize - 5)
606 {
607 return Unexpected(TokenCodecErrc::inputTooLarge);
608 }
609 if (input.size() == 0)
610 {
611 return Unexpected(TokenCodecErrc::inputTooSmall);
612 }
613 // <type (1 byte)><token (input len)><checksum (4 bytes)>
614 buf[0] = static_cast<std::uint8_t>(token_type);
615 // buf[1..=input.len()] = input;
616 memcpy(&buf[1], input.data(), input.size());
617 size_t const checksum_i = input.size() + 1;
618 // buf[checksum_i..checksum_i + 4] = checksum
619 checksum(buf.data() + checksum_i, buf.data(), checksum_i);
620 std::span<std::uint8_t const> b58Span(buf.data(), input.size() + 5);
621 return detail::b256_to_b58_be(b58Span, out);
622}
623// Convert from base 58 to base 256, largest coefficients first
624// The input is encoded in XRPL format, with the token in the first
625// byte and the checksum in the last four bytes.
626// The decoded base 256 value does not include the token type or checksum.
627// It is an error if the token type or checksum does not match.
628B58Result<std::span<std::uint8_t>>
630{
632 auto const decodeResult = detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size()));
633
634 if (!decodeResult)
635 return decodeResult;
636
637 auto const ret = decodeResult.value();
638
639 // Reject zero length tokens
640 if (ret.size() < 6)
641 return Unexpected(TokenCodecErrc::inputTooSmall);
642
643 // The type must match.
644 if (type != static_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
645 return Unexpected(TokenCodecErrc::mismatchedTokenType);
646
647 // And the checksum must as well.
649 checksum(guard.data(), ret.data(), ret.size() - guard.size());
650 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
651 {
652 return Unexpected(TokenCodecErrc::mismatchedChecksum);
653 }
654
655 std::size_t const outSize = ret.size() - 1 - guard.size();
656 if (outBuf.size() < outSize)
657 return Unexpected(TokenCodecErrc::outputTooSmall);
658 // Skip the leading type byte and the trailing checksum.
659 std::copy(ret.begin() + 1, ret.begin() + outSize + 1, outBuf.begin());
660 return outBuf.subspan(0, outSize);
661}
662
663[[nodiscard]] std::string
664encodeBase58Token(TokenType type, void const* token, std::size_t size)
665{
666 std::string sr;
667 // The largest object encoded as base58 is 33 bytes; This will be encoded in
668 // at most ceil(log(2^256,58)) bytes, or 46 bytes. 128 is plenty (and
669 // there's not real benefit making it smaller). Note that 46 bytes may be
670 // encoded in more than 46 base58 chars. Since decode uses 64 as the
671 // over-allocation, this function uses 128 (again, over-allocation assuming
672 // 2 base 58 char per byte)
673 sr.resize(128);
674 std::span<std::uint8_t> outSp(reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
675 std::span<std::uint8_t const> inSp(reinterpret_cast<std::uint8_t const*>(token), size);
676 auto r = b58_fast::encodeBase58Token(type, inSp, outSp);
677 if (!r)
678 return {};
679 sr.resize(r.value().size());
680 return sr;
681}
682
683[[nodiscard]] std::string
684decodeBase58Token(std::string const& s, TokenType type)
685{
686 std::string sr;
687 // The largest object encoded as base58 is 33 bytes; 64 is plenty (and
688 // there's no benefit making it smaller)
689 sr.resize(64);
690 std::span<std::uint8_t> outSp(reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
691 auto r = b58_fast::decodeBase58Token(type, s, outSp);
692 if (!r)
693 return {};
694 sr.resize(r.value().size());
695 return sr;
696}
697
698} // namespace b58_fast
699#endif // _MSC_VER
700} // namespace xrpl
T assign(T... args)
T begin(T... args)
T c_str(T... args)
T copy(T... args)
T count(T... args)
T data(T... args)
T end(T... args)
T equal(T... args)
T fill(T... args)
T find_if(T... args)
T memcpy(T... args)
std::string decodeBase58(std::string const &s)
Definition tokens.cpp:247
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition tokens.cpp:200
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition tokens.cpp:295
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:318
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static void checksum(void *out, void const *message, std::size_t size)
Calculate a 4-byte checksum of the data.
Definition tokens.cpp:169
Unexpected(E(&)[N]) -> Unexpected< E const * >
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition tokens.cpp:137
static constexpr char const * alphabetForward
Definition tokens.cpp:124
TokenType
Definition tokens.h:18
static Hasher::result_type digest2(Args const &... args)
Definition tokens.cpp:154
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Encode data in Base58Check format using XRPL alphabet.
Definition tokens.cpp:176
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:186
static constexpr std::array< int, 256 > const alphabetReverse
Definition tokens.cpp:126
T push_back(T... args)
T rbegin(T... args)
T rend(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)
T subspan(T... args)
T substr(T... args)