Promote 'amounts' to a new type. Codify storage format, both internal

and serialized. Define operators.
This commit is contained in:
JoelKatz
2012-04-09 19:36:51 -07:00
parent 3e91ddc6d7
commit fcdf42e799
5 changed files with 147 additions and 8 deletions

71
src/Amount.cpp Normal file
View File

@@ -0,0 +1,71 @@
#include <cmath>
#include <boost/lexical_cast.hpp>
#include "SerializedTypes.h"
void STAmount::canonicalize()
{
if(value==0)
{
offset=0;
value=0;
}
while(value<cMinValue)
{
value*=10;
offset-=1;
}
while(value>cMaxValue)
{ // Here we can make it throw on precision loss if we wish: ((value%10)!=0)
value/=10;
offset+=1;
}
assert( (value==0) || ( (value>=cMinValue) && (value<=cMaxValue) ) );
assert( (offset>=cMinOffset) && (offset<=cMaxOffset) );
}
STAmount* STAmount::construct(SerializerIterator& sit, const char *name)
{
uint64 value = sit.get64();
int offset = static_cast<int>(value>>(64-8));
offset-=142;
value&=~(255ull<<(64-8));
if(value==0)
{
if(offset!=0)
throw std::runtime_error("invalid currency value");
}
else
{
if( (value<cMinValue) || (value>cMaxValue) || (offset<cMinOffset) || (offset>cMaxOffset) )
throw std::runtime_error("invalid currency value");
}
return new STAmount(name, value, offset);
}
std::string STAmount::getText() const
{
return boost::lexical_cast<std::string>(static_cast<double>(*this));
}
void STAmount::add(Serializer& s) const
{
uint64 v=value;
v+=(static_cast<uint64>(offset+142) << (64-8));
s.add64(v);
}
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);
}
STAmount::operator double() const
{
if(!value) return 0.0;
return static_cast<double>(value) * pow(10.0, offset);
}

View File

@@ -9,7 +9,7 @@ LedgerEntryFormat LedgerFormats[]=
{ S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 },
{ S_FIELD(Account), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Account), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Sequence), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(Sequence), STI_UINT32, SOE_REQUIRED, 0 },
{ S_FIELD(Balance), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Balance), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(LastReceive), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(LastReceive), STI_UINT32, SOE_REQUIRED, 0 },
{ S_FIELD(LastTxn), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(LastTxn), STI_UINT32, SOE_REQUIRED, 0 },
{ S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 1 }, { S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 1 },
@@ -23,8 +23,8 @@ LedgerEntryFormat LedgerFormats[]=
{ S_FIELD(Borrower), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Borrower), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Lender), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Lender), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 }, { S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 },
{ S_FIELD(Limit), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Limit), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Balance), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Balance), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(CurrentRate), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(CurrentRate), STI_UINT32, SOE_IFFLAG, 2 },
{ S_FIELD(RateLock), STI_UINT32, SOE_IFFLAG, 4 }, { S_FIELD(RateLock), STI_UINT32, SOE_IFFLAG, 4 },
{ S_FIELD(NextRate), STI_UINT32, SOE_IFFLAG, 8 }, { S_FIELD(NextRate), STI_UINT32, SOE_IFFLAG, 8 },
@@ -37,7 +37,7 @@ LedgerEntryFormat LedgerFormats[]=
{ S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 },
{ S_FIELD(Nickname), STI_HASH256, SOE_REQUIRED, 0 }, { S_FIELD(Nickname), STI_HASH256, SOE_REQUIRED, 0 },
{ S_FIELD(Account), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Account), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(MinimumOffer), STI_UINT64, SOE_IFFLAG, 1 }, { S_FIELD(MinimumOffer), STI_AMOUNT, SOE_IFFLAG, 1 },
{ S_FIELD(OfferCurrency),STI_HASH160, SOE_IFFLAG, 2 }, { S_FIELD(OfferCurrency),STI_HASH160, SOE_IFFLAG, 2 },
{ S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 32768 }, { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 32768 },
{ sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } }

View File

