10#include <xrpl/protocol/tokens.h>
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>
18#include <boost/container/small_vector.hpp>
19#include <boost/endian/conversion.hpp>
126 "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz";
137template <
class Hasher>
138static Hasher::result_type
143 return static_cast<Hasher::result_type
>(h);
146template <
class Hasher,
class T, std::
size_t N,
class = std::enable_if_t<sizeof(T) == 1>>
147static Hasher::result_type
154template <
class Hasher,
class... Args>
155static Hasher::result_type
181 return b58_fast::encodeBase58Token(type, token, size);
191 return b58_fast::decodeBase58Token(s, type);
204 auto pbegin =
reinterpret_cast<unsigned char const*
>(message);
205 auto const pend = pbegin + size;
209 while (pbegin != pend && *pbegin == 0)
215 auto const b58begin =
reinterpret_cast<unsigned char*
>(temp);
216 auto const b58end = b58begin + tempSize;
220 while (pbegin != pend)
224 for (
auto iter = b58end; iter != b58begin; --iter)
226 carry += 256 * (iter[-1]);
227 iter[-1] = carry % 58;
230 XRPL_ASSERT(carry == 0,
"xrpl::b58_ref::detail::encodeBase58 : zero carry");
235 auto iter = b58begin;
236 while (iter != b58end && *iter == 0)
241 str.
reserve(zeroes + (b58end - iter));
243 while (iter != b58end)
251 auto psz =
reinterpret_cast<unsigned char const*
>(s.
c_str());
252 auto remain = s.
size();
274 for (
auto iter = b256.
rbegin(); iter != b256.
rend(); ++iter)
280 XRPL_ASSERT(carry == 0,
"xrpl::b58_ref::detail::decodeBase58 : zero carry");
288 result.assign(zeroes, 0x00);
289 while (iter != b256.
end())
290 result.push_back(*(iter++));
300 auto const expanded = 1 + size + 4;
305 auto const bufsize = expanded * 3;
307 boost::container::small_vector<std::uint8_t, 1024> buf(bufsize);
314 checksum(buf.data() + 1 + size, buf.data(), 1 + size);
349B58Result<std::span<std::uint8_t>>
354 if (input.
size() > 38)
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)
372 auto const inputZeros = countLeadingZeros(input);
373 input = input.
subspan(inputZeros);
377 std::array<std::uint64_t, 5> base264CoeffBuf{};
378 std::span<std::uint64_t>
const base264Coeff = [&]() -> std::span<std::uint64_t> {
380 std::size_t numCoeff = 0;
381 for (
int i = 0; i < base264CoeffBuf.
size(); ++i)
383 if (i * 8 >= input.
size())
387 auto const srcIEnd = input.
size() - (i * 8);
390 std::memcpy(&base264CoeffBuf[numCoeff], &input[srcIEnd - 8], 8);
391 boost::endian::big_to_native_inplace(base264CoeffBuf[numCoeff]);
395 std::uint64_t be = 0;
396 for (
int bi = 0; bi < srcIEnd; ++bi)
401 base264CoeffBuf[numCoeff] = be;
405 return std::span(base264CoeffBuf.
data(), numCoeff);
410 std::array<std::uint64_t, 6> base5810Coeff{};
411 constexpr std::uint64_t kB5810 = 430804206899405824;
412 std::size_t num5810Coeffs = 0;
413 std::size_t cur264End = base264Coeff.
size();
415 while (cur264End > 0)
417 base5810Coeff[num5810Coeffs] =
418 xrpl::b58_fast::detail::inplaceBigintDivRem(base264Coeff.
subspan(0, cur264End), kB5810);
420 if (base264Coeff[cur264End - 1] == 0)
433 bool skipZeros =
true;
434 auto outIndex = inputZeros;
435 for (
int i = num5810Coeffs - 1; i >= 0; --i)
437 if (skipZeros && base5810Coeff[i] == 0)
441 static constexpr std::uint64_t kB5810 = 430804206899405824;
442 if (base5810Coeff[i] >= kB5810)
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()};
452 toSkip = countLeadingZeros(b58BeS);
454 if (out.
size() < ((i + 1) * 10) - toSkip)
459 for (
auto b58Coeff : b58BeS.
subspan(toSkip))
466 return out.
subspan(0, outIndex);
470B58Result<std::span<std::uint8_t>>
471b58ToB256Be(std::string_view input, std::span<std::uint8_t> out)
477 if (input.
size() > 52)
486 auto countLeadingZeros = [&](
auto const& col) -> std::size_t {
487 std::size_t
count = 0;
488 for (
auto const& c : col)
499 auto const inputZeros = countLeadingZeros(input);
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;
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))
519 b5810Coeff[0] += curVal;
521 for (
int i = 0; i < 10; ++i)
523 for (
int j = 0; j < num_full_coeffs; ++j)
525 unsigned char const c = input[partial_coeff_len + (j * 10) + i];
531 b5810Coeff[numPartialCoeffs + j] *= 58;
532 b5810Coeff[numPartialCoeffs + j] += curVal;
536 constexpr std::uint64_t kB5810 = 430804206899405824;
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)
544 std::uint64_t
const c = b5810Coeff[i];
547 auto code = xrpl::b58_fast::detail::inplaceBigintMul(
548 std::span(&result[0], curResultSize + 1), kB5810);
549 if (code != TokenCodecErrc::Success)
555 auto code = xrpl::b58_fast::detail::inplaceBigintAdd(
556 std::span(&result[0], curResultSize + 1), c);
557 if (code != TokenCodecErrc::Success)
562 if (result[curResultSize] != 0)
568 auto curOutI = inputZeros;
572 std::uint64_t
const c = result[curResultSize - 1];
573 auto skipZero =
true;
575 for (
int i = 0; i < 8; ++i)
577 std::uint8_t
const b = (c >> (8 * (7 - i))) & 0xff;
590 if ((curOutI + (8 * (curResultSize - 1))) > out.
size())
595 for (
int i = curResultSize - 2; i >= 0; --i)
598 boost::endian::native_to_big_inplace(c);
599 memcpy(&out[curOutI], &c, 8);
603 return out.
subspan(0, curOutI);
607B58Result<std::span<std::uint8_t>>
610 std::span<std::uint8_t const> input,
611 std::span<std::uint8_t> out)
613 static constexpr std::size_t kTmpBufSize = 128;
614 std::array<std::uint8_t, kTmpBufSize> buf{};
615 if (input.
size() > kTmpBufSize - 5)
624 buf[0] =
static_cast<std::uint8_t
>(tokenType);
627 size_t const checksumI = input.
size() + 1;
630 std::span<std::uint8_t const>
const b58Span(buf.
data(), input.
size() + 5);
631 return detail::b256ToB58Be(b58Span, out);
638B58Result<std::span<std::uint8_t>>
639decodeBase58Token(TokenType type, std::string_view s, std::span<std::uint8_t> outBuf)
641 std::array<std::uint8_t, 64> tmpBuf{};
642 auto const decodeResult = detail::b58ToB256Be(s, std::span(tmpBuf.
data(), tmpBuf.
size()));
647 auto const ret = decodeResult.value();
654 if (type !=
static_cast<TokenType>(
static_cast<std::uint8_t
>(ret[0])))
658 std::array<std::uint8_t, 4> guard{};
665 std::size_t
const outSize = ret.size() - 1 - guard.
size();
666 if (outBuf.
size() < outSize)
669 std::copy(ret.begin() + 1, ret.begin() + outSize + 1, outBuf.
begin());
670 return outBuf.
subspan(0, outSize);
673[[nodiscard]] std::string
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);
689 sr.
resize(r.value().size());
693[[nodiscard]] std::string
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);
704 sr.
resize(r.value().size());
std::string decodeBase58(std::string const &s)
std::string encodeBase58(void const *message, std::size_t size, void *temp, std::size_t tempSize)
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
std::string decodeBase58Token(std::string const &s, TokenType type)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
static void checksum(void *out, void const *message, std::size_t size)
Calculate a 4-byte checksum of the data.
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
constexpr std::enable_if_t< std::is_integral_v< Dest > &&std::is_integral_v< Src >, Dest > safeCast(Src s) noexcept
static constexpr std::array< int, 256 > const kAlphabetReverse
static Hasher::result_type digest2(Args const &... args)
static constexpr char const * kAlphabetForward
std::string encodeBase58Token(TokenType type, void const *token, std::size_t size)
Encode data in Base58Check format using XRPL alphabet.
std::string decodeBase58Token(std::string const &s, TokenType type)