//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== SETUP_LOG (SerializedTransaction) SerializedTransaction::SerializedTransaction (TxType type) : STObject (sfTransaction) , mType (type) , mSigGood (false) , mSigBad (false) { mFormat = TxFormats::getInstance()->findByType (type); if (mFormat == nullptr) { WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << type; throw std::runtime_error ("invalid transaction type"); } set (mFormat->elements); setFieldU16 (sfTransactionType, mFormat->getType ()); } SerializedTransaction::SerializedTransaction (STObject const& object) : STObject (object) , mSigGood (false) , mSigBad (false) { mType = static_cast (getFieldU16 (sfTransactionType)); mFormat = TxFormats::getInstance()->findByType (mType); if (!mFormat) { WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType; throw std::runtime_error ("invalid transaction type"); } if (!setType (mFormat->elements)) { throw std::runtime_error ("transaction not valid"); } } SerializedTransaction::SerializedTransaction (SerializerIterator& sit) : STObject (sfTransaction), mSigGood (false), mSigBad (false) { int length = sit.getBytesLeft (); if ((length < Protocol::txMinSizeBytes) || (length > Protocol::txMaxSizeBytes)) { Log (lsERROR) << "Transaction has invalid length: " << length; throw std::runtime_error ("Transaction length invalid"); } set (sit); mType = static_cast (getFieldU16 (sfTransactionType)); mFormat = TxFormats::getInstance()->findByType (mType); if (!mFormat) { WriteLog (lsWARNING, SerializedTransaction) << "Transaction type: " << mType; throw std::runtime_error ("invalid transaction type"); } if (!setType (mFormat->elements)) { WriteLog (lsWARNING, SerializedTransaction) << "Transaction not legal for format"; throw std::runtime_error ("transaction not valid"); } } std::string SerializedTransaction::getFullText () const { std::string ret = "\""; ret += getTransactionID ().GetHex (); ret += "\" = {"; ret += STObject::getFullText (); ret += "}"; return ret; } std::string SerializedTransaction::getText () const { return STObject::getText (); } std::vector SerializedTransaction::getMentionedAccounts () const { std::vector accounts; BOOST_FOREACH (const SerializedType & it, peekData ()) { const STAccount* sa = dynamic_cast (&it); if (sa != NULL) { bool found = false; RippleAddress na = sa->getValueNCA (); BOOST_FOREACH (const RippleAddress & it, accounts) { if (it == na) { found = true; break; } } if (!found) accounts.push_back (na); } const STAmount* sam = dynamic_cast (&it); if (sam) { uint160 issuer = sam->getIssuer (); if (issuer.isNonZero ()) { RippleAddress na; na.setAccountID (issuer); bool found = false; BOOST_FOREACH (const RippleAddress & it, accounts) { if (it == na) { found = true; break; } } if (!found) accounts.push_back (na); } } } return accounts; } uint256 SerializedTransaction::getSigningHash () const { return STObject::getSigningHash (getConfig ().SIGN_TRANSACTION); } uint256 SerializedTransaction::getTransactionID () const { // perhaps we should cache this return getHash (HashPrefix::transactionID); } Blob SerializedTransaction::getSignature () const { try { return getFieldVL (sfTxnSignature); } catch (...) { return Blob (); } } void SerializedTransaction::sign (const RippleAddress& naAccountPrivate) { Blob signature; naAccountPrivate.accountPrivateSign (getSigningHash (), signature); setFieldVL (sfTxnSignature, signature); } bool SerializedTransaction::checkSign () const { if (mSigGood) return true; if (mSigBad) return false; try { RippleAddress n; n.setAccountPublic (getFieldVL (sfSigningPubKey)); if (checkSign (n)) { mSigGood = true; return true; } } catch (...) { ; } mSigBad = true; return false; } bool SerializedTransaction::checkSign (const RippleAddress& naAccountPublic) const { try { return naAccountPublic.accountPublicVerify (getSigningHash (), getFieldVL (sfTxnSignature)); } catch (...) { return false; } } void SerializedTransaction::setSigningPubKey (const RippleAddress& naSignPubKey) { setFieldVL (sfSigningPubKey, naSignPubKey.getAccountPublic ()); } void SerializedTransaction::setSourceAccount (const RippleAddress& naSource) { setFieldAccount (sfAccount, naSource); } Json::Value SerializedTransaction::getJson (int options, bool binary) const { if (binary) { Json::Value ret; Serializer s = STObject::getSerializer (); ret["tx"] = strHex (s.peekData ()); ret["hash"] = getTransactionID ().GetHex (); return ret; } Json::Value ret = STObject::getJson (0); ret["hash"] = getTransactionID ().GetHex (); return ret; } std::string SerializedTransaction::getSQLValueHeader () { return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn)"; } std::string SerializedTransaction::getMetaSQLValueHeader () { return "(TransID, TransType, FromAcct, FromSeq, LedgerSeq, Status, RawTxn, TxnMeta)"; } std::string SerializedTransaction::getMetaSQLInsertReplaceHeader () { return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader () + " VALUES "; } std::string SerializedTransaction::getSQL (uint32 inLedger, char status) const { Serializer s; add (s); return getSQL (s, inLedger, status); } std::string SerializedTransaction::getMetaSQL (uint32 inLedger, const std::string& escapedMetaData) const { Serializer s; add (s); return getMetaSQL (s, inLedger, TXN_SQL_VALIDATED, escapedMetaData); } std::string SerializedTransaction::getSQL (Serializer rawTxn, uint32 inLedger, char status) const { static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s)"); std::string rTxn = sqlEscape (rawTxn.peekData ()); return str (boost::format (bfTrans) % getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID () % getSequence () % inLedger % status % rTxn); } std::string SerializedTransaction::getMetaSQL (Serializer rawTxn, uint32 inLedger, char status, const std::string& escapedMetaData) const { static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); std::string rTxn = sqlEscape (rawTxn.peekData ()); return str (boost::format (bfTrans) % getTransactionID ().GetHex () % getTransactionType () % getSourceAccount ().humanAccountID () % getSequence () % inLedger % status % rTxn % escapedMetaData); } //------------------------------------------------------------------------------ class SerializedTransactionTests : public UnitTest { public: SerializedTransactionTests () : UnitTest ("SerializedTransaction", "ripple") { } void runTest () { beginTestCase ("tx"); RippleAddress seed; seed.setSeedRandom (); RippleAddress generator = RippleAddress::createGeneratorPublic (seed); RippleAddress publicAcct = RippleAddress::createAccountPublic (generator, 1); RippleAddress privateAcct = RippleAddress::createAccountPrivate (generator, seed, 1); SerializedTransaction j (ttACCOUNT_SET); j.setSourceAccount (publicAcct); j.setSigningPubKey (publicAcct); j.setFieldVL (sfMessageKey, publicAcct.getAccountPublic ()); j.sign (privateAcct); unexpected (!j.checkSign (), "Transaction fails signature test"); Serializer rawTxn; j.add (rawTxn); SerializerIterator sit (rawTxn); SerializedTransaction copy (sit); if (copy != j) { Log (lsFATAL) << j.getJson (0); Log (lsFATAL) << copy.getJson (0); fail ("Transaction fails serialize/deserialize test"); } else { pass (); } UPTR_T new_obj = STObject::parseJson (j.getJson (0), sfGeneric); if (new_obj.get () == NULL) fail ("Unable to build object from json"); if (STObject (j) != *new_obj) { Log (lsINFO) << "ORIG: " << j.getJson (0); Log (lsINFO) << "BUILT " << new_obj->getJson (0); fail ("Built a different transaction"); } else { pass (); } } }; static SerializedTransactionTests serializedTransactionTests;