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