diff --git a/.gitignore b/.gitignore index 12994b8846..2530f7eb82 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,13 @@ # Ignore python compiled files. *.pyc +# Ignore backup/temps +*~ + # Ignore object files. *.o obj/* +bin/newcoind newcoind diff --git a/bin/network-build b/bin/network-build new file mode 100755 index 0000000000..d40e3ee95e --- /dev/null +++ b/bin/network-build @@ -0,0 +1,6 @@ +#!/bin/sh -x + +cd ~/NewCoin +git pull +scons -j 2 +cp -p newcoind bin/ diff --git a/bin/network-init b/bin/network-init new file mode 100755 index 0000000000..60c3c26a8d --- /dev/null +++ b/bin/network-init @@ -0,0 +1,10 @@ +#!/bin/sh + +# XXX Should not need to make db directory. newcoind should do this. +for SITE in $HOSTS +do + echo "Clearing db for:" $SITE + DB_DIR="/var/www/$SITE/db/" + mkdir -p "/var/www/$SITE/db/" + rm -rf "/var/www/$SITE/db/*" +done diff --git a/bin/network-restart b/bin/network-restart new file mode 100755 index 0000000000..34dd6a3945 --- /dev/null +++ b/bin/network-restart @@ -0,0 +1,5 @@ +#!/bin/sh -x + +network-stop +sleep 1 +network-start diff --git a/bin/network-start b/bin/network-start new file mode 100755 index 0000000000..4d59c634b5 --- /dev/null +++ b/bin/network-start @@ -0,0 +1,6 @@ +#!/bin/sh + +for SITE in $HOSTS +do + (nx $SITE &) +done diff --git a/bin/network-stop b/bin/network-stop new file mode 100755 index 0000000000..cee718e5c6 --- /dev/null +++ b/bin/network-stop @@ -0,0 +1,6 @@ +#!/bin/sh + +for SITE in $HOSTS +do + (nx $SITE stop &) +done diff --git a/bin/network-update b/bin/network-update new file mode 100755 index 0000000000..03a14b6d6c --- /dev/null +++ b/bin/network-update @@ -0,0 +1,5 @@ +#!/bin/sh -x +echo "Building and restarting." + +network-build +network-restart diff --git a/bin/nx b/bin/nx new file mode 100755 index 0000000000..e602581b50 --- /dev/null +++ b/bin/nx @@ -0,0 +1,7 @@ +#!/bin/sh + +SITE=$1 +shift +COMMAND=$@ +cd "/var/www/$SITE" +newcoind $COMMAND diff --git a/src/Amount.cpp b/src/Amount.cpp new file mode 100644 index 0000000000..3cc30d7e0c --- /dev/null +++ b/src/Amount.cpp @@ -0,0 +1,177 @@ + +#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) + if(offset>=cMaxOffset) throw std::runtime_error("value underflow"); + 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); +} + +bool STAmount::operator==(const STAmount& a) const +{ + return (offset==a.offset) && (value==a.value); +} + +bool STAmount::operator!=(const STAmount& a) const +{ + return (offset!=a.offset) || (value!=a.value); +} + +bool STAmount::operator<(const STAmount& a) const +{ + if(offset(const STAmount& a) const +{ + if(offset>a.offset) return true; + if(a.offset>offset) return false; + return value > a.value; +} + +bool STAmount::operator<=(const STAmount& a) const +{ + if(offset=(const STAmount& a) const +{ + if(offset>a.offset) return true; + if(a.offset>offset) return false; + return value >= a.value; +} + +STAmount& STAmount::operator+=(const STAmount& a) +{ + *this = *this + a; + return *this; +} + +STAmount& STAmount::operator-=(const STAmount& a) +{ + *this = *this - a; + return *this; +} + +STAmount& STAmount::operator=(const STAmount& a) +{ + value=a.value; + offset=a.offset; + return *this; +} + +STAmount& STAmount::operator=(uint64 v) +{ + return *this=STAmount(v, 0); +} + +STAmount& STAmount::operator+=(uint64 v) +{ + return *this+=STAmount(v); +} + +STAmount& STAmount::operator-=(uint64 v) +{ + return *this-=STAmount(v); +} + +STAmount operator+(STAmount v1, STAmount v2) +{ // We can check for precision loss here (value%10)!=0 + while(v1.offset < v2.offset) + { + v1.value/=10; + v1.offset+=1; + } + while(v2.offset < v1.offset) + { + v2.value/=10; + v2.offset+=1; + } + // this addition cannot overflow + return STAmount(v1.name, v1.value + v2.value, v1.offset); +} + +STAmount operator-(STAmount v1, STAmount v2) +{ // We can check for precision loss here (value%10)!=0 + while(v1.offset < v2.offset) + { + v1.value/=10; + v1.offset+=1; + } + while(v2.offset < v1.offset) + { + v2.value/=10; + v2.offset+=1; + } + if(v1.value < v2.value) throw std::runtime_error("value overflow"); + return STAmount(v1.name, v1.value - v2.value, v1.offset); +} + +STAmount::operator double() const +{ + if(!value) return 0.0; + return static_cast(value) * pow(10.0, offset); +} diff --git a/src/Currency.cpp b/src/Currency.cpp new file mode 100644 index 0000000000..34a9d03fa2 --- /dev/null +++ b/src/Currency.cpp @@ -0,0 +1,56 @@ + +#include "Currency.h" + +#include +#include + +uint160 Currency::sNatMask("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000"); +uint160 Currency::sNatZero("FFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000"); +uint64 Amount::sMaxCanon(1ull << 63); + +Currency::Currency(const uint160& v) : mValue(v) +{ + if (!v) mType = ctNATIVE; + if (!(v & sNatZero)) mType = ctNATIONAL; + mType = ctNATIONAL; +} + +bool Currency::isCommensurate(const Currency& c) const +{ + if (isNative()) + return c.isNative(); + if (isCustom()) + return mValue == c.mValue; + if (!c.isNational()) return false; + return (mValue & sNatMask) == (c.mValue & sNatMask); +} + +unsigned char Currency::getScale() const +{ + return *(mValue.begin()); +} + +void Currency::setScale(unsigned char s) +{ + *(mValue.begin()) = s; +} + +void Amount::canonicalize() +{ // clear high bit to avoid overflows + if(mQuantity > sMaxCanon) + { + if (!mCurrency.isNational()) throw std::runtime_error("Currency overflow"); + unsigned char s = mCurrency.getScale(); + if (s==255) throw std::runtime_error("Currency overflow"); + mCurrency.setScale(s + 1); + mQuantity /= 10.0; + } +} + +double Amount::getDisplayQuantity() const +{ + if(!mCurrency.isNational()) throw std::runtime_error("Can only scale national currencies"); + int scale=mCurrency.getScale(); + return static_cast(mQuantity) * pow(10, 128-scale); +} + diff --git a/src/Currency.h b/src/Currency.h new file mode 100644 index 0000000000..2fca53bd36 --- /dev/null +++ b/src/Currency.h @@ -0,0 +1,80 @@ +#ifndef __CURRENCY__ +#define __CURRENCY__ + +#include + +#include "uint256.h" + +enum CurrencyType +{ + ctNATIVE, // Currency transaction fees are paid in + ctNATIONAL, // State-issued or ISO-recognized currencies + ctCUSTOM, // Custom currencies +}; + +class Currency +{ +protected: + uint160 mValue; + CurrencyType mType; + + static uint160 sNatMask; // bits that indicate national currency ISO code and version + static uint160 sNatZero; // bits that must be zero on a national currency + +public: + Currency() : mType(ctNATIVE) { ; } + Currency(const uint160& v); + Currency(const std::string& iso, uint16 version, unsigned char scale); + + bool isCommensurate(const Currency&) const; + bool isNational() const { return mType == ctNATIONAL; } + bool isNative() const { return mType == ctNATIVE; } + bool isCustom() const { return mType == ctCUSTOM; } + + const uint160& getCurrency() { return mValue; } + unsigned char getScale() const; + void setScale(unsigned char c); + + // These are only valid for national currencies + std::string getISO() const; + uint16 getVersion() const; +}; + +class Amount +{ + // CAUTION: Currency operations throw on overflows, underflos, or + // incommensurate currency opeations (like adding USD to Euros) +protected: + Currency mCurrency; + uint64 mQuantity; + + void canonicalize(); + + static uint64 sMaxCanon; // Max native currency value before shift + +public: + + Amount(const Currency& c, const uint64& q) : mCurrency(c), mQuantity(q) { canonicalize(); } + + const Currency& getCurrency() const { return mCurrency; } + uint64 getQuantity() const { return mQuantity; } + double getDisplayQuantity() const; + + // These throw if the currencies are incommensurate + // They handle scaling and represent the result as accurately as possible + bool operator==(const Amount&) const; + bool operator!=(const Amount&) const; + bool operator>=(const Amount&) const; + bool operator<=(const Amount&) const; + bool operator>(const Amount&) const; + bool operator<(const Amount&) const; + Amount& operator+=(const Amount& a) { return *this = *this + a; } + Amount& operator-=(const Amount& a) { return *this = *this - a; } + + // This is used to score offers and works with incommensurate currencies + friend void divide(const Amount& offering, const Amount& taking, uint16& exponent, uint64& mantissa); + friend Amount& operator+(const Amount&, const Amount&); + friend Amount& operator-(const Amount&, const Amount&); +}; + +#endif diff --git a/src/ECIES.cpp b/src/ECIES.cpp index c3ba79481b..94e4770546 100644 --- a/src/ECIES.cpp +++ b/src/ECIES.cpp @@ -32,6 +32,9 @@ // 3) Encrypted: Original plaintext // 4) Encrypted: Rest of block/padding +// ECIES operations throw on any error such as a corrupt message or incorrect +// key. They *must* be called in try/catch blocks. + // Algorithmic choices: #define ECIES_KEY_HASH SHA512 // Hash used to expand shared secret #define ECIES_KEY_LENGTH (512/8) // Size of expanded shared secret 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/SerializedLedger.cpp b/src/SerializedLedger.cpp index 3ae422ec79..937673d2ee 100644 --- a/src/SerializedLedger.cpp +++ b/src/SerializedLedger.cpp @@ -1,3 +1,52 @@ #include "SerializedLedger.h" +SerializedLedger::SerializedLedger(SerializerIterator& sit, const uint256& index) + : STObject("LedgerEntry"), mIndex(index) +{ + uint16 type=sit.get16(); + mFormat=getLgrFormat(static_cast(type)); + if(mFormat==NULL) throw std::runtime_error("invalid ledger entry type"); + mType=mFormat->t_type; + mVersion.setValue(type); + mObject=STObject(mFormat->elements, sit, "Entry"); +} + +SerializedLedger::SerializedLedger(LedgerEntryType type) : STObject("LedgerEntry"), mType(type) +{ + mFormat=getLgrFormat(type); + if(mFormat==NULL) throw std::runtime_error("invalid ledger entry type"); + mVersion.setValue(static_cast(mFormat->t_type)); + mObject=STObject(mFormat->elements, "Entry"); +} + +std::string SerializedLedger::getFullText() const +{ + std::string ret="\""; + ret+=mIndex.GetHex(); + ret+="\" = { "; + ret+=mFormat->t_name; + ret+=", "; + ret+=mObject.getFullText(); + ret+="}"; + return ret; +} + +std::string SerializedLedger::getText() const +{ + std::string ret="{"; + ret+=mIndex.GetHex(); + ret+=mVersion.getText(); + ret+=mObject.getText(); + ret+="}"; + return ret; +} + +bool SerializedLedger::isEquivalent(const SerializedType& t) const +{ // locators are not compared + const SerializedLedger* v=dynamic_cast(&t); + if(!v) return false; + if(mType != v->mType) return false; + if(mObject != v->mObject) return false; + return true; +} diff --git a/src/SerializedLedger.h b/src/SerializedLedger.h index 4bdc7d8d4a..3cca93bec4 100644 --- a/src/SerializedLedger.h +++ b/src/SerializedLedger.h @@ -10,21 +10,59 @@ public: typedef boost::shared_ptr pointer; protected: + uint256 mIndex; LedgerEntryType mType; STUInt16 mVersion; STObject mObject; LedgerEntryFormat* mFormat; public: - SerializedLedger(SerializerIterator& sit); + SerializedLedger(SerializerIterator& sit, const uint256& index); SerializedLedger(LedgerEntryType type); int getLength() const { return mVersion.getLength() + mObject.getLength(); } - SerializedTypeID getType() const { return STI_LEDGERENTRY; } - SerializedLedger* duplicate() const { return new SerializedLedger(*this); } - std::string getFullText() const; - std::string getText() const; - void add(Serializer& s) const { mVersion.add(s); mObject.add(s); } + SerializedTypeID getSType() const { return STI_LEDGERENTRY; } + SerializedLedger* duplicate() const { return new SerializedLedger(*this); } + std::string getFullText() const; + std::string getText() const; + void add(Serializer& s) const { mVersion.add(s); mObject.add(s); } + virtual bool isEquivalent(const SerializedType& t) const; + + const uint256& getIndex() const { return mIndex; } + void setIndex(const uint256& i) { mIndex=i; } + + LedgerEntryType getType() const { return mType; } + uint16 getVersion() const { return mVersion.getValue(); } + LedgerEntryFormat* getFormat() { return mFormat; } + + int getIFieldIndex(SOE_Field field) const { return mObject.getFieldIndex(field); } + int getIFieldCount() const { return mObject.getCount(); } + const SerializedType& peekIField(SOE_Field field) const { return mObject.peekAtField(field); } + SerializedType& getIField(SOE_Field field) { return mObject.getField(field); } + + std::string getIFieldString(SOE_Field field) const { return mObject.getFieldString(field); } + unsigned char getIFieldU8(SOE_Field field) const { return mObject.getValueFieldU8(field); } + uint16 getIFieldU16(SOE_Field field) const { return mObject.getValueFieldU16(field); } + uint32 getIFieldU32(SOE_Field field) const { return mObject.getValueFieldU32(field); } + uint64 getIFieldU64(SOE_Field field) const { return mObject.getValueFieldU64(field); } + uint160 getIFieldH160(SOE_Field field) const { return mObject.getValueFieldH160(field); } + uint256 getIFieldH256(SOE_Field field) const { return mObject.getValueFieldH256(field); } + std::vector getIFieldVL(SOE_Field field) const { return mObject.getValueFieldVL(field); } + std::vector getIFieldTL(SOE_Field field) const { return mObject.getValueFieldTL(field); } + void setIFieldU8(SOE_Field field, unsigned char v) { return mObject.setValueFieldU8(field, v); } + void setIFieldU16(SOE_Field field, uint16 v) { return mObject.setValueFieldU16(field, v); } + void setIFieldU32(SOE_Field field, uint32 v) { return mObject.setValueFieldU32(field, v); } + void setIFieldU64(SOE_Field field, uint32 v) { return mObject.setValueFieldU64(field, v); } + void setIFieldH160(SOE_Field field, const uint160& v) { return mObject.setValueFieldH160(field, v); } + void setIFieldH256(SOE_Field field, const uint256& v) { return mObject.setValueFieldH256(field, v); } + void setIFieldVL(SOE_Field field, const std::vector& v) + { return mObject.setValueFieldVL(field, v); } + void setIFieldTL(SOE_Field field, const std::vector& v) + { return mObject.setValueFieldTL(field, v); } + + bool getIFieldPresent(SOE_Field field) const { return mObject.isFieldPresent(field); } + void makeIFieldPresent(SOE_Field field) { return mObject.makeFieldPresent(field); } + void makeIFieldAbsent(SOE_Field field) { return mObject.makeFieldAbsent(field); } }; diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index e27ce55d28..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); @@ -167,6 +173,22 @@ std::string STObject::getText() const return ret; } +bool STObject::isEquivalent(const SerializedType& t) const +{ + const STObject* v=dynamic_cast(&t); + if(!v) return false; + boost::ptr_vector::const_iterator it1=mData.begin(), end1=mData.end(); + boost::ptr_vector::const_iterator it2=v->mData.begin(), end2=v->mData.end(); + while((it1!=end1) && (it2!=end2)) + { + if(it1->getSType() != it2->getSType()) return false; + if(!it1->isEquivalent(*it2)) return false; + ++it1; + ++it2; + } + return (it1==end1) && (it2==end2); +} + int STObject::getFieldIndex(SOE_Field field) const { int i=0; @@ -207,7 +229,7 @@ bool STObject::isFieldPresent(SOE_Field field) const { int index=getFieldIndex(field); if(index==-1) return false; - return peekAtIndex(field).getType()==STI_OBJECT; + return peekAtIndex(field).getSType()==STI_OBJECT; } bool STObject::setFlag(int f) @@ -240,7 +262,7 @@ void STObject::makeFieldPresent(SOE_Field field) { int index=getFieldIndex(field); if(index==-1) throw std::runtime_error("Field not found"); - if(peekAtIndex(field).getType()!=STI_OBJECT) return; + if(peekAtIndex(field).getSType()!=STI_OBJECT) return; mData.replace(index, makeDefaultObject(mType[index]->e_id, mType[index]->e_name)); setFlag(mType[index]->e_flags); } @@ -249,7 +271,7 @@ void STObject::makeFieldAbsent(SOE_Field field) { int index=getFieldIndex(field); if(index==-1) throw std::runtime_error("Field not found"); - if(peekAtIndex(field).getType()==STI_OBJECT) return; + if(peekAtIndex(field).getSType()==STI_OBJECT) return; mData.replace(index, new STObject(mType[index]->e_name)); clearFlag(mType[index]->e_flags); } @@ -265,7 +287,7 @@ unsigned char STObject::getValueFieldU8(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return 0; // optional field not present const STUInt8 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -276,7 +298,7 @@ uint16 STObject::getValueFieldU16(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return 0; // optional field not present const STUInt16 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -287,7 +309,7 @@ uint32 STObject::getValueFieldU32(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return 0; // optional field not present const STUInt32 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -298,7 +320,7 @@ uint64 STObject::getValueFieldU64(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return 0; // optional field not present const STUInt64 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -309,7 +331,7 @@ uint160 STObject::getValueFieldH160(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return uint160(); // optional field not present const STHash160 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -320,7 +342,7 @@ uint256 STObject::getValueFieldH256(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return uint256(); // optional field not present const STHash256 *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -331,7 +353,7 @@ std::vector STObject::getValueFieldVL(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return std::vector(); // optional field not present const STVariableLength *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -342,7 +364,7 @@ std::vector STObject::getValueFieldTL(SOE_Field field) const { const SerializedType* rf=peekAtPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) return std::vector(); // optional field not present const STTaggedList *cf=dynamic_cast(rf); if(!cf) throw std::runtime_error("Wrong field type"); @@ -353,12 +375,12 @@ void STObject::setValueFieldU8(SOE_Field field, unsigned char v) { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STUInt8* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -369,12 +391,12 @@ void STObject::setValueFieldU16(SOE_Field field, uint16 v) { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STUInt16* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -385,12 +407,12 @@ void STObject::setValueFieldU32(SOE_Field field, uint32 v) { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STUInt32* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -401,12 +423,12 @@ void STObject::setValueFieldU64(SOE_Field field, uint64 v) { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STUInt64* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -417,12 +439,12 @@ void STObject::setValueFieldH160(SOE_Field field, const uint160& v) { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STHash160* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -433,12 +455,12 @@ void STObject::setValueFieldVL(SOE_Field field, const std::vector { SerializedType* rf=getPField(field); if(!rf) throw std::runtime_error("Field not found"); - SerializedTypeID id=rf->getType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STVariableLength* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -449,12 +471,12 @@ void STObject::setValueFieldTL(SOE_Field field, const std::vectorgetType(); + SerializedTypeID id=rf->getSType(); if(id==STI_OBJECT) { makeFieldPresent(field); rf=getPField(field); - id=rf->getType(); + id=rf->getSType(); } STTaggedList* cf=dynamic_cast(rf); if(!cf) throw(std::runtime_error("Wrong field type")); @@ -467,7 +489,7 @@ Json::Value STObject::getJson(int options) const int index=1; for(boost::ptr_vector::const_iterator it=mData.begin(), end=mData.end(); it!=end; ++it, ++index) { - if(it->getType()!=STI_NOTPRESENT) + if(it->getSType()!=STI_NOTPRESENT) { if(it->getName()==NULL) ret[boost::lexical_cast(index)]=it->getText(); diff --git a/src/SerializedObject.h b/src/SerializedObject.h index f1e53ad141..89273571f1 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -56,8 +56,9 @@ public: virtual ~STObject() { ; } int getLength() const; - SerializedTypeID getType() const { return STI_OBJECT; } + SerializedTypeID getSType() const { return STI_OBJECT; } STObject* duplicate() const { return new STObject(*this); } + virtual bool isEquivalent(const SerializedType& t) const; void add(Serializer& s) const; std::string getFullText() const; diff --git a/src/SerializedTransaction.cpp b/src/SerializedTransaction.cpp index 8192a8e71b..ebcb290cac 100644 --- a/src/SerializedTransaction.cpp +++ b/src/SerializedTransaction.cpp @@ -77,6 +77,16 @@ int SerializedTransaction::getTransaction(Serializer& s, bool include_length) co return l; } +bool SerializedTransaction::isEquivalent(const SerializedType& t) const +{ // Signatures are not compared + const SerializedTransaction* v=dynamic_cast(&t); + if(!v) return false; + if(type != v->type) return false; + if(mMiddleTxn != v->mMiddleTxn) return false; + if(mInnerTxn != v->mInnerTxn) return false; + return true; +} + uint256 SerializedTransaction::getSigningHash() const { Serializer s; @@ -206,7 +216,7 @@ bool SerializedTransaction::getITFieldPresent(SOE_Field field) const return mInnerTxn.isFieldPresent(field); } -const SerializedType& SerializedTransaction::peekITField(SOE_Field field) +const SerializedType& SerializedTransaction::peekITField(SOE_Field field) const { return mInnerTxn.peekAtField(field); } diff --git a/src/SerializedTransaction.h b/src/SerializedTransaction.h index 644634d8f6..5ec8ca7001 100644 --- a/src/SerializedTransaction.h +++ b/src/SerializedTransaction.h @@ -26,11 +26,12 @@ public: // STObject functions int getLength() const; - SerializedTypeID getType() const { return STI_TRANSACTION; } + SerializedTypeID getSType() const { return STI_TRANSACTION; } SerializedTransaction* duplicate() const { return new SerializedTransaction(*this); } std::string getFullText() const; std::string getText() const; void add(Serializer& s) const { getTransaction(s, true); } + virtual bool isEquivalent(const SerializedType& t) const; // outer transaction functions / signature functions std::vector getSignature() const; @@ -62,7 +63,7 @@ public: // inner transaction field functions int getITFieldIndex(SOE_Field field) const; int getITFieldCount() const; - const SerializedType& peekITField(SOE_Field field); + const SerializedType& peekITField(SOE_Field field) const; SerializedType& getITField(SOE_Field field); // inner transaction field value functions diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index 80d63de302..4e3c1f2331 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -9,7 +9,7 @@ std::string SerializedType::getFullText() const { std::string ret; - if(getType()!=STI_NOTPRESENT) + if(getSType()!=STI_NOTPRESENT) { if(name!=NULL) { @@ -31,6 +31,12 @@ std::string STUInt8::getText() const return boost::lexical_cast(value); } +bool STUInt8::isEquivalent(const SerializedType& t) const +{ + const STUInt8* v=dynamic_cast(&t); + return v && (value==v->value); +} + STUInt16* STUInt16::construct(SerializerIterator& u, const char *name) { return new STUInt16(name, u.get16()); @@ -41,8 +47,14 @@ std::string STUInt16::getText() const return boost::lexical_cast(value); } -STUInt32* STUInt32::construct(SerializerIterator& u, const char *name) +bool STUInt16::isEquivalent(const SerializedType& t) const { + const STUInt16* v=dynamic_cast(&t); + return v && (value==v->value); +} + +STUInt32* STUInt32::construct(SerializerIterator& u, const char *name) + { return new STUInt32(name, u.get32()); } @@ -51,6 +63,12 @@ std::string STUInt32::getText() const return boost::lexical_cast(value); } +bool STUInt32::isEquivalent(const SerializedType& t) const +{ + const STUInt32* v=dynamic_cast(&t); + return v && (value==v->value); +} + STUInt64* STUInt64::construct(SerializerIterator& u, const char *name) { return new STUInt64(name, u.get64()); @@ -61,6 +79,12 @@ std::string STUInt64::getText() const return boost::lexical_cast(value); } +bool STUInt64::isEquivalent(const SerializedType& t) const +{ + const STUInt64* v=dynamic_cast(&t); + return v && (value==v->value); +} + STHash128* STHash128::construct(SerializerIterator& u, const char *name) { return new STHash128(name, u.get128()); @@ -71,6 +95,12 @@ std::string STHash128::getText() const return value.GetHex(); } +bool STHash128::isEquivalent(const SerializedType& t) const +{ + const STHash128* v=dynamic_cast(&t); + return v && (value==v->value); +} + STHash160* STHash160::construct(SerializerIterator& u, const char *name) { return new STHash160(name, u.get160()); @@ -81,6 +111,12 @@ std::string STHash160::getText() const return value.GetHex(); } +bool STHash160::isEquivalent(const SerializedType& t) const +{ + const STHash160* v=dynamic_cast(&t); + return v && (value==v->value); +} + STHash256* STHash256::construct(SerializerIterator& u, const char *name) { return new STHash256(name, u.get256()); @@ -91,6 +127,12 @@ std::string STHash256::getText() const return value.GetHex(); } +bool STHash256::isEquivalent(const SerializedType& t) const +{ + const STHash256* v=dynamic_cast(&t); + return v && (value==v->value); +} + static std::string hex(const std::vector& value) { int dlen=value.size(), i=0; @@ -115,6 +157,12 @@ int STVariableLength::getLength() const return Serializer::encodeLengthLength(value.size()) + value.size(); } +bool STVariableLength::isEquivalent(const SerializedType& t) const +{ + const STVariableLength* v=dynamic_cast(&t); + return v && (value==v->value); +} + std::string STAccount::getText() const { uint160 u; @@ -177,4 +225,8 @@ int STTaggedList::getLength() const return ret; } - +bool STTaggedList::isEquivalent(const SerializedType& t) const +{ + const STTaggedList* v=dynamic_cast(&t); + return v && (value==v->value); +} diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 53b1375c68..ceddccb173 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 @@ -29,13 +30,14 @@ public: SerializedType() : name(NULL) { ; } SerializedType(const char *n) : name(n) { ; } + SerializedType(const SerializedType& n) : name(n.name) { ; } virtual ~SerializedType() { ; } void setName(const char *n) { name=n; } const char *getName() const { return name; } virtual int getLength() const { return 0; } - virtual SerializedTypeID getType() const { return STI_NOTPRESENT; } + virtual SerializedTypeID getSType() const { return STI_NOTPRESENT; } virtual SerializedType* duplicate() const { return new SerializedType(name); } virtual std::string getFullText() const; @@ -46,6 +48,13 @@ public: SerializedType* new_clone(const SerializedType& s) { return s.duplicate(); } void delete_clone(const SerializedType* s) { boost::checked_delete(s); } + + virtual bool isEquivalent(const SerializedType& t) const { return true; } + + bool operator==(const SerializedType& t) const + { return (getSType()==t.getSType()) && isEquivalent(t); } + bool operator!=(const SerializedType& t) const + { return (getSType()!=t.getSType()) || !isEquivalent(t); } }; class STUInt8 : public SerializedType @@ -60,7 +69,7 @@ public: static STUInt8* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 1; } - SerializedTypeID getType() const { return STI_UINT8; } + SerializedTypeID getSType() const { return STI_UINT8; } STUInt8* duplicate() const { return new STUInt8(name, value); } std::string getText() const; void add(Serializer& s) const { s.add8(value); } @@ -70,6 +79,7 @@ public: operator unsigned char() const { return value; } STUInt8& operator=(unsigned char v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STUInt16 : public SerializedType @@ -84,7 +94,7 @@ public: static STUInt16* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 2; } - SerializedTypeID getType() const { return STI_UINT16; } + SerializedTypeID getSType() const { return STI_UINT16; } STUInt16* duplicate() const { return new STUInt16(name, value); } std::string getText() const; void add(Serializer& s) const { s.add16(value); } @@ -94,6 +104,7 @@ public: operator uint16() const { return value; } STUInt16& operator=(uint16 v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STUInt32 : public SerializedType @@ -108,7 +119,7 @@ public: static STUInt32* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 4; } - SerializedTypeID getType() const { return STI_UINT32; } + SerializedTypeID getSType() const { return STI_UINT32; } STUInt32* duplicate() const { return new STUInt32(name, value); } std::string getText() const; void add(Serializer& s) const { s.add32(value); } @@ -118,6 +129,7 @@ public: operator uint32() const { return value; } STUInt32& operator=(uint32 v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STUInt64 : public SerializedType @@ -132,7 +144,7 @@ public: static STUInt64* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 8; } - SerializedTypeID getType() const { return STI_UINT64; } + SerializedTypeID getSType() const { return STI_UINT64; } STUInt64* duplicate() const { return new STUInt64(name, value); } std::string getText() const; void add(Serializer& s) const { s.add64(value); } @@ -142,6 +154,77 @@ public: operator uint64() const { return value; } STUInt64& operator=(uint64 v) { value=v; return *this; } + 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 on entry/exit + 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(); } + STAmount(const STAmount& a) : SerializedType(a), offset(a.offset), value(a.value) { ; } + 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+(STAmount v1, STAmount v2); + friend STAmount operator-(STAmount v1, STAmount v2); + + // Someone is offering X for Y, what is the rate? + friend STAmount getRate(const STAmount& offerIn, const STAmount& offerOut); + + // Someone is offering X for Y, I pay Z, how much do I get? + friend STAmount getClaimed(const STAmount& offerIn, const STAmount& offerOut, const STAmount& paid); + + // Someone is offering X for Y, I need Z, how much do I pay + friend STAmount getNeeded(const STAmount& offerIn, const STAmount& offerOut, const STAmount& needed); }; class STHash128 : public SerializedType @@ -157,7 +240,7 @@ public: static STHash128* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 20; } - SerializedTypeID getType() const { return STI_HASH128; } + SerializedTypeID getSType() const { return STI_HASH128; } STHash128* duplicate() const { return new STHash128(name, value); } virtual std::string getText() const; void add(Serializer& s) const { s.add128(value); } @@ -167,6 +250,7 @@ public: operator uint128() const { return value; } STHash128& operator=(const uint128& v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STHash160 : public SerializedType @@ -182,7 +266,7 @@ public: static STHash160* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 20; } - SerializedTypeID getType() const { return STI_HASH160; } + SerializedTypeID getSType() const { return STI_HASH160; } STHash160* duplicate() const { return new STHash160(name, value); } virtual std::string getText() const; void add(Serializer& s) const { s.add160(value); } @@ -192,6 +276,7 @@ public: operator uint160() const { return value; } STHash160& operator=(const uint160& v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STHash256 : public SerializedType @@ -207,7 +292,7 @@ public: static STHash256* construct(SerializerIterator&, const char *name=NULL); int getLength() const { return 32; } - SerializedTypeID getType() const { return STI_HASH256; } + SerializedTypeID getSType() const { return STI_HASH256; } STHash256* duplicate() const { return new STHash256(name, value); } std::string getText() const; void add(Serializer& s) const { s.add256(value); } @@ -217,6 +302,7 @@ public: operator uint256() const { return value; } STHash256& operator=(const uint256& v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STVariableLength : public SerializedType @@ -233,7 +319,7 @@ public: static STVariableLength* construct(SerializerIterator&, const char *name=NULL); int getLength() const; - virtual SerializedTypeID getType() const { return STI_VL; } + virtual SerializedTypeID getSType() const { return STI_VL; } virtual STVariableLength* duplicate() const { return new STVariableLength(name, value); } virtual std::string getText() const; void add(Serializer& s) const { s.addVL(value); } @@ -245,6 +331,7 @@ public: operator std::vector() const { return value; } STVariableLength& operator=(const std::vector& v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; class STAccount : public STVariableLength @@ -257,7 +344,7 @@ public: STAccount() { ; } static STAccount* construct(SerializerIterator&, const char *name=NULL); - SerializedTypeID getType() const { return STI_ACCOUNT; } + SerializedTypeID getSType() const { return STI_ACCOUNT; } virtual STAccount* duplicate() const { return new STAccount(name, value); } std::string getText() const; @@ -280,7 +367,7 @@ public: static STTaggedList* construct(SerializerIterator&, const char *name=NULL); int getLength() const; - SerializedTypeID getType() const { return STI_TL; } + SerializedTypeID getSType() const { return STI_TL; } STTaggedList* duplicate() const { return new STTaggedList(name, value); } std::string getText() const; void add(Serializer& s) const { if(s.addTaggedList(value)<0) throw(0); } @@ -299,6 +386,7 @@ public: operator std::vector() const { return value; } STTaggedList& operator=(const std::vector& v) { value=v; return *this; } + virtual bool isEquivalent(const SerializedType& t) const; }; #endif 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 },