From fcdf42e7993b316328b77ce2b7f502619ee8013e Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 9 Apr 2012 19:36:51 -0700 Subject: [PATCH] Promote 'amounts' to a new type. Codify storage format, both internal and serialized. Define operators. --- src/Amount.cpp | 71 ++++++++++++++++++++++++++++++++++++++ src/LedgerFormats.cpp | 8 ++--- src/SerializedObject.cpp | 6 ++++ src/SerializedTypes.h | 62 +++++++++++++++++++++++++++++++++ src/TransactionFormats.cpp | 8 ++--- 5 files changed, 147 insertions(+), 8 deletions(-) create mode 100644 src/Amount.cpp diff --git a/src/Amount.cpp b/src/Amount.cpp new file mode 100644 index 0000000000..752a0f3bf6 --- /dev/null +++ b/src/Amount.cpp @@ -0,0 +1,71 @@ + +#include + +#include + +#include "SerializedTypes.h" + +void STAmount::canonicalize() +{ + if(value==0) + { + offset=0; + value=0; + } + while(valuecMaxValue) + { // 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(value>>(64-8)); + offset-=142; + value&=~(255ull<<(64-8)); + if(value==0) + { + if(offset!=0) + throw std::runtime_error("invalid currency value"); + } + else + { + if( (valuecMaxValue) || (offsetcMaxOffset) ) + throw std::runtime_error("invalid currency value"); + } + return new STAmount(name, value, offset); +} + +std::string STAmount::getText() const +{ + return boost::lexical_cast(static_cast(*this)); +} + +void STAmount::add(Serializer& s) const +{ + uint64 v=value; + v+=(static_cast(offset+142) << (64-8)); + s.add64(v); +} + +bool STAmount::isEquivalent(const SerializedType& t) const +{ + const STAmount* v=dynamic_cast(&t); + if(!v) return false; + return (value==v->value) && (offset==v->offset); +} + +STAmount::operator double() const +{ + if(!value) return 0.0; + return static_cast(value) * pow(10.0, offset); +} diff --git a/src/LedgerFormats.cpp b/src/LedgerFormats.cpp index 89f6b34b0e..1f76a9ffa2 100644 --- a/src/LedgerFormats.cpp +++ b/src/LedgerFormats.cpp @@ -9,7 +9,7 @@ LedgerEntryFormat LedgerFormats[]= { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Account), STI_ACCOUNT, 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(LastTxn), STI_UINT32, SOE_REQUIRED, 0 }, { S_FIELD(EmailHash), STI_HASH128, SOE_IFFLAG, 1 }, @@ -23,8 +23,8 @@ LedgerEntryFormat LedgerFormats[]= { S_FIELD(Borrower), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Lender), STI_ACCOUNT, SOE_REQUIRED, 0 }, { S_FIELD(Currency), STI_HASH160, SOE_IFFLAG, 1 }, - { S_FIELD(Limit), STI_UINT64, SOE_REQUIRED, 0 }, - { S_FIELD(Balance), STI_UINT64, SOE_REQUIRED, 0 }, + { S_FIELD(Limit), STI_AMOUNT, SOE_REQUIRED, 0 }, + { S_FIELD(Balance), STI_AMOUNT, SOE_REQUIRED, 0 }, { S_FIELD(CurrentRate), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(RateLock), STI_UINT32, SOE_IFFLAG, 4 }, { S_FIELD(NextRate), STI_UINT32, SOE_IFFLAG, 8 }, @@ -37,7 +37,7 @@ LedgerEntryFormat LedgerFormats[]= { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, { S_FIELD(Nickname), STI_HASH256, 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(Extensions), STI_TL, SOE_IFFLAG, 32768 }, { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index 07bf10772a..576d901030 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -16,6 +16,9 @@ SerializedType* STObject::makeDefaultObject(SerializedTypeID id, const char *nam case STI_UINT64: return new STUInt64(name); + case STI_AMOUNT: + return new STAmount(name); + case STI_HASH160: return new STHash160(name); @@ -49,6 +52,9 @@ SerializedType* STObject::makeDeserializedObject(SerializedTypeID id, const char case STI_UINT64: return STUInt64::construct(sit, name); + case STI_AMOUNT: + return STAmount::construct(sit, name); + case STI_HASH160: return STHash160::construct(sit, name); diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 5e894e1511..b65265b9f7 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -15,6 +15,7 @@ enum SerializedTypeID // standard types 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_AMOUNT=11, // high level types STI_ACCOUNT=100, STI_TRANSACTION=101, STI_LEDGERENTRY=102 @@ -155,6 +156,67 @@ public: 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 { protected: diff --git a/src/TransactionFormats.cpp b/src/TransactionFormats.cpp index 528a86d3ee..c59a78e638 100644 --- a/src/TransactionFormats.cpp +++ b/src/TransactionFormats.cpp @@ -8,7 +8,7 @@ TransactionFormat InnerTxnFormats[]= { "MakePayment", ttMAKE_PAYMENT, { { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 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(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 4 }, @@ -19,7 +19,7 @@ TransactionFormat InnerTxnFormats[]= { "Invoice", ttINVOICE, { { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 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(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 4 }, @@ -30,9 +30,9 @@ TransactionFormat InnerTxnFormats[]= }, { "Offer", ttEXCHANGE_OFFER, { { 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(AmountOut), STI_UINT64, SOE_REQUIRED, 0 }, + { S_FIELD(AmountOut), STI_AMOUNT, SOE_REQUIRED, 0 }, { S_FIELD(CurrencyOut), STI_HASH160, SOE_IFFLAG, 4 }, { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 }, { S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 16 },