//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== Transaction::Transaction (SerializedTransaction::ref sit, bool bValidate) : mInLedger (0), mStatus (INVALID), mResult (temUNCERTAIN), mTransaction (sit) { try { mFromPubKey.setAccountPublic (mTransaction->getSigningPubKey ()); mTransactionID = mTransaction->getTransactionID (); mAccountFrom = mTransaction->getSourceAccount (); } catch (...) { return; } if (!bValidate || checkSign ()) mStatus = NEW; } Transaction::pointer Transaction::sharedTransaction (Blob const& vucTransaction, bool bValidate) { try { Serializer s (vucTransaction); SerializerIterator sit (s); SerializedTransaction::pointer st = boost::make_shared (boost::ref (sit)); return boost::make_shared (st, bValidate); } catch (...) { Log (lsWARNING) << "Exception constructing transaction"; return boost::shared_ptr (); } } // // Generic transaction construction // Transaction::Transaction ( TxType ttKind, const RippleAddress& naPublicKey, const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag) : mAccountFrom (naSourceAccount), mFromPubKey (naPublicKey), mInLedger (0), mStatus (NEW), mResult (temUNCERTAIN) { assert (mFromPubKey.isValid ()); mTransaction = boost::make_shared (ttKind); // Log(lsINFO) << str(boost::format("Transaction: account: %s") % naSourceAccount.humanAccountID()); // Log(lsINFO) << str(boost::format("Transaction: mAccountFrom: %s") % mAccountFrom.humanAccountID()); mTransaction->setSigningPubKey (mFromPubKey); mTransaction->setSourceAccount (mAccountFrom); mTransaction->setSequence (uSeq); mTransaction->setTransactionFee (saFee); if (uSourceTag) { mTransaction->makeFieldPresent (sfSourceTag); mTransaction->setFieldU32 (sfSourceTag, uSourceTag); } } bool Transaction::sign (const RippleAddress& naAccountPrivate) { bool bResult = true; if (!naAccountPrivate.isValid ()) { Log (lsWARNING) << "No private key for signing"; bResult = false; } getSTransaction ()->sign (naAccountPrivate); if (bResult) { updateID (); } else { mStatus = INCOMPLETE; } return bResult; } // // Misc. // bool Transaction::checkSign () const { if (!mFromPubKey.isValid ()) { Log (lsWARNING) << "Transaction has bad source public key"; return false; } return mTransaction->checkSign (mFromPubKey); } void Transaction::setStatus (TransStatus ts, uint32 lseq) { mStatus = ts; mInLedger = lseq; } Transaction::pointer Transaction::transactionFromSQL (Database* db, bool bValidate) { Serializer rawTxn; std::string status; uint32 inLedger; int txSize = 2048; rawTxn.resize (txSize); db->getStr ("Status", status); inLedger = db->getInt ("LedgerSeq"); txSize = db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ()); if (txSize > rawTxn.getLength ()) { rawTxn.resize (txSize); db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ()); } rawTxn.resize (txSize); SerializerIterator it (rawTxn); SerializedTransaction::pointer txn = boost::make_shared (boost::ref (it)); Transaction::pointer tr = boost::make_shared (txn, bValidate); TransStatus st (INVALID); switch (status[0]) { case TXN_SQL_NEW: st = NEW; break; case TXN_SQL_CONFLICT: st = CONFLICTED; break; case TXN_SQL_HELD: st = HELD; break; case TXN_SQL_VALIDATED: st = COMMITTED; break; case TXN_SQL_INCLUDED: st = INCLUDED; break; case TXN_SQL_UNKNOWN: break; default: assert (false); } tr->setStatus (st); tr->setLedger (inLedger); return tr; } // DAVID: would you rather duplicate this code or keep the lock longer? Transaction::pointer Transaction::transactionFromSQL (const std::string& sql) { Serializer rawTxn; std::string status; uint32 inLedger; int txSize = 2048; rawTxn.resize (txSize); { ScopedLock sl (getApp().getTxnDB ()->getDBLock ()); Database* db = getApp().getTxnDB ()->getDB (); if (!db->executeSQL (sql, true) || !db->startIterRows ()) return Transaction::pointer (); db->getStr ("Status", status); inLedger = db->getInt ("LedgerSeq"); txSize = db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ()); if (txSize > rawTxn.getLength ()) { rawTxn.resize (txSize); db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ()); } db->endIterRows (); } rawTxn.resize (txSize); SerializerIterator it (rawTxn); SerializedTransaction::pointer txn = boost::make_shared (boost::ref (it)); Transaction::pointer tr = boost::make_shared (txn, true); TransStatus st (INVALID); switch (status[0]) { case TXN_SQL_NEW: st = NEW; break; case TXN_SQL_CONFLICT: st = CONFLICTED; break; case TXN_SQL_HELD: st = HELD; break; case TXN_SQL_VALIDATED: st = COMMITTED; break; case TXN_SQL_INCLUDED: st = INCLUDED; break; case TXN_SQL_UNKNOWN: break; default: assert (false); } tr->setStatus (st); tr->setLedger (inLedger); return tr; } Transaction::pointer Transaction::load (uint256 const& id) { std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE TransID='"; sql.append (id.GetHex ()); sql.append ("';"); return transactionFromSQL (sql); } bool Transaction::convertToTransactions (uint32 firstLedgerSeq, uint32 secondLedgerSeq, bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::Delta& inMap, std::map >& outMap) { // convert a straight SHAMap payload difference to a transaction difference table // return value: true=ledgers are valid, false=a ledger is invalid SHAMap::Delta::const_iterator it; for (it = inMap.begin (); it != inMap.end (); ++it) { uint256 const& id = it->first; SHAMapItem::ref first = it->second.first; SHAMapItem::ref second = it->second.second; Transaction::pointer firstTrans, secondTrans; if (!!first) { // transaction in our table firstTrans = sharedTransaction (first->getData (), checkFirstTransactions); if ((firstTrans->getStatus () == INVALID) || (firstTrans->getID () != id )) { firstTrans->setStatus (INVALID, firstLedgerSeq); return false; } else firstTrans->setStatus (INCLUDED, firstLedgerSeq); } if (!!second) { // transaction in other table secondTrans = sharedTransaction (second->getData (), checkSecondTransactions); if ((secondTrans->getStatus () == INVALID) || (secondTrans->getID () != id)) { secondTrans->setStatus (INVALID, secondLedgerSeq); return false; } else secondTrans->setStatus (INCLUDED, secondLedgerSeq); } assert (firstTrans || secondTrans); if (firstTrans && secondTrans && (firstTrans->getStatus () != INVALID) && (secondTrans->getStatus () != INVALID)) return false; // one or the other SHAMap is structurally invalid or a miracle has happened outMap[id] = std::pair (firstTrans, secondTrans); } return true; } // options 1 to include the date of the transaction Json::Value Transaction::getJson (int options, bool binary) const { Json::Value ret (mTransaction->getJson (0, binary)); if (mInLedger) { ret["inLedger"] = mInLedger; // Deprecated. ret["ledger_index"] = mInLedger; if (options == 1) { Ledger::pointer ledger = getApp().getLedgerMaster ().getLedgerBySeq (mInLedger); if (ledger) ret["date"] = ledger->getCloseTimeNC (); } } return ret; } // // Obsolete // static bool isHex (char j) { if ((j >= '0') && (j <= '9')) return true; if ((j >= 'A') && (j <= 'F')) return true; if ((j >= 'a') && (j <= 'f')) return true; return false; } bool Transaction::isHexTxID (const std::string& txid) { if (txid.size () != 64) return false; for (int i = 0; i < 64; ++i) if (!isHex (txid[i])) return false; return true; } // vim:ts=4