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