From 271bf901eca717dae3a19be82db93f9b7e391043 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Tue, 15 Jan 2013 23:47:42 -0800 Subject: [PATCH] Support for compiling on 32-bit platforms. The main issue was BN_ULONG size and not using 'long' where 'long long' or 'uint64' should be used. --- src/cpp/ripple/Amount.cpp | 98 ++++++++++++++++++----------- src/cpp/ripple/BigNum64.h | 22 +++++++ src/cpp/ripple/SerializedObject.cpp | 2 +- src/cpp/ripple/base58.h | 4 +- src/cpp/ripple/bignum.h | 90 ++++++++++++++++---------- 5 files changed, 142 insertions(+), 74 deletions(-) create mode 100644 src/cpp/ripple/BigNum64.h diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 8a3038fc2a..862613719d 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -15,8 +16,16 @@ SETUP_LOG(); uint64 STAmount::uRateOne = STAmount::getRate(STAmount(1), STAmount(1)); -static const uint64_t tenTo14 = 100000000000000ul; -static const uint64_t tenTo17 = 100000000000000000ul; +static const uint64 tenTo14 = 100000000000000ull; +static const uint64 tenTo17 = tenTo14 * 1000; + +#if (ULONG_MAX > UINT_MAX) +#define BN_add_word64(bn, word) BN_add_word(bn, word) +#define BN_mul_word64(bn, word) BN_mul_word(bn, word) +#define BN_div_word64(bn, word) BN_div_word(bn, word) +#else +#include "BigNum64.h" +#endif bool STAmount::issuerFromString(uint160& uDstIssuer, const std::string& sIssuer) { @@ -901,9 +910,9 @@ STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint16 // Compute (numerator * 10^17) / denominator CBigNum v; - if ((BN_add_word(&v, numVal) != 1) || - (BN_mul_word(&v, tenTo17) != 1) || - (BN_div_word(&v, denVal) == ((BN_ULONG) -1))) + if ((BN_add_word64(&v, numVal) != 1) || + (BN_mul_word64(&v, tenTo17) != 1) || + (BN_div_word64(&v, denVal) == ((uint64) -1))) { throw std::runtime_error("internal bn error"); } @@ -911,7 +920,7 @@ STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint16 // 10^16 <= quotient <= 10^18 assert(BN_num_bytes(&v) <= 64); - return STAmount(uCurrencyID, uIssuerID, v.getulong() + 5, + return STAmount(uCurrencyID, uIssuerID, v.getuint64() + 5, numOffset - denOffset - 17, num.mIsNegative != den.mIsNegative); } @@ -924,9 +933,9 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16 { uint64 minV = (v1.getSNValue() < v2.getSNValue()) ? v1.getSNValue() : v2.getSNValue(); uint64 maxV = (v1.getSNValue() < v2.getSNValue()) ? v2.getSNValue() : v1.getSNValue(); - if (minV > 3000000000) // sqrt(cMaxNative) + if (minV > 3000000000ull) // sqrt(cMaxNative) throw std::runtime_error("Native value overflow"); - if (((maxV >> 32) * minV) > 2095475792) // cMaxNative / 2^32 + if (((maxV >> 32) * minV) > 2095475792ull) // cMaxNative / 2^32 throw std::runtime_error("Native value overflow"); return STAmount(v1.getFName(), minV * maxV); } @@ -955,9 +964,9 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16 // Compute (numerator * denominator) / 10^14 with rounding // 10^16 <= result <= 10^18 CBigNum v; - if ((BN_add_word(&v, value1) != 1) || - (BN_mul_word(&v, value2) != 1) || - (BN_div_word(&v, tenTo14) == ((BN_ULONG) -1))) + if ((BN_add_word64(&v, value1) != 1) || + (BN_mul_word64(&v, value2) != 1) || + (BN_div_word64(&v, tenTo14) == ((uint64) -1))) { throw std::runtime_error("internal bn error"); } @@ -965,7 +974,7 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16 // 10^16 <= product <= 10^18 assert(BN_num_bytes(&v) <= 64); - return STAmount(uCurrencyID, uIssuerID, v.getulong() + 7, offset1 + offset2 + 14, + return STAmount(uCurrencyID, uIssuerID, v.getuint64() + 7, offset1 + offset2 + 14, v1.mIsNegative != v2.mIsNegative); } @@ -1119,13 +1128,13 @@ uint64 STAmount::muldiv(uint64 a, uint64 b, uint64 c) if ((a == 0) || (b == 0)) return 0; CBigNum v; - if ((BN_add_word(&v, a * 10 + 5) != 1) || - (BN_mul_word(&v, b * 10 + 5) != 1) || - (BN_div_word(&v, c) == ((BN_ULONG) -1)) || - (BN_div_word(&v, 100) == ((BN_ULONG) -1))) + if ((BN_add_word64(&v, a * 10 + 5) != 1) || + (BN_mul_word64(&v, b * 10 + 5) != 1) || + (BN_div_word64(&v, c) == ((uint64) -1)) || + (BN_div_word64(&v, 100) == ((uint64) -1))) throw std::runtime_error("muldiv error"); - return v.getulong(); + return v.getuint64(); } uint64 STAmount::convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit) @@ -1239,7 +1248,6 @@ BOOST_AUTO_TEST_CASE( NativeCurrency_test ) { STAmount zero, one(1), hundred(100); - if (sizeof(BN_ULONG) < (64 / 8)) BOOST_FAIL("BN too small"); if (serdes(zero) != zero) BOOST_FAIL("STAmount fail"); if (serdes(one) != one) BOOST_FAIL("STAmount fail"); if (serdes(hundred) != hundred) BOOST_FAIL("STAmount fail"); @@ -1385,13 +1393,13 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test ) if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31,-2).getText() != "0.31") BOOST_FAIL("STAmount fail"); if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") - BOOST_FAIL("STAmount multiply fail"); + BOOST_FAIL("STAmount multiply fail 1"); if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60") - BOOST_FAIL("STAmount multiply fail"); + BOOST_FAIL("STAmount multiply fail 2"); if (STAmount::multiply(STAmount(20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") - BOOST_FAIL("STAmount multiply fail"); + BOOST_FAIL("STAmount multiply fail 3"); if (STAmount::multiply(STAmount(20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60") - BOOST_FAIL("STAmount multiply fail"); + BOOST_FAIL("STAmount multiply fail 4"); if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20") { cLog(lsFATAL) << "60/3 = " << @@ -1466,25 +1474,39 @@ static void mulTest(int a, int b) BOOST_AUTO_TEST_CASE( CurrencyMulDivTests ) { + CBigNum b; + for (int i = 0; i < 16; ++i) + { + uint64 r = rand(); + r <<= 32; + r |= rand(); + b.setuint64(r); + if (b.getuint64() != r) + { + cLog(lsFATAL) << r << " != " << b.getuint64() << " " << b.ToString(16); + BOOST_FAIL("setull64/getull64 failure"); + } + } + // Test currency multiplication and division operations such as // convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed, and getNeeded - if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) - BOOST_FAIL("STAmount getRate fail"); + if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ull-14)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 1"); + if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ull-16)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 2"); + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ull-14)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 3"); + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ull-16)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 4"); + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(10)) != (((100ull-14)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 5"); + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(1)) != (((100ull-16)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 6"); + if (STAmount::getRate(STAmount(1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ull-14)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 7"); + if (STAmount::getRate(STAmount(10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ull-16)<<(64-8))|1000000000000000ull)) + BOOST_FAIL("STAmount getRate fail 8"); roundTest(1, 3, 3); roundTest(2, 3, 9); roundTest(1, 7, 21); roundTest(1, 2, 4); roundTest(3, 9, 18); roundTest(7, 11, 44); diff --git a/src/cpp/ripple/BigNum64.h b/src/cpp/ripple/BigNum64.h new file mode 100644 index 0000000000..9150a25cad --- /dev/null +++ b/src/cpp/ripple/BigNum64.h @@ -0,0 +1,22 @@ + +// Support 64-bit word operations on 32-bit platforms + +static int BN_add_word64(BIGNUM *a, uint64 w) +{ + CBigNum bn(w); + return BN_add(a, &bn, a); +} + +static int BN_mul_word64(BIGNUM *a, uint64 w) +{ + CBigNum bn(w); + CAutoBN_CTX ctx; + return BN_mul(a, &bn, a, ctx); +} + +static uint64 BN_div_word64(BIGNUM *a, uint64 w) +{ + CBigNum bn(w); + CAutoBN_CTX ctx; + return (BN_div(a, NULL, a, &bn, ctx) == 1) ? 0 : ((uint64)-1); +} diff --git a/src/cpp/ripple/SerializedObject.cpp b/src/cpp/ripple/SerializedObject.cpp index bc20aaf1a7..fc5e710c26 100644 --- a/src/cpp/ripple/SerializedObject.cpp +++ b/src/cpp/ripple/SerializedObject.cpp @@ -1039,7 +1039,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r if (value.isString()) data.push_back(new STUInt32(field, lexical_cast_st(value.asString()))); else if (value.isInt()) - data.push_back(new STUInt32(field, range_check_cast(value.asInt(), 0, 4294967295))); + data.push_back(new STUInt32(field, range_check_cast(value.asInt(), 0, 4294967295u))); else if (value.isUInt()) data.push_back(new STUInt32(field, static_cast(value.asUInt()))); else diff --git a/src/cpp/ripple/base58.h b/src/cpp/ripple/base58.h index 733216faa1..e2fa1c3673 100644 --- a/src/cpp/ripple/base58.h +++ b/src/cpp/ripple/base58.h @@ -51,7 +51,7 @@ inline std::string EncodeBase58(const unsigned char* pbegin, const unsigned char if (!BN_div(&dv, &rem, &bn, &bn58, pctx)) throw bignum_error("EncodeBase58 : BN_div failed"); bn = dv; - unsigned int c = rem.getulong(); + unsigned int c = rem.getuint(); str += ALPHABET[c]; } @@ -91,7 +91,7 @@ inline bool DecodeBase58(const char* psz, std::vector& vchRet) return false; break; } - bnChar.setulong(p1 - ALPHABET); + bnChar.setuint(p1 - ALPHABET); if (!BN_mul(&bn, &bn, &bn58, pctx)) throw bignum_error("DecodeBase58 : BN_mul failed"); bn += bnChar; diff --git a/src/cpp/ripple/bignum.h b/src/cpp/ripple/bignum.h index a9a08eda2d..87e1d892c5 100644 --- a/src/cpp/ripple/bignum.h +++ b/src/cpp/ripple/bignum.h @@ -89,7 +89,6 @@ public: CBigNum(unsigned char n) { BN_init(this); setulong(n); } CBigNum(unsigned short n) { BN_init(this); setulong(n); } CBigNum(unsigned int n) { BN_init(this); setulong(n); } - CBigNum(unsigned long n) { BN_init(this); setulong(n); } CBigNum(uint64 n) { BN_init(this); setuint64(n); } explicit CBigNum(uint256 n) { BN_init(this); setuint256(n); } @@ -99,15 +98,9 @@ public: setvch(vch); } - void setulong(unsigned long n) + void setuint(unsigned int n) { - if (!BN_set_word(this, n)) - throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); - } - - unsigned long getulong() const - { - return BN_get_word(this); + setulong(static_cast(n)); } unsigned int getuint() const @@ -159,31 +152,46 @@ public: BN_mpi2bn(pch, p - pch, this); } + uint64 getuint64() const + { +#if (ULONG_MAX > UINT_MAX) + return static_cast(getulong()); +#else + int len = BN_num_bytes(this); + if (len > 9) + throw std::runtime_error("BN getuint64 overflow"); + + unsigned char buf[9]; + memset(buf, 0, sizeof(buf)); + BN_bn2bin(this, buf + 9 - len); + if (buf[0] != 0) + throw std::runtime_error("BN getuint64 overflow"); + + return + static_cast(buf[1]) << 56 | static_cast(buf[2]) << 48 | + static_cast(buf[3]) << 40 | static_cast(buf[4]) << 32 | + static_cast(buf[5]) << 24 | static_cast(buf[6]) << 16 | + static_cast(buf[7]) << 8 | static_cast(buf[8]); +#endif + } + void setuint64(uint64 n) { - unsigned char pch[sizeof(n) + 6]; - unsigned char* p = pch + 4; - 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++ = 0; - 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); +#if (ULONG_MAX > UINT_MAX) + setulong(static_cast(n)); +#else + unsigned char buf[9]; + buf[0] = 0; + buf[1] = static_cast((n >> 56) & 0xff); + buf[2] = static_cast((n >> 48) & 0xff); + buf[3] = static_cast((n >> 40) & 0xff); + buf[4] = static_cast((n >> 32) & 0xff); + buf[5] = static_cast((n >> 24) & 0xff); + buf[6] = static_cast((n >> 16) & 0xff); + buf[7] = static_cast((n >> 8) & 0xff); + buf[8] = static_cast((n) & 0xff); + BN_bin2bn(buf, 9, this); +#endif } void setuint256(const uint256& n) @@ -300,7 +308,7 @@ public: if (!BN_div(&dv, &rem, &bn, &bnBase, pctx)) throw bignum_error("CBigNum::ToString() : BN_div failed"); bn = dv; - unsigned int c = rem.getulong(); + unsigned int c = rem.getuint(); str += "0123456789abcdef"[c]; } if (BN_is_negative(this)) @@ -435,6 +443,22 @@ public: friend inline const CBigNum operator-(const CBigNum& a, const CBigNum& b); friend inline const CBigNum operator/(const CBigNum& a, const CBigNum& b); friend inline const CBigNum operator%(const CBigNum& a, const CBigNum& b); + + private: + + // private because the size of an unsigned long varies by platform + + void setulong(unsigned long n) + { + if (!BN_set_word(this, n)) + throw bignum_error("CBigNum conversion from unsigned long : BN_set_word failed"); + } + + unsigned long getulong() const + { + return BN_get_word(this); + } + };