diff --git a/SQLiteDatabases.sql b/SQLiteDatabases.sql index 13ad457ad..7efa3eb28 100644 --- a/SQLiteDatabases.sql +++ b/SQLiteDatabases.sql @@ -1,17 +1,15 @@ CREATE TABLE Transactions ( -- transactions in all states TransID CHARACTER(64) PRIMARY KEY, -- in hex + TransType CHARACTER(24), FromAcct CHARACTER(40), -- 20 byte hash of pub key in hex FromSeq BIGINT UNSIGNED, -- account seq - FromLedger BIGINT UNSIGNED, - Identifier BIGINT UNSIGNED, - ToAcct CHARACTER(40), -- 20 byte hash of pub key - Amount BIGINT UNSIGNED, - Fee BIGINT UNSIGNED, + OtherAcct CHARACTER(40), -- 20 byte hash of pub key + Amount BIGINT UNSINGED< -- if newcoin transfer FirstSeen TEXT, -- time first seen CommitSeq BIGINT UNSIGNED, -- ledger commited to, 0 if none Status CHARACTER(1), -- (N)ew, (A)ctive, (C)onflicted, (D)one, (H)eld - Signature BLOB + RawTxn BLOB ); CREATE TABLE PubKeys ( -- holds pub keys for nodes and accounts diff --git a/src/DBInit.cpp b/src/DBInit.cpp index 3bc64d4f9..116df7887 100644 --- a/src/DBInit.cpp +++ b/src/DBInit.cpp @@ -4,17 +4,15 @@ const char *TxnDBInit[] = { "CREATE TABLE Transactions ( \ TransID CHARACTER(64) PRIMARY KEY, \ + TransType CHARACTER(24) \ FromAcct CHARACTER(35), \ FromSeq BIGINT UNSIGNED, \ - FromLedger BIGINT UNSIGNED, \ - Identifier BIGINT UNSIGNED, \ - ToAcct CHARACTER(35), \ + OtherAcct CHARACTER(40), \ Amount BIGINT UNSIGNED, \ - Fee BIGINT UNSIGNED, \ FirstSeen TEXT, \ CommitSeq BIGINT UNSIGNED, \ Status CHARACTER(1), \ - Signature BLOB \ + RawTxn BLOB \ );", "CREATE TABLE PubKeys ( \ ID CHARACTER(35) PRIMARY KEY, \ diff --git a/src/Ledger.cpp b/src/Ledger.cpp index a6d3bd57a..7779e72fc 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -154,7 +154,9 @@ bool Ledger::addTransaction(Transaction::pointer trans) { // low-level - just add to table assert(!mAccepted); assert(!!trans->getID()); - SHAMapItem::pointer item=boost::make_shared(trans->getID(), trans->getSigned()->getData()); + Serializer s; + trans->getSTransaction()->getTransaction(s, false); + SHAMapItem::pointer item=boost::make_shared(trans->getID(), s.peekData()); return mTransactionMap->addGiveItem(item, true); } diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index e5a1279c6..5ef3a0e8c 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -71,7 +71,8 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, newcoin::TMTransaction *tx=new newcoin::TMTransaction(); - Serializer::pointer s(trans->getSigned()); + Serializer::pointer s; + trans->getSTransaction()->getTransaction(*s, false); tx->set_rawtransaction(&s->getData().front(), s->getLength()); tx->set_status(newcoin::tsCURRENT); tx->set_receivetimestamp(getNetworkTime()); diff --git a/src/Peer.cpp b/src/Peer.cpp index c032b77f4..14e190004 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -5,6 +5,7 @@ //#include #include #include +#include #include "../json/writer.h" @@ -13,6 +14,7 @@ #include "Config.h" #include "Application.h" #include "Conversion.h" +#include "SerializedTransaction.h" Peer::Peer(boost::asio::io_service& io_service) : mSocket(io_service) @@ -335,12 +337,20 @@ void Peer::recvTransaction(newcoin::TMTransaction& packet) std::cerr << "Got transaction from peer" << std::endl; #endif - std::string rawTx=packet.rawtransaction(); - std::vector rTx(rawTx.begin(), rawTx.end()); - Transaction::pointer tx=boost::make_shared(rTx, true); + Transaction::pointer tx; + try + { + std::string rawTx=packet.rawtransaction(); + Serializer s(std::vector(rawTx.begin(), rawTx.end())); + SerializerIterator sit(s); + SerializedTransaction::pointer stx=boost::make_shared(boost::ref(sit), -1); - if(tx->getStatus()==INVALID) - { // transaction fails basic validity tests + if(stx->getTxnType()!=ttMAKE_PAYMENT) throw(0); // FIXME to support other transaction + tx=boost::make_shared(stx, true); + if(tx->getStatus()==INVALID) throw(0); + } + catch (...) + { #ifdef DEBUG std::cerr << "Transaction from peer fails validity tests" << std::endl; Json::StyledStreamWriter w; diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index e9805ed83..e27ce55d2 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -1,53 +1,98 @@ #include "SerializedObject.h" -STUObject::STUObject(SOElement* elem, const char *name) : SerializedType(name) +#include "boost/lexical_cast.hpp" + +SerializedType* STObject::makeDefaultObject(SerializedTypeID id, const char *name) +{ + switch(id) + { + case STI_UINT16: + return new STUInt16(name); + + case STI_UINT32: + return new STUInt32(name); + + case STI_UINT64: + return new STUInt64(name); + + case STI_HASH160: + return new STHash160(name); + + case STI_HASH256: + return new STHash256(name); + + case STI_VL: + return new STVariableLength(name); + + case STI_TL: + return new STTaggedList(name); + + case STI_ACCOUNT: + return new STAccount(name); + + default: + return NULL; + } +} + +SerializedType* STObject::makeDeserializedObject(SerializedTypeID id, const char *name, SerializerIterator& sit) +{ + switch(id) + { + case STI_UINT16: + return STUInt16::construct(sit, name); + + case STI_UINT32: + return STUInt32::construct(sit, name); + + case STI_UINT64: + return STUInt64::construct(sit, name); + + case STI_HASH160: + return STHash160::construct(sit, name); + + case STI_HASH256: + return STHash256::construct(sit, name); + + case STI_VL: + return STVariableLength::construct(sit, name); + + case STI_TL: + return STTaggedList::construct(sit, name); + + case STI_ACCOUNT: + return STAccount::construct(sit, name); + + default: + return NULL; + } +} + +STObject::STObject(SOElement* elem, const char *name) : SerializedType(name), mFlagIdx(-1) { while(elem->e_id!=STI_DONE) { - type.push_back(elem); + if(elem->e_type==SOE_FLAGS) mFlagIdx=mType.size(); + mType.push_back(elem); if( (elem->e_type==SOE_IFFLAG) || (elem->e_type==SOE_IFNFLAG) ) - giveObject(new STUObject(elem->e_name)); - else switch(elem->e_id) + giveObject(new STObject(elem->e_name)); + else { - case STI_UINT16: - giveObject(new STUInt16(elem->e_name)); - break; - case STI_UINT32: - giveObject(new STUInt32(elem->e_name)); - break; - case STI_UINT64: - giveObject(new STUInt64(elem->e_name)); - break; - case STI_HASH160: - giveObject(new STHash160(elem->e_name)); - break; - case STI_HASH256: - giveObject(new STHash256(elem->e_name)); - break; - case STI_VL: - giveObject(new STVariableLength(elem->e_name)); - break; - case STI_TL: - giveObject(new STTaggedList(elem->e_name)); - break; -#if 0 - case STI_ACCOUNT: // CHECKME: Should an account be variable length? - giveObject(new STVariableLength(elem->e_name)); - break; -#endif - default: throw(std::runtime_error("invalid transaction element")); + SerializedType* t=makeDefaultObject(elem->e_id, elem->e_name); + if(!t) throw(std::runtime_error("invalid transaction element")); + giveObject(t); } elem++; } } -STUObject::STUObject(SOElement* elem, SerializerIterator& sit, const char *name) : SerializedType(name) +STObject::STObject(SOElement* elem, SerializerIterator& sit, const char *name) : SerializedType(name), mFlagIdx(-1) { int flags=-1; while(elem->e_id!=STI_DONE) { - type.push_back(elem); + mType.push_back(elem); bool done=false; if(elem->e_type==SOE_IFFLAG) { @@ -63,47 +108,20 @@ STUObject::STUObject(SOElement* elem, SerializerIterator& sit, const char *name) { assert(elem->e_id==STI_UINT16); flags=sit.get16(); - giveObject(new STUInt16(elem->e_name, flags)); + mFlagIdx=giveObject(new STUInt16(elem->e_name, flags)); done=true; } if(!done) { - switch(elem->e_id) - { - case STI_UINT16: - giveObject(STUInt16::construct(sit, elem->e_name)); - break; - case STI_UINT32: - giveObject(STUInt32::construct(sit, elem->e_name)); - break; - case STI_UINT64: - giveObject(STUInt64::construct(sit, elem->e_name)); - break; - case STI_HASH160: - giveObject(STHash160::construct(sit, elem->e_name)); - break; - case STI_HASH256: - giveObject(STHash256::construct(sit, elem->e_name)); - break; - case STI_VL: - giveObject(STVariableLength::construct(sit, elem->e_name)); - break; - case STI_TL: - giveObject(STTaggedList::construct(sit, elem->e_name)); - break; - #if 0 - case STI_ACCOUNT: // CHECKME: Should an account be variable length? - giveObject(STVariableLength::construct(sit, elem->e_name)); - break; - #endif - default: throw(std::runtime_error("invalid transaction element")); - } + SerializedType* t=makeDeserializedObject(elem->e_id, elem->e_name, sit); + if(!t) throw(std::runtime_error("invalid transaction element")); + giveObject(t); } elem++; } } -std::string STUObject::getFullText() const +std::string STObject::getFullText() const { std::string ret; if(name!=NULL) @@ -112,31 +130,31 @@ std::string STUObject::getFullText() const ret+=" = {"; } else ret="{"; - for(boost::ptr_vector::const_iterator it=data.begin(), end=data.end(); it!=end; ++it) + for(boost::ptr_vector::const_iterator it=mData.begin(), end=mData.end(); it!=end; ++it) ret+=it->getFullText(); ret+="}"; return ret; } -int STUObject::getLength() const +int STObject::getLength() const { int ret=0; - for(boost::ptr_vector::const_iterator it=data.begin(), end=data.end(); it!=end; ++it) + for(boost::ptr_vector::const_iterator it=mData.begin(), end=mData.end(); it!=end; ++it) ret+=it->getLength(); return ret; } -void STUObject::add(Serializer& s) const +void STObject::add(Serializer& s) const { - for(boost::ptr_vector::const_iterator it=data.begin(), end=data.end(); it!=end; ++it) + for(boost::ptr_vector::const_iterator it=mData.begin(), end=mData.end(); it!=end; ++it) it->add(s); } -std::string STUObject::getText() const +std::string STObject::getText() const { std::string ret="{"; bool first=false; - for(boost::ptr_vector::const_iterator it=data.begin(), end=data.end(); it!=end; ++it) + for(boost::ptr_vector::const_iterator it=mData.begin(), end=mData.end(); it!=end; ++it) { if(!first) { @@ -148,3 +166,313 @@ std::string STUObject::getText() const ret+="}"; return ret; } + +int STObject::getFieldIndex(SOE_Field field) const +{ + int i=0; + for(std::vector::const_iterator it=mType.begin(), end=mType.end(); it!=end; ++it, ++i) + if((*it)->e_field==field) return i; + return -1; +} + +const SerializedType& STObject::peekAtField(SOE_Field field) const +{ + int index=getFieldIndex(field); + if(index==-1) throw std::runtime_error("Field not found"); + return peekAtIndex(index); +} + +SerializedType& STObject::getField(SOE_Field field) +{ + int index=getFieldIndex(field); + if(index==-1) throw std::runtime_error("Field not found"); + return getIndex(index); +} + +const SerializedType* STObject::peekAtPField(SOE_Field field) const +{ + int index=getFieldIndex(field); + if(index==-1) return NULL; + return peekAtPIndex(index); +} + +SerializedType* STObject::getPField(SOE_Field field) +{ + int index=getFieldIndex(field); + if(index==-1) return NULL; + return getPIndex(index); +} + +bool STObject::isFieldPresent(SOE_Field field) const +{ + int index=getFieldIndex(field); + if(index==-1) return false; + return peekAtIndex(field).getType()==STI_OBJECT; +} + +bool STObject::setFlag(int f) +{ + if(mFlagIdx<0) return false; + STUInt16* t=dynamic_cast(getPIndex(mFlagIdx)); + assert(t); + t->setValue(t->getValue() | f); + return true; +} + +bool STObject::clearFlag(int f) +{ + if(mFlagIdx<0) return false; + STUInt16* t=dynamic_cast(getPIndex(mFlagIdx)); + assert(t); + t->setValue(t->getValue() & ~f); + return true; +} + +int STObject::getFlag(void) const +{ + if(mFlagIdx<0) return 0; + const STUInt16* t=dynamic_cast(peekAtPIndex(mFlagIdx)); + assert(t); + return t->getValue(); +} + +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; + mData.replace(index, makeDefaultObject(mType[index]->e_id, mType[index]->e_name)); + setFlag(mType[index]->e_flags); +} + +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; + mData.replace(index, new STObject(mType[index]->e_name)); + clearFlag(mType[index]->e_flags); +} + +std::string STObject::getFieldString(SOE_Field field) const +{ + const SerializedType* rf=peekAtPField(field); + if(!rf) throw std::runtime_error("Field not found"); + return rf->getText(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + 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"); + return cf->getValue(); +} + +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(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STUInt8* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +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(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STUInt16* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +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(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STUInt32* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +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(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STUInt64* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +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(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STHash160* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +void STObject::setValueFieldVL(SOE_Field field, const std::vector& v) +{ + SerializedType* rf=getPField(field); + if(!rf) throw std::runtime_error("Field not found"); + SerializedTypeID id=rf->getType(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STVariableLength* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +void STObject::setValueFieldTL(SOE_Field field, const std::vector& v) +{ + SerializedType* rf=getPField(field); + if(!rf) throw std::runtime_error("Field not found"); + SerializedTypeID id=rf->getType(); + if(id==STI_OBJECT) + { + makeFieldPresent(field); + rf=getPField(field); + id=rf->getType(); + } + STTaggedList* cf=dynamic_cast(rf); + if(!cf) throw(std::runtime_error("Wrong field type")); + cf->setValue(v); +} + +Json::Value STObject::getJson(int options) const +{ + Json::Value ret(Json::objectValue); + 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->getName()==NULL) + ret[boost::lexical_cast(index)]=it->getText(); + else ret[it->getName()]=it->getText(); + } + } + return ret; +} diff --git a/src/SerializedObject.h b/src/SerializedObject.h index a0f8c3413..10f4585b7 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -5,6 +5,8 @@ #include +#include "../json/value.h" + #include "SerializedTypes.h" enum SOE_Type @@ -12,42 +14,101 @@ enum SOE_Type SOE_NEVER=-1, SOE_REQUIRED=0, SOE_FLAGS, SOE_IFFLAG=1, SOE_IFNFLAG=2 }; +enum SOE_Field +{ + sfInvalid=-1, + sfGeneric=0, + + // common fields + sfFlags, sfExtensions, sfTargetLedger, sfSourceTag, sfIdentifier, + sfDestination, sfTarget, sfAmount, sfCurrency, + sfAmountIn, sfAmountOut, sfCurrencyIn, sfCurrencyOut, + sfInvoiceID, + sfExpireLedger +}; + struct SOElement { // An element in the description of a serialized object + SOE_Field e_field; const char *e_name; SerializedTypeID e_id; SOE_Type e_type; int e_flags; }; -class STUObject : public SerializedType +class STObject : public SerializedType { protected: - boost::ptr_vector data; - std::vector type; + int mFlagIdx; // the offset to the flags object, -1 if none + boost::ptr_vector mData; + std::vector mType; + + static SerializedType* makeDefaultObject(SerializedTypeID id, const char *name); + static SerializedType* makeDeserializedObject(SerializedTypeID id, const char *name, SerializerIterator&); public: - STUObject(const char *n=NULL) : SerializedType(n) { ; } - STUObject(SOElement *t, const char *n=NULL); - STUObject(SOElement *t, SerializerIterator& u, const char *n=NULL); - virtual ~STUObject() { ; } + STObject(const char *n=NULL) : SerializedType(n), mFlagIdx(-1) { ; } + STObject(SOElement *t, const char *n=NULL); + STObject(SOElement *t, SerializerIterator& u, const char *n=NULL); + virtual ~STObject() { ; } int getLength() const; SerializedTypeID getType() const { return STI_OBJECT; } - STUObject* duplicate() const { return new STUObject(*this); } + STObject* duplicate() const { return new STObject(*this); } void add(Serializer& s) const; std::string getFullText() const; std::string getText() const; + virtual Json::Value getJson(int options) const; - void addObject(const SerializedType& t) { data.push_back(t.duplicate()); } - void giveObject(SerializedType* t) { data.push_back(t); } - const boost::ptr_vector& peekData() const { return data; } - boost::ptr_vector& peekData() { return data; } + int addObject(const SerializedType& t) { mData.push_back(t.duplicate()); return mData.size()-1; } + int giveObject(SerializedType* t) { mData.push_back(t); return mData.size()-1; } + const boost::ptr_vector& peekData() const { return mData; } + boost::ptr_vector& peekData() { return mData; } - int getCount() const { return data.size(); } - const SerializedType& peekAt(int offset) const { return data[offset]; } - SerializedType& getAt(int offset) { return data[offset]; } + int getCount() const { return mData.size(); } + + bool setFlag(int); + bool clearFlag(int); + int getFlag() const; + + const SerializedType& peekAtIndex(int offset) const { return mData[offset]; } + SerializedType& getIndex(int offset) { return mData[offset]; } + const SerializedType* peekAtPIndex(int offset) const { return &(mData[offset]); } + SerializedType* getPIndex(int offset) { return &(mData[offset]); } + + int getFieldIndex(SOE_Field field) const; + + const SerializedType& peekAtField(SOE_Field field) const; + SerializedType& getField(SOE_Field field); + const SerializedType* peekAtPField(SOE_Field field) const; + SerializedType* getPField(SOE_Field field); + const SOElement* getFieldType(SOE_Field field) const; + + // these throw if the field type doesn't match, or return default values if the + // field is optional but not present + std::string getFieldString(SOE_Field field) const; + unsigned char getValueFieldU8(SOE_Field field) const; + uint16 getValueFieldU16(SOE_Field field) const; + uint32 getValueFieldU32(SOE_Field field) const; + uint64 getValueFieldU64(SOE_Field field) const; + uint160 getValueFieldH160(SOE_Field field) const; + uint256 getValueFieldH256(SOE_Field field) const; + std::vector getValueFieldVL(SOE_Field field) const; + std::vector getValueFieldTL(SOE_Field field) const; + + void setValueFieldU8(SOE_Field field, unsigned char); + void setValueFieldU16(SOE_Field field, uint16); + void setValueFieldU32(SOE_Field field, uint32); + void setValueFieldU64(SOE_Field field, uint64); + void setValueFieldH160(SOE_Field field, const uint160&); + void setValueFieldH256(SOE_Field field, const uint256&); + void setValueFieldVL(SOE_Field field, const std::vector&); + void setValueFieldTL(SOE_Field field, const std::vector&); + + bool isFieldPresent(SOE_Field field) const; + void makeFieldPresent(SOE_Field field); + void makeFieldAbsent(SOE_Field field); }; diff --git a/src/SerializedTransaction.cpp b/src/SerializedTransaction.cpp index ce7d97f29..b88472d55 100644 --- a/src/SerializedTransaction.cpp +++ b/src/SerializedTransaction.cpp @@ -7,15 +7,18 @@ SerializedTransaction::SerializedTransaction(TransactionType type) if(mFormat==NULL) throw(std::runtime_error("invalid transaction type")); mMiddleTxn.giveObject(new STUInt32("Magic", TransactionMagic)); - mMiddleTxn.giveObject(new STVariableLength("Signature")); // signature + mMiddleTxn.giveObject(new STVariableLength("SigningAccount")); + mMiddleTxn.giveObject(new STUInt32("Sequence")); mMiddleTxn.giveObject(new STUInt8("Type", static_cast(type))); + mMiddleTxn.giveObject(new STUInt64("Fee")); - mInnerTxn=STUObject(mFormat->elements, "InnerTransaction"); + mInnerTxn=STObject(mFormat->elements, "InnerTransaction"); } SerializedTransaction::SerializedTransaction(SerializerIterator& sit, int length) { - if(length==0) length=sit.get32(); + if(length==-1) length=sit.getBytesLeft(); + else if(length==0) length=sit.get32(); if( (lengthTransactionMaxLen) ) throw(std::runtime_error("Transaction length invalid")); @@ -25,14 +28,16 @@ SerializedTransaction::SerializedTransaction(SerializerIterator& sit, int length throw(std::runtime_error("Transaction has invalid magic")); mMiddleTxn.giveObject(new STUInt32("Magic", TransactionMagic)); - mMiddleTxn.giveObject(new STVariableLength("Signature", sit.getVL())); + mMiddleTxn.giveObject(new STVariableLength("SigningAccount", sit.getVL())); + mMiddleTxn.giveObject(new STUInt32("Sequence", sit.get32())); int type=sit.get32(); mMiddleTxn.giveObject(new STUInt32("Type", type)); mFormat=getFormat(static_cast(type)); if(!mFormat) throw(std::runtime_error("Transaction has invalid type")); + mMiddleTxn.giveObject(new STUInt64("Fee", sit.get64())); - mInnerTxn=STUObject(mFormat->elements, sit, "InnerTransaction"); + mInnerTxn=STObject(mFormat->elements, sit, "InnerTransaction"); } int SerializedTransaction::getLength() const @@ -41,8 +46,10 @@ int SerializedTransaction::getLength() const } std::string SerializedTransaction::getFullText() const -{ // WRITEME: Add transaction ID - std::string ret="{"; +{ + std::string ret="\""; + ret+=getTransactionID().GetHex(); + ret+="\" = {"; ret+=mSignature.getFullText(); ret+=mMiddleTxn.getFullText(); ret+=mInnerTxn.getFullText(); @@ -56,9 +63,176 @@ std::string SerializedTransaction::getText() const ret+=mSignature.getText(); ret+=mMiddleTxn.getText(); ret+=mInnerTxn.getText(); + ret+="}"; return ret; } -void SerializedTransaction::add(Serializer& s) const +int SerializedTransaction::getTransaction(Serializer& s, bool include_length) const { + int l=getLength(); + if(include_length) s.add32(l); + mSignature.add(s); + mMiddleTxn.add(s); + mInnerTxn.add(s); + return l; +} + +uint256 SerializedTransaction::getSigningHash() const +{ + Serializer s; + mMiddleTxn.add(s); + mInnerTxn.add(s); + return s.getSHA512Half(); +} + +uint256 SerializedTransaction::getTransactionID() const +{ // perhaps we should cache this + Serializer s; + mSignature.add(s); + mMiddleTxn.add(s); + mInnerTxn.add(s); + return s.getSHA512Half(); +} + +std::vector SerializedTransaction::getSignature() const +{ + return mSignature.getValue(); +} + +const std::vector& SerializedTransaction::peekSignature() const +{ + return mSignature.peekValue(); +} + +bool SerializedTransaction::sign(CKey& key) +{ + return key.Sign(getSigningHash(), mSignature.peekValue()); +} + +bool SerializedTransaction::checkSign(const CKey& key) const +{ + return key.Verify(getSigningHash(), mSignature.getValue()); +} + +void SerializedTransaction::setSignature(const std::vector& sig) +{ + mSignature.setValue(sig); +} + +uint32 SerializedTransaction::getVersion() const +{ + const STUInt32* v=dynamic_cast(mMiddleTxn.peekAtPIndex(TransactionIVersion)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->getValue(); +} + +void SerializedTransaction::setVersion(uint32 ver) +{ + STUInt32* v=dynamic_cast(mMiddleTxn.getPIndex(TransactionIVersion)); + if(!v) throw(std::runtime_error("corrupt transaction")); + v->setValue(ver); +} + +uint64 SerializedTransaction::getTransactionFee() const +{ + const STUInt64* v=dynamic_cast(mMiddleTxn.peekAtPIndex(TransactionIFee)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->getValue(); +} + +void SerializedTransaction::setTransactionFee(uint64 fee) +{ + STUInt64* v=dynamic_cast(mMiddleTxn.getPIndex(TransactionIFee)); + if(!v) throw(std::runtime_error("corrupt transaction")); + v->setValue(fee); +} + +uint32 SerializedTransaction::getSequence() const +{ + const STUInt32* v=dynamic_cast(mMiddleTxn.peekAtPIndex(TransactionISequence)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->getValue(); +} + +void SerializedTransaction::setSequence(uint32 seq) +{ + STUInt32* v=dynamic_cast(mMiddleTxn.getPIndex(TransactionISequence)); + if(!v) throw(std::runtime_error("corrupt transaction")); + v->setValue(seq); +} + +std::vector SerializedTransaction::getSigningAccount() const +{ + const STVariableLength* v= + dynamic_cast(mMiddleTxn.peekAtPIndex(TransactionISigningAccount)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->getValue(); +} + +const std::vector& SerializedTransaction::peekSigningAccount() const +{ + const STVariableLength* v= + dynamic_cast(mMiddleTxn.peekAtPIndex(TransactionISigningAccount)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->peekValue(); +} + +std::vector& SerializedTransaction::peekSigningAccount() +{ + STVariableLength* v=dynamic_cast(mMiddleTxn.getPIndex(TransactionISigningAccount)); + if(!v) throw(std::runtime_error("corrupt transaction")); + return v->peekValue(); +} + +void SerializedTransaction::setSigningAccount(const std::vector& s) +{ + STVariableLength* v=dynamic_cast(mMiddleTxn.getPIndex(TransactionISigningAccount)); + if(!v) throw(std::runtime_error("corrupt transaction")); + v->setValue(s); +} + +int SerializedTransaction::getITFieldIndex(SOE_Field field) const +{ + return mInnerTxn.getFieldIndex(field); +} + +int SerializedTransaction::getITFieldCount() const +{ + return mInnerTxn.getCount(); +} + +bool SerializedTransaction::getITFieldPresent(SOE_Field field) const +{ + return mInnerTxn.isFieldPresent(field); +} + +const SerializedType& SerializedTransaction::peekITField(SOE_Field field) +{ + return mInnerTxn.peekAtField(field); +} + +SerializedType& SerializedTransaction::getITField(SOE_Field field) +{ + return mInnerTxn.getField(field); +} + +void SerializedTransaction::makeITFieldPresent(SOE_Field field) +{ + return mInnerTxn.makeFieldPresent(field); +} + +void SerializedTransaction::makeITFieldAbsent(SOE_Field field) +{ + return mInnerTxn.makeFieldAbsent(field); +} + +Json::Value SerializedTransaction::getJson(int options) const +{ + Json::Value ret(Json::objectValue); + ret["Type"]=mFormat->t_name; + ret["ID"]=getTransactionID().GetHex(); + ret["Signature"]=mSignature.getText(); + ret["Middle"]=mMiddleTxn.getJson(options); + ret["Inner"]=mInnerTxn.getJson(options); + return ret; } diff --git a/src/SerializedTransaction.h b/src/SerializedTransaction.h index d5cc95e2b..644634d8f 100644 --- a/src/SerializedTransaction.h +++ b/src/SerializedTransaction.h @@ -3,41 +3,52 @@ #include +#include + #include "uint256.h" #include "SerializedObject.h" #include "TransactionFormats.h" -class SerializedTransaction : public STUObject +class SerializedTransaction : public STObject { +public: + typedef boost::shared_ptr pointer; + protected: TransactionType type; STVariableLength mSignature; - STUObject mMiddleTxn, mInnerTxn; + STObject mMiddleTxn, mInnerTxn; TransactionFormat* mFormat; public: - SerializedTransaction(SerializerIterator&, int length); + SerializedTransaction(SerializerIterator& sit, int length); // -1=all remaining, 0=get from sit SerializedTransaction(TransactionType type); - // STUObject functions + // STObject functions int getLength() const; SerializedTypeID getType() const { return STI_TRANSACTION; } SerializedTransaction* duplicate() const { return new SerializedTransaction(*this); } std::string getFullText() const; std::string getText() const; - void add(Serializer& s) const; + void add(Serializer& s) const { getTransaction(s, true); } // outer transaction functions / signature functions std::vector getSignature() const; + const std::vector& peekSignature() const; void setSignature(const std::vector& s); uint256 getSigningHash() const; // middle transaction functions uint32 getVersion() const; void setVersion(uint32); - int getTransactionType() const; + TransactionType getTxnType() const { return type; } uint64 getTransactionFee() const; void setTransactionFee(uint64); + std::vector getSigningAccount() const; + const std::vector& peekSigningAccount() const; + std::vector& peekSigningAccount(); + void setSigningAccount(const std::vector& s); + std::string getTransactionType() const { return mFormat->t_name; } // inner transaction functions uint16 getFlags() const; @@ -49,15 +60,45 @@ public: void setSequence(uint32); // inner transaction field functions - int getITFieldIndex(const char *) const; + int getITFieldIndex(SOE_Field field) const; int getITFieldCount() const; - bool getITFieldPresent(int index) const; - const SerializedType& peekITField(int index); - SerializedType& getITField(int index); - void makeITFieldPresent(int index); + const SerializedType& peekITField(SOE_Field field); + SerializedType& getITField(SOE_Field field); + + // inner transaction field value functions + std::string getITFieldString(SOE_Field field) const { return mInnerTxn.getFieldString(field); } + unsigned char getITFieldU8(SOE_Field field) const { return mInnerTxn.getValueFieldU8(field); } + uint16 getITFieldU16(SOE_Field field) const { return mInnerTxn.getValueFieldU16(field); } + uint32 getITFieldU32(SOE_Field field) const { return mInnerTxn.getValueFieldU32(field); } + uint64 getITFieldU64(SOE_Field field) const { return mInnerTxn.getValueFieldU64(field); } + uint160 getITFieldH160(SOE_Field field) const { return mInnerTxn.getValueFieldH160(field); } + uint256 getITFieldH256(SOE_Field field) const { return mInnerTxn.getValueFieldH256(field); } + std::vector getITFieldVL(SOE_Field field) const { return mInnerTxn.getValueFieldVL(field); } + std::vector getITFieldTL(SOE_Field field) const { return mInnerTxn.getValueFieldTL(field); } + void setITFieldU8(SOE_Field field, unsigned char v) { return mInnerTxn.setValueFieldU8(field, v); } + void setITFieldU16(SOE_Field field, uint16 v) { return mInnerTxn.setValueFieldU16(field, v); } + void setITFieldU32(SOE_Field field, uint32 v) { return mInnerTxn.setValueFieldU32(field, v); } + void setITFieldU64(SOE_Field field, uint32 v) { return mInnerTxn.setValueFieldU64(field, v); } + void setITFieldH160(SOE_Field field, const uint160& v) { return mInnerTxn.setValueFieldH160(field, v); } + void setITFieldH256(SOE_Field field, const uint256& v) { return mInnerTxn.setValueFieldH256(field, v); } + void setITFieldVL(SOE_Field field, const std::vector& v) + { return mInnerTxn.setValueFieldVL(field, v); } + void setITFieldTL(SOE_Field field, const std::vector& v) + { return mInnerTxn.setValueFieldTL(field, v); } + + // optional field functions + bool getITFieldPresent(SOE_Field field) const; + void makeITFieldPresent(SOE_Field field); + void makeITFieldAbsent(SOE_Field field); // whole transaction functions - int getTransaction(Serializer& s, bool include_length); + int getTransaction(Serializer& s, bool include_length) const; + uint256 getTransactionID() const; + + virtual Json::Value getJson(int options) const; + + bool sign(CKey& key); + bool checkSign(const CKey& key) const; }; #endif diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index 9f94631b7..885886adf 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -4,6 +4,7 @@ #include "SerializedTypes.h" #include "SerializedObject.h" #include "TransactionFormats.h" +#include "NewcoinAddress.h" std::string SerializedType::getFullText() const { @@ -104,6 +105,44 @@ int STVariableLength::getLength() const return Serializer::encodeLengthLength(value.size()) + value.size(); } +std::string STAccount::getText() const +{ + uint160 u; + NewcoinAddress a; + if(!getValueH160(u)) return STVariableLength::getText(); + a.setAccountID(u); + return a.humanAccountPublic(); +} + +STAccount* STAccount::construct(SerializerIterator& u, const char *name) +{ + STAccount *ret=new STAccount(u.getVL()); + if(!ret->isValueH160()) + { + delete ret; + throw(std::runtime_error("invalid account in transaction")); + } + return ret; +} + +bool STAccount::isValueH160() const +{ + return peekValue().size() == (160/8); +} + +void STAccount::setValueH160(const uint160& v) +{ + peekValue().empty(); + peekValue().insert(peekValue().end(), v.begin(), v.end()); +} + +bool STAccount::getValueH160(uint160& v) const +{ + if(!isValueH160()) return false; + memcpy(v.begin(), &(peekValue().front()), 32); + return true; +} + std::string STTaggedList::getText() const { std::string ret; @@ -128,3 +167,4 @@ int STTaggedList::getLength() const return ret; } + diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 8a9fced78..824d170fa 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -17,7 +17,7 @@ enum SerializedTypeID STI_HASH160=6, STI_HASH256=7, STI_VL=8, STI_TL=9, // high level types - STI_TRANSACTION=10 + STI_ACCOUNT=10, STI_TRANSACTION=10 }; class SerializedType @@ -32,7 +32,7 @@ public: virtual ~SerializedType() { ; } void setName(const char *n) { name=n; } - const char *getName() { return name; } + const char *getName() const { return name; } virtual int getLength() const { return 0; } virtual SerializedTypeID getType() const { return STI_NOTPRESENT; } @@ -61,9 +61,9 @@ public: int getLength() const { return 1; } SerializedTypeID getType() const { return STI_UINT8; } - STUInt8 *duplicate() const { return new STUInt8(name, value); } + STUInt8* duplicate() const { return new STUInt8(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { s.add8(value); } + void add(Serializer& s) const { s.add8(value); } unsigned char getValue() const { return value; } void setValue(unsigned char v) { value=v; } @@ -85,9 +85,9 @@ public: int getLength() const { return 2; } SerializedTypeID getType() const { return STI_UINT16; } - STUInt16 *duplicate() const { return new STUInt16(name, value); } + STUInt16* duplicate() const { return new STUInt16(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { s.add16(value); } + void add(Serializer& s) const { s.add16(value); } uint16 getValue() const { return value; } void setValue(uint16 v) { value=v; } @@ -109,9 +109,9 @@ public: int getLength() const { return 4; } SerializedTypeID getType() const { return STI_UINT32; } - STUInt32 *duplicate() const { return new STUInt32(name, value); } + STUInt32* duplicate() const { return new STUInt32(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { s.add32(value); } + void add(Serializer& s) const { s.add32(value); } uint32 getValue() const { return value; } void setValue(uint32 v) { value=v; } @@ -133,9 +133,9 @@ public: int getLength() const { return 8; } SerializedTypeID getType() const { return STI_UINT64; } - STUInt64 *duplicate() const { return new STUInt64(name, value); } + STUInt64* duplicate() const { return new STUInt64(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { s.add64(value); } + void add(Serializer& s) const { s.add64(value); } uint64 getValue() const { return value; } void setValue(uint64 v) { value=v; } @@ -158,9 +158,9 @@ public: int getLength() const { return 20; } SerializedTypeID getType() const { return STI_HASH160; } - STHash160 *duplicate() const { return new STHash160(name, value); } - std::string getText() const; - virtual void add(Serializer& s) const { s.add160(value); } + STHash160* duplicate() const { return new STHash160(name, value); } + virtual std::string getText() const; + void add(Serializer& s) const { s.add160(value); } const uint160& getValue() const { return value; } void setValue(const uint160& v) { value=v; } @@ -183,9 +183,9 @@ public: int getLength() const { return 32; } SerializedTypeID getType() const { return STI_HASH256; } - STHash256 *duplicate() const { return new STHash256(name, value); } + STHash256* duplicate() const { return new STHash256(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { s.add256(value); } + void add(Serializer& s) const { s.add256(value); } const uint256& getValue() const { return value; } void setValue(const uint256& v) { value=v; } @@ -208,10 +208,10 @@ public: static STVariableLength* construct(SerializerIterator&, const char *name=NULL); int getLength() const; - SerializedTypeID getType() const { return STI_VL; } - STVariableLength *duplicate() const { return new STVariableLength(name, value); } - std::string getText() const; - virtual void add(Serializer& s) const { s.addVL(value); } + virtual SerializedTypeID getType() 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); } const std::vector& peekValue() const { return value; } std::vector& peekValue() { return value; } @@ -222,6 +222,25 @@ public: STVariableLength& operator=(const std::vector& v) { value=v; return *this; } }; +class STAccount : public STVariableLength +{ +public: + + STAccount(const std::vector& v) : STVariableLength(v) { ; } + STAccount(const char *n, const std::vector& v) : STVariableLength(n, v) { ; } + STAccount(const char *n) : STVariableLength(n) { ; } + STAccount() { ; } + static STAccount* construct(SerializerIterator&, const char *name=NULL); + + SerializedTypeID getType() const { return STI_ACCOUNT; } + virtual STAccount* duplicate() const { return new STAccount(name, value); } + std::string getText() const; + + void setValueH160(const uint160& v); + bool getValueH160(uint160&) const; + bool isValueH160() const; +}; + class STTaggedList : public SerializedType { protected: @@ -237,9 +256,9 @@ public: int getLength() const; SerializedTypeID getType() const { return STI_TL; } - STTaggedList *duplicate() const { return new STTaggedList(name, value); } + STTaggedList* duplicate() const { return new STTaggedList(name, value); } std::string getText() const; - virtual void add(Serializer& s) const { if(s.addTaggedList(value)<0) throw(0); } + void add(Serializer& s) const { if(s.addTaggedList(value)<0) throw(0); } const std::vector& peekValue() const { return value; } std::vector& peekValue() { return value; } diff --git a/src/Serializer.cpp b/src/Serializer.cpp index 24be5f05a..30fbfae9c 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -102,14 +102,14 @@ bool Serializer::get64(uint64& o, int offset) const bool Serializer::get160(uint160& o, int offset) const { if((offset+20)>mData.size()) return false; - memcpy(&o, &(mData.front())+offset, 20); + memcpy(o.begin(), &(mData.front())+offset, 20); return true; } bool Serializer::get256(uint256& o, int offset) const { if((offset+32)>mData.size()) return false; - memcpy(&o, &(mData.front())+offset, 32); + memcpy(o.begin(), &(mData.front())+offset, 32); return true; } diff --git a/src/Transaction.cpp b/src/Transaction.cpp index 01f27b37b..bd54e6b1c 100644 --- a/src/Transaction.cpp +++ b/src/Transaction.cpp @@ -2,45 +2,45 @@ #include "boost/lexical_cast.hpp" #include "boost/make_shared.hpp" +#include "boost/ref.hpp" #include "Application.h" #include "Transaction.h" #include "Wallet.h" #include "BitcoinUtil.h" -#include "BinaryFormats.h" - -using namespace std; - -Transaction::Transaction() : mTransactionID(0), mAccountFrom(), mAccountTo(), - mAmount(0), mFee(0), mFromAccountSeq(0), mSourceLedger(0), mIdent(0), - mInLedger(0), mStatus(INVALID) -{ -} +#include "Serializer.h" +#include "SerializedTransaction.h" Transaction::Transaction(LocalAccount::pointer fromLocalAccount, const NewcoinAddress& toAccount, uint64 amount, - uint32 ident, uint32 ledger) : - mAccountTo(toAccount), mAmount(amount), mSourceLedger(ledger), mIdent(ident), mInLedger(0), mStatus(NEW) + uint32 ident, uint32 ledger) : mInLedger(0), mStatus(NEW) { mAccountFrom=fromLocalAccount->getAddress(); + mAccountTo=toAccount; + + mTransaction=boost::make_shared(ttMAKE_PAYMENT); + mFromPubKey=fromLocalAccount->getPublicKey(); assert(mFromPubKey); - mFromAccountSeq=fromLocalAccount->getTxnSeq(); + mTransaction->setSigningAccount(mFromPubKey->GetPubKey()); -#ifdef DEBUG - std::cerr << "Construct local Txn" << std::endl; - std::cerr << "ledger(" << ledger << "), fromseq(" << mFromAccountSeq << ")" << std::endl; -#endif + mTransaction->setSequence(fromLocalAccount->getTxnSeq()); + assert(mTransaction->getSequence()!=0); + mTransaction->setTransactionFee(100); // for now - if(!mFromAccountSeq) + mTransaction->setITFieldVL(sfDestination, toAccount.getAccountPublic()); + mTransaction->setITFieldU64(sfAmount, amount); + if(ledger!=0) { -#ifdef DEBUG - std::cerr << "Bad source account sequence" << std::endl; - assert(false); -#endif - mStatus=INCOMPLETE; + mTransaction->makeITFieldPresent(sfTargetLedger); + mTransaction->setITFieldU32(sfTargetLedger, ledger); } + if(ident!=0) + { + mTransaction->makeITFieldPresent(sfSourceTag); + mTransaction->setITFieldU32(sfSourceTag, ident); + } + assert(mFromPubKey); - updateFee(); if(!sign(fromLocalAccount)) { #ifdef DEBUG @@ -50,22 +50,22 @@ Transaction::Transaction(LocalAccount::pointer fromLocalAccount, const NewcoinAd } } -Transaction::Transaction(const std::vector &t, bool validate) : mStatus(INVALID) +Transaction::Transaction(SerializedTransaction::pointer sit, bool validate) : mStatus(INVALID), mTransaction(sit) { uint160 toAccountID; uint160 fromAccountID; - - Serializer s(t); - if(s.getLength() pubKey; - if(!s.getRaw(pubKey, BTxPSPubK, BTxLSPubK)) { assert(false); return; } + + try + { + toAccountID=mTransaction->getITFieldH160(sfDestination); + pubKey=mTransaction->getSigningAccount(); + mTransactionID=mTransaction->getTransactionID(); + } + catch(...) + { + return; + } mAccountTo.setAccountID(toAccountID); mAccountFrom.setAccountID(fromAccountID); @@ -75,13 +75,62 @@ Transaction::Transaction(const std::vector &t, bool validate) : m mAccountFrom.setAccountPublic(pubKey); mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey); - updateID(); - updateFee(); - if(!validate || checkSign()) mStatus=NEW; } +Transaction::Transaction(const std::vector& raw, bool validate) : mStatus(INVALID) +{ + uint160 toAccountID; + uint160 fromAccountID; + std::vector pubKey; + + try + { + Serializer s(raw); + SerializerIterator sit(s); + mTransaction=boost::make_shared(boost::ref(sit), -1); + + mFromPubKey=boost::make_shared(); + if(!mFromPubKey->SetPubKey(pubKey)) return; + mAccountFrom.setAccountPublic(pubKey); + mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey); + if(!mFromPubKey->SetPubKey(pubKey)) return; + mAccountFrom.setAccountPublic(pubKey); + mFromPubKey=theApp->getPubKeyCache().store(mAccountFrom, mFromPubKey); + } + catch(...) + { + return; + } + if(!validate || checkSign()) + mStatus=NEW; +} + +Transaction::Transaction(const NewcoinAddress& fromID, const NewcoinAddress& toID, + CKey::pointer pubKey, uint64 amount, uint64 fee, uint32 fromSeq, uint32 fromLedger, + uint32 ident, const std::vector& signature, uint32 ledgerSeq, TransStatus st) : + mAccountFrom(fromID), mAccountTo(toID), mFromPubKey(pubKey), mInLedger(ledgerSeq), mStatus(st) +{ + mTransaction=boost::make_shared(ttMAKE_PAYMENT); + mTransaction->setSignature(signature); + mTransaction->setTransactionFee(fee); + mTransaction->setSigningAccount(pubKey->GetPubKey()); + mTransaction->setSequence(fromSeq); + if(fromLedger!=0) + { + mTransaction->makeITFieldPresent(sfTargetLedger); + mTransaction->setITFieldU32(sfTargetLedger, fromLedger); + } + if(ident!=0) + { + mTransaction->makeITFieldPresent(sfSourceTag); + mTransaction->setITFieldU32(sfSourceTag, ident); + } + mTransaction->setValueFieldU64(sfAmount, amount); + updateID(); +} + bool Transaction::sign(LocalAccount::pointer fromLocalAccount) { CKey::pointer privateKey=fromLocalAccount->getPrivateKey(); @@ -93,10 +142,10 @@ bool Transaction::sign(LocalAccount::pointer fromLocalAccount) return false; } - if( (mAmount==0) || (mSourceLedger==0) || !mAccountTo.IsValid() ) + if( (mTransaction->getITFieldU64(sfAmount)==0) || !mAccountTo.IsValid() ) { #ifdef DEBUG - std::cerr << "Bad amount, source ledger, or destination" << std::endl; + std::cerr << "Bad amount or destination" << std::endl; #endif assert(false); return false; @@ -110,7 +159,8 @@ bool Transaction::sign(LocalAccount::pointer fromLocalAccount) assert(false); return false; } - if(!getRaw(true)->makeSignature(mSignature, *privateKey)) + + if(!getSTransaction()->sign(*privateKey)) { #ifdef DEBUG std::cerr << "Failed to make signature" << std::endl; @@ -118,50 +168,14 @@ bool Transaction::sign(LocalAccount::pointer fromLocalAccount) assert(false); return false; } - assert(mSignature.size()==72); updateID(); return true; } -void Transaction::updateFee() -{ // for now, all transactions have a 1,000 unit fee - mFee=1000; -} - bool Transaction::checkSign() const { assert(mFromPubKey); - Serializer::pointer signBuf=getRaw(true); - return getRaw(true)->checkSignature(mSignature, *mFromPubKey); -} - -Serializer::pointer Transaction::getRaw(bool prefix) const -{ - uint160 toAccountID = mAccountTo.getAccountID(); - - Serializer::pointer ret=boost::make_shared(77); - if(prefix) ret->add32(0x54584e00u); - ret->add160(toAccountID); - ret->add64(mAmount); - ret->add32(mFromAccountSeq); - ret->add32(mSourceLedger); - ret->add32(mIdent); - ret->addRaw(mFromPubKey->GetPubKey()); - assert( (prefix&&(ret->getLength()==77)) || (!prefix&&(ret->getLength()==73)) ); - return ret; -} - -Serializer::pointer Transaction::getSigned() const -{ - Serializer::pointer ret(getRaw(false)); - ret->addRaw(mSignature); - assert(ret->getLength()==BTxSize); - return ret; -} - -void Transaction::updateID() -{ - mTransactionID=getSigned()->getSHA512Half(); + return mTransaction->checkSign(*mFromPubKey); } void Transaction::setStatus(TransStatus ts, uint32 lseq) @@ -176,29 +190,26 @@ void Transaction::saveTransaction(Transaction::pointer txn) } bool Transaction::save() const -{ +{ // This code needs to be fixed to support new-style transactions - FIXME if((mStatus==INVALID)||(mStatus==REMOVED)) return false; std::string sql="INSERT INTO Transactions " - "(TransID,FromAcct,FromSeq,FromLedger,Identifier,ToAcct,Amount,Fee,FirstSeen,CommitSeq,Status, Signature)" + "(TransID,TransType,FromAcct,FromSeq,OtherAcct,Amount,FirstSeen,CommitSeq,Status,RawTxn)" " VALUES ('"; sql.append(mTransactionID.GetHex()); sql.append("','"); + sql.append(mTransaction->getTransactionType()); + sql.append("','"); sql.append(mAccountFrom.humanAccountID()); sql.append("','"); - sql.append(boost::lexical_cast(mFromAccountSeq)); + sql.append(boost::lexical_cast(mTransaction->getSequence())); sql.append("','"); - sql.append(boost::lexical_cast(mSourceLedger)); + sql.append(mTransaction->getITFieldString(sfDestination)); sql.append("','"); - sql.append(boost::lexical_cast(mIdent)); - sql.append("','"); - sql.append(mAccountTo.humanAccountID()); - sql.append("','"); - sql.append(boost::lexical_cast(mAmount)); - sql.append("','"); - sql.append(boost::lexical_cast(mFee)); + sql.append(mTransaction->getITFieldString(sfAmount)); sql.append("',now(),'"); - sql.append(boost::lexical_cast(mInLedger)); switch(mStatus) + sql.append(boost::lexical_cast(mInLedger)); + switch(mStatus) { case NEW: sql.append("','N',"); break; case INCLUDED: sql.append("','A',"); break; @@ -207,9 +218,13 @@ bool Transaction::save() const case HELD: sql.append("','H',"); break; default: sql.append("','U',"); break; } - std::string signature; - theApp->getTxnDB()->getDB()->escape(&(mSignature.front()), mSignature.size(), signature); - sql.append(signature); + + Serializer s; + mTransaction->getTransaction(s, false); + + std::string rawTxn; + theApp->getTxnDB()->getDB()->escape(static_cast(s.getDataPtr()), s.getLength(), rawTxn); + sql.append(rawTxn); sql.append(");"); ScopedLock sl(theApp->getTxnDB()->getDBLock()); @@ -218,12 +233,11 @@ bool Transaction::save() const } Transaction::pointer Transaction::transactionFromSQL(const std::string& sql) -{ - std::string transID, fromID, toID, status; - uint64 amount, fee; - uint32 ledgerSeq, fromSeq, fromLedger, ident; - std::vector signature; - signature.reserve(78); +{ // This code needs to be fixed to support new-style transactions - FIXME + std::vector rawTxn; + std::string status; + + rawTxn.reserve(2048); if(1) { ScopedLock sl(theApp->getTxnDB()->getDBLock()); @@ -232,29 +246,17 @@ Transaction::pointer Transaction::transactionFromSQL(const std::string& sql) if(!db->executeSQL(sql.c_str(), true) || !db->startIterRows() || !db->getNextRow()) return Transaction::pointer(); - db->getStr("TransID", transID); - db->getStr("FromID", fromID); - fromSeq=db->getBigInt("FromSeq"); - fromLedger=db->getBigInt("FromLedger"); - db->getStr("ToID", toID); - amount=db->getBigInt("Amount"); - fee=db->getBigInt("Fee"); - ledgerSeq=db->getBigInt("CommitSeq"); - ident=db->getBigInt("Identifier"); db->getStr("Status", status); - int sigSize=db->getBinary("Signature", &(signature.front()), signature.size()); - signature.resize(sigSize); + int txSize=db->getBinary("RawTxn", &(rawTxn.front()), rawTxn.size()); + rawTxn.resize(txSize); + if(txSize>rawTxn.size()) db->getBinary("RawTxn", &(rawTxn.front()), rawTxn.size()); db->endIterRows(); } - uint256 trID; - NewcoinAddress frID, tID; - trID.SetHex(transID); - frID.setAccountID(fromID); - tID.setAccountID(toID); - - CKey::pointer pubkey=theApp->getPubKeyCache().locate(frID); - if(!pubkey) return Transaction::pointer(); + Serializer s(rawTxn); + SerializerIterator it(s); + SerializedTransaction::pointer txn=boost::make_shared(boost::ref(it), -1); + Transaction::pointer tr=boost::make_shared(txn, true); TransStatus st(INVALID); switch(status[0]) @@ -265,15 +267,15 @@ Transaction::pointer Transaction::transactionFromSQL(const std::string& sql) case 'D': st=COMMITTED; break; case 'H': st=HELD; break; } - - return Transaction::pointer(new Transaction(trID, frID, tID, pubkey, amount, fee, fromSeq, fromLedger, - ident, signature, ledgerSeq, st)); + tr->setStatus(st); + + return tr; } Transaction::pointer Transaction::load(const uint256& id) { - std::string sql="SELECT * FROM Transactions WHERE TransID='"; + std::string sql="SELECT Status,RawTxn FROM Transactions WHERE TransID='"; sql.append(id.GetHex()); sql.append("';"); return transactionFromSQL(sql); @@ -281,7 +283,7 @@ Transaction::pointer Transaction::load(const uint256& id) Transaction::pointer Transaction::findFrom(const NewcoinAddress& fromID, uint32 seq) { - std::string sql="SELECT * FROM Transactions WHERE FromID='"; + std::string sql="SELECT Status,RawTxn FROM Transactions WHERE FromID='"; sql.append(fromID.humanAccountID()); sql.append("' AND FromSeq='"); sql.append(boost::lexical_cast(seq)); @@ -304,6 +306,8 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg Transaction::pointer firstTrans, secondTrans; if(!!first) { // transaction in our table + Serializer s(first->getData()); + SerializerIterator sit(s); firstTrans=boost::make_shared(first->getData(), checkFirstTransactions); if( (firstTrans->getStatus()==INVALID) || (firstTrans->getID()!=id) ) { @@ -350,10 +354,7 @@ bool Transaction::isHexTxID(const std::string& txid) Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const { - Json::Value ret(Json::objectValue); - ret["TransactionID"]=mTransactionID.GetHex(); - ret["Amount"]=boost::lexical_cast(mAmount); - ret["Fee"]=boost::lexical_cast(mFee); + Json::Value ret(mTransaction->getJson(0)); if(mInLedger) ret["InLedger"]=mInLedger; switch(mStatus) @@ -371,11 +372,7 @@ Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const } Json::Value source(Json::objectValue); - source["AccountID"]=mAccountFrom.humanAccountID(); - source["AccountSeq"]=mFromAccountSeq; - source["Ledger"]=mSourceLedger; - if(!!mIdent) source["Identifier"]=mIdent; Json::Value destination(Json::objectValue); destination["AccountID"]=mAccountTo.humanAccountID(); @@ -393,4 +390,3 @@ Json::Value Transaction::getJson(bool decorate, bool paid, bool credited) const ret["Destination"]=destination; return ret; } - diff --git a/src/Transaction.h b/src/Transaction.h index e3495c943..66c490eed 100644 --- a/src/Transaction.h +++ b/src/Transaction.h @@ -16,10 +16,7 @@ #include "Serializer.h" #include "SHAMap.h" #include "LocalAccount.h" - -/* -We could have made something that inherited from the protobuf transaction but this seemed simpler -*/ +#include "SerializedTransaction.h" enum TransStatus { @@ -40,41 +37,42 @@ public: typedef boost::shared_ptr pointer; - static const uint32 TransSignMagic=0x54584E00; // "TXN" - private: uint256 mTransactionID; - NewcoinAddress mAccountFrom, mAccountTo; - uint64 mAmount, mFee; - uint32 mFromAccountSeq, mSourceLedger, mIdent; + NewcoinAddress mAccountFrom, mAccountTo; CKey::pointer mFromPubKey; - std::vector mSignature; uint32 mInLedger; TransStatus mStatus; + SerializedTransaction::pointer mTransaction; + public: - Transaction(); - Transaction(const std::vector& rawTransaction, bool validate); - Transaction(LocalAccount::pointer fromLocal, const NewcoinAddress& to, uint64 amount, uint32 ident, uint32 ledger); + Transaction(const std::vector&, bool validate); + Transaction(SerializedTransaction::pointer st, bool validate); + + Transaction(LocalAccount::pointer fromLocal, const NewcoinAddress& to, uint64 amount, + uint32 ident, uint32 ledger); + + Transaction(const NewcoinAddress& fromID, const NewcoinAddress& toID, + CKey::pointer pubKey, uint64 amount, uint64 fee, uint32 fromSeq, uint32 fromLedger, + uint32 ident, const std::vector& signature, uint32 ledgerSeq, TransStatus st); bool sign(LocalAccount::pointer fromLocalAccount); bool checkSign() const; - void updateID(); - void updateFee(); + void updateID() { mTransactionID=mTransaction->getTransactionID(); } - Serializer::pointer getRaw(bool prefix) const; - Serializer::pointer getSigned() const; + SerializedTransaction::pointer getSTransaction() { return mTransaction; } const uint256& getID() const { return mTransactionID; } const NewcoinAddress& getFromAccount() const { return mAccountFrom; } const NewcoinAddress& getToAccount() const { return mAccountTo; } - uint64 getAmount() const { return mAmount; } - uint64 getFee() const { return mFee; } - uint32 getFromAccountSeq() const { return mFromAccountSeq; } - uint32 getSourceLedger() const { return mSourceLedger; } - uint32 getIdent() const { return mIdent; } - const std::vector& getSignature() const { return mSignature; } + uint64 getAmount() const { return mTransaction->getITFieldU64(sfAmount); } + uint64 getFee() const { return mTransaction->getTransactionFee(); } + uint32 getFromAccountSeq() const { return mTransaction->getSequence(); } + uint32 getSourceLedger() const { return mTransaction->getITFieldU32(sfTargetLedger); } + uint32 getIdent() const { return mTransaction->getITFieldU32(sfSourceTag); } + std::vector getSignature() const { return mTransaction->getSignature(); } uint32 getLedger() const { return mInLedger; } TransStatus getStatus() const { return mStatus; } @@ -107,12 +105,7 @@ protected: static Transaction::pointer transactionFromSQL(const std::string& statement); Transaction(const uint256& transactionID, const NewcoinAddress& accountFrom, const NewcoinAddress& accountTo, CKey::pointer key, uint64 amount, uint64 fee, uint32 fromAccountSeq, uint32 sourceLedger, - uint32 ident, const std::vector& signature, uint32 inLedger, TransStatus status) : - mTransactionID(transactionID), mAccountFrom(accountFrom), mAccountTo(accountTo), - mAmount(amount), mFee(fee), mFromAccountSeq(fromAccountSeq), mSourceLedger(sourceLedger), - mIdent(ident), mFromPubKey(key), mSignature(signature), mInLedger(inLedger), mStatus(status) - { ; } - + uint32 ident, const std::vector& signature, uint32 inLedger, TransStatus status); }; #endif diff --git a/src/TransactionFormats.cpp b/src/TransactionFormats.cpp index ce11a294d..455b3a9e4 100644 --- a/src/TransactionFormats.cpp +++ b/src/TransactionFormats.cpp @@ -1,49 +1,48 @@ #include "TransactionFormats.h" +#define S_FIELD(x) sf##x, #x + TransactionFormat InnerTxnFormats[]= { { "MakePayment", ttMAKE_PAYMENT, { - { "Flags", STI_UINT16, SOE_FLAGS, 0 }, - { "Sequence", STI_UINT32, SOE_REQUIRED, 0 }, - { "Destination", STI_ACCOUNT, SOE_REQUIRED, 0 }, - { "Amount", STI_UINT64, SOE_REQUIRED, 0 }, - { "Currency", STI_HASH160, SOE_IFFLAG, 1 }, - { "SourceTag", STI_UINT32, SOE_IFFLAG, 2 }, - { "TargetLedger", STI_UINT32, SOE_IFFLAG, 4 }, - { "InvoiceID", STI_HASH256, SOE_IFFLAG, 8 }, - { "Extensions", STI_TL, SOE_IFFLAG, 32768 }, - { NULL, STI_DONE, SOE_NEVER, -1 } } + { 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(Currency), STI_HASH160, SOE_IFFLAG, 1 }, + { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, + { S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 4 }, + { S_FIELD(InvoiceID), STI_HASH256, SOE_IFFLAG, 8 }, + { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 32768 }, + { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, { "Invoice", ttINVOICE, { - { "Flags", STI_UINT16, SOE_FLAGS, 0 }, - { "Sequence", STI_UINT32, SOE_REQUIRED, 0 }, - { "Target", STI_ACCOUNT, SOE_REQUIRED, 0 }, - { "Amount", STI_UINT64, SOE_REQUIRED, 0 }, - { "Currency", STI_HASH160, SOE_IFFLAG, 1 }, - { "SourceTag", STI_UINT32, SOE_IFFLAG, 2 }, - { "Destination", STI_ACCOUNT, SOE_IFFLAG, 4 }, - { "TargetLedger", STI_UINT32, SOE_IFFLAG, 8 }, - { "Identifier", STI_VL, SOE_IFFLAG, 16 }, - { "Extensions", STI_TL, SOE_IFFLAG, 32768 }, - { NULL, STI_DONE, SOE_NEVER, -1 } } + { 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(Currency), STI_HASH160, SOE_IFFLAG, 1 }, + { S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 2 }, + { S_FIELD(Destination), STI_ACCOUNT, SOE_IFFLAG, 4 }, + { S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 8 }, + { S_FIELD(Identifier), STI_VL, SOE_IFFLAG, 16 }, + { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 32768 }, + { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, { "Offer", ttEXCHANGE_OFFER, { - { "Flags", STI_UINT16, SOE_FLAGS, 0 }, - { "Sequence", STI_UINT32, SOE_REQUIRED, 0 }, - { "AmountIn", STI_UINT64, SOE_REQUIRED, 0 }, - { "CurrencyIn", STI_HASH160, SOE_IFFLAG, 2 }, - { "AmountOut", STI_UINT64, SOE_REQUIRED, 0 }, - { "CurrencyOut", STI_HASH160, SOE_IFFLAG, 4 }, - { "SourceTag", STI_UINT32, SOE_IFFLAG, 8 }, - { "Destination", STI_ACCOUNT, SOE_IFFLAG, 16 }, - { "TargetLedger", STI_UINT32, SOE_IFFLAG, 32 }, - { "ExpireLedger", STI_UINT32, SOE_IFFLAG, 64 }, - { "Identifier", STI_VL, SOE_IFFLAG, 128 }, - { "Extensions", STI_TL, SOE_IFFLAG, 32768 }, - { NULL, STI_DONE, SOE_NEVER, -1 } } + { S_FIELD(Flags), STI_UINT16, SOE_FLAGS, 0 }, + { S_FIELD(AmountIn), STI_UINT64, SOE_REQUIRED, 0 }, + { S_FIELD(CurrencyIn), STI_HASH160, SOE_IFFLAG, 2 }, + { S_FIELD(AmountOut), STI_UINT64, 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 }, + { S_FIELD(TargetLedger), STI_UINT32, SOE_IFFLAG, 32 }, + { S_FIELD(ExpireLedger), STI_UINT32, SOE_IFFLAG, 64 }, + { S_FIELD(Identifier), STI_VL, SOE_IFFLAG, 128 }, + { S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 32768 }, + { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } } }, - { NULL, ttINVALID } + { NULL, ttINVALID } }; TransactionFormat* getFormat(TransactionType t) diff --git a/src/TransactionFormats.h b/src/TransactionFormats.h index 52e91e1c9..6c68d12c3 100644 --- a/src/TransactionFormats.h +++ b/src/TransactionFormats.h @@ -3,8 +3,6 @@ #include "SerializedObject.h" -#define STI_ACCOUNT STI_HASH160 - enum TransactionType { ttINVALID=-1, @@ -20,7 +18,11 @@ struct TransactionFormat SOElement elements[16]; }; -const int32 TransactionMagic=0x54583000; +const int32 TransactionMagic=0x54584E00; + +const int TransactionIVersion=0, TransactionISigningAccount=1, TransactionISequence=2; +const int TransactionIType=3, TransactionIFee=4; + const int TransactionMinLen=32; const int TransactionMaxLen=1048576;