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