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