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