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 ripple {
123
124static constexpr char const* alphabetForward =
125 "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
126
127static constexpr std::array<int, 256> const alphabetReverse = []() {
129 for (auto& m : map)
130 m = -1;
131 for (int i = 0, j = 0; alphabetForward[i] != 0; ++i)
132 map[static_cast<unsigned char>(alphabetForward[i])] = j++;
133 return map;
134}();
135
136template <class Hasher>
137static typename Hasher::result_type
138digest(void const* data, std::size_t size) noexcept
139{
140 Hasher h;
141 h(data, size);
142 return static_cast<typename Hasher::result_type>(h);
143}
144
145template <
146 class Hasher,
147 class T,
148 std::size_t N,
149 class = std::enable_if_t<sizeof(T) == 1>>
150static typename Hasher::result_type
152{
153 return digest<Hasher>(v.data(), v.size());
154}
155
156// Computes a double digest (e.g. digest of the digest)
157template <class Hasher, class... Args>
158static typename Hasher::result_type
159digest2(Args const&... args)
160{
161 return digest<Hasher>(digest<Hasher>(args...));
162}
163
173static void
174checksum(void* out, void const* message, std::size_t size)
175{
176 auto const h = digest2<sha256_hasher>(message, size);
177 std::memcpy(out, h.data(), 4);
178}
179
180[[nodiscard]] std::string
181encodeBase58Token(TokenType type, void const* token, std::size_t size)
182{
183#ifndef _MSC_VER
184 return b58_fast::encodeBase58Token(type, token, size);
185#else
186 return b58_ref::encodeBase58Token(type, token, size);
187#endif
188}
189
190[[nodiscard]] std::string
192{
193#ifndef _MSC_VER
194 return b58_fast::decodeBase58Token(s, type);
195#else
196 return b58_ref::decodeBase58Token(s, type);
197#endif
198}
199
200namespace b58_ref {
201
202namespace detail {
203
206 void const* message,
207 std::size_t size,
208 void* temp,
209 std::size_t temp_size)
210{
211 auto pbegin = reinterpret_cast<unsigned char const*>(message);
212 auto const pend = pbegin + size;
213
214 // Skip & count leading zeroes.
215 int zeroes = 0;
216 while (pbegin != pend && *pbegin == 0)
217 {
218 pbegin++;
219 zeroes++;
220 }
221
222 auto const b58begin = reinterpret_cast<unsigned char*>(temp);
223 auto const b58end = b58begin + temp_size;
224
225 std::fill(b58begin, b58end, 0);
226
227 while (pbegin != pend)
228 {
229 int carry = *pbegin;
230 // Apply "b58 = b58 * 256 + ch".
231 for (auto iter = b58end; iter != b58begin; --iter)
232 {
233 carry += 256 * (iter[-1]);
234 iter[-1] = carry % 58;
235 carry /= 58;
236 }
237 XRPL_ASSERT(
238 carry == 0, "ripple::b58_ref::detail::encodeBase58 : zero carry");
239 pbegin++;
240 }
241
242 // Skip leading zeroes in base58 result.
243 auto iter = b58begin;
244 while (iter != b58end && *iter == 0)
245 ++iter;
246
247 // Translate the result into a string.
248 std::string str;
249 str.reserve(zeroes + (b58end - iter));
250 str.assign(zeroes, alphabetForward[0]);
251 while (iter != b58end)
252 str += alphabetForward[*(iter++)];
253 return str;
254}
255
258{
259 auto psz = reinterpret_cast<unsigned char const*>(s.c_str());
260 auto remain = s.size();
261 // Skip and count leading zeroes
262 int zeroes = 0;
263 while (remain > 0 && alphabetReverse[*psz] == 0)
264 {
265 ++zeroes;
266 ++psz;
267 --remain;
268 }
269
270 if (remain > 64)
271 return {};
272
273 // Allocate enough space in big-endian base256 representation.
274 // log(58) / log(256), rounded up.
275 std::vector<unsigned char> b256(remain * 733 / 1000 + 1);
276 while (remain > 0)
277 {
278 auto carry = alphabetReverse[*psz];
279 if (carry == -1)
280 return {};
281 // Apply "b256 = b256 * 58 + carry".
282 for (auto iter = b256.rbegin(); iter != b256.rend(); ++iter)
283 {
284 carry += 58 * *iter;
285 *iter = carry % 256;
286 carry /= 256;
287 }
288 XRPL_ASSERT(
289 carry == 0, "ripple::b58_ref::detail::decodeBase58 : zero carry");
290 ++psz;
291 --remain;
292 }
293 // Skip leading zeroes in b256.
294 auto iter = std::find_if(
295 b256.begin(), b256.end(), [](unsigned char c) { return c != 0; });
296 std::string result;
297 result.reserve(zeroes + (b256.end() - iter));
298 result.assign(zeroes, 0x00);
299 while (iter != b256.end())
300 result.push_back(*(iter++));
301 return result;
302}
303
304} // namespace detail
305
307encodeBase58Token(TokenType type, void const* token, std::size_t size)
308{
309 // expanded token includes type + 4 byte checksum
310 auto const expanded = 1 + size + 4;
311
312 // We need expanded + expanded * (log(256) / log(58)) which is
313 // bounded by expanded + expanded * (138 / 100 + 1) which works
314 // out to expanded * 3:
315 auto const bufsize = expanded * 3;
316
317 boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
318
319 // Lay the data out as
320 // <type><token><checksum>
321 buf[0] = safe_cast<std::underlying_type_t<TokenType>>(type);
322 if (size)
323 std::memcpy(buf.data() + 1, token, size);
324 checksum(buf.data() + 1 + size, buf.data(), 1 + size);
325
327 buf.data(), expanded, buf.data() + expanded, bufsize - expanded);
328}
329
332{
333 std::string const ret = detail::decodeBase58(s);
334
335 // Reject zero length tokens
336 if (ret.size() < 6)
337 return {};
338
339 // The type must match.
340 if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
341 return {};
342
343 // And the checksum must as well.
345 checksum(guard.data(), ret.data(), ret.size() - guard.size());
346 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
347 return {};
348
349 // Skip the leading type byte and the trailing checksum.
350 return ret.substr(1, ret.size() - 1 - guard.size());
351}
352} // namespace b58_ref
353
354#ifndef _MSC_VER
355// The algorithms use gcc's int128 (fast MS version will have to wait, in the
356// meantime MS falls back to the slower reference implementation)
357namespace b58_fast {
358namespace detail {
359// Note: both the input and output will be BIG ENDIAN
360B58Result<std::span<std::uint8_t>>
362{
363 // Max valid input is 38 bytes:
364 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
365 if (input.size() > 38)
366 {
367 return Unexpected(TokenCodecErrc::inputTooLarge);
368 };
369
370 auto count_leading_zeros =
372 std::size_t count = 0;
373 for (auto const& c : col)
374 {
375 if (c != 0)
376 {
377 return count;
378 }
379 count += 1;
380 }
381 return count;
382 };
383
384 auto const input_zeros = count_leading_zeros(input);
385 input = input.subspan(input_zeros);
386
387 // Allocate enough base 2^64 coeff for encoding 38 bytes
388 // log(2^(38*8),2^64)) ~= 4.75. So 5 coeff are enough
389 std::array<std::uint64_t, 5> base_2_64_coeff_buf{};
390 std::span<std::uint64_t> const base_2_64_coeff =
391 [&]() -> std::span<std::uint64_t> {
392 // convert input from big endian to native u64, lowest coeff first
393 std::size_t num_coeff = 0;
394 for (int i = 0; i < base_2_64_coeff_buf.size(); ++i)
395 {
396 if (i * 8 >= input.size())
397 {
398 break;
399 }
400 auto const src_i_end = input.size() - i * 8;
401 if (src_i_end >= 8)
402 {
404 &base_2_64_coeff_buf[num_coeff], &input[src_i_end - 8], 8);
405 boost::endian::big_to_native_inplace(
406 base_2_64_coeff_buf[num_coeff]);
407 }
408 else
409 {
410 std::uint64_t be = 0;
411 for (int bi = 0; bi < src_i_end; ++bi)
412 {
413 be <<= 8;
414 be |= input[bi];
415 }
416 base_2_64_coeff_buf[num_coeff] = be;
417 };
418 num_coeff += 1;
419 }
420 return std::span(base_2_64_coeff_buf.data(), num_coeff);
421 }();
422
423 // Allocate enough base 58^10 coeff for encoding 38 bytes
424 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
425 std::array<std::uint64_t, 6> base_58_10_coeff{};
426 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
427 std::size_t num_58_10_coeffs = 0;
428 std::size_t cur_2_64_end = base_2_64_coeff.size();
429 // compute the base 58^10 coeffs
430 while (cur_2_64_end > 0)
431 {
432 base_58_10_coeff[num_58_10_coeffs] =
433 ripple::b58_fast::detail::inplace_bigint_div_rem(
434 base_2_64_coeff.subspan(0, cur_2_64_end), B_58_10);
435 num_58_10_coeffs += 1;
436 if (base_2_64_coeff[cur_2_64_end - 1] == 0)
437 {
438 cur_2_64_end -= 1;
439 }
440 }
441
442 // Translate the result into the alphabet
443 // Put all the zeros at the beginning, then all the values from the output
444 std::fill(
445 out.begin(), out.begin() + input_zeros, ::ripple::alphabetForward[0]);
446
447 // iterate through the base 58^10 coeff
448 // convert to base 58 big endian then
449 // convert to alphabet big endian
450 bool skip_zeros = true;
451 auto out_index = input_zeros;
452 for (int i = num_58_10_coeffs - 1; i >= 0; --i)
453 {
454 if (skip_zeros && base_58_10_coeff[i] == 0)
455 {
456 continue;
457 }
458 static constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
459 if (base_58_10_coeff[i] >= B_58_10)
460 {
461 return Unexpected(TokenCodecErrc::inputTooLarge);
462 }
463 std::array<std::uint8_t, 10> const b58_be =
464 ripple::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]);
465 std::size_t to_skip = 0;
466 std::span<std::uint8_t const> b58_be_s{b58_be.data(), b58_be.size()};
467 if (skip_zeros)
468 {
469 to_skip = count_leading_zeros(b58_be_s);
470 skip_zeros = false;
471 if (out.size() < (i + 1) * 10 - to_skip)
472 {
473 return Unexpected(TokenCodecErrc::outputTooSmall);
474 }
475 }
476 for (auto b58_coeff : b58_be_s.subspan(to_skip))
477 {
478 out[out_index] = ::ripple::alphabetForward[b58_coeff];
479 out_index += 1;
480 }
481 }
482
483 return out.subspan(0, out_index);
484}
485
486// Note the input is BIG ENDIAN (some fn in this module use little endian)
487B58Result<std::span<std::uint8_t>>
488b58_to_b256_be(std::string_view input, std::span<std::uint8_t> out)
489{
490 // Convert from b58 to b 58^10
491
492 // Max encoded value is 38 bytes
493 // log(2^(38*8),58) ~= 51.9
494 if (input.size() > 52)
495 {
496 return Unexpected(TokenCodecErrc::inputTooLarge);
497 };
498 if (out.size() < 8)
499 {
500 return Unexpected(TokenCodecErrc::outputTooSmall);
501 }
502
503 auto count_leading_zeros = [&](auto const& col) -> std::size_t {
504 std::size_t count = 0;
505 for (auto const& c : col)
506 {
507 if (c != ::ripple::alphabetForward[0])
508 {
509 return count;
510 }
511 count += 1;
512 }
513 return count;
514 };
515
516 auto const input_zeros = count_leading_zeros(input);
517
518 // Allocate enough base 58^10 coeff for encoding 38 bytes
519 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
520 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
521 std::array<std::uint64_t, 6> b_58_10_coeff{};
522 auto [num_full_coeffs, partial_coeff_len] =
523 ripple::b58_fast::detail::div_rem(input.size(), 10);
524 auto const num_partial_coeffs = partial_coeff_len ? 1 : 0;
525 auto const num_b_58_10_coeffs = num_full_coeffs + num_partial_coeffs;
526 XRPL_ASSERT(
527 num_b_58_10_coeffs <= b_58_10_coeff.size(),
528 "ripple::b58_fast::detail::b58_to_b256_be : maximum coeff");
529 for (unsigned char c : input.substr(0, partial_coeff_len))
530 {
531 auto cur_val = ::ripple::alphabetReverse[c];
532 if (cur_val < 0)
533 {
534 return Unexpected(TokenCodecErrc::invalidEncodingChar);
535 }
536 b_58_10_coeff[0] *= 58;
537 b_58_10_coeff[0] += cur_val;
538 }
539 for (int i = 0; i < 10; ++i)
540 {
541 for (int j = 0; j < num_full_coeffs; ++j)
542 {
543 unsigned char c = input[partial_coeff_len + j * 10 + i];
544 auto cur_val = ::ripple::alphabetReverse[c];
545 if (cur_val < 0)
546 {
547 return Unexpected(TokenCodecErrc::invalidEncodingChar);
548 }
549 b_58_10_coeff[num_partial_coeffs + j] *= 58;
550 b_58_10_coeff[num_partial_coeffs + j] += cur_val;
551 }
552 }
553
554 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
555
556 // log(2^(38*8),2^64) ~= 4.75)
558 result[0] = b_58_10_coeff[0];
559 std::size_t cur_result_size = 1;
560 for (int i = 1; i < num_b_58_10_coeffs; ++i)
561 {
562 std::uint64_t const c = b_58_10_coeff[i];
563
564 {
565 auto code = ripple::b58_fast::detail::inplace_bigint_mul(
566 std::span(&result[0], cur_result_size + 1), B_58_10);
567 if (code != TokenCodecErrc::success)
568 {
569 return Unexpected(code);
570 }
571 }
572 {
573 auto code = ripple::b58_fast::detail::inplace_bigint_add(
574 std::span(&result[0], cur_result_size + 1), c);
575 if (code != TokenCodecErrc::success)
576 {
577 return Unexpected(code);
578 }
579 }
580 if (result[cur_result_size] != 0)
581 {
582 cur_result_size += 1;
583 }
584 }
585 std::fill(out.begin(), out.begin() + input_zeros, 0);
586 auto cur_out_i = input_zeros;
587 // Don't write leading zeros to the output for the most significant
588 // coeff
589 {
590 std::uint64_t const c = result[cur_result_size - 1];
591 auto skip_zero = true;
592 // start and end of output range
593 for (int i = 0; i < 8; ++i)
594 {
595 std::uint8_t const b = (c >> (8 * (7 - i))) & 0xff;
596 if (skip_zero)
597 {
598 if (b == 0)
599 {
600 continue;
601 }
602 skip_zero = false;
603 }
604 out[cur_out_i] = b;
605 cur_out_i += 1;
606 }
607 }
608 if ((cur_out_i + 8 * (cur_result_size - 1)) > out.size())
609 {
610 return Unexpected(TokenCodecErrc::outputTooSmall);
611 }
612
613 for (int i = cur_result_size - 2; i >= 0; --i)
614 {
615 auto c = result[i];
616 boost::endian::native_to_big_inplace(c);
617 memcpy(&out[cur_out_i], &c, 8);
618 cur_out_i += 8;
619 }
620
621 return out.subspan(0, cur_out_i);
622}
623} // namespace detail
624
625B58Result<std::span<std::uint8_t>>
627 TokenType token_type,
630{
631 constexpr std::size_t tmpBufSize = 128;
633 if (input.size() > tmpBufSize - 5)
634 {
635 return Unexpected(TokenCodecErrc::inputTooLarge);
636 }
637 if (input.size() == 0)
638 {
639 return Unexpected(TokenCodecErrc::inputTooSmall);
640 }
641 // <type (1 byte)><token (input len)><checksum (4 bytes)>
642 buf[0] = static_cast<std::uint8_t>(token_type);
643 // buf[1..=input.len()] = input;
644 memcpy(&buf[1], input.data(), input.size());
645 size_t const checksum_i = input.size() + 1;
646 // buf[checksum_i..checksum_i + 4] = checksum
647 checksum(buf.data() + checksum_i, buf.data(), checksum_i);
648 std::span<std::uint8_t const> b58Span(buf.data(), input.size() + 5);
649 return detail::b256_to_b58_be(b58Span, out);
650}
651// Convert from base 58 to base 256, largest coefficients first
652// The input is encoded in XPRL format, with the token in the first
653// byte and the checksum in the last four bytes.
654// The decoded base 256 value does not include the token type or checksum.
655// It is an error if the token type or checksum does not match.
656B58Result<std::span<std::uint8_t>>
658 TokenType type,
661{
663 auto const decodeResult =
664 detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size()));
665
666 if (!decodeResult)
667 return decodeResult;
668
669 auto const ret = decodeResult.value();
670
671 // Reject zero length tokens
672 if (ret.size() < 6)
673 return Unexpected(TokenCodecErrc::inputTooSmall);
674
675 // The type must match.
676 if (type != static_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
677 return Unexpected(TokenCodecErrc::mismatchedTokenType);
678
679 // And the checksum must as well.
681 checksum(guard.data(), ret.data(), ret.size() - guard.size());
682 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
683 {
684 return Unexpected(TokenCodecErrc::mismatchedChecksum);
685 }
686
687 std::size_t const outSize = ret.size() - 1 - guard.size();
688 if (outBuf.size() < outSize)
689 return Unexpected(TokenCodecErrc::outputTooSmall);
690 // Skip the leading type byte and the trailing checksum.
691 std::copy(ret.begin() + 1, ret.begin() + outSize + 1, outBuf.begin());
692 return outBuf.subspan(0, outSize);
693}
694
695[[nodiscard]] std::string
696encodeBase58Token(TokenType type, void const* token, std::size_t size)
697{
698 std::string sr;
699 // The largest object encoded as base58 is 33 bytes; This will be encoded in
700 // at most ceil(log(2^256,58)) bytes, or 46 bytes. 128 is plenty (and
701 // there's not real benefit making it smaller). Note that 46 bytes may be
702 // encoded in more than 46 base58 chars. Since decode uses 64 as the
703 // over-allocation, this function uses 128 (again, over-allocation assuming
704 // 2 base 58 char per byte)
705 sr.resize(128);
707 reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
709 reinterpret_cast<std::uint8_t const*>(token), size);
710 auto r = b58_fast::encodeBase58Token(type, inSp, outSp);
711 if (!r)
712 return {};
713 sr.resize(r.value().size());
714 return sr;
715}
716
717[[nodiscard]] std::string
718decodeBase58Token(std::string const& s, TokenType type)
719{
720 std::string sr;
721 // The largest object encoded as base58 is 33 bytes; 64 is plenty (and
722 // there's no benefit making it smaller)
723 sr.resize(64);
725 reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
726 auto r = b58_fast::decodeBase58Token(type, s, outSp);
727 if (!r)
728 return {};
729 sr.resize(r.value().size());
730 return sr;
731}
732
733} // namespace b58_fast
734#endif // _MSC_VER
735} // namespace ripple
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:257
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition tokens.cpp:205
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition tokens.cpp:307
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:331
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
TokenType
Definition tokens.h:19
static constexpr std::array< int, 256 > const alphabetReverse
Definition tokens.cpp:127
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Encode data in Base58Check format using XRPL alphabet.
Definition tokens.cpp:181
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:191
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition tokens.cpp:138
static Hasher::result_type digest2(Args const &... args)
Definition tokens.cpp:159
Unexpected(E(&)[N]) -> Unexpected< E const * >
static void checksum(void *out, void const *message, std::size_t size)
Calculate a 4-byte checksum of the data.
Definition tokens.cpp:174
static constexpr char const * alphabetForward
Definition tokens.cpp:124
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)