mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
- Simplify and consolidate code for parsing hex input. - Replace beast::endian::order with boost::endian::order. - Simplify CountedObject code. - Remove pre-C++17 workarounds in favor of C++17 based solutions. - Improve `base_uint` and simplify its hex-parsing interface by consolidating the `SexHex` and `SetHexExact` methods into one API: `parseHex` which forces callers to verify the result of the operation; as a result some public-facing API endpoints may now return errors when passed values that were previously accepted. - Remove the simple fallback implementations of SHA2 and RIPEMD introduced to reduce our dependency on OpenSSL. The code is slow and rarely, if ever, exercised and we rely on OpenSSL functionality for Boost.ASIO as well.
597 lines
14 KiB
C++
597 lines
14 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2011 The Bitcoin developers
|
|
// Distributed under the MIT/X11 software license, see the accompanying
|
|
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef RIPPLE_BASICS_BASE_UINT_H_INCLUDED
|
|
#define RIPPLE_BASICS_BASE_UINT_H_INCLUDED
|
|
|
|
#include <ripple/basics/hardened_hash.h>
|
|
#include <ripple/basics/strHex.h>
|
|
#include <ripple/beast/utility/Zero.h>
|
|
#include <boost/endian/conversion.hpp>
|
|
#include <boost/functional/hash.hpp>
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
|
|
namespace ripple {
|
|
|
|
namespace detail {
|
|
|
|
template <class Container, class = std::void_t<>>
|
|
struct is_contiguous_container : std::false_type
|
|
{
|
|
};
|
|
|
|
template <class Container>
|
|
struct is_contiguous_container<
|
|
Container,
|
|
std::void_t<
|
|
decltype(std::declval<Container const>().size()),
|
|
decltype(std::declval<Container const>().data()),
|
|
typename Container::value_type>> : std::true_type
|
|
{
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
/** Integers of any length that is a multiple of 32-bits
|
|
|
|
@note This class stores its values internally in big-endian
|
|
form and that internal representation is part of the
|
|
binary protocol of the XRP Ledger and cannot be changed
|
|
arbitrarily without causing breakage.
|
|
|
|
@tparam Bits The number of bits this integer should have; must
|
|
be at least 64 and a multiple of 32.
|
|
@tparam Tag An arbitrary type that functions as a tag and allows
|
|
the instantiation of "distinct" types that the same
|
|
number of bits.
|
|
*/
|
|
template <std::size_t Bits, class Tag = void>
|
|
class base_uint
|
|
{
|
|
static_assert(
|
|
(Bits % 32) == 0,
|
|
"The length of a base_uint in bits must be a multiple of 32.");
|
|
|
|
static_assert(
|
|
Bits >= 64,
|
|
"The length of a base_uint in bits must be at least 64.");
|
|
|
|
static constexpr std::size_t WIDTH = Bits / 32;
|
|
|
|
// This is really big-endian in byte order.
|
|
// We sometimes use std::uint32_t for speed.
|
|
|
|
std::array<std::uint32_t, WIDTH> data_;
|
|
|
|
public:
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// STL Container Interface
|
|
//
|
|
|
|
static std::size_t constexpr bytes = Bits / 8;
|
|
static_assert(sizeof(data_) == bytes, "");
|
|
|
|
using size_type = std::size_t;
|
|
using difference_type = std::ptrdiff_t;
|
|
using value_type = unsigned char;
|
|
using pointer = value_type*;
|
|
using reference = value_type&;
|
|
using const_pointer = value_type const*;
|
|
using const_reference = value_type const&;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
using tag_type = Tag;
|
|
|
|
pointer
|
|
data()
|
|
{
|
|
return reinterpret_cast<pointer>(data_.data());
|
|
}
|
|
const_pointer
|
|
data() const
|
|
{
|
|
return reinterpret_cast<const_pointer>(data_.data());
|
|
}
|
|
|
|
iterator
|
|
begin()
|
|
{
|
|
return data();
|
|
}
|
|
iterator
|
|
end()
|
|
{
|
|
return data() + bytes;
|
|
}
|
|
const_iterator
|
|
begin() const
|
|
{
|
|
return data();
|
|
}
|
|
const_iterator
|
|
end() const
|
|
{
|
|
return data() + bytes;
|
|
}
|
|
const_iterator
|
|
cbegin() const
|
|
{
|
|
return data();
|
|
}
|
|
const_iterator
|
|
cend() const
|
|
{
|
|
return data() + bytes;
|
|
}
|
|
|
|
/** Value hashing function.
|
|
The seed prevents crafted inputs from causing degenerate parent
|
|
containers.
|
|
*/
|
|
using hasher = hardened_hash<>;
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
private:
|
|
/** Construct from a raw pointer.
|
|
The buffer pointed to by `data` must be at least Bits/8 bytes.
|
|
|
|
@note the structure is used to disambiguate this from the std::uint64_t
|
|
constructor: something like base_uint(0) is ambiguous.
|
|
*/
|
|
// NIKB TODO Remove the need for this constructor.
|
|
struct VoidHelper
|
|
{
|
|
explicit VoidHelper() = default;
|
|
};
|
|
|
|
explicit base_uint(void const* data, VoidHelper)
|
|
{
|
|
memcpy(data_.data(), data, bytes);
|
|
}
|
|
|
|
public:
|
|
base_uint()
|
|
{
|
|
*this = beast::zero;
|
|
}
|
|
|
|
base_uint(beast::Zero)
|
|
{
|
|
*this = beast::zero;
|
|
}
|
|
|
|
explicit base_uint(std::uint64_t b)
|
|
{
|
|
*this = b;
|
|
}
|
|
|
|
template <
|
|
class Container,
|
|
class = std::enable_if_t<
|
|
detail::is_contiguous_container<Container>::value &&
|
|
std::is_trivially_copyable<typename Container::value_type>::value>>
|
|
explicit base_uint(Container const& c)
|
|
{
|
|
assert(c.size() * sizeof(typename Container::value_type) == size());
|
|
std::memcpy(data_.data(), c.data(), size());
|
|
}
|
|
|
|
template <class Container>
|
|
std::enable_if_t<
|
|
detail::is_contiguous_container<Container>::value &&
|
|
std::is_trivially_copyable<typename Container::value_type>::value,
|
|
base_uint&>
|
|
operator=(Container const& c)
|
|
{
|
|
assert(c.size() * sizeof(typename Container::value_type) == size());
|
|
std::memcpy(data_.data(), c.data(), size());
|
|
return *this;
|
|
}
|
|
|
|
/* Construct from a raw pointer.
|
|
The buffer pointed to by `data` must be at least Bits/8 bytes.
|
|
*/
|
|
static base_uint
|
|
fromVoid(void const* data)
|
|
{
|
|
return base_uint(data, VoidHelper());
|
|
}
|
|
|
|
int
|
|
signum() const
|
|
{
|
|
for (int i = 0; i < WIDTH; i++)
|
|
if (data_[i] != 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
operator!() const
|
|
{
|
|
return *this == beast::zero;
|
|
}
|
|
|
|
const base_uint
|
|
operator~() const
|
|
{
|
|
base_uint ret;
|
|
|
|
for (int i = 0; i < WIDTH; i++)
|
|
ret.data_[i] = ~data_[i];
|
|
|
|
return ret;
|
|
}
|
|
|
|
base_uint&
|
|
operator=(std::uint64_t uHost)
|
|
{
|
|
*this = beast::zero;
|
|
union
|
|
{
|
|
unsigned u[2];
|
|
std::uint64_t ul;
|
|
};
|
|
// Put in least significant bits.
|
|
ul = boost::endian::native_to_big(uHost);
|
|
data_[WIDTH - 2] = u[0];
|
|
data_[WIDTH - 1] = u[1];
|
|
return *this;
|
|
}
|
|
|
|
base_uint&
|
|
operator^=(const base_uint& b)
|
|
{
|
|
for (int i = 0; i < WIDTH; i++)
|
|
data_[i] ^= b.data_[i];
|
|
|
|
return *this;
|
|
}
|
|
|
|
base_uint&
|
|
operator&=(const base_uint& b)
|
|
{
|
|
for (int i = 0; i < WIDTH; i++)
|
|
data_[i] &= b.data_[i];
|
|
|
|
return *this;
|
|
}
|
|
|
|
base_uint&
|
|
operator|=(const base_uint& b)
|
|
{
|
|
for (int i = 0; i < WIDTH; i++)
|
|
data_[i] |= b.data_[i];
|
|
|
|
return *this;
|
|
}
|
|
|
|
base_uint&
|
|
operator++()
|
|
{
|
|
// prefix operator
|
|
for (int i = WIDTH - 1; i >= 0; --i)
|
|
{
|
|
data_[i] = boost::endian::native_to_big(
|
|
boost::endian::big_to_native(data_[i]) + 1);
|
|
if (data_[i] != 0)
|
|
break;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
const base_uint
|
|
operator++(int)
|
|
{
|
|
// postfix operator
|
|
const base_uint ret = *this;
|
|
++(*this);
|
|
|
|
return ret;
|
|
}
|
|
|
|
base_uint&
|
|
operator--()
|
|
{
|
|
for (int i = WIDTH - 1; i >= 0; --i)
|
|
{
|
|
auto prev = data_[i];
|
|
data_[i] = boost::endian::native_to_big(
|
|
boost::endian::big_to_native(data_[i]) - 1);
|
|
|
|
if (prev != 0)
|
|
break;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
const base_uint
|
|
operator--(int)
|
|
{
|
|
// postfix operator
|
|
const base_uint ret = *this;
|
|
--(*this);
|
|
|
|
return ret;
|
|
}
|
|
|
|
base_uint&
|
|
operator+=(const base_uint& b)
|
|
{
|
|
std::uint64_t carry = 0;
|
|
|
|
for (int i = WIDTH; i--;)
|
|
{
|
|
std::uint64_t n = carry + boost::endian::big_to_native(data_[i]) +
|
|
boost::endian::big_to_native(b.data_[i]);
|
|
|
|
data_[i] =
|
|
boost::endian::native_to_big(static_cast<std::uint32_t>(n));
|
|
carry = n >> 32;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Hasher>
|
|
friend void
|
|
hash_append(Hasher& h, base_uint const& a) noexcept
|
|
{
|
|
// Do not allow any endian transformations on this memory
|
|
h(a.data_.data(), sizeof(a.data_));
|
|
}
|
|
|
|
/** Parse a hex string into a base_uint
|
|
|
|
The input must be precisely `2 * bytes` hexadecimal characters
|
|
long, with one exception: the value '0'.
|
|
|
|
@param sv A null-terminated string of hexadecimal characters
|
|
@return true if the input was parsed properly; false otherwise.
|
|
*/
|
|
[[nodiscard]] bool
|
|
parseHex(std::string_view sv)
|
|
{
|
|
if (sv == "0")
|
|
{
|
|
zero();
|
|
return true;
|
|
}
|
|
|
|
if (sv.size() != bytes * 2)
|
|
return false;
|
|
|
|
auto out = data();
|
|
|
|
auto in = sv.begin();
|
|
|
|
while (in != sv.end())
|
|
{
|
|
auto const hi = charUnHex(*in++);
|
|
auto const lo = charUnHex(*in++);
|
|
|
|
if (hi == -1 || lo == -1)
|
|
return false;
|
|
|
|
*out++ = static_cast<std::uint8_t>((hi << 4) + lo);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
parseHex(const char* str)
|
|
{
|
|
return parseHex(std::string_view{str});
|
|
}
|
|
|
|
[[nodiscard]] bool
|
|
parseHex(std::string const& str)
|
|
{
|
|
return parseHex(std::string_view{str});
|
|
}
|
|
|
|
constexpr static std::size_t
|
|
size()
|
|
{
|
|
return bytes;
|
|
}
|
|
|
|
base_uint<Bits, Tag>& operator=(beast::Zero)
|
|
{
|
|
data_.fill(0);
|
|
return *this;
|
|
}
|
|
|
|
// Deprecated.
|
|
bool
|
|
isZero() const
|
|
{
|
|
return *this == beast::zero;
|
|
}
|
|
bool
|
|
isNonZero() const
|
|
{
|
|
return *this != beast::zero;
|
|
}
|
|
void
|
|
zero()
|
|
{
|
|
*this = beast::zero;
|
|
}
|
|
};
|
|
|
|
using uint128 = base_uint<128>;
|
|
using uint160 = base_uint<160>;
|
|
using uint256 = base_uint<256>;
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline int
|
|
compare(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
auto ret = std::mismatch(a.cbegin(), a.cend(), b.cbegin());
|
|
|
|
if (ret.first == a.cend())
|
|
return 0;
|
|
|
|
// a > b
|
|
if (*ret.first > *ret.second)
|
|
return 1;
|
|
|
|
// a < b
|
|
return -1;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator<(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) < 0;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator<=(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) <= 0;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator>(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) > 0;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator>=(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) >= 0;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator==(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) == 0;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator!=(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return compare(a, b) != 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator==(base_uint<Bits, Tag> const& a, std::uint64_t b)
|
|
{
|
|
return a == base_uint<Bits, Tag>(b);
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline bool
|
|
operator!=(base_uint<Bits, Tag> const& a, std::uint64_t b)
|
|
{
|
|
return !(a == b);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <std::size_t Bits, class Tag>
|
|
inline const base_uint<Bits, Tag>
|
|
operator^(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return base_uint<Bits, Tag>(a) ^= b;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline const base_uint<Bits, Tag>
|
|
operator&(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return base_uint<Bits, Tag>(a) &= b;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline const base_uint<Bits, Tag>
|
|
operator|(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return base_uint<Bits, Tag>(a) |= b;
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline const base_uint<Bits, Tag>
|
|
operator+(base_uint<Bits, Tag> const& a, base_uint<Bits, Tag> const& b)
|
|
{
|
|
return base_uint<Bits, Tag>(a) += b;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
template <std::size_t Bits, class Tag>
|
|
inline std::string
|
|
to_string(base_uint<Bits, Tag> const& a)
|
|
{
|
|
return strHex(a.cbegin(), a.cend());
|
|
}
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
inline std::ostream&
|
|
operator<<(std::ostream& out, base_uint<Bits, Tag> const& u)
|
|
{
|
|
return out << to_string(u);
|
|
}
|
|
|
|
#ifndef __INTELLISENSE__
|
|
static_assert(sizeof(uint128) == 128 / 8, "There should be no padding bytes");
|
|
static_assert(sizeof(uint160) == 160 / 8, "There should be no padding bytes");
|
|
static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes");
|
|
#endif
|
|
|
|
} // namespace ripple
|
|
|
|
namespace beast {
|
|
|
|
template <std::size_t Bits, class Tag>
|
|
struct is_uniquely_represented<ripple::base_uint<Bits, Tag>>
|
|
: public std::true_type
|
|
{
|
|
explicit is_uniquely_represented() = default;
|
|
};
|
|
|
|
} // namespace beast
|
|
|
|
#endif
|