@@ -16,6 +16,9 @@ SerializedType* STObject::makeDefaultObject(SerializedTypeID id, const char *nam
case STI_UINT64: case STI_UINT64:
return new STUInt64(name); return new STUInt64(name);
case STI_AMOUNT:
return new STAmount(name);
case STI_HASH160: case STI_HASH160:
return new STHash160(name); return new STHash160(name);
@@ -49,6 +52,9 @@ SerializedType* STObject::makeDeserializedObject(SerializedTypeID id, const char
case STI_UINT64: case STI_UINT64:
return STUInt64::construct(sit, name); return STUInt64::construct(sit, name);
case STI_AMOUNT:
return STAmount::construct(sit, name);
case STI_HASH160: case STI_HASH160:
return STHash160::construct(sit, name); return STHash160::construct(sit, name);

View File

@@ -15,6 +15,7 @@ enum SerializedTypeID
// standard types // standard types
STI_OBJECT=1, STI_UINT8=2, STI_UINT16=3, STI_UINT32=4, STI_UINT64=5, STI_OBJECT=1, STI_UINT8=2, STI_UINT16=3, STI_UINT32=4, STI_UINT64=5,
STI_HASH128=6, STI_HASH160=7, STI_HASH256=8, STI_VL=9, STI_TL=10, STI_HASH128=6, STI_HASH160=7, STI_HASH256=8, STI_VL=9, STI_TL=10,
STI_AMOUNT=11,
// high level types // high level types
STI_ACCOUNT=100, STI_TRANSACTION=101, STI_LEDGERENTRY=102 STI_ACCOUNT=100, STI_TRANSACTION=101, STI_LEDGERENTRY=102
@@ -155,6 +156,67 @@ public:
virtual bool isEquivalent(const SerializedType& t) const; virtual bool isEquivalent(const SerializedType& t) const;
}; };
class STAmount : public SerializedType
{
// Internal form:
// 1: If amount is zero, then offset and value are both zero.
// 2: Otherwise:
// legal offset range is -96 to +80 inclusive
// value range is 10^15 to (10^16 - 1) inclusive
// amount = value * [10 ^ offset]
// Wire form:
// High 8 bits are (offset+142), legal range is, 80 to 22 inclusive
// Low 56 bits are value, legal range is 10^15 to (10^16 - 1) inclusive
protected:
int offset; // These variables *always* hold canonical values
uint64 value;
void canonicalize();
static const int cMinOffset=-96, cMaxOffset=80;
static const uint64 cMinValue=1000000000000000ull, cMaxValue=9999999999999999ull;
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=1) : SerializedType(n), offset(off), value(v)
{ canonicalize(); }
static STAmount* construct(SerializerIterator&, const char *name=NULL);
int getLength() const { return 8; }
SerializedTypeID getSType() const { return STI_AMOUNT; }
STAmount* duplicate() const { return new STAmount(name, offset, value); }
std::string getText() const;
void add(Serializer& s) const;
int getOffset() const { return offset; }
uint64 getValue() const { return value; }
virtual bool isEquivalent(const SerializedType& t) const;
bool operator==(const STAmount&) const;
bool operator!=(const STAmount&) const;
bool operator<(const STAmount&) const;
bool operator>(const STAmount&) const;
bool operator<=(const STAmount&) const;
bool operator>=(const STAmount&) const;
STAmount& operator+=(const STAmount&);
STAmount& operator-=(const STAmount&);
STAmount& operator=(const STAmount&);
STAmount& operator+=(uint64);
STAmount& operator-=(uint64);
STAmount& operator=(uint64);
operator double() const;
friend STAmount operator+(const STAmount& v1, const STAmount& v2);
friend STAmount operator-(const STAmount& v1, const STAmount& v2);
friend STAmount operator/(const STAmount& v1, const STAmount& v2);
};
class STHash128 : public SerializedType class STHash128 : public SerializedType
{ {
protected: protected:

View File

@@ -8,7 +8,7 @@ TransactionFormat InnerTxnFormats[]=
{ "MakePayment", ttMAKE_PAYMENT, { { "MakePayment", ttMAKE_PAYMENT, {
{ S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 },
{ S_FIELD(Destination), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Amount), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Amount), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 }, { S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 },
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 },
{ S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 4 }, { S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 4 },
@@ -19,7 +19,7 @@ TransactionFormat InnerTxnFormats[]=
{ "Invoice", ttINVOICE, { { "Invoice", ttINVOICE, {
{ S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 },
{ S_FIELD(Target), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Target), STI_ACCOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Amount), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(Amount), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 }, { S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 },
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 },
{ S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 4 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 4 },
@@ -30,9 +30,9 @@ TransactionFormat InnerTxnFormats[]=
}, },
{ "Offer", ttEXCHANGE_OFFER, { { "Offer", ttEXCHANGE_OFFER, {
{ S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 },
{ S_FIELD(AmountIn), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(AmountIn), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(CurrencyIn), STI_HASH160, SOE_IFFLAG, 2 }, { S_FIELD(CurrencyIn), STI_HASH160, SOE_IFFLAG, 2 },
{ S_FIELD(AmountOut), STI_UINT64, SOE_REQUIRED, 0 }, { S_FIELD(AmountOut), STI_AMOUNT, SOE_REQUIRED, 0 },
{ S_FIELD(CurrencyOut), STI_HASH160, SOE_IFFLAG, 4 }, { S_FIELD(CurrencyOut), STI_HASH160, SOE_IFFLAG, 4 },
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 },
{ S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 16 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 16 },