dncurrency/nano/lib/numbers.hpp
2024-11-27 08:17:19 +01:00

656 lines
15 KiB
C++

#pragma once
#include <nano/lib/assert.hpp>
#include <boost/functional/hash_fwd.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <array>
#include <compare>
#include <limits>
#include <ostream>
#include <string_view>
#include <fmt/ostream.h>
namespace nano
{
using uint128_t = boost::multiprecision::uint128_t;
using uint256_t = boost::multiprecision::uint256_t;
using uint512_t = boost::multiprecision::uint512_t;
// SI dividers
nano::uint128_t const Knano_ratio = nano::uint128_t ("1000000000000000000000000000000000"); // 10^33 = 1000 nano
nano::uint128_t const nano_ratio = nano::uint128_t ("1000000000000000000000000000000"); // 10^30 = 1 nano
nano::uint128_t const raw_ratio = nano::uint128_t ("1"); // 10^0
using bucket_index = uint64_t;
using priority_timestamp = uint64_t; // Priority within the bucket
class uint128_union
{
public:
// Type that is implicitly convertible to this union
using underlying_type = nano::uint128_t;
public:
uint128_union () = default;
uint128_union (uint64_t value) :
uint128_union (nano::uint128_t{ value }){};
uint128_union (nano::uint128_t const & value)
{
bytes.fill (0);
boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false);
}
/**
* Decode from hex string
* @warning Aborts at runtime if the input is invalid
*/
explicit uint128_union (std::string const &);
void encode_hex (std::string &) const;
bool decode_hex (std::string const &);
void encode_dec (std::string &) const;
bool decode_dec (std::string const &, bool = false);
bool decode_dec (std::string const &, nano::uint128_t);
std::string format_balance (nano::uint128_t scale, int precision, bool group_digits) const;
std::string format_balance (nano::uint128_t scale, int precision, bool group_digits, std::locale const & locale) const;
void clear ()
{
qwords.fill (0);
}
bool is_zero () const
{
return qwords[0] == 0 && qwords[1] == 0;
}
nano::uint128_t number () const
{
nano::uint128_t result;
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
return result;
}
std::string to_string () const;
std::string to_string_dec () const;
public:
union
{
std::array<uint8_t, 16> bytes;
std::array<char, 16> chars;
std::array<uint32_t, 4> dwords;
std::array<uint64_t, 2> qwords;
};
public: // Keep operators inlined
std::strong_ordering operator<=> (nano::uint128_union const & other) const
{
return std::memcmp (bytes.data (), other.bytes.data (), 16) <=> 0;
};
bool operator== (nano::uint128_union const & other) const
{
return *this <=> other == 0;
}
operator nano::uint128_t () const
{
return number ();
}
uint128_union const & as_union () const
{
return *this;
}
};
static_assert (std::is_nothrow_move_constructible<uint128_union>::value, "uint128_union should be noexcept MoveConstructible");
// Balances are 128 bit.
class amount : public uint128_union
{
public:
using uint128_union::uint128_union;
auto operator<=> (nano::amount const & other) const
{
return uint128_union::operator<=> (other);
}
operator nano::uint128_t () const
{
return number ();
}
};
class raw_key;
class uint256_union
{
public:
// Type that is implicitly convertible to this union
using underlying_type = nano::uint256_t;
public:
uint256_union () = default;
uint256_union (uint64_t value) :
uint256_union (nano::uint256_t{ value }){};
uint256_union (nano::uint256_t const & value)
{
bytes.fill (0);
boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false);
}
/**
* Decode from hex string
* @warning Aborts at runtime if the input is invalid
*/
explicit uint256_union (std::string const &);
void encrypt (nano::raw_key const &, nano::raw_key const &, uint128_union const &);
uint256_union & operator^= (uint256_union const &);
uint256_union operator^ (uint256_union const &) const;
void encode_hex (std::string &) const;
bool decode_hex (std::string const &);
void encode_dec (std::string &) const;
bool decode_dec (std::string const &);
void clear ()
{
qwords.fill (0);
}
bool is_zero () const
{
return owords[0].is_zero () && owords[1].is_zero ();
}
nano::uint256_t number () const
{
nano::uint256_t result;
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
return result;
}
std::string to_string () const;
public:
union
{
std::array<uint8_t, 32> bytes;
std::array<char, 32> chars;
std::array<uint32_t, 8> dwords;
std::array<uint64_t, 4> qwords;
std::array<uint128_union, 2> owords;
};
public: // Keep operators inlined
std::strong_ordering operator<=> (nano::uint256_union const & other) const
{
return std::memcmp (bytes.data (), other.bytes.data (), 32) <=> 0;
};
bool operator== (nano::uint256_union const & other) const
{
return *this <=> other == 0;
}
operator nano::uint256_t () const
{
return number ();
}
uint256_union const & as_union () const
{
return *this;
}
};
static_assert (std::is_nothrow_move_constructible<uint256_union>::value, "uint256_union should be noexcept MoveConstructible");
// All keys and hashes are 256 bit.
class block_hash final : public uint256_union
{
public:
using uint256_union::uint256_union;
public: // Keep operators inlined
auto operator<=> (nano::block_hash const & other) const
{
return uint256_union::operator<=> (other);
}
bool operator== (nano::block_hash const & other) const
{
return *this <=> other == 0;
}
operator nano::uint256_t () const
{
return number ();
}
};
class public_key final : public uint256_union
{
public:
using uint256_union::uint256_union;
public_key () :
uint256_union{ 0 } {};
static const public_key & null ();
bool decode_node_id (std::string const &);
void encode_account (std::string &) const;
bool decode_account (std::string const &);
std::string to_node_id () const;
std::string to_account () const;
/**
* Decode from account string
* @warning Aborts at runtime if the input is invalid
*/
static public_key from_account (std::string const &);
/**
* Decode from node id string
* @warning Aborts at runtime if the input is invalid
*/
static public_key from_node_id (std::string const &);
public: // Keep operators inlined
auto operator<=> (nano::public_key const & other) const
{
return uint256_union::operator<=> (other);
}
bool operator== (nano::public_key const & other) const
{
return *this <=> other == 0;
}
bool operator== (std::nullptr_t) const
{
return *this == null ();
}
operator nano::uint256_t () const
{
return number ();
}
};
class wallet_id : public uint256_union
{
using uint256_union::uint256_union;
};
// These are synonymous
using account = public_key;
class hash_or_account
{
public:
// Type that is implicitly convertible to this union
using underlying_type = nano::uint256_t;
public:
hash_or_account () :
account{} {};
hash_or_account (uint64_t value) :
raw{ value } {};
hash_or_account (uint256_union const & value) :
raw{ value } {};
void clear ()
{
raw.clear ();
}
bool is_zero () const
{
return raw.is_zero ();
}
bool decode_hex (std::string const &);
bool decode_account (std::string const &);
std::string to_string () const;
std::string to_account () const;
public:
union
{
std::array<uint8_t, 32> bytes;
nano::uint256_union raw; // This can be used when you don't want to explicitly mention either of the types
nano::account account;
nano::block_hash hash;
};
public: // Keep operators inlined
auto operator<=> (nano::hash_or_account const & other) const
{
return raw <=> other.raw;
};
bool operator== (nano::hash_or_account const & other) const
{
return *this <=> other == 0;
}
explicit operator nano::uint256_t () const
{
return raw.number ();
}
explicit operator nano::uint256_union () const
{
return raw;
}
nano::account const & as_account () const
{
return account;
}
nano::block_hash const & as_block_hash () const
{
return hash;
}
nano::uint256_union const & as_union () const
{
return raw;
}
};
// A link can either be a destination account or source hash
class link final : public hash_or_account
{
public:
using hash_or_account::hash_or_account;
explicit link (std::string_view str)
{
release_assert (str.size () <= bytes.size ());
std::copy_n (str.data (), str.size (), bytes.begin ());
}
public: // Keep operators inlined
auto operator<=> (nano::link const & other) const
{
return hash_or_account::operator<=> (other);
}
bool operator== (nano::link const & other) const
{
return *this <=> other == 0;
}
};
// A root can either be an open block hash or a previous hash
class root final : public hash_or_account
{
public:
using hash_or_account::hash_or_account;
nano::block_hash const & previous () const
{
return hash;
}
public: // Keep operators inlined
auto operator<=> (nano::root const & other) const
{
return hash_or_account::operator<=> (other);
}
bool operator== (nano::root const & other) const
{
return *this <=> other == 0;
}
};
// The seed or private key
class raw_key final : public uint256_union
{
public:
using uint256_union::uint256_union;
~raw_key ();
void decrypt (nano::uint256_union const &, nano::raw_key const &, uint128_union const &);
};
class uint512_union
{
public:
// Type that is implicitly convertible to this union
using underlying_type = nano::uint512_t;
public:
uint512_union () = default;
uint512_union (nano::uint512_t const & value)
{
bytes.fill (0);
boost::multiprecision::export_bits (value, bytes.rbegin (), 8, false);
}
uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower) :
uint256s{ upper, lower } {};
nano::uint512_union & operator^= (nano::uint512_union const & other)
{
uint256s[0] ^= other.uint256s[0];
uint256s[1] ^= other.uint256s[1];
return *this;
}
void encode_hex (std::string &) const;
bool decode_hex (std::string const &);
void clear ()
{
bytes.fill (0);
}
bool is_zero () const
{
return uint256s[0].is_zero () && uint256s[1].is_zero ();
}
nano::uint512_t number () const
{
nano::uint512_t result;
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
return result;
}
std::string to_string () const;
public:
union
{
std::array<uint8_t, 64> bytes;
std::array<uint32_t, 16> dwords;
std::array<uint64_t, 8> qwords;
std::array<uint256_union, 2> uint256s;
};
public: // Keep operators inlined
std::strong_ordering operator<=> (nano::uint512_union const & other) const
{
return std::memcmp (bytes.data (), other.bytes.data (), 64) <=> 0;
};
bool operator== (nano::uint512_union const & other) const
{
return *this <=> other == 0;
}
operator nano::uint512_t () const
{
return number ();
}
uint512_union const & as_union () const
{
return *this;
}
};
static_assert (std::is_nothrow_move_constructible<uint512_union>::value, "uint512_union should be noexcept MoveConstructible");
class signature : public uint512_union
{
public:
using uint512_union::uint512_union;
};
class qualified_root : public uint512_union
{
public:
qualified_root () = default;
qualified_root (nano::root const & root, nano::block_hash const & previous) :
uint512_union{ root.as_union (), previous.as_union () } {};
qualified_root (nano::uint512_t const & value) :
uint512_union{ value } {};
nano::root root () const
{
return nano::root{ uint256s[0] };
}
nano::block_hash previous () const
{
return nano::block_hash{ uint256s[1] };
}
};
nano::signature sign_message (nano::raw_key const &, nano::public_key const &, nano::uint256_union const &);
nano::signature sign_message (nano::raw_key const &, nano::public_key const &, uint8_t const *, size_t);
bool validate_message (nano::public_key const &, nano::uint256_union const &, nano::signature const &);
bool validate_message (nano::public_key const &, uint8_t const *, size_t, nano::signature const &);
nano::raw_key deterministic_key (nano::raw_key const &, uint32_t);
nano::public_key pub_key (nano::raw_key const &);
/* Conversion methods */
std::string to_string_hex (uint64_t const);
std::string to_string_hex (uint16_t const);
bool from_string_hex (std::string const &, uint64_t &);
/* Printing adapters */
std::ostream & operator<< (std::ostream &, const uint128_union &);
std::ostream & operator<< (std::ostream &, const uint256_union &);
std::ostream & operator<< (std::ostream &, const uint512_union &);
std::ostream & operator<< (std::ostream &, const hash_or_account &);
/**
* Convert a double to string in fixed format
* @param precision_a (optional) use a specific precision (default is the maximum)
*/
std::string to_string (double const, int const precision_a = std::numeric_limits<double>::digits10);
namespace difficulty
{
uint64_t from_multiplier (double const, uint64_t const);
double to_multiplier (uint64_t const, uint64_t const);
}
/**
* Add to or substract from a value without overflow
* TODO: C++26 replace with std::add_sat and std::sub_sat
*/
template <typename T>
T add_sat (T const & value, T const & diff) noexcept
{
static_assert (std::numeric_limits<T>::is_specialized, "std::numeric_limits<T> must be specialized");
return (value > std::numeric_limits<T>::max () - diff) ? std::numeric_limits<T>::max () : value + diff;
}
template <typename T>
T sub_sat (T const & value, T const & diff) noexcept
{
static_assert (std::numeric_limits<T>::is_specialized, "std::numeric_limits<T> must be specialized");
return (value < std::numeric_limits<T>::min () + diff) ? std::numeric_limits<T>::min () : value - diff;
}
template <typename T>
T inc_sat (T const & value) noexcept
{
return add_sat (value, static_cast<T> (1));
}
template <typename T>
T dec_sat (T const & value) noexcept
{
return sub_sat (value, static_cast<T> (1));
}
}
/*
* Hashing
*/
namespace std
{
template <>
struct hash<::nano::uint128_union>;
template <>
struct hash<::nano::uint256_union>;
template <>
struct hash<::nano::public_key>;
template <>
struct hash<::nano::block_hash>;
template <>
struct hash<::nano::hash_or_account>;
template <>
struct hash<::nano::root>;
template <>
struct hash<::nano::link>;
template <>
struct hash<::nano::raw_key>;
template <>
struct hash<::nano::wallet_id>;
template <>
struct hash<::nano::uint512_union>;
template <>
struct hash<::nano::qualified_root>;
}
namespace boost
{
template <>
struct hash<::nano::uint128_union>;
template <>
struct hash<::nano::uint256_union>;
template <>
struct hash<::nano::public_key>;
template <>
struct hash<::nano::block_hash>;
template <>
struct hash<::nano::hash_or_account>;
template <>
struct hash<::nano::root>;
template <>
struct hash<::nano::link>;
template <>
struct hash<::nano::raw_key>;
template <>
struct hash<::nano::wallet_id>;
template <>
struct hash<::nano::uint512_union>;
template <>
struct hash<::nano::qualified_root>;
}
/*
* Formatters
*/
template <>
struct fmt::formatter<nano::uint128_union> : fmt::ostream_formatter
{
};
template <>
struct fmt::formatter<nano::uint256_union> : fmt::ostream_formatter
{
};
template <>
struct fmt::formatter<nano::uint512_union> : fmt::ostream_formatter
{
};
template <>
struct fmt::formatter<nano::hash_or_account> : fmt::ostream_formatter
{
};
template <>
struct fmt::formatter<nano::block_hash> : fmt::formatter<nano::uint256_union>
{
};
template <>
struct fmt::formatter<nano::public_key> : fmt::formatter<nano::uint256_union>
{
};
template <>
struct fmt::formatter<nano::qualified_root> : fmt::formatter<nano::uint512_union>
{
};