Merge branch 'master' into create

This commit is contained in:
Arthur Britto
2012-05-18 12:12:42 -07:00
7 changed files with 477 additions and 198 deletions

View File

@@ -3,6 +3,7 @@
#include <iomanip>
#include <boost/lexical_cast.hpp>
#include <boost/test/unit_test.hpp>
#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<uint64>(mOffset + 396) << (64 - 9)));
s.add160(mCurrency);
}
STAmount* STAmount::construct(SerializerIterator& sit, const char *name)
{
uint64 value = sit.get64();
int offset = static_cast<int>(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<int>(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<std::string>(value) + "e" + boost::lexical_cast<std::string>(offset);
if (mValue == 0) return "0";
if (mIsNative) return boost::lexical_cast<std::string>(mValue);
return mCurrency.GetHex() + ": " +
boost::lexical_cast<std::string>(mValue) + "e" + boost::lexical_cast<std::string>(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<std::string>(value) + "e" + boost::lexical_cast<std::string>(offset);
if (mIsNative)
return boost::lexical_cast<std::string>(mValue);
if ((mOffset < -25) || (mOffset > -5))
return boost::lexical_cast<std::string>(mValue) + "e" + boost::lexical_cast<std::string>(mOffset);
std::string val = "000000000000000000000000000";
val += boost::lexical_cast<std::string>(value);
val += boost::lexical_cast<std::string>(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<uint64>(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<const STAmount*>(&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<double>(value) * pow(10.0, offset);
return static_cast<double>(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

View File

@@ -25,6 +25,7 @@ LedgerStateParms Ledger::writeBack(LedgerStateParms parms, SerializedLedgerEntry
if (create)
{
assert(!mAccountStateMap->hasItem(entry->getIndex()));
if(!mAccountStateMap->addGiveItem(item, false))
{
assert(false);

View File

@@ -12,7 +12,7 @@
SHAMap::SHAMap(uint32 seq) : mSeq(seq), mState(Modifying)
{
root = boost::make_shared<SHAMapTreeNode>(SHAMapNode(0, uint256()), mSeq);
root = boost::make_shared<SHAMapTreeNode>(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<SHAMapTreeNode::pointer> 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<SHAMapTreeNode>(node->getChildNodeID(b1), mSeq);
boost::make_shared<SHAMapTreeNode>(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<SHAMapNode, SHAMapTreeNode::pointer, hash_SMN>::iterator it=mTNByID.begin();
it!=mTNByID.end(); ++it)
for(boost::unordered_map<SHAMapNode, SHAMapTreeNode::pointer, hash_SMN>::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;

View File

@@ -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);

View File

@@ -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<SHAMapItem>(*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<std::string>(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;
}

View File

@@ -87,9 +87,6 @@ protected:
boost::ptr_vector<SerializedType> mData;
std::vector<SOElement*> mType;
static std::auto_ptr<SerializedType> makeDefaultObject(SerializedTypeID id, const char *name);
static std::auto_ptr<SerializedType> 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<SerializedType> makeDefaultObject(SerializedTypeID id, const char *name);
static std::auto_ptr<SerializedType> makeDeserializedObject(SerializedTypeID id, const char *name,
SerializerIterator&);
static void unitTest();
};

View File

@@ -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<SerializedType> deserialize(SerializerIterator& sit, const char *name)
{ return std::auto_ptr<SerializedType>(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