diff --git a/src/Amount.cpp b/src/Amount.cpp index def807b71..7a00b6c40 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "SerializedTypes.h" @@ -13,69 +14,124 @@ void STAmount::canonicalize() { - if (value == 0) + if (!mCurrency) { - offset = -100; + mIsNative = true; + + if (mValue == 0) + { + mOffset = 0; + return; + } + + while (mOffset < 0) + { + mValue /= 10; + ++mOffset; + } + + while (mOffset > 0) + { + mValue *= 10; + --mOffset; + } + + assert(mValue <= cMaxNative); return; } - while (value < cMinValue) + + mIsNative = false; + if (mValue == 0) { - if (offset <= cMinOffset) + mOffset = -100; + return; + } + + while (mValue < cMinValue) + { + if (mOffset <= cMinOffset) throw std::runtime_error("value overflow"); - value *= 10; - --offset; + mValue *= 10; + --mOffset; } - while (value > cMaxValue) + + while (mValue > cMaxValue) { - if (offset >= cMaxOffset) + if (mOffset >= cMaxOffset) throw std::runtime_error("value underflow"); - value /= 10; - ++offset; + mValue /= 10; + ++mOffset; } - assert( (value == 0) || ( (value >= cMinValue) && (value <= cMaxValue) ) ); - assert( (value == 0) || ( (offset >= cMinOffset) && (offset <= cMaxOffset) ) ); - assert( (value != 0) || (offset != -100) ); + assert((mValue == 0) || ((mValue >= cMinValue) && (mValue <= cMaxValue)) ); + assert((mValue == 0) || ((mOffset >= cMinOffset) && (mOffset <= cMaxOffset)) ); + assert((mValue != 0) || (mOffset != -100) ); +} + +void STAmount::add(Serializer& s) const +{ + if (mIsNative) + { + assert(mOffset == 0); + s.add64(mValue); + return; + } + if (isZero()) + s.add64(cNotNative); + else + s.add64(mValue + (static_cast(mOffset + 396) << (64 - 9))); + s.add160(mCurrency); } STAmount* STAmount::construct(SerializerIterator& sit, const char *name) { uint64 value = sit.get64(); - int offset = static_cast(value >> (64-8)); - value &= ~(255ull << (64-8)); + if ((value & cNotNative) == 0) + return new STAmount(name, value); + + uint160 currency = sit.get160(); + if (!currency) + throw std::runtime_error("invalid native currency"); + + int offset = static_cast(value >> (64-9)); + value &= ~(1023ull << (64-9)); if (value == 0) { - if (offset != 0) + if (offset != 256) throw std::runtime_error("invalid currency value"); } else { - offset -= 142; // center the range - if ( (value < cMinValue) || (value > cMaxValue) || (offset < cMinOffset) || (offset > cMaxOffset) ) + offset -= 396; // center the range and set the "not native" bit + if ((value < cMinValue) || (value > cMaxValue) || (offset < cMinOffset) || (offset > cMaxOffset)) throw std::runtime_error("invalid currency value"); } - return new STAmount(name, value, offset); + return new STAmount(name, currency, value, offset); } std::string STAmount::getRaw() const { // show raw internal form - if (isZero()) return "0"; - return boost::lexical_cast(value) + "e" + boost::lexical_cast(offset); + if (mValue == 0) return "0"; + if (mIsNative) return boost::lexical_cast(mValue); + return mCurrency.GetHex() + ": " + + boost::lexical_cast(mValue) + "e" + boost::lexical_cast(mOffset); } std::string STAmount::getText() const { // keep full internal accuracy, but make more human friendly if posible if (isZero()) return "0"; - if ( (offset < -25) || (offset > -5) ) - return boost::lexical_cast(value) + "e" + boost::lexical_cast(offset); + if (mIsNative) + return boost::lexical_cast(mValue); + if ((mOffset < -25) || (mOffset > -5)) + return boost::lexical_cast(mValue) + "e" + boost::lexical_cast(mOffset); std::string val = "000000000000000000000000000"; - val += boost::lexical_cast(value); + val += boost::lexical_cast(mValue); val += "00000000000000000000000"; - std::string pre = val.substr(0, offset+43); - std::string post = val.substr(offset+43); + std::string pre = val.substr(0, mOffset + 43); + std::string post = val.substr(mOffset + 43); size_t s_pre = pre.find_first_not_of('0'); if (s_pre == std::string::npos) @@ -90,57 +146,66 @@ std::string STAmount::getText() const return pre + "." + post.substr(0, s_post+1); } -void STAmount::add(Serializer& s) const +bool STAmount::isComparable(const STAmount& t) const { - if (isZero()) - s.add64(0); - else - s.add64(value + (static_cast(offset + 142) << (64 - 8))); + if (mIsNative) return t.mIsNative; + if (t.mIsNative) return false; + return mCurrency == t.mCurrency; } bool STAmount::isEquivalent(const SerializedType& t) const { const STAmount* v = dynamic_cast(&t); if (!v) return false; - return (value == v->value) && (offset == v->offset); + return isComparable(*v) && (mValue == v->mValue) && (mOffset == v->mOffset); +} + +void STAmount::throwComparable(const STAmount& t) const +{ + if (!isComparable(t)) + throw std::runtime_error("amounts are not comparable"); } bool STAmount::operator==(const STAmount& a) const { - return (offset == a.offset) && (value == a.value); + return isComparable(a) && (mOffset == a.mOffset) && (mValue == a.mValue); } bool STAmount::operator!=(const STAmount& a) const { - return (offset != a.offset) || (value != a.value); + return (mOffset != a.mOffset) || (mValue != a.mValue) || !isComparable(a); } bool STAmount::operator<(const STAmount& a) const { - if (offset < a.offset) return true; - if (a.offset < offset) return false; - return value < a.value; + throwComparable(a); + if (mOffset < a.mOffset) return true; + if (a.mOffset < mOffset) return false; + return mValue < a.mValue; } bool STAmount::operator>(const STAmount& a) const { - if (offset > a.offset) return true; - if (a.offset > offset) return false; - return value > a.value; + throwComparable(a); + if (mOffset > a.mOffset) return true; + if (a.mOffset > mOffset) return false; + return mValue > a.mValue; } bool STAmount::operator<=(const STAmount& a) const { - if (offset < a.offset) return true; - if (a.offset < offset) return false; - return value <= a.value; + throwComparable(a); + if (mOffset < a.mOffset) return true; + if (a.mOffset < mOffset) return false; + return mValue <= a.mValue; } bool STAmount::operator>=(const STAmount& a) const { - if (offset > a.offset) return true; - if (a.offset > offset) return false; - return value >= a.value; + throwComparable(a); + if (mOffset > a.mOffset) return true; + if (a.mOffset > mOffset) return false; + return mValue >= a.mValue; } STAmount& STAmount::operator+=(const STAmount& a) @@ -157,112 +222,191 @@ STAmount& STAmount::operator-=(const STAmount& a) STAmount& STAmount::operator=(const STAmount& a) { - value = a.value; - offset = a.offset; + mValue = a.mValue; + mOffset = a.mOffset; + mCurrency = a.mCurrency; + mIsNative = a.mIsNative; return *this; } STAmount& STAmount::operator=(uint64 v) { // does not copy name - return *this = STAmount(v, 0); + mOffset = 0; + mValue = v; + if (!mIsNative) canonicalize(); + return *this; } STAmount& STAmount::operator+=(uint64 v) { - return *this += STAmount(v); + if (mIsNative) mValue += v; + *this += STAmount(mCurrency, v); + return *this; } STAmount& STAmount::operator-=(uint64 v) { - return *this -= STAmount(v); + if (mIsNative) + { + if (v > mValue) + throw std::runtime_error("amount underflow"); + mValue -= v; + } + *this -= STAmount(mCurrency, v); + return *this; } STAmount::operator double() const { // Does not keep the precise value. Not recommended - if (!value) + if (!mValue) return 0.0; - return static_cast(value) * pow(10.0, offset); + return static_cast(mValue) * pow(10.0, mOffset); } STAmount operator+(STAmount v1, STAmount v2) { // We can check for precision loss here (value%10)!=0 + v1.throwComparable(v2); + if (v1.mIsNative) + return STAmount(v1.mValue + v2.mValue); + if (v1.isZero()) return v2; if (v2.isZero()) return v1; - while (v1.offset < v2.offset) + while (v1.mOffset < v2.mOffset) { - v1.value /= 10; - ++v1.offset; + v1.mValue /= 10; + ++v1.mOffset; } - while (v2.offset < v1.offset) + while (v2.mOffset < v1.mOffset) { - v2.value /= 10; - ++v2.offset; + v2.mValue /= 10; + ++v2.mOffset; } // this addition cannot overflow - return STAmount(v1.name, v1.value + v2.value, v1.offset); + return STAmount(v1.name, v1.mCurrency, v1.mValue + v2.mValue, v1.mOffset); } STAmount operator-(STAmount v1, STAmount v2) { - if (v2.isZero()) return v1; - if ( v1.isZero() || (v2.offset > v1.offset) ) - throw std::runtime_error("value underflow"); - - while (v1.offset > v2.offset) + v1.throwComparable(v2); + if (v2.mIsNative) { - v2.value /= 10; - ++v2.offset; + if (v2.mValue > v1.mValue) + throw std::runtime_error("amount underflow"); + return STAmount(v1.mValue - v2.mValue); } - if (v1.value < v2.value) + if (v2.isZero()) return v1; + if ( v1.isZero() || (v2.mOffset > v1.mOffset) ) throw std::runtime_error("value underflow"); - return STAmount(v1.name, v1.value - v2.value, v1.offset); + while (v1.mOffset > v2.mOffset) + { + v2.mValue /= 10; + ++v2.mOffset; + } + if (v1.mValue < v2.mValue) + throw std::runtime_error("value underflow"); + + return STAmount(v1.name, v1.mCurrency, v1.mValue - v2.mValue, v1.mOffset); } -STAmount operator/(const STAmount& num, const STAmount& den) +STAmount divide(const STAmount& num, const STAmount& den, const uint160& currencyOut) { if (den.isZero()) throw std::runtime_error("division by zero"); - if (num.isZero()) return STAmount(); + if (num.isZero()) return STAmount(currencyOut); + + uint64 numVal = num.mValue, denVal = den.mValue; + int numOffset = num.mOffset, denOffset = den.mOffset; + + if (num.mIsNative) + while (numVal < STAmount::cMinValue) + { + numVal *= 10; + --numOffset; + } + + if (den.mIsNative) + while (denVal < STAmount::cMinValue) + { + denVal *= 10; + --denOffset; + } // Compute (numerator * 10^16) / denominator CBigNum v; - if ((BN_add_word(&v, num.value) != 1) || + if ((BN_add_word(&v, numVal) != 1) || (BN_mul_word(&v, 10000000000000000ull) != 1) || - (BN_div_word(&v, den.value) == ((BN_ULONG) - 1))) + (BN_div_word(&v, denVal) == ((BN_ULONG) - 1))) { throw std::runtime_error("internal bn error"); } // 10^15 <= quotient <= 10^17 - assert(BN_num_bytes(&v) <= 60); + assert(BN_num_bytes(&v) <= 64); - return STAmount(v.getulong(), num.offset - den.offset - 16); + return STAmount(currencyOut, v.getulong(), numOffset - denOffset - 16); } -STAmount operator*(const STAmount &v1, const STAmount &v2) +STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut) { if (v1.isZero() || v2.isZero()) - return STAmount(); + return STAmount(currencyOut); + + if (v1.mIsNative && v2.mIsNative) + return STAmount(currencyOut, v1.mValue * v2.mValue); + + uint64 value1 = v1.mValue, value2 = v2.mValue; + int offset1 = v1.mOffset, offset2 = v2.mOffset; + + if (v1.mIsNative) + { + while (value1 < STAmount::cMinValue) + { + value1 *= 10; + --offset1; + } + } + else + { + value1 *= 10; + value1 += 3; + --offset1; + } + + if (v2.mIsNative) + { + while (value2 < STAmount::cMinValue) + { + value2 *= 10; + --offset2; + } + } + else + { + value2 *= 10; + value2 += 3; + --offset2; + } // Compute (numerator*10 * denominator*10) / 10^18 with rounding CBigNum v; - if ((BN_add_word(&v, (v1.value*10) + 3) != 1) || - (BN_mul_word(&v, (v2.value*10) + 3) != 1) || + if ((BN_add_word(&v, value1) != 1) || + (BN_mul_word(&v, value2) != 1) || (BN_div_word(&v, 1000000000000000000ull) == ((BN_ULONG) - 1))) { throw std::runtime_error("internal bn error"); } // 10^16 <= product <= 10^18 - assert(BN_num_bytes(&v) <= 60); + assert(BN_num_bytes(&v) <= 64); - return STAmount(v.getulong(), v1.offset + v2.offset + 16); + return STAmount(currencyOut, v.getulong(), offset1 + offset2 + 14); } -STAmount getRate(const STAmount& offerOut, const STAmount& offerIn) +uint64 getRate(const STAmount& offerOut, const STAmount& offerIn) { // offerOut = how much comes out of the offer, from the offeror to the taker // offerIn = how much goes into the offer, from the taker to the offeror + // FIXME return offerOut / offerIn; } @@ -322,80 +466,186 @@ static uint64_t muldiv(uint64_t a, uint64_t b, uint64_t c) return v.getulong(); } -STAmount convertToDisplayAmount(uint64_t internalAmount, uint64_t totalNow, uint64_t totalInit) +uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64_t totalNow, uint64_t totalInit) { // Convert an internal ledger/account quantity of native currency to a display amount - return muldiv(internalAmount, totalInit, totalNow); + if (internalAmount.isNative()) throw std::runtime_error("not native curency"); + return muldiv(internalAmount.getValue(), totalInit, totalNow); } -STAmount convertToInternalAmount(uint64_t displayAmount, uint64_t totalNow, uint64_t totalInit) +STAmount convertToInternalAmount(uint64_t displayAmount, uint64_t totalNow, uint64_t totalInit, + const char *name) { // Convert a display/request currency amount to an internal amount - return muldiv(displayAmount, totalNow, totalInit); + return STAmount(name, muldiv(displayAmount, totalNow, totalInit)); } -void STAmount::unitTest() +STAmount STAmount::deSerialize(SerializerIterator& it) +{ + STAmount *s = construct(it); + STAmount ret(*s); + delete s; + return ret; +} + +static STAmount serdes(const STAmount &s) +{ + Serializer ser; + s.add(ser); + SerializerIterator sit(ser); + return STAmount::deSerialize(sit); +} + + +BOOST_AUTO_TEST_SUITE(amount) + +BOOST_AUTO_TEST_CASE( NativeCurrency_test ) { STAmount zero, one(1), hundred(100); - if (!zero.isZero()) throw std::runtime_error("STAmount fail"); - if (one.isZero()) throw std::runtime_error("STAmount fail"); - if (hundred.isZero()) throw std::runtime_error("STAmount fail"); - if ((zero < zero)) throw std::runtime_error("STAmount fail"); - if (!(zero < one)) throw std::runtime_error("STAmount fail"); - if (!(zero < hundred)) throw std::runtime_error("STAmount fail"); - if ((one < zero)) throw std::runtime_error("STAmount fail"); - if ((one < one)) throw std::runtime_error("STAmount fail"); - if (!(one < hundred)) throw std::runtime_error("STAmount fail"); - if ((hundred < zero)) throw std::runtime_error("STAmount fail"); - if ((hundred < one)) throw std::runtime_error("STAmount fail"); - if ((hundred < hundred)) throw std::runtime_error("STAmount fail"); - if ((zero > zero)) throw std::runtime_error("STAmount fail"); - if ((zero > one)) throw std::runtime_error("STAmount fail"); - if ((zero > hundred)) throw std::runtime_error("STAmount fail"); - if (!(one > zero)) throw std::runtime_error("STAmount fail"); - if ((one > one)) throw std::runtime_error("STAmount fail"); - if ((one > hundred)) throw std::runtime_error("STAmount fail"); - if (!(hundred > zero)) throw std::runtime_error("STAmount fail"); - if (!(hundred > one)) throw std::runtime_error("STAmount fail"); - if ((hundred > hundred)) throw std::runtime_error("STAmount fail"); - if (!(zero <= zero)) throw std::runtime_error("STAmount fail"); - if (!(zero <= one)) throw std::runtime_error("STAmount fail"); - if (!(zero <= hundred)) throw std::runtime_error("STAmount fail"); - if ((one <= zero)) throw std::runtime_error("STAmount fail"); - if (!(one <= one)) throw std::runtime_error("STAmount fail"); - if (!(one <= hundred)) throw std::runtime_error("STAmount fail"); - if ((hundred <= zero)) throw std::runtime_error("STAmount fail"); - if ((hundred <= one)) throw std::runtime_error("STAmount fail"); - if (!(hundred <= hundred)) throw std::runtime_error("STAmount fail"); - if (!(zero >= zero)) throw std::runtime_error("STAmount fail"); - if ((zero >= one)) throw std::runtime_error("STAmount fail"); - if ((zero >= hundred)) throw std::runtime_error("STAmount fail"); - if (!(one >= zero)) throw std::runtime_error("STAmount fail"); - if (!(one >= one)) throw std::runtime_error("STAmount fail"); - if ((one >= hundred)) throw std::runtime_error("STAmount fail"); - if (!(hundred >= zero)) throw std::runtime_error("STAmount fail"); - if (!(hundred >= one)) throw std::runtime_error("STAmount fail"); - if (!(hundred >= hundred)) throw std::runtime_error("STAmount fail"); - if (!(zero == zero)) throw std::runtime_error("STAmount fail"); - if ((zero == one)) throw std::runtime_error("STAmount fail"); - if ((zero == hundred)) throw std::runtime_error("STAmount fail"); - if ((one == zero)) throw std::runtime_error("STAmount fail"); - if (!(one == one)) throw std::runtime_error("STAmount fail"); - if ((one == hundred)) throw std::runtime_error("STAmount fail"); - if ((hundred == zero)) throw std::runtime_error("STAmount fail"); - if ((hundred == one)) throw std::runtime_error("STAmount fail"); - if (!(hundred == hundred)) throw std::runtime_error("STAmount fail"); - if ((zero != zero)) throw std::runtime_error("STAmount fail"); - if (!(zero != one)) throw std::runtime_error("STAmount fail"); - if (!(zero != hundred)) throw std::runtime_error("STAmount fail"); - if (!(one != zero)) throw std::runtime_error("STAmount fail"); - if ((one != one)) throw std::runtime_error("STAmount fail"); - if (!(one != hundred)) throw std::runtime_error("STAmount fail"); - if (!(hundred != zero)) throw std::runtime_error("STAmount fail"); - if (!(hundred != one)) throw std::runtime_error("STAmount fail"); - if ((hundred != hundred)) throw std::runtime_error("STAmount fail"); - if (STAmount().getText() != "0") throw std::runtime_error("STAmount fail"); - if (STAmount(31).getText() != "31") throw std::runtime_error("STAmount fail"); - if (STAmount(31,1).getText() != "310") throw std::runtime_error("STAmount fail"); - if (STAmount(31,-1).getText() != "3.1") throw std::runtime_error("STAmount fail"); - if (STAmount(31,-2).getText() != "0.31") throw std::runtime_error("STAmount fail"); + if (serdes(zero) != zero) BOOST_FAIL("STAmount fail"); + if (serdes(one) != one) BOOST_FAIL("STAmount fail"); + if (serdes(hundred) != hundred) BOOST_FAIL("STAmount fail"); + + if (!zero.isNative()) BOOST_FAIL("STAmount fail"); + if (!hundred.isNative()) BOOST_FAIL("STAmount fail"); + if (!zero.isZero()) BOOST_FAIL("STAmount fail"); + if (one.isZero()) BOOST_FAIL("STAmount fail"); + if (hundred.isZero()) BOOST_FAIL("STAmount fail"); + if ((zero < zero)) BOOST_FAIL("STAmount fail"); + if (!(zero < one)) BOOST_FAIL("STAmount fail"); + if (!(zero < hundred)) BOOST_FAIL("STAmount fail"); + if ((one < zero)) BOOST_FAIL("STAmount fail"); + if ((one < one)) BOOST_FAIL("STAmount fail"); + if (!(one < hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred < zero)) BOOST_FAIL("STAmount fail"); + if ((hundred < one)) BOOST_FAIL("STAmount fail"); + if ((hundred < hundred)) BOOST_FAIL("STAmount fail"); + if ((zero > zero)) BOOST_FAIL("STAmount fail"); + if ((zero > one)) BOOST_FAIL("STAmount fail"); + if ((zero > hundred)) BOOST_FAIL("STAmount fail"); + if (!(one > zero)) BOOST_FAIL("STAmount fail"); + if ((one > one)) BOOST_FAIL("STAmount fail"); + if ((one > hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred > zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred > one)) BOOST_FAIL("STAmount fail"); + if ((hundred > hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero <= zero)) BOOST_FAIL("STAmount fail"); + if (!(zero <= one)) BOOST_FAIL("STAmount fail"); + if (!(zero <= hundred)) BOOST_FAIL("STAmount fail"); + if ((one <= zero)) BOOST_FAIL("STAmount fail"); + if (!(one <= one)) BOOST_FAIL("STAmount fail"); + if (!(one <= hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred <= zero)) BOOST_FAIL("STAmount fail"); + if ((hundred <= one)) BOOST_FAIL("STAmount fail"); + if (!(hundred <= hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero >= zero)) BOOST_FAIL("STAmount fail"); + if ((zero >= one)) BOOST_FAIL("STAmount fail"); + if ((zero >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(one >= zero)) BOOST_FAIL("STAmount fail"); + if (!(one >= one)) BOOST_FAIL("STAmount fail"); + if ((one >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= one)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero == zero)) BOOST_FAIL("STAmount fail"); + if ((zero == one)) BOOST_FAIL("STAmount fail"); + if ((zero == hundred)) BOOST_FAIL("STAmount fail"); + if ((one == zero)) BOOST_FAIL("STAmount fail"); + if (!(one == one)) BOOST_FAIL("STAmount fail"); + if ((one == hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred == zero)) BOOST_FAIL("STAmount fail"); + if ((hundred == one)) BOOST_FAIL("STAmount fail"); + if (!(hundred == hundred)) BOOST_FAIL("STAmount fail"); + if ((zero != zero)) BOOST_FAIL("STAmount fail"); + if (!(zero != one)) BOOST_FAIL("STAmount fail"); + if (!(zero != hundred)) BOOST_FAIL("STAmount fail"); + if (!(one != zero)) BOOST_FAIL("STAmount fail"); + if ((one != one)) BOOST_FAIL("STAmount fail"); + if (!(one != hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred != zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred != one)) BOOST_FAIL("STAmount fail"); + if ((hundred != hundred)) BOOST_FAIL("STAmount fail"); + if (STAmount().getText() != "0") BOOST_FAIL("STAmount fail"); + if (STAmount(31).getText() != "31") BOOST_FAIL("STAmount fail"); + if (STAmount(310).getText() != "310") BOOST_FAIL("STAmount fail"); + BOOST_TEST_MESSAGE("Amount NC Complete"); } + +BOOST_AUTO_TEST_CASE( CustomCurrency_test ) +{ + uint160 currency(1); + STAmount zero(currency), one(currency, 1), hundred(currency, 100); + + if (serdes(zero) != zero) BOOST_FAIL("STAmount fail"); + if (serdes(one) != one) BOOST_FAIL("STAmount fail"); + if (serdes(hundred) != hundred) BOOST_FAIL("STAmount fail"); + + if (zero.isNative()) BOOST_FAIL("STAmount fail"); + if (hundred.isNative()) BOOST_FAIL("STAmount fail"); + if (!zero.isZero()) BOOST_FAIL("STAmount fail"); + if (one.isZero()) BOOST_FAIL("STAmount fail"); + if (hundred.isZero()) BOOST_FAIL("STAmount fail"); + if ((zero < zero)) BOOST_FAIL("STAmount fail"); + if (!(zero < one)) BOOST_FAIL("STAmount fail"); + if (!(zero < hundred)) BOOST_FAIL("STAmount fail"); + if ((one < zero)) BOOST_FAIL("STAmount fail"); + if ((one < one)) BOOST_FAIL("STAmount fail"); + if (!(one < hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred < zero)) BOOST_FAIL("STAmount fail"); + if ((hundred < one)) BOOST_FAIL("STAmount fail"); + if ((hundred < hundred)) BOOST_FAIL("STAmount fail"); + if ((zero > zero)) BOOST_FAIL("STAmount fail"); + if ((zero > one)) BOOST_FAIL("STAmount fail"); + if ((zero > hundred)) BOOST_FAIL("STAmount fail"); + if (!(one > zero)) BOOST_FAIL("STAmount fail"); + if ((one > one)) BOOST_FAIL("STAmount fail"); + if ((one > hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred > zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred > one)) BOOST_FAIL("STAmount fail"); + if ((hundred > hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero <= zero)) BOOST_FAIL("STAmount fail"); + if (!(zero <= one)) BOOST_FAIL("STAmount fail"); + if (!(zero <= hundred)) BOOST_FAIL("STAmount fail"); + if ((one <= zero)) BOOST_FAIL("STAmount fail"); + if (!(one <= one)) BOOST_FAIL("STAmount fail"); + if (!(one <= hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred <= zero)) BOOST_FAIL("STAmount fail"); + if ((hundred <= one)) BOOST_FAIL("STAmount fail"); + if (!(hundred <= hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero >= zero)) BOOST_FAIL("STAmount fail"); + if ((zero >= one)) BOOST_FAIL("STAmount fail"); + if ((zero >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(one >= zero)) BOOST_FAIL("STAmount fail"); + if (!(one >= one)) BOOST_FAIL("STAmount fail"); + if ((one >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= one)) BOOST_FAIL("STAmount fail"); + if (!(hundred >= hundred)) BOOST_FAIL("STAmount fail"); + if (!(zero == zero)) BOOST_FAIL("STAmount fail"); + if ((zero == one)) BOOST_FAIL("STAmount fail"); + if ((zero == hundred)) BOOST_FAIL("STAmount fail"); + if ((one == zero)) BOOST_FAIL("STAmount fail"); + if (!(one == one)) BOOST_FAIL("STAmount fail"); + if ((one == hundred)) BOOST_FAIL("STAmount fail"); + if ((hundred == zero)) BOOST_FAIL("STAmount fail"); + if ((hundred == one)) BOOST_FAIL("STAmount fail"); + if (!(hundred == hundred)) BOOST_FAIL("STAmount fail"); + if ((zero != zero)) BOOST_FAIL("STAmount fail"); + if (!(zero != one)) BOOST_FAIL("STAmount fail"); + if (!(zero != hundred)) BOOST_FAIL("STAmount fail"); + if (!(one != zero)) BOOST_FAIL("STAmount fail"); + if ((one != one)) BOOST_FAIL("STAmount fail"); + if (!(one != hundred)) BOOST_FAIL("STAmount fail"); + if (!(hundred != zero)) BOOST_FAIL("STAmount fail"); + if (!(hundred != one)) BOOST_FAIL("STAmount fail"); + if ((hundred != hundred)) BOOST_FAIL("STAmount fail"); + if (STAmount(currency).getText() != "0") BOOST_FAIL("STAmount fail"); + if (STAmount(currency,31).getText() != "31") BOOST_FAIL("STAmount fail"); + if (STAmount(currency,31,1).getText() != "310") BOOST_FAIL("STAmount fail"); + if (STAmount(currency,31,-1).getText() != "3.1") BOOST_FAIL("STAmount fail"); + if (STAmount(currency,31,-2).getText() != "0.31") BOOST_FAIL("STAmount fail"); + BOOST_TEST_MESSAGE("Amount CC Complete"); +} + +BOOST_AUTO_TEST_SUITE_END() + +// vim:ts=4 diff --git a/src/LedgerNode.cpp b/src/LedgerNode.cpp index fb5422f89..dc932ccbd 100644 --- a/src/LedgerNode.cpp +++ b/src/LedgerNode.cpp @@ -25,6 +25,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SerializedLedgerEntry if (create) { + assert(!mAccountStateMap->hasItem(entry->getIndex())); if(!mAccountStateMap->addGiveItem(item, false)) { assert(false); diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 93f355c99..8a2c7cc33 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -12,7 +12,7 @@ SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(Modifying) { - root = boost::make_shared(SHAMapNode(0, uint256()), mSeq); + root = boost::make_shared(mSeq, SHAMapNode(0, uint256())); root->makeInner(); mTNByID[*root] = root; } @@ -88,6 +88,7 @@ SHAMapTreeNode::pointer SHAMap::checkCacheNode(const SHAMapNode& iNode) SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify) { // walk down to the terminal node for this ID + SHAMapTreeNode::pointer inNode = root; while (!inNode->isLeaf()) @@ -323,10 +324,10 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id) else for(int i=node->selectBranch(id)-1; i>=0; i--) if(!node->isEmptyBranch(i)) { - node=getNode(node->getChildNodeID(i), node->getChildHash(i), false); + node = getNode(node->getChildNodeID(i), node->getChildHash(i), false); if(!node) throw SHAMapException(MissingNode); - SHAMapItem::pointer item=firstBelow(node); - if(!item) throw SHAMapException(MissingNode); + SHAMapItem::pointer item = firstBelow(node); + if (!item) throw SHAMapException(MissingNode); return item; } } @@ -337,8 +338,8 @@ SHAMapItem::pointer SHAMap::peekPrevItem(const uint256& id) SHAMapItem::pointer SHAMap::peekItem(const uint256& id) { boost::recursive_mutex::scoped_lock sl(mLock); - SHAMapTreeNode::pointer leaf=walkTo(id, false); - if(!leaf) return SHAMapItem::pointer(); + SHAMapTreeNode::pointer leaf = walkTo(id, false); + if (!leaf) return SHAMapItem::pointer(); return leaf->peekItem(); } @@ -433,7 +434,7 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) std::stack stack = getStack(tag, true); if (stack.empty()) throw SHAMapException(MissingNode); - SHAMapTreeNode::pointer node=stack.top(); + SHAMapTreeNode::pointer node = stack.top(); stack.pop(); if (node->isLeaf() && (node->peekItem()->getTag() == tag)) @@ -464,9 +465,10 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) { // this is a leaf node that has to be made an inner node holding two items #ifdef ST_DEBUG std::cerr << "aGI leaf " << node->getString() << std::endl; + std::cerr << "Existing: " << node->peekItem()->getTag().GetHex() << std::endl; #endif SHAMapItem::pointer otherItem = node->peekItem(); - assert(otherItem && (tag != otherItem->getTag()) ); + assert(otherItem && (tag != otherItem->getTag())); node->makeInner(); @@ -475,10 +477,11 @@ bool SHAMap::addGiveItem(SHAMapItem::pointer item, bool isTransaction) while ((b1 = node->selectBranch(tag)) == (b2 = node->selectBranch(otherItem->getTag()))) { // we need a new inner node, since both go on same branch at this level #ifdef ST_DEBUG - std::cerr << "need new inner node at " << node->getDepth() << std::endl; + std::cerr << "need new inner node at " << node->getDepth() << ", " + << b1 << "==" << b2 << std::endl; #endif SHAMapTreeNode::pointer newNode = - boost::make_shared(node->getChildNodeID(b1), mSeq); + boost::make_shared(mSeq, node->getChildNodeID(b1)); newNode->makeInner(); if(!mTNByID.insert(std::make_pair(SHAMapNode(*newNode), newNode)).second) assert(false); @@ -607,8 +610,8 @@ void SHAMap::dump(bool hash) std::cerr << " MAP Contains" << std::endl; boost::recursive_mutex::scoped_lock sl(mLock); - for(boost::unordered_map::iterator it=mTNByID.begin(); - it!=mTNByID.end(); ++it) + for(boost::unordered_map::iterator it = mTNByID.begin(); + it != mTNByID.end(); ++it) { std::cerr << it->second->getString() << std::endl; if(hash) std::cerr << " " << it->second->getNodeHash().GetHex() << std::endl; diff --git a/src/SHAMap.h b/src/SHAMap.h index 35cc4a579..836d4f97a 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -153,7 +153,7 @@ private: SHAMapTreeNode& operator=(const SHAMapTreeNode&); // no implementation public: - SHAMapTreeNode(const SHAMapNode& nodeID, uint32 seq); // empty node + SHAMapTreeNode(uint32 seq, const SHAMapNode& nodeID); // empty node SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq); // copy node from older tree SHAMapTreeNode(const SHAMapNode& nodeID, SHAMapItem::pointer item, TNType type, uint32 seq); diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index badd100ad..e791e28e8 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -74,18 +74,18 @@ bool SHAMapNode::operator!=(const uint256 &s) const void SHAMapNode::ClassInit() { // set up the depth masks uint256 selector; - for(int i=0; i<64; i+=2) + for(int i = 0; i < 64; i += 2) { - smMasks[i]=selector; - *(selector.begin()+(i/2))=0x0F; - smMasks[i+1]=selector; - *(selector.begin()+(i/2))=0xFF; + smMasks[i] = selector; + *(selector.begin() + (i / 2)) = 0x0F; + smMasks[i + 1]=selector; + *(selector.begin() + (i / 2)) = 0xFF; } } uint256 SHAMapNode::getNodeID(int depth, const uint256& hash) { - assert(depth>=0 && depth<64); + assert(depth >= 0 && depth < 64); return hash & smMasks[depth]; } @@ -134,6 +134,7 @@ int SHAMapNode::selectBranch(const uint256& hash) const assert(false); return -1; } + if ((hash & smMasks[mDepth]) != mNodeID) { std::cerr << "selectBranch(" << getString() << std::endl; @@ -155,15 +156,15 @@ void SHAMapNode::dump() const std::cerr << getString() << std::endl; } -SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& nodeID, uint32 seq) : SHAMapNode(nodeID), mHash(0), mSeq(seq), +SHAMapTreeNode::SHAMapTreeNode(uint32 seq, const SHAMapNode& nodeID) : SHAMapNode(nodeID), mHash(0), mSeq(seq), mType(tnERROR), mFullBelow(false) { } SHAMapTreeNode::SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq) : SHAMapNode(node), - mHash(node.mHash), mItem(node.mItem), mSeq(seq), mType(node.mType), mFullBelow(false) + mHash(node.mHash), mSeq(seq), mType(node.mType), mFullBelow(false) { - if(node.mItem) + if (node.mItem) mItem = boost::make_shared(*node.mItem); else memcpy(mHashes, node.mHashes, sizeof(mHashes)); @@ -342,13 +343,19 @@ std::string SHAMapTreeNode::getString() const for(int i = 0; i < 16; ++i) if (!isEmptyBranch(i)) { - ret += ",b"; + ret += "\nb"; ret += boost::lexical_cast(i); + ret += " = "; + ret += mHashes[i].GetHex(); } } if (isLeaf()) { - ret += ",leaf"; + ret += ",leaf\n"; + ret += " Tag="; + ret += getTag().GetHex(); + ret += "\n Hash="; + ret += mHash.GetHex(); } return ret; } diff --git a/src/SerializedObject.h b/src/SerializedObject.h index 9a2815171..8669fb18f 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -87,9 +87,6 @@ protected: boost::ptr_vector mData; std::vector mType; - static std::auto_ptr makeDefaultObject(SerializedTypeID id, const char *name); - static std::auto_ptr makeDeserializedObject(SerializedTypeID id, const char *name, - SerializerIterator&); STObject* duplicate() const { return new STObject(*this); } public: @@ -164,6 +161,10 @@ public: SerializedType* makeFieldPresent(SOE_Field field); void makeFieldAbsent(SOE_Field field); + static std::auto_ptr makeDefaultObject(SerializedTypeID id, const char *name); + static std::auto_ptr makeDeserializedObject(SerializedTypeID id, const char *name, + SerializerIterator&); + static void unitTest(); }; diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 2e8ebbfd5..57b94b0db 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -188,34 +188,49 @@ class STAmount : public SerializedType // Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive protected: - int offset; // These variables *always* hold canonical values on entry/exit - uint64 value; + uint160 mCurrency; + uint64 mValue; + int mOffset; + bool mIsNative; void canonicalize(); - STAmount* duplicate() const { return new STAmount(name, offset, value); } + STAmount* duplicate() const { return new STAmount(name, mCurrency, mOffset, mValue); } static STAmount* construct(SerializerIterator&, const char *name = NULL); - static const int cMinOffset=-96, cMaxOffset=80; - static const uint64 cMinValue=1000000000000000ull, cMaxValue=9999999999999999ull; + static const int cMinOffset = -96, cMaxOffset = 80; + static const uint64 cMinValue = 1000000000000000ull, cMaxValue = 9999999999999999ull; + static const uint64 cMaxNative = 9000000000000000000ull; + static const uint64 cNotNative = 0x8000000000000000ull; public: - STAmount(uint64 v = 0, int off = 0) : offset(off), value(v) - { canonicalize(); } // (1,0)=$1 (1,-2)=$.01 (100,0)=(10000,-2)=$.01 - STAmount(const char *n, uint64 v = 0, int off = 0) : SerializedType(n), offset(off), value(v) + STAmount(uint64 v = 0) : mValue(v), mOffset(0), mIsNative(true) + { ; } + + STAmount(const char *n, uint64 v = 0) : SerializedType(n), mValue(v), mOffset(0), mIsNative(true) + { ; } + + STAmount(const uint160& currency, uint64 v = 0, int off = 0) : mCurrency(currency), mValue(v), mOffset(off) { canonicalize(); } + + STAmount(const char *n, const uint160& currency, uint64 v = 0, int off = 0) : SerializedType(n), + mCurrency(currency), mValue(v), mOffset(off) + { canonicalize(); } + static std::auto_ptr deserialize(SerializerIterator& sit, const char *name) { return std::auto_ptr(construct(sit, name)); } - int getLength() const { return 8; } + int getLength() const { return mIsNative ? 8 : 28; } SerializedTypeID getSType() const { return STI_AMOUNT; } std::string getText() const; std::string getRaw() const; void add(Serializer& s) const; - int getOffset() const { return offset; } - uint64 getValue() const { return value; } - void zero() { offset = -100; value = 0; } - bool isZero() const { return value == 0; } + int getOffset() const { return mOffset; } + uint64 getValue() const { return mValue; } + bool isNative() const { return mIsNative; } + const uint160& getCurrency() const { return mCurrency; } + void zero() { mOffset = mIsNative ? -100 : 0; mValue = 0; } + bool isZero() const { return mValue == 0; } virtual bool isEquivalent(const SerializedType& t) const; @@ -225,6 +240,8 @@ public: bool operator>(const STAmount&) const; bool operator<=(const STAmount&) const; bool operator>=(const STAmount&) const; + bool isComparable(const STAmount&) const; + void throwComparable(const STAmount&) const; STAmount& operator+=(const STAmount&); STAmount& operator-=(const STAmount&); @@ -237,11 +254,12 @@ public: friend STAmount operator+(STAmount v1, STAmount v2); friend STAmount operator-(STAmount v1, STAmount v2); - friend STAmount operator/(const STAmount& v1, const STAmount& v2); - friend STAmount operator*(const STAmount& v1, const STAmount& v2); - // Someone is offering X for Y, what is the rate? - friend STAmount getRate(const STAmount& offerOut, const STAmount& offerIn); + friend STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); + friend STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); + + // Someone iis offering X for Y, what is the rate? + friend uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); // Someone is offering X for Y, I try to pay Z, how much do I get? // And what's left of the offer? And how much do I actually pay? @@ -251,12 +269,11 @@ public: friend STAmount getNeeded(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed); // Native currency conversions, to/from display format - friend STAmount convertToDisplayAmount(const STAmount& internalAmount, - const STAmount& totalNow, const STAmount& totalInit); - friend STAmount convertToInternalAmount(const STAmount& displayAmount, - const STAmount& totalNow, const STAmount& totalInit); + friend uint64 convertToDisplayAmount(const STAmount& internalAmount, uint64 totalNow, uint64 totalInit); + friend STAmount convertToInternalAmount(uint64 displayAmount, uint64 totalNow, uint64 totalInit, + const char *name = NULL); - static void unitTest(); + static STAmount deSerialize(SerializerIterator&); }; class STHash128 : public SerializedType