rippled
b58_utils.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2022 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 #ifndef RIPPLE_PROTOCOL_B58_UTILS_H_INCLUDED
21 #define RIPPLE_PROTOCOL_B58_UTILS_H_INCLUDED
22 
23 #include <ripple/basics/contract.h>
24 
25 #include <boost/outcome.hpp>
26 #include <boost/outcome/result.hpp>
27 
28 #include <cassert>
29 #include <cinttypes>
30 #include <span>
31 #include <system_error>
32 #include <tuple>
33 
34 namespace ripple {
35 
36 template <class T>
37 using Result = boost::outcome_v2::result<T, std::error_code>;
38 
39 #ifndef _MSC_VER
40 namespace b58_fast {
41 namespace detail {
42 
43 // This optimizes to what hand written asm would do (single divide)
45 div_rem(std::uint64_t a, std::uint64_t b)
46 {
47  return {a / b, a % b};
48 }
49 
50 // This optimizes to what hand written asm would do (single multiply)
52 carrying_mul(std::uint64_t a, std::uint64_t b, std::uint64_t carry)
53 {
54  unsigned __int128 const x = a;
55  unsigned __int128 const y = b;
56  unsigned __int128 const c = x * y + carry;
57  return {c & 0xffff'ffff'ffff'ffff, c >> 64};
58 }
59 
60 [[nodiscard]] inline std::tuple<std::uint64_t, std::uint64_t>
61 carrying_add(std::uint64_t a, std::uint64_t b)
62 {
63  unsigned __int128 const x = a;
64  unsigned __int128 const y = b;
65  unsigned __int128 const c = x + y;
66  return {c & 0xffff'ffff'ffff'ffff, c >> 64};
67 }
68 
69 // Add a u64 to a "big uint" value inplace.
70 // The bigint value is stored with the smallest coefficients first
71 // (i.e a[0] is the 2^0 coefficient, a[n] is the 2^(64*n) coefficient)
72 // panics if overflows (this is a specialized adder for b58 decoding.
73 // it should never overflow).
74 inline void
75 inplace_bigint_add(std::span<std::uint64_t> a, std::uint64_t b)
76 {
77  if (a.size() <= 1)
78  {
79  ripple::LogicError("Input span too small for inplace_bigint_add");
80  }
81 
82  std::uint64_t carry;
83  std::tie(a[0], carry) = carrying_add(a[0], b);
84 
85  for (auto& v : a.subspan(1))
86  {
87  if (!carry)
88  {
89  return;
90  }
91  std::tie(v, carry) = carrying_add(v, 1);
92  }
93  if (carry)
94  {
95  LogicError("Overflow in inplace_bigint_add");
96  }
97 }
98 
99 inline void
100 inplace_bigint_mul(std::span<std::uint64_t> a, std::uint64_t b)
101 {
102  if (a.empty())
103  {
104  LogicError("Empty span passed to inplace_bigint_mul");
105  }
106 
107  auto const last_index = a.size() - 1;
108  if (a[last_index] != 0)
109  {
110  LogicError("Non-zero element in inplace_bigint_mul last index");
111  }
112 
113  std::uint64_t carry = 0;
114  for (auto& coeff : a.subspan(0, last_index))
115  {
116  std::tie(coeff, carry) = carrying_mul(coeff, b, carry);
117  }
118  a[last_index] = carry;
119 }
120 // divide a "big uint" value inplace and return the mod
121 // numerator is stored so smallest coefficients come first
122 [[nodiscard]] inline std::uint64_t
123 inplace_bigint_div_rem(std::span<uint64_t> numerator, std::uint64_t divisor)
124 {
125  if (numerator.size() == 0)
126  {
127  // should never happen, but if it does then it seems natural to define
128  // the a null set of numbers to be zero, so the remainder is also zero.
129  assert(0);
130  return 0;
131  }
132 
133  auto to_u128 = [](std::uint64_t high,
134  std::uint64_t low) -> unsigned __int128 {
135  unsigned __int128 const high128 = high;
136  unsigned __int128 const low128 = low;
137  return ((high128 << 64) | low128);
138  };
139  auto div_rem_64 =
140  [](unsigned __int128 num,
142  unsigned __int128 const denom128 = denom;
143  unsigned __int128 const d = num / denom128;
144  unsigned __int128 const r = num - (denom128 * d);
145  assert(d >> 64 == 0);
146  assert(r >> 64 == 0);
147  return {static_cast<std::uint64_t>(d), static_cast<std::uint64_t>(r)};
148  };
149 
150  std::uint64_t prev_rem = 0;
151  int const last_index = numerator.size() - 1;
152  std::tie(numerator[last_index], prev_rem) =
153  div_rem(numerator[last_index], divisor);
154  for (int i = last_index - 1; i >= 0; --i)
155  {
156  unsigned __int128 const cur_num = to_u128(prev_rem, numerator[i]);
157  std::tie(numerator[i], prev_rem) = div_rem_64(cur_num, divisor);
158  }
159  return prev_rem;
160 }
161 
162 // convert from base 58^10 to base 58
163 // put largest coeffs first
164 // the `_be` suffix stands for "big endian"
165 [[nodiscard]] inline std::array<std::uint8_t, 10>
166 b58_10_to_b58_be(std::uint64_t input)
167 {
168  constexpr std::uint64_t B_58_10 = 430804206899405824; // 58^10;
169  if (input >= B_58_10)
170  {
171  LogicError("Input to b58_10_to_b58_be equals or exceeds 58^10.");
172  }
173 
174  constexpr std::size_t resultSize = 10;
176  int i = 0;
177  while (input > 0)
178  {
179  std::uint64_t rem;
180  std::tie(input, rem) = div_rem(input, 58);
181  result[resultSize - 1 - i] = rem;
182  i += 1;
183  }
184 
185  return result;
186 }
187 } // namespace detail
188 } // namespace b58_fast
189 #endif
190 
191 } // namespace ripple
192 #endif // RIPPLE_PROTOCOL_B58_UTILS_H_INCLUDED
std::span::subspan
T subspan(T... args)
cinttypes
span
system_error
std::span::size
T size(T... args)
tuple
std::tie
T tie(T... args)
std::array
STL class.
std::uint64_t
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Result
boost::outcome_v2::result< T, std::error_code > Result
Definition: b58_utils.h:37
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:47
cassert
std::span::empty
T empty(T... args)
std::size_t