Remove legacy arbitrary precision integer support:

The CBigNum class is a wrapper around OpenSSL's BIGNUM implementation
to make use simpler.

Replacing the implementation with boost::multiprecision helps reduce
the size of the codebase and improves performance (benchmarks show
the new boost-based implementation is ~7x faster).
This commit is contained in:
Nik Bougalis
2016-04-30 13:30:55 -07:00
committed by Howard Hinnant
parent 1c3ee48146
commit f081e80c28
10 changed files with 111 additions and 1592 deletions

View File

@@ -2079,20 +2079,12 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\core\TimeKeeper.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\CAutoBN_CTX.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\CBigNum.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\csprng.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\ECIES.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\GenerateDeterministicKey.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\crypto\impl\CBigNum.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\crypto\impl\csprng.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -2137,10 +2129,6 @@
</None>
<ClInclude Include="..\..\src\ripple\crypto\RFC1751.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\crypto\tests\CBigNum_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\crypto\tests\CKey.test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -2604,12 +2604,6 @@
<ClInclude Include="..\..\src\ripple\core\TimeKeeper.h">
<Filter>ripple\core</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\CAutoBN_CTX.h">
<Filter>ripple\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\CBigNum.h">
<Filter>ripple\crypto</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\crypto\csprng.h">
<Filter>ripple\crypto</Filter>
</ClInclude>
@@ -2619,9 +2613,6 @@
<ClInclude Include="..\..\src\ripple\crypto\GenerateDeterministicKey.h">
<Filter>ripple\crypto</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\crypto\impl\CBigNum.cpp">
<Filter>ripple\crypto\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\crypto\impl\csprng.cpp">
<Filter>ripple\crypto\impl</Filter>
</ClCompile>
@@ -2664,9 +2655,6 @@
<ClInclude Include="..\..\src\ripple\crypto\RFC1751.h">
<Filter>ripple\crypto</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\crypto\tests\CBigNum_test.cpp">
<Filter>ripple\crypto\tests</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\crypto\tests\CKey.test.cpp">
<Filter>ripple\crypto\tests</Filter>
</ClCompile>

View File

