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