Herder::TransactionSubmitStatus LoadGenerator::TxInfo::execute(Application& app, bool isCreate, TransactionResultCode& code, int32_t batchSize) { auto seqNum = mFrom->getLastSequenceNumber(); mFrom->setSequenceNumber(seqNum + 1); TransactionFramePtr txf = transactionFromOperations(app, mFrom->getSecretKey(), seqNum + 1, mOps); TxMetrics txm(app.getMetrics()); // Record tx metrics. if (isCreate) { while (batchSize--) { txm.mAccountCreated.Mark(); } } else { txm.mPayment.Mark(); txm.mNativePayment.Mark(); } txm.mTxnAttempted.Mark(); StellarMessage msg; msg.type(TRANSACTION); msg.transaction() = txf->getEnvelope(); txm.mTxnBytes.Mark(xdr::xdr_argpack_size(msg)); auto status = app.getHerder().recvTransaction(txf); if (status != Herder::TX_STATUS_PENDING) { CLOG(INFO, "LoadGen") << "tx rejected '" << Herder::TX_STATUS_STRING[status] << "': " << xdr::xdr_to_string(txf->getEnvelope()) << " ===> " << xdr::xdr_to_string(txf->getResult()); if (status == Herder::TX_STATUS_ERROR) { code = txf->getResultCode(); } txm.mTxnRejected.Mark(); } else { app.getOverlayManager().broadcastMessage(msg); } return status; }
// 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; }