@@ -1,80 +0,0 @@
//------------------------------------------------------------------------------
/*
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_CRYPTO_CAUTOBN_CTX_H_INCLUDED
#define RIPPLE_CRYPTO_CAUTOBN_CTX_H_INCLUDED
#include <ripple/basics/contract.h>
#include <openssl/bn.h>
#include <stdexcept>
#include <string>
namespace ripple {
class CAutoBN_CTX
{
protected:
BN_CTX* pctx;
public:
CAutoBN_CTX ()
{
pctx = BN_CTX_new ();
if (pctx == nullptr)
Throw<std::runtime_error> ("CAutoBN_CTX : BN_CTX_new() returned nullptr");
}
~CAutoBN_CTX ()
{
if (pctx != nullptr)
BN_CTX_free (pctx);
}
CAutoBN_CTX (CAutoBN_CTX const&) = delete;
CAutoBN_CTX& operator= (CAutoBN_CTX const&) = delete;
operator BN_CTX* ()
{
return pctx;
}
BN_CTX& operator* ()
{
return *pctx;
}
BN_CTX** operator& ()
{
return &pctx;
}
bool operator! ()
{
return (pctx == nullptr);
}
};
}
#endif

View File

@@ -1,119 +0,0 @@
//------------------------------------------------------------------------------
/*
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_CRYPTO_CBIGNUM_H_INCLUDED
#define RIPPLE_CRYPTO_CBIGNUM_H_INCLUDED
#include <ripple/basics/base_uint.h>
#include <openssl/bn.h>
namespace ripple {
// VFALCO TODO figure out a way to remove the dependency on openssl in the
// header. Maybe rewrite this to use cryptopp.
class CBigNum : public BIGNUM
{
public:
CBigNum ();
CBigNum (const CBigNum& b);
CBigNum& operator= (const CBigNum& b);
CBigNum (char n);
CBigNum (short n);
CBigNum (int n);
CBigNum (long n);
CBigNum (long long n);
CBigNum (unsigned char n);
CBigNum (unsigned short n);
CBigNum (unsigned int n);
CBigNum (unsigned long long n);
explicit CBigNum (uint256 n);
explicit CBigNum (Blob const& vch);
explicit CBigNum (BIGNUM const* b);
CBigNum (unsigned char const* begin, unsigned char const* end);
~CBigNum ();
void setuint (unsigned int n);
unsigned int getuint () const;
int getint () const;
void setint64 (std::int64_t n);
std::uint64_t getuint64 () const;
void setuint64 (std::uint64_t n);
void setuint256 (uint256 const& n);
uint256 getuint256 ();
void setvch (unsigned char const* begin, unsigned char const* end);
void setvch (Blob const& vch);
Blob getvch () const;
CBigNum& SetCompact (unsigned int nCompact);
unsigned int GetCompact () const;
void SetHex (std::string const& str);
std::string ToString (int nBase = 10) const;
std::string GetHex () const;
bool operator! () const;
CBigNum& operator+= (const CBigNum& b);
CBigNum& operator-= (const CBigNum& b);
CBigNum& operator*= (const CBigNum& b);
CBigNum& operator/= (const CBigNum& b);
CBigNum& operator%= (const CBigNum& b);
CBigNum& operator<<= (unsigned int shift);
CBigNum& operator>>= (unsigned int shift);
CBigNum& operator++ ();
CBigNum& operator-- ();
const CBigNum operator++ (int);
const CBigNum operator-- (int);
private:
// private because the size of an unsigned long varies by platform
void setulong (unsigned long n);
unsigned long getulong () const;
};
const CBigNum operator+ (const CBigNum& a, const CBigNum& b);
const CBigNum operator- (const CBigNum& a, const CBigNum& b);
const CBigNum operator- (const CBigNum& a);
const CBigNum operator* (const CBigNum& a, const CBigNum& b);
const CBigNum operator/ (const CBigNum& a, const CBigNum& b);
const CBigNum operator% (const CBigNum& a, const CBigNum& b);
const CBigNum operator<< (const CBigNum& a, unsigned int shift);
const CBigNum operator>> (const CBigNum& a, unsigned int shift);
bool operator== (const CBigNum& a, const CBigNum& b);
bool operator!= (const CBigNum& a, const CBigNum& b);
bool operator<= (const CBigNum& a, const CBigNum& b);
bool operator>= (const CBigNum& a, const CBigNum& b);
bool operator< (const CBigNum& a, const CBigNum& b);
bool operator> (const CBigNum& a, const CBigNum& b);
//------------------------------------------------------------------------------
// Only STAmount uses these (January 2016).
int BN_add_word64 (BIGNUM* a, std::uint64_t w);
int BN_sub_word64 (BIGNUM* a, std::uint64_t w);
int BN_mul_word64 (BIGNUM* a, std::uint64_t w);
int BN_div_word64 (BIGNUM* a, std::uint64_t w);
}
#endif

View File

@@ -1,689 +0,0 @@
//------------------------------------------------------------------------------
/*
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.
#include <BeastConfig.h>
#include <ripple/basics/contract.h>
#include <ripple/crypto/CBigNum.h>
#include <ripple/crypto/CAutoBN_CTX.h>
#include <stdexcept>
#include <string>
#include <algorithm>
namespace ripple {
CBigNum::CBigNum ()
{
BN_init (this);
}
CBigNum::CBigNum (const CBigNum& b)
: CBigNum (&b)
{ }
CBigNum& CBigNum::operator= (const CBigNum& b)
{
if (!BN_copy (this, &b))
Throw<std::runtime_error> ("CBigNum::operator= : BN_copy failed");
return (*this);
}
CBigNum::~CBigNum ()
{
BN_clear_free (this);
}
CBigNum::CBigNum (char n)
{
BN_init (this);
if (n >= 0) setulong (n);
else setint64 (n);
}
CBigNum::CBigNum (short n)
{
BN_init (this);
if (n >= 0) setulong (n);
else setint64 (n);
}
CBigNum::CBigNum (int n)
{
BN_init (this);
if (n >= 0) setulong (n);
else setint64 (n);
}
CBigNum::CBigNum (long n)
{
BN_init (this);
if (n >= 0) setulong (n);
else setint64 (n);
}
CBigNum::CBigNum (long long n)
{
BN_init (this);
setint64 (n);
}
CBigNum::CBigNum (unsigned char n)
{
BN_init (this);
setulong (n);
}
CBigNum::CBigNum (unsigned short n)
{
BN_init (this);
setulong (n);
}
CBigNum::CBigNum (unsigned int n)
{
BN_init (this);
setulong (n);
}
CBigNum::CBigNum (unsigned long long n)
{
BN_init (this);
setuint64 (n);
}
CBigNum::CBigNum (uint256 n)
{
BN_init (this);
setuint256 (n);
}
CBigNum::CBigNum (Blob const& vch)
{
BN_init (this);
setvch (&vch.front(), &vch.back()+1);
}
CBigNum::CBigNum (BIGNUM const* b)
{
BN_init (this);
if (!BN_copy (this, b))
{
BN_clear_free (this);
Throw<std::runtime_error> ("CBigNum::CBigNum(BIGNUM const* b) : BN_copy failed");
}
}
CBigNum::CBigNum (unsigned char const* begin, unsigned char const* end)
{
BN_init (this);
setvch (begin, end);
}
void CBigNum::setuint (unsigned int n)
{
setulong (static_cast<unsigned long> (n));
}
unsigned int CBigNum::getuint () const
{
return BN_get_word (this);
}
int CBigNum::getint () const
{
unsigned long n = BN_get_word (this);
if (!BN_is_negative (this))
return (n > INT_MAX ? INT_MAX : n);
else
return (n > INT_MAX ? INT_MIN : - (int)n);
}
void CBigNum::setint64 (std::int64_t n)
{
unsigned char pch[sizeof (n) + 6];
unsigned char* p = pch + 4;
bool fNegative = false;
if (n < (std::int64_t)0)
{
n = -n;
fNegative = true;
}
bool fLeadingZeroes = true;
for (int i = 0; i < 8; i++)
{
unsigned char c = (n >> 56) & 0xff;
n <<= 8;
if (fLeadingZeroes)
{
if (c == 0)
continue;
if (c & 0x80)
*p++ = (fNegative ? 0x80 : 0);
else if (fNegative)
c |= 0x80;
fLeadingZeroes = false;
}
*p++ = c;
}
unsigned int nSize = p - (pch + 4);
pch[0] = (nSize >> 24) & 0xff;
pch[1] = (nSize >> 16) & 0xff;
pch[2] = (nSize >> 8) & 0xff;
pch[3] = (nSize) & 0xff;
BN_mpi2bn (pch, p - pch, this);
}
std::uint64_t CBigNum::getuint64 () const
{
#if (ULONG_MAX > UINT_MAX)
return static_cast<std::uint64_t> (getulong ());
#else
int len = BN_num_bytes (this);
if (len > 8)
Throw<std::runtime_error> ("BN getuint64 overflow");
unsigned char buf[8];
memset (buf, 0, sizeof (buf));
BN_bn2bin (this, buf + 8 - len);
return
static_cast<std::uint64_t> (buf[0]) << 56 | static_cast<std::uint64_t> (buf[1]) << 48 |
static_cast<std::uint64_t> (buf[2]) << 40 | static_cast<std::uint64_t> (buf[3]) << 32 |
static_cast<std::uint64_t> (buf[4]) << 24 | static_cast<std::uint64_t> (buf[5]) << 16 |
static_cast<std::uint64_t> (buf[6]) << 8 | static_cast<std::uint64_t> (buf[7]);
#endif
}
void CBigNum::setuint64 (std::uint64_t n)
{
#if (ULONG_MAX > UINT_MAX)
setulong (static_cast<unsigned long> (n));
#else
unsigned char buf[8];
buf[0] = static_cast<unsigned char> ((n >> 56) & 0xff);
buf[1] = static_cast<unsigned char> ((n >> 48) & 0xff);
buf[2] = static_cast<unsigned char> ((n >> 40) & 0xff);
buf[3] = static_cast<unsigned char> ((n >> 32) & 0xff);
buf[4] = static_cast<unsigned char> ((n >> 24) & 0xff);
buf[5] = static_cast<unsigned char> ((n >> 16) & 0xff);
buf[6] = static_cast<unsigned char> ((n >> 8) & 0xff);
buf[7] = static_cast<unsigned char> ((n) & 0xff);
BN_bin2bn (buf, 8, this);
#endif
}
void CBigNum::setuint256 (uint256 const& n)
{
BN_bin2bn (n.begin (), n.size (), this);
}
uint256 CBigNum::getuint256 ()
{
uint256 ret;
unsigned int size = BN_num_bytes (this);
if (size > ret.size ())
return ret;
BN_bn2bin (this, ret.begin () + (ret.size () - BN_num_bytes (this)));
return ret;
}
void CBigNum::setvch (unsigned char const* begin, unsigned char const* end)
{
std::size_t const size (std::distance (begin, end));
Blob vch2 (size + 4);
unsigned int nSize (size);
// BIGNUM's byte stream format expects 4 bytes of
// big endian size data info at the front
vch2[0] = (nSize >> 24) & 0xff;
vch2[1] = (nSize >> 16) & 0xff;
vch2[2] = (nSize >> 8) & 0xff;
vch2[3] = (nSize >> 0) & 0xff;
// swap data to big endian
std::reverse_copy (begin, end, vch2.begin() + 4);
BN_mpi2bn (&vch2[0], vch2.size (), this);
}
void CBigNum::setvch (Blob const& vch)
{
setvch (&vch.front(), &vch.back()+1);
}
Blob CBigNum::getvch () const
{
unsigned int nSize = BN_bn2mpi (this, nullptr);
if (nSize < 4)
return Blob ();
Blob vch (nSize);
BN_bn2mpi (this, &vch[0]);
vch.erase (vch.begin (), vch.begin () + 4);
reverse (vch.begin (), vch.end ());
return vch;
}
CBigNum& CBigNum::SetCompact (unsigned int nCompact)
{
unsigned int nSize = nCompact >> 24;
Blob vch (4 + nSize);
vch[3] = nSize;
if (nSize >= 1) vch[4] = (nCompact >> 16) & 0xff;
if (nSize >= 2) vch[5] = (nCompact >> 8) & 0xff;
if (nSize >= 3) vch[6] = (nCompact >> 0) & 0xff;
BN_mpi2bn (&vch[0], vch.size (), this);
return *this;
}
unsigned int CBigNum::GetCompact () const
{
unsigned int nSize = BN_bn2mpi (this, nullptr);
Blob vch (nSize);
nSize -= 4;
BN_bn2mpi (this, &vch[0]);
unsigned int nCompact = nSize << 24;
if (nSize >= 1) nCompact |= (vch[4] << 16);
if (nSize >= 2) nCompact |= (vch[5] << 8);
if (nSize >= 3) nCompact |= (vch[6] << 0);
return nCompact;
}
void CBigNum::SetHex (std::string const& str)
{
// skip 0x
const char* psz = str.c_str ();
while (isspace (*psz))
psz++;
bool fNegative = false;
if (*psz == '-')
{
fNegative = true;
psz++;
}
if (psz[0] == '0' && tolower (psz[1]) == 'x')
psz += 2;
while (isspace (*psz))
psz++;
// hex string to bignum
static char phexdigit[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
*this = 0;
while (isxdigit (*psz))
{
*this <<= 4;
int n = phexdigit[ (int) * psz++];
*this += n;
}
if (fNegative)
*this = 0 - *this;
}
std::string CBigNum::ToString (int nBase) const
{
CAutoBN_CTX pctx;
CBigNum bnBase = nBase;
CBigNum bn0 = 0;
std::string str;
CBigNum bn = *this;
BN_set_negative (&bn, false);
CBigNum dv;
CBigNum rem;
if (BN_cmp (&bn, &bn0) == 0)
return "0";
while (BN_cmp (&bn, &bn0) > 0)
{
if (!BN_div (&dv, &rem, &bn, &bnBase, pctx))
Throw<std::runtime_error> ("CBigNum::ToString() : BN_div failed");
bn = dv;
unsigned int c = rem.getuint ();
str += "0123456789abcdef"[c];
}
if (BN_is_negative (this))
str += "-";
reverse (str.begin (), str.end ());
return str;
}
std::string CBigNum::GetHex () const
{
return ToString (16);
}
bool CBigNum::operator! () const
{
return BN_is_zero (this);
}
CBigNum& CBigNum::operator+= (const CBigNum& b)
{
if (!BN_add (this, this, &b))
Throw<std::runtime_error> ("CBigNum::operator+= : BN_add failed");
return *this;
}
CBigNum& CBigNum::operator-= (const CBigNum& b)
{
*this = *this - b;
return *this;
}
CBigNum& CBigNum::operator*= (const CBigNum& b)
{
CAutoBN_CTX pctx;
if (!BN_mul (this, this, &b, pctx))
Throw<std::runtime_error> ("CBigNum::operator*= : BN_mul failed");
return *this;
}
CBigNum& CBigNum::operator/= (const CBigNum& b)
{
*this = *this / b;
return *this;
}
CBigNum& CBigNum::operator%= (const CBigNum& b)
{
*this = *this % b;
return *this;
}
CBigNum& CBigNum::operator<<= (unsigned int shift)
{
if (!BN_lshift (this, this, shift))
Throw<std::runtime_error> ("CBigNum:operator<<= : BN_lshift failed");
return *this;
}
CBigNum& CBigNum::operator>>= (unsigned int shift)
{
// Note: BN_rshift segfaults on 64-bit if 2^shift is greater than the number
// if built on ubuntu 9.04 or 9.10, probably depends on version of openssl
CBigNum a = 1;
a <<= shift;
if (BN_cmp (&a, this) > 0)
{
*this = 0;
return *this;
}
if (! BN_rshift (this, this, shift))
Throw<std::runtime_error> ("CBigNum:operator>>= : BN_rshift failed");
return *this;
}
CBigNum& CBigNum::operator++ ()
{
// prefix operator
if (! BN_add (this, this, BN_value_one ()))
Throw<std::runtime_error> ("CBigNum::operator++ : BN_add failed");
return *this;
}
const CBigNum CBigNum::operator++ (int)
{
// postfix operator
const CBigNum ret = *this;
++ (*this);
return ret;
}
CBigNum& CBigNum::operator-- ()
{
// prefix operator
CBigNum r;
if (! BN_sub (&r, this, BN_value_one ()))
Throw<std::runtime_error> ("CBigNum::operator-- : BN_sub failed");
*this = r;
return *this;
}
const CBigNum CBigNum::operator-- (int)
{
// postfix operator
const CBigNum ret = *this;
-- (*this);
return ret;
}
void CBigNum::setulong (unsigned long n)
{
if (! BN_set_word (this, n))
Throw<std::runtime_error> (
"CBigNum conversion from unsigned long : BN_set_word failed");
}
unsigned long CBigNum::getulong () const
{
return BN_get_word (this);
}
const CBigNum operator+ (const CBigNum& a, const CBigNum& b)
{
CBigNum r;
if (! BN_add (&r, &a, &b))
Throw<std::runtime_error> ("CBigNum::operator+ : BN_add failed");
return r;
}
const CBigNum operator- (const CBigNum& a, const CBigNum& b)
{
CBigNum r;
if (! BN_sub (&r, &a, &b))
Throw<std::runtime_error> ("CBigNum::operator- : BN_sub failed");
return r;
}
const CBigNum operator- (const CBigNum& a)
{
CBigNum r (a);
BN_set_negative (&r, !BN_is_negative (&r));
return r;
}
const CBigNum operator* (const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (! BN_mul (&r, &a, &b, pctx))
Throw<std::runtime_error> ("CBigNum::operator* : BN_mul failed");
return r;
}
const CBigNum operator/ (const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (! BN_div (&r, nullptr, &a, &b, pctx))
Throw<std::runtime_error> ("CBigNum::operator/ : BN_div failed");
return r;
}
const CBigNum operator% (const CBigNum& a, const CBigNum& b)
{
CAutoBN_CTX pctx;
CBigNum r;
if (! BN_mod (&r, &a, &b, pctx))
Throw<std::runtime_error> ("CBigNum::operator% : BN_div failed");
return r;
}
const CBigNum operator<< (const CBigNum& a, unsigned int shift)
{
CBigNum r;
if (! BN_lshift (&r, &a, shift))
Throw<std::runtime_error> ("CBigNum:operator<< : BN_lshift failed");
return r;
}
const CBigNum operator>> (const CBigNum& a, unsigned int shift)
{
CBigNum r = a;
r >>= shift;
return r;
}
bool operator== (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) == 0);
}
bool operator!= (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) != 0);
}
bool operator<= (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) <= 0);
}
bool operator>= (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) >= 0);
}
bool operator< (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) < 0);
}
bool operator> (const CBigNum& a, const CBigNum& b)
{
return (BN_cmp (&a, &b) > 0);
}
#if (ULONG_MAX > UINT_MAX)
int BN_add_word64 (BIGNUM* bn, std::uint64_t word)
{
return BN_add_word (bn, word);
}
int BN_sub_word64 (BIGNUM* bn, std::uint64_t word)
{
return BN_sub_word (bn, word);
}
int BN_mul_word64 (BIGNUM* bn, std::uint64_t word)
{
return BN_mul_word (bn, word);
}
// This function returns 1 on success like the three preceding functions.
// In contrast, BN_div_word returns ( BN_ULONG )-1 on error.
int BN_div_word64 (BIGNUM* bn, std::uint64_t word)
{
BN_ULONG const remainder {BN_div_word (bn, word)};
return remainder == static_cast<BN_ULONG>(-1) ? 0 : 1;
}
#else
int BN_add_word64 (BIGNUM* a, std::uint64_t w)
{
CBigNum bn (w);
return BN_add (a, a, &bn);
}
int BN_sub_word64 (BIGNUM* a, std::uint64_t w)
{
CBigNum bn (w);
return BN_sub (a, a, &bn);
}
int BN_mul_word64 (BIGNUM* a, std::uint64_t w)
{
CBigNum bn (w);
CAutoBN_CTX ctx;
return BN_mul (a, a, &bn, ctx);
}
int BN_div_word64 (BIGNUM* a, std::uint64_t w)
{
CBigNum bn (w);
CBigNum temp (a); // Copy a. Destination may not be the same as dividend.
CAutoBN_CTX ctx;
return BN_div (a, nullptr, &temp, &bn, ctx);
}
#endif
}

View File

@@ -1,580 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 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.
*/
//==============================================================================
#include <BeastConfig.h>
#include <ripple/crypto/CBigNum.h>
#include <ripple/basics/base_uint.h>
#include <ripple/beast/unit_test.h>
namespace ripple {
class CBigNum_test : public beast::unit_test::suite
{
private:
// Generic test for constructing CBigNum from native types.
template<typename T>
void
testNativeCtor()
{
static_assert (std::numeric_limits<T>::is_integer, "Requires int type");
if (std::numeric_limits<T>::is_signed)
{
CBigNum const neg (static_cast<T>(std::numeric_limits<T>::min()));
this->expect (neg == std::numeric_limits<T>::min());
this->expect (neg < 0);
}
else
{
CBigNum const naught (static_cast<T>(0));
this->expect (naught == 0);
}
CBigNum const big (static_cast<T>(std::numeric_limits<T>::max()));
this->expect (big == std::numeric_limits<T>::max());
this->expect (big > 0);
}
// Return pointer to start of array.
template<typename T, std::size_t N>
static auto
array_cbegin(T (&a)[N]) -> T const*
{
return &a[0];
}
// Return pointer to one past end of array.
template<typename T, std::size_t N>
static auto
array_cend(T (&a)[N]) -> T const*
{
return &a[N];
}
public:
void
run ()
{
{
// Default constructor.
CBigNum const big0;
expect (big0 == 0);
// Construct from unsigned char.
CBigNum const big1 (static_cast<unsigned char>(1));
expect (big1 == 1);
// Assignment.
CBigNum bigA;
bigA = big1;
expect (bigA == big1);
}
{
// Test constructing from native types.
testNativeCtor<char>();
testNativeCtor<unsigned char>();
testNativeCtor<short>();
testNativeCtor<unsigned short>();
testNativeCtor<int>();
testNativeCtor<unsigned int>();
testNativeCtor<long>();
// testNativeCtor<unsigned long>(); // unsigned long not supported
testNativeCtor<long long>();
testNativeCtor<unsigned long long>();
}
{
// Construction from uint256.
uint256 const naught256 (0);
uint256 big256 = naught256;
--big256;
CBigNum naught (naught256);
expect (naught == 0);
CBigNum big (big256);
expect (big.GetHex() == "ffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
}
{
// Construction from Blob.
Blob const naughtBlob (67, 0);
Blob const bigBlob (40, 255);
CBigNum const naught (naughtBlob);
expect (naught == 0);
CBigNum big (bigBlob);
expect (big.GetHex() == "-7fffffffffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
}
{
// Construction from BIGNUM*.
BIGNUM b;
BN_init (&b);
// Scope guard to free b when we leave scope.
std::shared_ptr<void> guard (nullptr, [&b] (void*)
{
BN_clear_free (&b);
});
expect (BN_set_word (&b, 0x123456789ABCDF0ull) == 1);
CBigNum big (&b);
--big;
expect (BN_cmp (&big, &b) == -1);
++big;
expect (BN_cmp (&big, &b) == 0);
++big;
expect (BN_cmp (&big, &b) == 1);
}
{
// Construction from unsigned char*.
static unsigned char const a[] {0x0,0xF,
0xE,0xD,0xC,0xB,0xA,0x9,0x8,0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0};
CBigNum big (array_cbegin(a), array_cend(a));
expect (big.GetHex() == "102030405060708090a0b0c0d0e0f00");
}
{
// setuint() and getuint().
CBigNum big;
expect (big.getuint() == 0);
--big;
// Note that getuint() does not throw an exception on underflow.
expect (big.getuint() == 1);
unsigned int const biggest_uint =
std::numeric_limits<unsigned int>::max();
big.setuint (biggest_uint);
expect (big.getuint() == biggest_uint);
// Note that getuint() does not throw an exception on overflow.
++big;
expect (big.getuint() == 0);
}
{
// getint().
CBigNum neg (std::numeric_limits<int>::min());
expect (neg.getint() == std::numeric_limits<int>::min());
// Note that getint() limits at INT_MIN on negative overflow.
--neg;
expect (neg.getint() == std::numeric_limits<int>::min());
CBigNum pos (std::numeric_limits<int>::max());
expect (pos.getint() == std::numeric_limits<int>::max());
// Note that getint() limits at INT_MAX on positive overflow.
++pos;
expect (pos.getint() == std::numeric_limits<int>::max());
}
{
// setint64().
CBigNum big;
big.setint64 (std::numeric_limits<std::int64_t>::min());
expect (big.GetHex() == "-8000000000000000");
--big;
expect (big.GetHex() == "-8000000000000001");
big.setint64 (std::numeric_limits<std::int64_t>::max());
expect (big.GetHex() == "7fffffffffffffff");
++big;
expect (big.GetHex() == "8000000000000000");
}
{
// setuint64() and getuint64().
CBigNum big;
big.setuint64 (static_cast<std::uint64_t>(0));
expect (big.getuint64() == 0);
// Note that getuint64() drops the sign.
--big;
expect (big.getuint64() == 1);
big.setuint64 (std::numeric_limits<std::uint64_t>::max());
expect (
big.getuint64() == std::numeric_limits<std::uint64_t>::max());
// Note that one some platforms getuint() throws on positive
// overflow and on other platforms it does not. Hence the macro.
++big;
#if (ULONG_MAX > UINT_MAX)
expect (
big.getuint64() == std::numeric_limits<std::uint64_t>::max());
#else
bool overflowException = false;
try
{
big.getuint64();
}
catch (std::runtime_error const&)
{
overflowException = true;
}
expect (overflowException);
#endif
}
{
// setuint256() and getuint256().
uint256 const naught256 (Blob (32, 0));
uint256 const max256 (Blob (32, 0xFF));
CBigNum big;
big.setuint256(max256);
expect (big.GetHex() == "ffffffffffffffff"
"ffffffffffffffffffffffffffffffffffffffffffffffff");
expect (big.getuint256() == max256);
// Note that getuint256() returns zero on overflow.
++big;
expect (big.GetHex() == "1000000000000000"
"0000000000000000000000000000000000000000000000000");
expect (big.getuint256() == naught256);
--big;
expect (big.getuint256() == max256);
big.setuint256(naught256);
expect (big == 0);
expect (big.getuint256() == naught256);
--big;
expect (big == -1);
++big;
expect (big.getuint256() == naught256);
}
{
// setvch() and getvch().
CBigNum big;
expect (big.getvch().size() == 0);
// Small values.
static unsigned char const one[] {1,0};
big.setvch(array_cbegin(one), array_cend(one));
expect (big == 1);
--big;
expect (big.getvch().size() == 0);
--big;
Blob smallBlob = big.getvch();
expect (smallBlob.size() == 1);
expect (smallBlob[0] ==0x81);
smallBlob[0] = 0xff;
expect (big == -1);
big.setvch(smallBlob);
expect (big == -127);
expect (big.getvch().size() == 1);
// Big values
static unsigned char const large[80] {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
};
static unsigned char const larger[81] {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00
};
big.setvch (array_cbegin(large), array_cend(large));
CBigNum bigger;
bigger.setvch(array_cbegin(larger), array_cend(larger));
expect (big > 0);
expect (big < bigger);
Blob bigBlob = big.getvch();
expect (bigBlob.size() == 80);
expect (bigBlob.back() == 0x7f);
++big;
expect (big == bigger);
bigBlob = big.getvch();
expect (bigBlob.size() == 81);
expect (std::equal(array_cbegin(larger), array_cend(larger),
bigBlob.begin(), bigBlob.end()));
bigBlob[0] = 1;
bigger.setvch(bigBlob);
expect (big < bigger);
++big;
expect (big == bigger);
}
{
// GetCompact() and SetCompact().
CBigNum big (0);
expect (big.GetCompact() == 0);
big.SetCompact (0x1010000);
expect (big == 1);
big.SetCompact (0x1810000);
expect (big == -1);
// Positive values.
big.SetCompact (0x2010000);
expect (big.GetCompact() == 0x2010000);
++big;
expect (big.GetCompact() == 0x2010100);
big.SetCompact (0x3010000);
expect (big.GetCompact() == 0x3010000);
++big;
expect (big.GetCompact() == 0x3010001);
{
big.SetCompact (0x4010000);
expect (big.getuint64() == 0x1000000);
unsigned int const compact = big.GetCompact();
++big;
expect (compact == big.GetCompact());
}
big.SetCompact (0xFF7FFFFF);
--big;
expect (big.GetCompact() == 0xFF7FFFFE);
expect (big.GetHex() ==
"7ffffefffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffff");
// Negative values.
big.SetCompact (0x2810000);
expect (big.GetCompact() == 0x2810000);
--big;
expect (big.GetCompact() == 0x2810100);
big.SetCompact (0x3810000);
expect (big.GetCompact() == 0x3810000);
--big;
expect (big.GetCompact() == 0x3810001);
{
big.SetCompact (0x4810000);
expect (big.getint() == -16777216);
unsigned int const compact = big.GetCompact();
--big;
expect (compact == big.GetCompact());
}
big.SetCompact (0xFFFFFFFF);
++big;
expect (big.GetCompact() == 0xFFFFFFFE);
expect (big.GetHex() ==
"-7ffffefffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffffffffffffffffffffff"
"fffffffffffffff");
}
{
// SetHex() and GetHex().
CBigNum big (1);
expect (big != 0);
big.SetHex (" ");
expect (big == 0);
big.SetHex (" -0x 1");
expect (big == -1);
big.SetHex (" -0");
expect (big.GetHex() == "0");
big.SetHex ("Feeble");
expect (big.GetHex () == "feeb");
}
{
// ToString().
CBigNum big;
expect (big.ToString (0) == "0");
// Trying to use base 0 throws an exception.
++big;
{
bool badDivisor = false;
try
{
big.ToString (0);
}
catch (std::runtime_error const&)
{
badDivisor = true;
}
expect (badDivisor);
}
++big;
expect (big.ToString (2) == "10");
expect (big.ToString (10) == "2");
}
{
// Member math operators.
CBigNum big;
expect (!big);
--big;
expect (! (!big));
big += 2;
expect (big == 1);
big -= 3;
expect (big == -2);
big *= -1;
expect (big == 2);
big /= 2;
expect (big == 1);
big = 8;
big %= 5;
expect (big == 3);
++big;
expect (big == 4);
--big;
expect (big == 3);
{
CBigNum const preIncr = big++;
expect (preIncr == 3);
expect (big == 4);
}
{
CBigNum const preDecr = big--;
expect (preDecr == 4);
expect (big == 3);
}
big.setuint64 (0x80);
big >>= 1;
expect (big == 0x40);
big <<= 2;
expect (big == 0x100);
big >>= 9;
expect (big == 0);
}
{
// Non-member math operators.
CBigNum a (5);
CBigNum b (3);
CBigNum c = a + b;
expect (c == 8);
c = c * a;
expect (c == 40);
c = c - b;
expect (c == 37);
a = c / b;
expect (a == 12);
a = c % b;
expect (a == 1);
a = -c;
expect (a == -37);
b = a << 1;
expect (b == -74);
a = c >> 2;
expect (a == 9);
// All right shifts of negative numbers yield zero.
a = (b >> 1);
expect (a == 0);
}
{
// Test non-member comparison operators.
auto comparison_test = [this] (int center)
{
CBigNum delta (1);
CBigNum lo (center);
lo -= delta;
CBigNum const ref (center);
CBigNum const mid (center);
CBigNum hi (center);
hi += delta;
this->expect ( (lo < ref));
this->expect (!(mid < ref));
this->expect (!(hi < ref));
this->expect ( (lo <= ref));
this->expect ( (mid <= ref));
this->expect (!(hi <= ref));
this->expect (!(lo > ref));
this->expect (!(mid > ref));
this->expect ( (hi > ref));
this->expect (!(lo >= ref));
this->expect ( (mid >= ref));
this->expect ( (hi >= ref));
this->expect (!(lo == ref));
this->expect ( (mid == ref));
this->expect (!(hi == ref));
this->expect ( (lo != ref));
this->expect (!(mid != ref));
this->expect ( (hi != ref));
};
comparison_test (537);
comparison_test (0);
comparison_test (-2058);
}
{
// BN math functions defined in CBigNum.h.
CBigNum a;
expect (BN_is_zero (&a));
expect (BN_add_word64 (&a, 0xF000000000000000ull) == 1);
expect (BN_add_word64 (&a, 0x0FFFFFFFFFFFFFFFull) == 1);
CBigNum b;
expect (BN_set_word (&b, 0xFFFFFFFFFFFFFFFFull) == 1);
expect (BN_cmp (&a, &b) == 0);
expect (BN_sub_word64 (&a, 0xFF00000000000000ull) == 1);
expect (BN_set_word (&b, 0x00FFFFFFFFFFFFFFull) == 1);
expect (BN_cmp (&a, &b) == 0);
expect (BN_mul_word64 (&a, 0x10) == 1);
expect (BN_set_word (&b, 0x0FFFFFFFFFFFFFF0ull) == 1);
expect (BN_cmp (&a, &b) == 0);
expect (BN_div_word64 (&a, 0x10) == 1);
expect (BN_set_word (&b, 0x00FFFFFFFFFFFFFFull) == 1);
expect (BN_cmp (&a, &b) == 0);
expect (BN_div_word64 (&a, 0x200) == 1);
expect (BN_set_word (&b, 0x00007FFFFFFFFFFFull) == 1);
expect (BN_cmp (&a, &b) == 0);
a *= -1;
expect (a < 0);
expect (BN_div_word64 (&a, 0x400) == 1);
expect (BN_set_word (&b, 0x0000001FFFFFFFFFull) == 1);
b *= -1;
expect (a == b);
// Divide by 0 should return an error.
expect (BN_div_word64 (&a, 0) != 1);
}
}
};
BEAST_DEFINE_TESTSUITE(CBigNum,ripple_data,ripple);
} // ripple

View File

@@ -171,8 +171,6 @@ public:
STAmount
zeroed() const
{
// TODO(tom): what does this next comment mean here?
// See https://ripplelabs.atlassian.net/browse/WC-1847?jql=
return STAmount (mIssue);
}

View File

@@ -22,13 +22,13 @@
#include <ripple/basics/contract.h>
#include <ripple/basics/Log.h>
#include <ripple/protocol/JsonFields.h>
#include <ripple/crypto/CBigNum.h>
#include <ripple/protocol/SystemParameters.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/protocol/UintTypes.h>
#include <ripple/beast/core/LexicalCast.h>
#include <boost/regex.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <iterator>
#include <memory>
#include <iostream>
@@ -980,6 +980,58 @@ operator- (STAmount const& value)
// Arithmetic
//
//------------------------------------------------------------------------------
// Calculate (a * b) / c when all three values are 64-bit
// without loss of precision:
static
std::uint64_t
muldiv(
std::uint64_t multiplier,
std::uint64_t multiplicand,
std::uint64_t divisor)
{
boost::multiprecision::uint128_t ret;
boost::multiprecision::multiply(ret, multiplier, multiplicand);
ret /= divisor;
if (ret > std::numeric_limits<std::uint64_t>::max())
{
Throw<std::overflow_error> ("overflow: (" +
std::to_string (multiplier) + " * " +
std::to_string (multiplicand) + ") / " +
std::to_string (divisor));
}
return static_cast<uint64_t>(ret);
}
static
std::uint64_t
muldiv_round(
std::uint64_t multiplier,
std::uint64_t multiplicand,
std::uint64_t divisor,
std::uint64_t rounding)
{
boost::multiprecision::uint128_t ret;
boost::multiprecision::multiply(ret, multiplier, multiplicand);
ret += rounding;
ret /= divisor;
if (ret > std::numeric_limits<std::uint64_t>::max())
{
Throw<std::overflow_error> ("overflow: ((" +
std::to_string (multiplier) + " * " +
std::to_string (multiplicand) + ") + " +
std::to_string (rounding) + ") / " +
std::to_string (divisor));
}
return static_cast<uint64_t>(ret);
}
STAmount
divide (STAmount const& num, STAmount const& den, Issue const& issue)
{
@@ -1013,21 +1065,13 @@ divide (STAmount const& num, STAmount const& den, Issue const& issue)
}
}
// Compute (numerator * 10^17) / denominator
CBigNum v;
if ((BN_add_word64 (&v, numVal) != 1) ||
(BN_mul_word64 (&v, tenTo17) != 1) ||
(BN_div_word64 (&v, denVal) != 1))
{
Throw<std::runtime_error> ("internal bn error");
}
// 10^16 <= quotient <= 10^18
assert (BN_num_bytes (&v) <= 64);
// TODO(tom): where do 5 and 17 come from?
return STAmount (issue, v.getuint64 () + 5,
// We divide the two mantissas (each is between 10^15
// and 10^16). To maintain precision, we multiply the
// numerator by 10^17 (the product is in the range of
// 10^32 to 10^33) followed by a division, so the result
// is in the range of 10^16 to 10^15.
return STAmount (issue,
muldiv(numVal, tenTo17, denVal) + 5,
numOffset - denOffset - 17,
num.negative() != den.negative());
}
@@ -1077,32 +1121,20 @@ multiply (STAmount const& v1, STAmount const& v2, Issue const& issue)
}
}
// Compute (numerator * denominator) / 10^14 with rounding
// 10^16 <= result <= 10^18
CBigNum v;
if ((BN_add_word64 (&v, value1) != 1) ||
(BN_mul_word64 (&v, value2) != 1) ||
(BN_div_word64 (&v, tenTo14) != 1))
{
Throw<std::runtime_error> ("internal bn error");
}
// 10^16 <= product <= 10^18
assert (BN_num_bytes (&v) <= 64);
// TODO(tom): where do 7 and 14 come from?
return STAmount (issue, v.getuint64 () + 7,
offset1 + offset2 + 14, v1.negative() != v2.negative());
// We multiply the two mantissas (each is between 10^15
// and 10^16), so their product is in the 10^30 to 10^32
// range. Dividing their product by 10^14 maintains the
// precision, by scaling the result to 10^16 to 10^18.
return STAmount (issue,
muldiv(value1, value2, tenTo14) + 7,
offset1 + offset2 + 14,
v1.negative() != v2.negative());
}
static
void
canonicalizeRound (bool native, std::uint64_t& value, int& offset, bool roundUp)
canonicalizeRound (bool native, std::uint64_t& value, int& offset)
{
if (!roundUp) // canonicalize already rounds down
return;
if (native)
{
if (offset < 0)
@@ -1142,7 +1174,9 @@ mulRound (STAmount const& v1, STAmount const& v2, Issue const& issue,
if (v1 == zero || v2 == zero)
return {issue};
if (v1.native() && v2.native() && isXRP (issue))
bool const xrp = isXRP (issue);
if (v1.native() && v2.native() && xrp)
{
std::uint64_t minV = (getSNValue (v1) < getSNValue (v2)) ?
getSNValue (v1) : getSNValue (v2);
@@ -1179,32 +1213,29 @@ mulRound (STAmount const& v1, STAmount const& v2, Issue const& issue,
}
}
bool resultNegative = v1.negative() != v2.negative();
// Compute (numerator * denominator) / 10^14 with rounding
// 10^16 <= result <= 10^18
CBigNum v;
bool const resultNegative = v1.negative() != v2.negative();
if ((BN_add_word64 (&v, value1) != 1) || (BN_mul_word64 (&v, value2) != 1))
Throw<std::runtime_error> ("internal bn error");
// We multiply the two mantissas (each is between 10^15
// and 10^16), so their product is in the 10^30 to 10^32
// range. Dividing their product by 10^14 maintains the
// precision, by scaling the result to 10^16 to 10^18.
//
// If the we're rounding up, we want to round up away
// from zero, and if we're rounding down, truncation
// is implicit.
std::uint64_t amount = muldiv_round (
value1, value2, tenTo14,
(resultNegative != roundUp) ? tenTo14m1 : 0);
if (resultNegative != roundUp) // rounding down is automatic when we divide
BN_add_word64 (&v, tenTo14m1);
if (BN_div_word64 (&v, tenTo14) != 1)
Throw<std::runtime_error> ("internal bn error");
// 10^16 <= product <= 10^18
assert (BN_num_bytes (&v) <= 64);
std::uint64_t amount = v.getuint64 ();
int offset = offset1 + offset2 + 14;
canonicalizeRound (
isXRP (issue), amount, offset, resultNegative != roundUp);
if (resultNegative != roundUp)
canonicalizeRound (xrp, amount, offset);
STAmount result (issue, amount, offset, resultNegative);
// Control when bugfixes that require switchover dates are enabled
if (roundUp && !resultNegative && !result && *stAmountCalcSwitchover)
{
if (isXRP(issue) && *stAmountCalcSwitchover2)
if (xrp && *stAmountCalcSwitchover2)
{
// return the smallest value above zero
amount = 1;
@@ -1235,40 +1266,43 @@ divRound (STAmount const& num, STAmount const& den,
int numOffset = num.exponent(), denOffset = den.exponent();
if (num.native())
{
while (numVal < STAmount::cMinValue)
{
// Need to bring into range
numVal *= 10;
--numOffset;
}
}
if (den.native())
{
while (denVal < STAmount::cMinValue)
{
denVal *= 10;
--denOffset;
}
}
bool resultNegative = num.negative() != den.negative();
// Compute (numerator * 10^17) / denominator
CBigNum v;
bool const resultNegative =
(num.negative() != den.negative());
if ((BN_add_word64 (&v, numVal) != 1) || (BN_mul_word64 (&v, tenTo17) != 1))
Throw<std::runtime_error> ("internal bn error");
// We divide the two mantissas (each is between 10^15
// and 10^16). To maintain precision, we multiply the
// numerator by 10^17 (the product is in the range of
// 10^32 to 10^33) followed by a division, so the result
// is in the range of 10^16 to 10^15.
//
// We round away from zero if we're rounding up or
// truncate if we're rounding down.
std::uint64_t amount = muldiv_round (
numVal, tenTo17, denVal,
(resultNegative != roundUp) ? denVal - 1 : 0);
if (resultNegative != roundUp) // Rounding down is automatic when we divide
BN_add_word64 (&v, denVal - 1);
if (BN_div_word64 (&v, denVal) != 1)
Throw<std::runtime_error> ("internal bn error");
// 10^16 <= quotient <= 10^18
assert (BN_num_bytes (&v) <= 64);
std::uint64_t amount = v.getuint64 ();
int offset = numOffset - denOffset - 17;
canonicalizeRound (
isXRP (issue), amount, offset, resultNegative != roundUp);
if (resultNegative != roundUp)
canonicalizeRound (isXRP (issue), amount, offset);
STAmount result (issue, amount, offset, resultNegative);
// Control when bugfixes that require switchover dates are enabled
if (roundUp && !resultNegative && !result && *stAmountCalcSwitchover)

View File

@@ -20,7 +20,6 @@
#include <BeastConfig.h>
#include <ripple/basics/Log.h>
#include <ripple/basics/random.h>
#include <ripple/crypto/CBigNum.h>
#include <ripple/protocol/STAmount.h>
#include <ripple/beast/unit_test.h>
@@ -404,24 +403,6 @@ public:
{
testcase ("arithmetic");
CBigNum b;
for (int i = 0; i < 16; ++i)
{
std::uint64_t r = rand_int<std::uint64_t>();
b.setuint64 (r);
if (b.getuint64 () != r)
{
log << r << " != " << b.getuint64 () << " " << b.ToString (16);
fail ("setull64/getull64 failure");
}
else
{
pass ();
}
}
// Test currency multiplication and division operations such as
// convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed, and getNeeded

View File

@@ -19,7 +19,6 @@
#include <BeastConfig.h>
#include <ripple/crypto/impl/CBigNum.cpp>
#include <ripple/crypto/impl/ec_key.cpp>
#include <ripple/crypto/impl/ECDSAKey.cpp>
#include <ripple/crypto/impl/ECIES.cpp>
@@ -29,7 +28,6 @@
#include <ripple/crypto/impl/csprng.cpp>
#include <ripple/crypto/impl/RFC1751.cpp>
#include <ripple/crypto/tests/CBigNum_test.cpp>
#include <ripple/crypto/tests/CKey.test.cpp>
#if DOXYGEN