715 lines
18 KiB
C++
715 lines
18 KiB
C++
#include <nano/lib/numbers.hpp>
|
|
#include <nano/lib/utility.hpp>
|
|
|
|
#include <crypto/ed25519-donna/ed25519.h>
|
|
|
|
#include <crypto/blake2/blake2.h>
|
|
|
|
#include <crypto/cryptopp/aes.h>
|
|
#include <crypto/cryptopp/modes.h>
|
|
|
|
std::mutex nano::random_pool::mutex;
|
|
CryptoPP::AutoSeededRandomPool nano::random_pool::pool;
|
|
|
|
void nano::random_pool::generate_block (unsigned char * output, size_t size)
|
|
{
|
|
std::lock_guard<std::mutex> lk (mutex);
|
|
pool.GenerateBlock (output, size);
|
|
}
|
|
|
|
unsigned nano::random_pool::generate_word32 (unsigned min, unsigned max)
|
|
{
|
|
std::lock_guard<std::mutex> lk (mutex);
|
|
return pool.GenerateWord32 (min, max);
|
|
}
|
|
|
|
unsigned char nano::random_pool::generate_byte ()
|
|
{
|
|
std::lock_guard<std::mutex> lk (mutex);
|
|
return pool.GenerateByte ();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
char const * base58_reverse ("~012345678~~~~~~~9:;<=>?@~ABCDE~FGHIJKLMNOP~~~~~~QRSTUVWXYZ[~\\]^_`abcdefghi");
|
|
uint8_t base58_decode (char value)
|
|
{
|
|
assert (value >= '0');
|
|
assert (value <= '~');
|
|
return static_cast<uint8_t> (base58_reverse[value - 0x30] - 0x30);
|
|
}
|
|
char const * account_lookup ("13456789abcdefghijkmnopqrstuwxyz");
|
|
char const * account_reverse ("~0~1234567~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~89:;<=>?@AB~CDEFGHIJK~LMNO~~~~~");
|
|
char account_encode (uint8_t value)
|
|
{
|
|
assert (value < 32);
|
|
auto result (account_lookup[value]);
|
|
return result;
|
|
}
|
|
uint8_t account_decode (char value)
|
|
{
|
|
assert (value >= '0');
|
|
assert (value <= '~');
|
|
auto result (account_reverse[value - 0x30]);
|
|
if (result != '~')
|
|
{
|
|
result -= 0x30;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
void nano::uint256_union::encode_account (std::string & destination_a) const
|
|
{
|
|
assert (destination_a.empty ());
|
|
destination_a.reserve (64);
|
|
uint64_t check (0);
|
|
blake2b_state hash;
|
|
blake2b_init (&hash, 5);
|
|
blake2b_update (&hash, bytes.data (), bytes.size ());
|
|
blake2b_final (&hash, reinterpret_cast<uint8_t *> (&check), 5);
|
|
nano::uint512_t number_l (number ());
|
|
number_l <<= 40;
|
|
number_l |= nano::uint512_t (check);
|
|
for (auto i (0); i < 60; ++i)
|
|
{
|
|
uint8_t r (number_l & static_cast<uint8_t> (0x1f));
|
|
number_l >>= 5;
|
|
destination_a.push_back (account_encode (r));
|
|
}
|
|
destination_a.append ("_brx"); // xrb_
|
|
std::reverse (destination_a.begin (), destination_a.end ());
|
|
}
|
|
|
|
std::string nano::uint256_union::to_account () const
|
|
{
|
|
std::string result;
|
|
encode_account (result);
|
|
return result;
|
|
}
|
|
|
|
bool nano::uint256_union::decode_account (std::string const & source_a)
|
|
{
|
|
auto error (source_a.size () < 5);
|
|
if (!error)
|
|
{
|
|
auto xrb_prefix (source_a[0] == 'x' && source_a[1] == 'r' && source_a[2] == 'b' && (source_a[3] == '_' || source_a[3] == '-'));
|
|
auto nano_prefix (source_a[0] == 'n' && source_a[1] == 'a' && source_a[2] == 'n' && source_a[3] == 'o' && (source_a[4] == '_' || source_a[4] == '-'));
|
|
error = (xrb_prefix && source_a.size () != 64) || (nano_prefix && source_a.size () != 65);
|
|
if (!error)
|
|
{
|
|
if (xrb_prefix || nano_prefix)
|
|
{
|
|
auto i (source_a.begin () + (xrb_prefix ? 4 : 5));
|
|
if (*i == '1' || *i == '3')
|
|
{
|
|
nano::uint512_t number_l;
|
|
for (auto j (source_a.end ()); !error && i != j; ++i)
|
|
{
|
|
uint8_t character (*i);
|
|
error = character < 0x30 || character >= 0x80;
|
|
if (!error)
|
|
{
|
|
uint8_t byte (account_decode (character));
|
|
error = byte == '~';
|
|
if (!error)
|
|
{
|
|
number_l <<= 5;
|
|
number_l += byte;
|
|
}
|
|
}
|
|
}
|
|
if (!error)
|
|
{
|
|
*this = (number_l >> 40).convert_to<nano::uint256_t> ();
|
|
uint64_t check (number_l & static_cast<uint64_t> (0xffffffffff));
|
|
uint64_t validation (0);
|
|
blake2b_state hash;
|
|
blake2b_init (&hash, 5);
|
|
blake2b_update (&hash, bytes.data (), bytes.size ());
|
|
blake2b_final (&hash, reinterpret_cast<uint8_t *> (&validation), 5);
|
|
error = check != validation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
nano::uint256_union::uint256_union (nano::uint256_t const & number_a)
|
|
{
|
|
bytes.fill (0);
|
|
boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false);
|
|
}
|
|
|
|
bool nano::uint256_union::operator== (nano::uint256_union const & other_a) const
|
|
{
|
|
return bytes == other_a.bytes;
|
|
}
|
|
|
|
// Construct a uint256_union = AES_ENC_CTR (cleartext, key, iv)
|
|
void nano::uint256_union::encrypt (nano::raw_key const & cleartext, nano::raw_key const & key, uint128_union const & iv)
|
|
{
|
|
CryptoPP::AES::Encryption alg (key.data.bytes.data (), sizeof (key.data.bytes));
|
|
CryptoPP::CTR_Mode_ExternalCipher::Encryption enc (alg, iv.bytes.data ());
|
|
enc.ProcessData (bytes.data (), cleartext.data.bytes.data (), sizeof (cleartext.data.bytes));
|
|
}
|
|
|
|
bool nano::uint256_union::is_zero () const
|
|
{
|
|
return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0;
|
|
}
|
|
|
|
std::string nano::uint256_union::to_string () const
|
|
{
|
|
std::string result;
|
|
encode_hex (result);
|
|
return result;
|
|
}
|
|
|
|
bool nano::uint256_union::operator< (nano::uint256_union const & other_a) const
|
|
{
|
|
return std::memcmp (bytes.data (), other_a.bytes.data (), 32) < 0;
|
|
}
|
|
|
|
nano::uint256_union & nano::uint256_union::operator^= (nano::uint256_union const & other_a)
|
|
{
|
|
auto j (other_a.qwords.begin ());
|
|
for (auto i (qwords.begin ()), n (qwords.end ()); i != n; ++i, ++j)
|
|
{
|
|
*i ^= *j;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
nano::uint256_union nano::uint256_union::operator^ (nano::uint256_union const & other_a) const
|
|
{
|
|
nano::uint256_union result;
|
|
auto k (result.qwords.begin ());
|
|
for (auto i (qwords.begin ()), j (other_a.qwords.begin ()), n (qwords.end ()); i != n; ++i, ++j, ++k)
|
|
{
|
|
*k = *i ^ *j;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
nano::uint256_union::uint256_union (std::string const & hex_a)
|
|
{
|
|
auto error (decode_hex (hex_a));
|
|
|
|
release_assert (!error);
|
|
}
|
|
|
|
void nano::uint256_union::clear ()
|
|
{
|
|
qwords.fill (0);
|
|
}
|
|
|
|
nano::uint256_t nano::uint256_union::number () const
|
|
{
|
|
nano::uint256_t result;
|
|
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
|
|
return result;
|
|
}
|
|
|
|
void nano::uint256_union::encode_hex (std::string & text) const
|
|
{
|
|
assert (text.empty ());
|
|
std::stringstream stream;
|
|
stream << std::hex << std::noshowbase << std::setw (64) << std::setfill ('0');
|
|
stream << number ();
|
|
text = stream.str ();
|
|
}
|
|
|
|
bool nano::uint256_union::decode_hex (std::string const & text)
|
|
{
|
|
auto error (false);
|
|
if (!text.empty () && text.size () <= 64)
|
|
{
|
|
std::stringstream stream (text);
|
|
stream << std::hex << std::noshowbase;
|
|
nano::uint256_t number_l;
|
|
try
|
|
{
|
|
stream >> number_l;
|
|
*this = number_l;
|
|
if (!stream.eof ())
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
catch (std::runtime_error &)
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error = true;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void nano::uint256_union::encode_dec (std::string & text) const
|
|
{
|
|
assert (text.empty ());
|
|
std::stringstream stream;
|
|
stream << std::dec << std::noshowbase;
|
|
stream << number ();
|
|
text = stream.str ();
|
|
}
|
|
|
|
bool nano::uint256_union::decode_dec (std::string const & text)
|
|
{
|
|
auto error (text.size () > 78 || (text.size () > 1 && text.front () == '0') || (!text.empty () && text.front () == '-'));
|
|
if (!error)
|
|
{
|
|
std::stringstream stream (text);
|
|
stream << std::dec << std::noshowbase;
|
|
nano::uint256_t number_l;
|
|
try
|
|
{
|
|
stream >> number_l;
|
|
*this = number_l;
|
|
if (!stream.eof ())
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
catch (std::runtime_error &)
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
nano::uint256_union::uint256_union (uint64_t value0)
|
|
{
|
|
*this = nano::uint256_t (value0);
|
|
}
|
|
|
|
bool nano::uint256_union::operator!= (nano::uint256_union const & other_a) const
|
|
{
|
|
return !(*this == other_a);
|
|
}
|
|
|
|
bool nano::uint512_union::operator== (nano::uint512_union const & other_a) const
|
|
{
|
|
return bytes == other_a.bytes;
|
|
}
|
|
|
|
nano::uint512_union::uint512_union (nano::uint256_union const & upper, nano::uint256_union const & lower)
|
|
{
|
|
uint256s[0] = upper;
|
|
uint256s[1] = lower;
|
|
}
|
|
|
|
nano::uint512_union::uint512_union (nano::uint512_t const & number_a)
|
|
{
|
|
bytes.fill (0);
|
|
boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false);
|
|
}
|
|
|
|
bool nano::uint512_union::is_zero () const
|
|
{
|
|
return qwords[0] == 0 && qwords[1] == 0 && qwords[2] == 0 && qwords[3] == 0
|
|
&& qwords[4] == 0 && qwords[5] == 0 && qwords[6] == 0 && qwords[7] == 0;
|
|
}
|
|
|
|
void nano::uint512_union::clear ()
|
|
{
|
|
bytes.fill (0);
|
|
}
|
|
|
|
nano::uint512_t nano::uint512_union::number () const
|
|
{
|
|
nano::uint512_t result;
|
|
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
|
|
return result;
|
|
}
|
|
|
|
void nano::uint512_union::encode_hex (std::string & text) const
|
|
{
|
|
assert (text.empty ());
|
|
std::stringstream stream;
|
|
stream << std::hex << std::noshowbase << std::setw (128) << std::setfill ('0');
|
|
stream << number ();
|
|
text = stream.str ();
|
|
}
|
|
|
|
bool nano::uint512_union::decode_hex (std::string const & text)
|
|
{
|
|
auto error (text.size () > 128);
|
|
if (!error)
|
|
{
|
|
std::stringstream stream (text);
|
|
stream << std::hex << std::noshowbase;
|
|
nano::uint512_t number_l;
|
|
try
|
|
{
|
|
stream >> number_l;
|
|
*this = number_l;
|
|
if (!stream.eof ())
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
catch (std::runtime_error &)
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
bool nano::uint512_union::operator!= (nano::uint512_union const & other_a) const
|
|
{
|
|
return !(*this == other_a);
|
|
}
|
|
|
|
nano::uint512_union & nano::uint512_union::operator^= (nano::uint512_union const & other_a)
|
|
{
|
|
uint256s[0] ^= other_a.uint256s[0];
|
|
uint256s[1] ^= other_a.uint256s[1];
|
|
return *this;
|
|
}
|
|
|
|
std::string nano::uint512_union::to_string () const
|
|
{
|
|
std::string result;
|
|
encode_hex (result);
|
|
return result;
|
|
}
|
|
|
|
nano::raw_key::~raw_key ()
|
|
{
|
|
data.clear ();
|
|
}
|
|
|
|
bool nano::raw_key::operator== (nano::raw_key const & other_a) const
|
|
{
|
|
return data == other_a.data;
|
|
}
|
|
|
|
bool nano::raw_key::operator!= (nano::raw_key const & other_a) const
|
|
{
|
|
return !(*this == other_a);
|
|
}
|
|
|
|
// This this = AES_DEC_CTR (ciphertext, key, iv)
|
|
void nano::raw_key::decrypt (nano::uint256_union const & ciphertext, nano::raw_key const & key_a, uint128_union const & iv)
|
|
{
|
|
CryptoPP::AES::Encryption alg (key_a.data.bytes.data (), sizeof (key_a.data.bytes));
|
|
CryptoPP::CTR_Mode_ExternalCipher::Decryption dec (alg, iv.bytes.data ());
|
|
dec.ProcessData (data.bytes.data (), ciphertext.bytes.data (), sizeof (ciphertext.bytes));
|
|
}
|
|
|
|
nano::uint512_union nano::sign_message (nano::raw_key const & private_key, nano::public_key const & public_key, nano::uint256_union const & message)
|
|
{
|
|
nano::uint512_union result;
|
|
ed25519_sign (message.bytes.data (), sizeof (message.bytes), private_key.data.bytes.data (), public_key.bytes.data (), result.bytes.data ());
|
|
return result;
|
|
}
|
|
|
|
void nano::deterministic_key (nano::uint256_union const & seed_a, uint32_t index_a, nano::uint256_union & prv_a)
|
|
{
|
|
blake2b_state hash;
|
|
blake2b_init (&hash, prv_a.bytes.size ());
|
|
blake2b_update (&hash, seed_a.bytes.data (), seed_a.bytes.size ());
|
|
nano::uint256_union index (index_a);
|
|
blake2b_update (&hash, reinterpret_cast<uint8_t *> (&index.dwords[7]), sizeof (uint32_t));
|
|
blake2b_final (&hash, prv_a.bytes.data (), prv_a.bytes.size ());
|
|
}
|
|
|
|
nano::public_key nano::pub_key (nano::private_key const & privatekey_a)
|
|
{
|
|
nano::uint256_union result;
|
|
ed25519_publickey (privatekey_a.bytes.data (), result.bytes.data ());
|
|
return result;
|
|
}
|
|
|
|
bool nano::validate_message (nano::public_key const & public_key, nano::uint256_union const & message, nano::uint512_union const & signature)
|
|
{
|
|
auto result (0 != ed25519_sign_open (message.bytes.data (), sizeof (message.bytes), public_key.bytes.data (), signature.bytes.data ()));
|
|
return result;
|
|
}
|
|
|
|
bool nano::validate_message_batch (const unsigned char ** m, size_t * mlen, const unsigned char ** pk, const unsigned char ** RS, size_t num, int * valid)
|
|
{
|
|
bool result (0 == ed25519_sign_open_batch (m, mlen, pk, RS, num, valid));
|
|
return result;
|
|
}
|
|
|
|
nano::uint128_union::uint128_union (std::string const & string_a)
|
|
{
|
|
auto error (decode_hex (string_a));
|
|
|
|
release_assert (!error);
|
|
}
|
|
|
|
nano::uint128_union::uint128_union (uint64_t value_a)
|
|
{
|
|
*this = nano::uint128_t (value_a);
|
|
}
|
|
|
|
nano::uint128_union::uint128_union (nano::uint128_t const & number_a)
|
|
{
|
|
bytes.fill (0);
|
|
boost::multiprecision::export_bits (number_a, bytes.rbegin (), 8, false);
|
|
}
|
|
|
|
bool nano::uint128_union::operator== (nano::uint128_union const & other_a) const
|
|
{
|
|
return qwords[0] == other_a.qwords[0] && qwords[1] == other_a.qwords[1];
|
|
}
|
|
|
|
bool nano::uint128_union::operator!= (nano::uint128_union const & other_a) const
|
|
{
|
|
return !(*this == other_a);
|
|
}
|
|
|
|
bool nano::uint128_union::operator< (nano::uint128_union const & other_a) const
|
|
{
|
|
return std::memcmp (bytes.data (), other_a.bytes.data (), 16) < 0;
|
|
}
|
|
|
|
bool nano::uint128_union::operator> (nano::uint128_union const & other_a) const
|
|
{
|
|
return std::memcmp (bytes.data (), other_a.bytes.data (), 16) > 0;
|
|
}
|
|
|
|
nano::uint128_t nano::uint128_union::number () const
|
|
{
|
|
nano::uint128_t result;
|
|
boost::multiprecision::import_bits (result, bytes.begin (), bytes.end ());
|
|
return result;
|
|
}
|
|
|
|
void nano::uint128_union::encode_hex (std::string & text) const
|
|
{
|
|
assert (text.empty ());
|
|
std::stringstream stream;
|
|
stream << std::hex << std::noshowbase << std::setw (32) << std::setfill ('0');
|
|
stream << number ();
|
|
text = stream.str ();
|
|
}
|
|
|
|
bool nano::uint128_union::decode_hex (std::string const & text)
|
|
{
|
|
auto error (text.size () > 32);
|
|
if (!error)
|
|
{
|
|
std::stringstream stream (text);
|
|
stream << std::hex << std::noshowbase;
|
|
nano::uint128_t number_l;
|
|
try
|
|
{
|
|
stream >> number_l;
|
|
*this = number_l;
|
|
if (!stream.eof ())
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
catch (std::runtime_error &)
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void nano::uint128_union::encode_dec (std::string & text) const
|
|
{
|
|
assert (text.empty ());
|
|
std::stringstream stream;
|
|
stream << std::dec << std::noshowbase;
|
|
stream << number ();
|
|
text = stream.str ();
|
|
}
|
|
|
|
bool nano::uint128_union::decode_dec (std::string const & text)
|
|
{
|
|
auto error (text.size () > 39 || (text.size () > 1 && text.front () == '0') || (!text.empty () && text.front () == '-'));
|
|
if (!error)
|
|
{
|
|
std::stringstream stream (text);
|
|
stream << std::dec << std::noshowbase;
|
|
boost::multiprecision::checked_uint128_t number_l;
|
|
try
|
|
{
|
|
stream >> number_l;
|
|
nano::uint128_t unchecked (number_l);
|
|
*this = unchecked;
|
|
if (!stream.eof ())
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
catch (std::runtime_error &)
|
|
{
|
|
error = true;
|
|
}
|
|
}
|
|
return error;
|
|
}
|
|
|
|
void format_frac (std::ostringstream & stream, nano::uint128_t value, nano::uint128_t scale, int precision)
|
|
{
|
|
auto reduce = scale;
|
|
auto rem = value;
|
|
while (reduce > 1 && rem > 0 && precision > 0)
|
|
{
|
|
reduce /= 10;
|
|
auto val = rem / reduce;
|
|
rem -= val * reduce;
|
|
stream << val;
|
|
precision--;
|
|
}
|
|
}
|
|
|
|
void format_dec (std::ostringstream & stream, nano::uint128_t value, char group_sep, const std::string & groupings)
|
|
{
|
|
auto largestPow10 = nano::uint256_t (1);
|
|
int dec_count = 1;
|
|
while (1)
|
|
{
|
|
auto next = largestPow10 * 10;
|
|
if (next > value)
|
|
{
|
|
break;
|
|
}
|
|
largestPow10 = next;
|
|
dec_count++;
|
|
}
|
|
|
|
if (dec_count > 39)
|
|
{
|
|
// Impossible.
|
|
return;
|
|
}
|
|
|
|
// This could be cached per-locale.
|
|
bool emit_group[39];
|
|
if (group_sep != 0)
|
|
{
|
|
int group_index = 0;
|
|
int group_count = 0;
|
|
for (int i = 0; i < dec_count; i++)
|
|
{
|
|
group_count++;
|
|
if (group_count > groupings[group_index])
|
|
{
|
|
group_index = std::min (group_index + 1, (int)groupings.length () - 1);
|
|
group_count = 1;
|
|
emit_group[i] = true;
|
|
}
|
|
else
|
|
{
|
|
emit_group[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto reduce = nano::uint128_t (largestPow10);
|
|
nano::uint128_t rem = value;
|
|
while (reduce > 0)
|
|
{
|
|
auto val = rem / reduce;
|
|
rem -= val * reduce;
|
|
stream << val;
|
|
dec_count--;
|
|
if (group_sep != 0 && emit_group[dec_count] && reduce > 1)
|
|
{
|
|
stream << group_sep;
|
|
}
|
|
reduce /= 10;
|
|
}
|
|
}
|
|
|
|
std::string format_balance (nano::uint128_t balance, nano::uint128_t scale, int precision, bool group_digits, char thousands_sep, char decimal_point, std::string & grouping)
|
|
{
|
|
std::ostringstream stream;
|
|
auto int_part = balance / scale;
|
|
auto frac_part = balance % scale;
|
|
auto prec_scale = scale;
|
|
for (int i = 0; i < precision; i++)
|
|
{
|
|
prec_scale /= 10;
|
|
}
|
|
if (int_part == 0 && frac_part > 0 && frac_part / prec_scale == 0)
|
|
{
|
|
// Display e.g. "< 0.01" rather than 0.
|
|
stream << "< ";
|
|
if (precision > 0)
|
|
{
|
|
stream << "0";
|
|
stream << decimal_point;
|
|
for (int i = 0; i < precision - 1; i++)
|
|
{
|
|
stream << "0";
|
|
}
|
|
}
|
|
stream << "1";
|
|
}
|
|
else
|
|
{
|
|
format_dec (stream, int_part, group_digits && grouping.length () > 0 ? thousands_sep : 0, grouping);
|
|
if (precision > 0 && frac_part > 0)
|
|
{
|
|
stream << decimal_point;
|
|
format_frac (stream, frac_part, scale, precision);
|
|
}
|
|
}
|
|
return stream.str ();
|
|
}
|
|
|
|
std::string nano::uint128_union::format_balance (nano::uint128_t scale, int precision, bool group_digits)
|
|
{
|
|
auto thousands_sep = std::use_facet<std::numpunct<char>> (std::locale ()).thousands_sep ();
|
|
auto decimal_point = std::use_facet<std::numpunct<char>> (std::locale ()).decimal_point ();
|
|
std::string grouping = "\3";
|
|
return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping);
|
|
}
|
|
|
|
std::string nano::uint128_union::format_balance (nano::uint128_t scale, int precision, bool group_digits, const std::locale & locale)
|
|
{
|
|
auto thousands_sep = std::use_facet<std::moneypunct<char>> (locale).thousands_sep ();
|
|
auto decimal_point = std::use_facet<std::moneypunct<char>> (locale).decimal_point ();
|
|
std::string grouping = std::use_facet<std::moneypunct<char>> (locale).grouping ();
|
|
return ::format_balance (number (), scale, precision, group_digits, thousands_sep, decimal_point, grouping);
|
|
}
|
|
|
|
void nano::uint128_union::clear ()
|
|
{
|
|
qwords.fill (0);
|
|
}
|
|
|
|
bool nano::uint128_union::is_zero () const
|
|
{
|
|
return qwords[0] == 0 && qwords[1] == 0;
|
|
}
|
|
|
|
std::string nano::uint128_union::to_string () const
|
|
{
|
|
std::string result;
|
|
encode_hex (result);
|
|
return result;
|
|
}
|
|
|
|
std::string nano::uint128_union::to_string_dec () const
|
|
{
|
|
std::string result;
|
|
encode_dec (result);
|
|
return result;
|
|
}
|