// TODO.3 this and checkValid share a lot of code void TxSetFrame::trimInvalid(Application& app, std::vector<TransactionFramePtr>& trimmed) { soci::transaction sqltx(app.getDatabase().getSession()); app.getDatabase().setCurrentTransactionReadOnly(); sortForHash(); map<AccountID, vector<TransactionFramePtr>> accountTxMap; for (auto tx : mTransactions) { accountTxMap[tx->getSourceID()].push_back(tx); } for (auto& item : accountTxMap) { // order by sequence number std::sort(item.second.begin(), item.second.end(), SeqSorter); TransactionFramePtr lastTx; SequenceNumber lastSeq = 0; int64_t totFee = 0; for (auto& tx : item.second) { if (!tx->checkValid(app, lastSeq)) { trimmed.push_back(tx); removeTx(tx); continue; } totFee += tx->getFee(); lastTx = tx; lastSeq = tx->getSeqNum(); } if (lastTx) { // make sure account can pay the fee for all these tx int64_t newBalance = lastTx->getSourceAccount().getBalance() - totFee; if (newBalance < lastTx->getSourceAccount().getMinimumBalance( app.getLedgerManager())) { for (auto& tx : item.second) { trimmed.push_back(tx); removeTx(tx); } } } } }
// need to make sure every account that is submitting a tx has enough to pay // the fees of all the tx it has submitted in this set // check seq num bool TxSetFrame::checkValid(Application& app) const { // Establish read-only transaction for duration of checkValid. soci::transaction sqltx(app.getDatabase().getSession()); app.getDatabase().setCurrentTransactionReadOnly(); // Start by checking previousLedgerHash if (app.getLedgerManager().getLastClosedLedgerHeader().hash != mPreviousLedgerHash) { CLOG(DEBUG, "Herder") << "Got bad txSet: " << hexAbbrev(mPreviousLedgerHash) << " ; expected: " << hexAbbrev( app.getLedgerManager().getLastClosedLedgerHeader().hash); return false; } map<AccountID, vector<TransactionFramePtr>> accountTxMap; Hash lastHash; for (auto tx : mTransactions) { // make sure the set is sorted correctly if (tx->getFullHash() < lastHash) { CLOG(DEBUG, "Herder") << "bad txSet: " << hexAbbrev(mPreviousLedgerHash) << " not sorted correctly"; return false; } accountTxMap[tx->getSourceID()].push_back(tx); lastHash = tx->getFullHash(); } for (auto& item : accountTxMap) { // order by sequence number std::sort(item.second.begin(), item.second.end(), SeqSorter); TransactionFramePtr lastTx; SequenceNumber lastSeq = 0; int64_t totFee = 0; for (auto& tx : item.second) { if (!tx->checkValid(app, lastSeq)) { CLOG(DEBUG, "Herder") << "bad txSet: " << hexAbbrev(mPreviousLedgerHash) << " tx invalid" << " lastSeq:" << lastSeq << " tx: " << xdr::xdr_to_string(tx->getEnvelope()) << " result: " << tx->getResultCode(); return false; } totFee += tx->getFee(); lastTx = tx; lastSeq = tx->getSeqNum(); } if (lastTx) { // make sure account can pay the fee for all these tx int64_t newBalance = lastTx->getSourceAccount().getBalance() - totFee; if (newBalance < lastTx->getSourceAccount().getMinimumBalance( app.getLedgerManager())) { CLOG(DEBUG, "Herder") << "bad txSet: " << hexAbbrev(mPreviousLedgerHash) << " account can't pay fee" << " tx:" << xdr::xdr_to_string(lastTx->getEnvelope()); return false; } } } return true; }
bool TxSetFrame::checkOrTrim( Application& app, std::function<bool(TransactionFramePtr, SequenceNumber)> processInvalidTxLambda, std::function<bool(std::vector<TransactionFramePtr> const&)> processInsufficientBalance) { map<AccountID, vector<TransactionFramePtr>> accountTxMap; Hash lastHash; for (auto& tx : mTransactions) { if (tx->getFullHash() < lastHash) { CLOG(DEBUG, "Herder") << "bad txSet: " << hexAbbrev(mPreviousLedgerHash) << " not sorted correctly"; return false; } accountTxMap[tx->getSourceID()].push_back(tx); lastHash = tx->getFullHash(); } for (auto& item : accountTxMap) { // order by sequence number std::sort(item.second.begin(), item.second.end(), SeqSorter); TransactionFramePtr lastTx; SequenceNumber lastSeq = 0; int64_t totFee = 0; for (auto& tx : item.second) { if (!tx->checkValid(app, lastSeq)) { if (processInvalidTxLambda(tx, lastSeq)) continue; return false; } totFee += tx->getFee(); lastTx = tx; lastSeq = tx->getSeqNum(); } if (lastTx) { // make sure account can pay the fee for all these tx int64_t newBalance = lastTx->getSourceAccount().getBalance() - totFee; if (newBalance < lastTx->getSourceAccount().getMinimumBalance( app.getLedgerManager())) { if (!processInsufficientBalance(item.second)) return false; } } } return true; }