rippled
Loading...
Searching...
No Matches
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 <xrpl/basics/contract.h>
24#include <xrpl/beast/utility/instrumentation.h>
25#include <xrpl/protocol/detail/token_errors.h>
26
27#include <boost/outcome.hpp>
28#include <boost/outcome/result.hpp>
29
30#include <span>
31#include <system_error>
32#include <tuple>
33
34namespace ripple {
35
36template <class T>
37using Result = boost::outcome_v2::result<T, std::error_code>;
38
39#ifndef _MSC_VER
40namespace b58_fast {
41namespace detail {
42
43// This optimizes to what hand written asm would do (single divide)
45div_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)
52carrying_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
61carrying_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[[nodiscard]] inline TokenCodecErrc
75inplace_bigint_add(std::span<std::uint64_t> a, std::uint64_t b)
76{
77 if (a.size() <= 1)
78 {
79 return TokenCodecErrc::inputTooSmall;
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 TokenCodecErrc::success;
90 }
91 std::tie(v, carry) = carrying_add(v, 1);
92 }
93 if (carry)
94 {
95 return TokenCodecErrc::overflowAdd;
96 }
97 return TokenCodecErrc::success;
98}
99
100[[nodiscard]] inline TokenCodecErrc
101inplace_bigint_mul(std::span<std::uint64_t> a, std::uint64_t b)
102{
103 if (a.empty())
104 {
105 return TokenCodecErrc::inputTooSmall;
106 }
107
108 auto const last_index = a.size() - 1;
109 if (a[last_index] != 0)
110 {
111 return TokenCodecErrc::inputTooLarge;
112 }
113
114 std::uint64_t carry = 0;
115 for (auto& coeff : a.subspan(0, last_index))
116 {
117 std::tie(coeff, carry) = carrying_mul(coeff, b, carry);
118 }
119 a[last_index] = carry;
120 return TokenCodecErrc::success;
121}
122
123// divide a "big uint" value inplace and return the mod
124// numerator is stored so smallest coefficients come first
125[[nodiscard]] inline std::uint64_t
126inplace_bigint_div_rem(std::span<uint64_t> numerator, std::uint64_t divisor)
127{
128 if (numerator.size() == 0)
129 {
130 // should never happen, but if it does then it seems natural to define
131 // the a null set of numbers to be zero, so the remainder is also zero.
132 // LCOV_EXCL_START
133 UNREACHABLE(
134 "ripple::b58_fast::detail::inplace_bigint_div_rem : empty "
135 "numerator");
136 return 0;
137 // LCOV_EXCL_STOP
138 }
139
140 auto to_u128 = [](std::uint64_t high,
141 std::uint64_t low) -> unsigned __int128 {
142 unsigned __int128 const high128 = high;
143 unsigned __int128 const low128 = low;
144 return ((high128 << 64) | low128);
145 };
146 auto div_rem_64 =
147 [](unsigned __int128 num,
149 unsigned __int128 const denom128 = denom;
150 unsigned __int128 const d = num / denom128;
151 unsigned __int128 const r = num - (denom128 * d);
152 XRPL_ASSERT(
153 d >> 64 == 0,
154 "ripple::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
155 "valid division result");
156 XRPL_ASSERT(
157 r >> 64 == 0,
158 "ripple::b58_fast::detail::inplace_bigint_div_rem::div_rem_64 : "
159 "valid remainder");
160 return {static_cast<std::uint64_t>(d), static_cast<std::uint64_t>(r)};
161 };
162
163 std::uint64_t prev_rem = 0;
164 int const last_index = numerator.size() - 1;
165 std::tie(numerator[last_index], prev_rem) =
166 div_rem(numerator[last_index], divisor);
167 for (int i = last_index - 1; i >= 0; --i)
168 {
169 unsigned __int128 const cur_num = to_u128(prev_rem, numerator[i]);
170 std::tie(numerator[i], prev_rem) = div_rem_64(cur_num, divisor);
171 }
172 return prev_rem;
173}
174
175// convert from base 58^10 to base 58
176// put largest coeffs first
177// the `_be` suffix stands for "big endian"
178[[nodiscard]] inline std::array<std::uint8_t, 10>
179b58_10_to_b58_be(std::uint64_t input)
180{
181 [[maybe_unused]] static constexpr std::uint64_t B_58_10 =
182 430804206899405824; // 58^10;
183 XRPL_ASSERT(
184 input < B_58_10,
185 "ripple::b58_fast::detail::b58_10_to_b58_be : valid input");
186 constexpr std::size_t resultSize = 10;
188 int i = 0;
189 while (input > 0)
190 {
191 std::uint64_t rem;
192 std::tie(input, rem) = div_rem(input, 58);
193 result[resultSize - 1 - i] = rem;
194 i += 1;
195 }
196
197 return result;
198}
199} // namespace detail
200} // namespace b58_fast
201#endif
202
203} // namespace ripple
204#endif // RIPPLE_PROTOCOL_B58_UTILS_H_INCLUDED
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
boost::outcome_v2::result< T, std::error_code > Result
Definition b58_utils.h:37
T size(T... args)
T tie(T... args)