rippled
Loading...
Searching...
No Matches
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 <xrpl/protocol/tokens.h>
29
30#include <xrpl/basics/safe_cast.h>
31#include <xrpl/beast/utility/instrumentation.h>
32#include <xrpl/protocol/detail/b58_utils.h>
33#include <xrpl/protocol/digest.h>
34
35#include <boost/container/small_vector.hpp>
36#include <boost/endian.hpp>
37#include <boost/endian/conversion.hpp>
38
39#include <cstring>
40#include <memory>
41#include <type_traits>
42#include <utility>
43#include <vector>
44
45/*
46Converting between bases is straight forward. First, some background:
47
48Given the coefficients C[m], ... ,C[0] and base B, those coefficients represent
49the number C[m]*B^m + ... + C[0]*B^0; The following pseudo-code converts the
50coefficients to the (infinite precision) integer N:
51
52```
53N = 0;
54i = m ;; N.B. m is the index of the largest coefficient
55while (i>=0)
56 N = N + C[i]*B^i
57 i = i - 1
58```
59
60For example, in base 10, the number 437 represents the integer 4*10^2 + 3*10^1 +
617*10^0. In base 16, 437 is the same as 4*16^2 + 3*16^1 + 7*16^0.
62
63To find the coefficients that represent the integer N in base B, we start by
64computing the lowest order coefficients and work up to the highest order
65coefficients. The following pseudo-code converts the (infinite precision)
66integer N to the correct coefficients:
67
68```
69i = 0
70while(N)
71 C[i] = N mod B
72 N = floor(N/B)
73 i = i + 1
74```
75
76For example, to find the coefficients of the integer 437 in base 10:
77
78C[0] is 437 mod 10; C[0] = 7;
79N is floor(437/10); N = 43;
80C[1] is 43 mod 10; C[1] = 3;
81N is floor(43/10); N = 4;
82C[2] is 4 mod 10; C[2] = 4;
83N is floor(4/10); N = 0;
84Since N is 0, the algorithm stops.
85
86
87To convert between a number represented with coefficients from base B1 to that
88same number represented with coefficients from base B2, we can use the algorithm
89that converts coefficients from base B1 to an integer, and then use the
90algorithm that converts a number to coefficients from base B2.
91
92There is a useful shortcut that can be used if one of the bases is a power of
93the other base. If B1 == B2^G, then each coefficient from base B1 can be
94converted to base B2 independently to create a group of "G" B2 coefficient.
95These coefficients can be simply concatenated together. Since 16 == 2^4, this
96property is what makes base 16 useful when dealing with binary numbers. For
97example consider converting the base 16 number "93" to binary. The base 16
98coefficient 9 is represented in base 2 with the coefficients 1,0,0,1. The base
9916 coefficient 3 is represented in base 2 with the coefficients 0,0,1,1. To get
100the final answer, just concatenate those two independent conversions together.
101The base 16 number "93" is the binary number "10010011".
102
103The original (now reference) algorithm to convert from base 58 to a binary
104number used the
105
106```
107N = 0;
108for i in m to 0 inclusive
109 N = N + C[i]*B^i
110```
111
112algorithm.
113
114However, the algorithm above is pseudo-code. In particular, the variable "N" is
115an infinite precision integer in that pseudo-code. Real computers do
116computations on registers, and these registers have limited length. Modern
117computers use 64-bit general purpose registers, and can multiply two 64 bit
118numbers and obtain a 128 bit result (in two registers).
119
120The original algorithm in essence converted from base 58 to base 256 (base
1212^8). The new, faster algorithm converts from base 58 to base 58^10 (this is
122fast 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
124to base 2^8 (this is fast, using the shortcut described above). Base 58^10 is
125chosen because it is the largest power of 58 that will fit into a 64-bit
126register.
127
128While 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
1302^8, it is actually 10x-15x faster. The reason for the speed increase is two of
131the conversions are trivial (converting between bases where one base is a power
132of another base), and doing the multi-precision computations with larger
133coefficients sizes greatly speeds up the multi-precision computations.
134*/
135
136namespace ripple {
137
138static constexpr char const* alphabetForward =
139 "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
140
141static constexpr std::array<int, 256> const alphabetReverse = []() {
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
150template <class Hasher>
151static typename Hasher::result_type
152digest(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
159template <
160 class Hasher,
161 class T,
162 std::size_t N,
163 class = std::enable_if_t<sizeof(T) == 1>>
164static 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)
171template <class Hasher, class... Args>
172static typename Hasher::result_type
173digest2(Args const&... args)
174{
175 return digest<Hasher>(digest<Hasher>(args...));
176}
177
187static void
188checksum(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
195encodeBase58Token(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
214namespace b58_ref {
215
216namespace 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 XRPL_ASSERT(
252 carry == 0, "ripple::b58_ref::detail::encodeBase58 : zero carry");
253 pbegin++;
254 }
255
256 // Skip leading zeroes in base58 result.
257 auto iter = b58begin;
258 while (iter != b58end && *iter == 0)
259 ++iter;
260
261 // Translate the result into a string.
262 std::string str;
263 str.reserve(zeroes + (b58end - iter));
264 str.assign(zeroes, alphabetForward[0]);
265 while (iter != b58end)
266 str += alphabetForward[*(iter++)];
267 return str;
268}
269
272{
273 auto psz = reinterpret_cast<unsigned char const*>(s.c_str());
274 auto remain = s.size();
275 // Skip and count leading zeroes
276 int zeroes = 0;
277 while (remain > 0 && alphabetReverse[*psz] == 0)
278 {
279 ++zeroes;
280 ++psz;
281 --remain;
282 }
283
284 if (remain > 64)
285 return {};
286
287 // Allocate enough space in big-endian base256 representation.
288 // log(58) / log(256), rounded up.
289 std::vector<unsigned char> b256(remain * 733 / 1000 + 1);
290 while (remain > 0)
291 {
292 auto carry = alphabetReverse[*psz];
293 if (carry == -1)
294 return {};
295 // Apply "b256 = b256 * 58 + carry".
296 for (auto iter = b256.rbegin(); iter != b256.rend(); ++iter)
297 {
298 carry += 58 * *iter;
299 *iter = carry % 256;
300 carry /= 256;
301 }
302 XRPL_ASSERT(
303 carry == 0, "ripple::b58_ref::detail::decodeBase58 : zero carry");
304 ++psz;
305 --remain;
306 }
307 // Skip leading zeroes in b256.
308 auto iter = std::find_if(
309 b256.begin(), b256.end(), [](unsigned char c) { return c != 0; });
310 std::string result;
311 result.reserve(zeroes + (b256.end() - iter));
312 result.assign(zeroes, 0x00);
313 while (iter != b256.end())
314 result.push_back(*(iter++));
315 return result;
316}
317
318} // namespace detail
319
321encodeBase58Token(TokenType type, void const* token, std::size_t size)
322{
323 // expanded token includes type + 4 byte checksum
324 auto const expanded = 1 + size + 4;
325
326 // We need expanded + expanded * (log(256) / log(58)) which is
327 // bounded by expanded + expanded * (138 / 100 + 1) which works
328 // out to expanded * 3:
329 auto const bufsize = expanded * 3;
330
331 boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
332
333 // Lay the data out as
334 // <type><token><checksum>
335 buf[0] = safe_cast<std::underlying_type_t<TokenType>>(type);
336 if (size)
337 std::memcpy(buf.data() + 1, token, size);
338 checksum(buf.data() + 1 + size, buf.data(), 1 + size);
339
341 buf.data(), expanded, buf.data() + expanded, bufsize - expanded);
342}
343
346{
347 std::string const ret = detail::decodeBase58(s);
348
349 // Reject zero length tokens
350 if (ret.size() < 6)
351 return {};
352
353 // The type must match.
354 if (type != safe_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
355 return {};
356
357 // And the checksum must as well.
359 checksum(guard.data(), ret.data(), ret.size() - guard.size());
360 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
361 return {};
362
363 // Skip the leading type byte and the trailing checksum.
364 return ret.substr(1, ret.size() - 1 - guard.size());
365}
366} // namespace b58_ref
367
368#ifndef _MSC_VER
369// The algorithms use gcc's int128 (fast MS version will have to wait, in the
370// meantime MS falls back to the slower reference implementation)
371namespace b58_fast {
372namespace detail {
373// Note: both the input and output will be BIG ENDIAN
374B58Result<std::span<std::uint8_t>>
376{
377 // Max valid input is 38 bytes:
378 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
379 if (input.size() > 38)
380 {
381 return Unexpected(TokenCodecErrc::inputTooLarge);
382 };
383
384 auto count_leading_zeros =
386 std::size_t count = 0;
387 for (auto const& c : col)
388 {
389 if (c != 0)
390 {
391 return count;
392 }
393 count += 1;
394 }
395 return count;
396 };
397
398 auto const input_zeros = count_leading_zeros(input);
399 input = input.subspan(input_zeros);
400
401 // Allocate enough base 2^64 coeff for encoding 38 bytes
402 // log(2^(38*8),2^64)) ~= 4.75. So 5 coeff are enough
403 std::array<std::uint64_t, 5> base_2_64_coeff_buf{};
404 std::span<std::uint64_t> const base_2_64_coeff =
405 [&]() -> std::span<std::uint64_t> {
406 // convert input from big endian to native u64, lowest coeff first
407 std::size_t num_coeff = 0;
408 for (int i = 0; i < base_2_64_coeff_buf.size(); ++i)
409 {
410 if (i * 8 >= input.size())
411 {
412 break;
413 }
414 auto const src_i_end = input.size() - i * 8;
415 if (src_i_end >= 8)
416 {
418 &base_2_64_coeff_buf[num_coeff], &input[src_i_end - 8], 8);
419 boost::endian::big_to_native_inplace(
420 base_2_64_coeff_buf[num_coeff]);
421 }
422 else
423 {
424 std::uint64_t be = 0;
425 for (int bi = 0; bi < src_i_end; ++bi)
426 {
427 be <<= 8;
428 be |= input[bi];
429 }
430 base_2_64_coeff_buf[num_coeff] = be;
431 };
432 num_coeff += 1;
433 }
434 return std::span(base_2_64_coeff_buf.data(), num_coeff);
435 }();
436
437 // Allocate enough base 58^10 coeff for encoding 38 bytes
438 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
439 std::array<std::uint64_t, 6> base_58_10_coeff{};
440 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
441 std::size_t num_58_10_coeffs = 0;
442 std::size_t cur_2_64_end = base_2_64_coeff.size();
443 // compute the base 58^10 coeffs
444 while (cur_2_64_end > 0)
445 {
446 base_58_10_coeff[num_58_10_coeffs] =
447 ripple::b58_fast::detail::inplace_bigint_div_rem(
448 base_2_64_coeff.subspan(0, cur_2_64_end), B_58_10);
449 num_58_10_coeffs += 1;
450 if (base_2_64_coeff[cur_2_64_end - 1] == 0)
451 {
452 cur_2_64_end -= 1;
453 }
454 }
455
456 // Translate the result into the alphabet
457 // Put all the zeros at the beginning, then all the values from the output
458 std::fill(
459 out.begin(), out.begin() + input_zeros, ::ripple::alphabetForward[0]);
460
461 // iterate through the base 58^10 coeff
462 // convert to base 58 big endian then
463 // convert to alphabet big endian
464 bool skip_zeros = true;
465 auto out_index = input_zeros;
466 for (int i = num_58_10_coeffs - 1; i >= 0; --i)
467 {
468 if (skip_zeros && base_58_10_coeff[i] == 0)
469 {
470 continue;
471 }
472 static constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
473 if (base_58_10_coeff[i] >= B_58_10)
474 {
475 return Unexpected(TokenCodecErrc::inputTooLarge);
476 }
477 std::array<std::uint8_t, 10> const b58_be =
478 ripple::b58_fast::detail::b58_10_to_b58_be(base_58_10_coeff[i]);
479 std::size_t to_skip = 0;
480 std::span<std::uint8_t const> b58_be_s{b58_be.data(), b58_be.size()};
481 if (skip_zeros)
482 {
483 to_skip = count_leading_zeros(b58_be_s);
484 skip_zeros = false;
485 if (out.size() < (i + 1) * 10 - to_skip)
486 {
487 return Unexpected(TokenCodecErrc::outputTooSmall);
488 }
489 }
490 for (auto b58_coeff : b58_be_s.subspan(to_skip))
491 {
492 out[out_index] = ::ripple::alphabetForward[b58_coeff];
493 out_index += 1;
494 }
495 }
496
497 return out.subspan(0, out_index);
498}
499
500// Note the input is BIG ENDIAN (some fn in this module use little endian)
501B58Result<std::span<std::uint8_t>>
502b58_to_b256_be(std::string_view input, std::span<std::uint8_t> out)
503{
504 // Convert from b58 to b 58^10
505
506 // Max encoded value is 38 bytes
507 // log(2^(38*8),58) ~= 51.9
508 if (input.size() > 52)
509 {
510 return Unexpected(TokenCodecErrc::inputTooLarge);
511 };
512 if (out.size() < 8)
513 {
514 return Unexpected(TokenCodecErrc::outputTooSmall);
515 }
516
517 auto count_leading_zeros = [&](auto const& col) -> std::size_t {
518 std::size_t count = 0;
519 for (auto const& c : col)
520 {
521 if (c != ::ripple::alphabetForward[0])
522 {
523 return count;
524 }
525 count += 1;
526 }
527 return count;
528 };
529
530 auto const input_zeros = count_leading_zeros(input);
531
532 // Allocate enough base 58^10 coeff for encoding 38 bytes
533 // (33 bytes for nodepublic + 1 byte token + 4 bytes checksum)
534 // log(2^(38*8),58^10)) ~= 5.18. So 6 coeff are enough
535 std::array<std::uint64_t, 6> b_58_10_coeff{};
536 auto [num_full_coeffs, partial_coeff_len] =
537 ripple::b58_fast::detail::div_rem(input.size(), 10);
538 auto const num_partial_coeffs = partial_coeff_len ? 1 : 0;
539 auto const num_b_58_10_coeffs = num_full_coeffs + num_partial_coeffs;
540 XRPL_ASSERT(
541 num_b_58_10_coeffs <= b_58_10_coeff.size(),
542 "ripple::b58_fast::detail::b58_to_b256_be : maximum coeff");
543 for (auto c : input.substr(0, partial_coeff_len))
544 {
545 auto cur_val = ::ripple::alphabetReverse[c];
546 if (cur_val < 0)
547 {
548 return Unexpected(TokenCodecErrc::invalidEncodingChar);
549 }
550 b_58_10_coeff[0] *= 58;
551 b_58_10_coeff[0] += cur_val;
552 }
553 for (int i = 0; i < 10; ++i)
554 {
555 for (int j = 0; j < num_full_coeffs; ++j)
556 {
557 auto c = input[partial_coeff_len + j * 10 + i];
558 auto cur_val = ::ripple::alphabetReverse[c];
559 if (cur_val < 0)
560 {
561 return Unexpected(TokenCodecErrc::invalidEncodingChar);
562 }
563 b_58_10_coeff[num_partial_coeffs + j] *= 58;
564 b_58_10_coeff[num_partial_coeffs + j] += cur_val;
565 }
566 }
567
568 constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
569
570 // log(2^(38*8),2^64) ~= 4.75)
572 result[0] = b_58_10_coeff[0];
573 std::size_t cur_result_size = 1;
574 for (int i = 1; i < num_b_58_10_coeffs; ++i)
575 {
576 std::uint64_t const c = b_58_10_coeff[i];
577
578 {
579 auto code = ripple::b58_fast::detail::inplace_bigint_mul(
580 std::span(&result[0], cur_result_size + 1), B_58_10);
581 if (code != TokenCodecErrc::success)
582 {
583 return Unexpected(code);
584 }
585 }
586 {
587 auto code = ripple::b58_fast::detail::inplace_bigint_add(
588 std::span(&result[0], cur_result_size + 1), c);
589 if (code != TokenCodecErrc::success)
590 {
591 return Unexpected(code);
592 }
593 }
594 if (result[cur_result_size] != 0)
595 {
596 cur_result_size += 1;
597 }
598 }
599 std::fill(out.begin(), out.begin() + input_zeros, 0);
600 auto cur_out_i = input_zeros;
601 // Don't write leading zeros to the output for the most significant
602 // coeff
603 {
604 std::uint64_t const c = result[cur_result_size - 1];
605 auto skip_zero = true;
606 // start and end of output range
607 for (int i = 0; i < 8; ++i)
608 {
609 std::uint8_t const b = (c >> (8 * (7 - i))) & 0xff;
610 if (skip_zero)
611 {
612 if (b == 0)
613 {
614 continue;
615 }
616 skip_zero = false;
617 }
618 out[cur_out_i] = b;
619 cur_out_i += 1;
620 }
621 }
622 if ((cur_out_i + 8 * (cur_result_size - 1)) > out.size())
623 {
624 return Unexpected(TokenCodecErrc::outputTooSmall);
625 }
626
627 for (int i = cur_result_size - 2; i >= 0; --i)
628 {
629 auto c = result[i];
630 boost::endian::native_to_big_inplace(c);
631 memcpy(&out[cur_out_i], &c, 8);
632 cur_out_i += 8;
633 }
634
635 return out.subspan(0, cur_out_i);
636}
637} // namespace detail
638
639B58Result<std::span<std::uint8_t>>
641 TokenType token_type,
644{
645 constexpr std::size_t tmpBufSize = 128;
647 if (input.size() > tmpBufSize - 5)
648 {
649 return Unexpected(TokenCodecErrc::inputTooLarge);
650 }
651 if (input.size() == 0)
652 {
653 return Unexpected(TokenCodecErrc::inputTooSmall);
654 }
655 // <type (1 byte)><token (input len)><checksum (4 bytes)>
656 buf[0] = static_cast<std::uint8_t>(token_type);
657 // buf[1..=input.len()] = input;
658 memcpy(&buf[1], input.data(), input.size());
659 size_t const checksum_i = input.size() + 1;
660 // buf[checksum_i..checksum_i + 4] = checksum
661 checksum(buf.data() + checksum_i, buf.data(), checksum_i);
662 std::span<std::uint8_t const> b58Span(buf.data(), input.size() + 5);
663 return detail::b256_to_b58_be(b58Span, out);
664}
665// Convert from base 58 to base 256, largest coefficients first
666// The input is encoded in XPRL format, with the token in the first
667// byte and the checksum in the last four bytes.
668// The decoded base 256 value does not include the token type or checksum.
669// It is an error if the token type or checksum does not match.
670B58Result<std::span<std::uint8_t>>
672 TokenType type,
675{
677 auto const decodeResult =
678 detail::b58_to_b256_be(s, std::span(tmpBuf.data(), tmpBuf.size()));
679
680 if (!decodeResult)
681 return decodeResult;
682
683 auto const ret = decodeResult.value();
684
685 // Reject zero length tokens
686 if (ret.size() < 6)
687 return Unexpected(TokenCodecErrc::inputTooSmall);
688
689 // The type must match.
690 if (type != static_cast<TokenType>(static_cast<std::uint8_t>(ret[0])))
691 return Unexpected(TokenCodecErrc::mismatchedTokenType);
692
693 // And the checksum must as well.
695 checksum(guard.data(), ret.data(), ret.size() - guard.size());
696 if (!std::equal(guard.rbegin(), guard.rend(), ret.rbegin()))
697 {
698 return Unexpected(TokenCodecErrc::mismatchedChecksum);
699 }
700
701 std::size_t const outSize = ret.size() - 1 - guard.size();
702 if (outBuf.size() < outSize)
703 return Unexpected(TokenCodecErrc::outputTooSmall);
704 // Skip the leading type byte and the trailing checksum.
705 std::copy(ret.begin() + 1, ret.begin() + outSize + 1, outBuf.begin());
706 return outBuf.subspan(0, outSize);
707}
708
709[[nodiscard]] std::string
710encodeBase58Token(TokenType type, void const* token, std::size_t size)
711{
712 std::string sr;
713 // The largest object encoded as base58 is 33 bytes; This will be encoded in
714 // at most ceil(log(2^256,58)) bytes, or 46 bytes. 128 is plenty (and
715 // there's not real benefit making it smaller). Note that 46 bytes may be
716 // encoded in more than 46 base58 chars. Since decode uses 64 as the
717 // over-allocation, this function uses 128 (again, over-allocation assuming
718 // 2 base 58 char per byte)
719 sr.resize(128);
721 reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
723 reinterpret_cast<std::uint8_t const*>(token), size);
724 auto r = b58_fast::encodeBase58Token(type, inSp, outSp);
725 if (!r)
726 return {};
727 sr.resize(r.value().size());
728 return sr;
729}
730
731[[nodiscard]] std::string
732decodeBase58Token(std::string const& s, TokenType type)
733{
734 std::string sr;
735 // The largest object encoded as base58 is 33 bytes; 64 is plenty (and
736 // there's no benefit making it smaller)
737 sr.resize(64);
739 reinterpret_cast<std::uint8_t*>(sr.data()), sr.size());
740 auto r = b58_fast::decodeBase58Token(type, s, outSp);
741 if (!r)
742 return {};
743 sr.resize(r.value().size());
744 return sr;
745}
746
747} // namespace b58_fast
748#endif // _MSC_VER
749} // namespace ripple
T assign(T... args)
T begin(T... args)
T c_str(T... args)
T copy(T... args)
T count(T... args)
T data(T... args)
T end(T... args)
T equal(T... args)
T fill(T... args)
T find_if(T... args)
T memcpy(T... args)
std::string decodeBase58(std::string const &s)
Definition: tokens.cpp:271
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t temp_size)
Definition: tokens.cpp:219
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Definition: tokens.cpp:321
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition: tokens.cpp:345
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
TokenType
Definition: tokens.h:38
static constexpr std::array< int, 256 > const alphabetReverse
Definition: tokens.cpp:141
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::string decodeBase58Token(std::string const &s, TokenType type)
Definition: tokens.cpp:205
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition: tokens.cpp:152
static Hasher::result_type digest2(Args const &... args)
Definition: tokens.cpp:173
Unexpected(E(&)[N]) -> Unexpected< E const * >
static void checksum(void *out, void const *message, std::size_t size)
Calculate a 4-byte checksum of the data.
Definition: tokens.cpp:188
static constexpr char const * alphabetForward
Definition: tokens.cpp:138
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)