static bool HashTxSorter(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) { // need to use the hash of whole tx here since multiple txs could have // the same Contents return tx1->getFullHash() < tx2->getFullHash(); }
bool operator()(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) const { // need to use the hash of whole tx here since multiple txs could have // the same Contents return lessThanXored(tx1->getFullHash(), tx2->getFullHash(), mSetHash); }
// 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); } } } } }
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; }
void applyAllowTrust(Application& app, SecretKey& from, SecretKey& trustor, SequenceNumber seq, std::string const& currencyCode, bool authorize, AllowTrustResultCode result) { TransactionFramePtr txFrame; txFrame = createAllowTrust(from, trustor, seq, currencyCode, authorize); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); txFrame->apply(delta, app); checkTransaction(*txFrame); REQUIRE(AllowTrustOpFrame::getInnerCode( txFrame->getResult().result.results()[0]) == result); }
void applyChangeTrust(Application& app, SecretKey& from, SecretKey& to, SequenceNumber seq, std::string const& currencyCode, int64_t limit, ChangeTrustResultCode result) { TransactionFramePtr txFrame; txFrame = createChangeTrust(from, to, seq, currencyCode, limit); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); txFrame->apply(delta, app); checkTransaction(*txFrame); REQUIRE(ChangeTrustOpFrame::getInnerCode( txFrame->getResult().result.results()[0]) == result); }
bool operator()(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) const { // need to use the hash of whole tx here since multiple txs could have // the same Contents Hash h1 = tx1->getFullHash(); Hash h2 = tx2->getFullHash(); Hash v1, v2; for (int n = 0; n < 32; n++) { v1[n] = mSetHash[n] ^ h1[n]; v2[n] = mSetHash[n] ^ h2[n]; } return v1 < v2; }
void applySetOptions(Application& app, SecretKey& source, AccountID* inflationDest, uint32_t* setFlags, uint32_t* clearFlags, Thresholds* thrs, Signer* signer, SequenceNumber seq, SetOptionsResultCode result) { TransactionFramePtr txFrame; txFrame = createSetOptions(source, inflationDest, setFlags, clearFlags, thrs, signer, seq); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); txFrame->apply(delta, app); checkTransaction(*txFrame); REQUIRE(SetOptionsOpFrame::getInnerCode( txFrame->getResult().result.results()[0]) == result); }
void applyPaymentTx(Application& app, SecretKey& from, SecretKey& to, SequenceNumber seq, int64_t amount, PaymentResultCode result) { TransactionFramePtr txFrame; AccountFrame fromAccount; AccountFrame toAccount; bool beforeToExists = AccountFrame::loadAccount( to.getPublicKey(), toAccount, app.getDatabase()); REQUIRE(AccountFrame::loadAccount(from.getPublicKey(), fromAccount, app.getDatabase())); txFrame = createPaymentTx(from, to, seq, amount); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); txFrame->apply(delta, app); checkTransaction(*txFrame); auto txResult = txFrame->getResult(); auto innerCode = PaymentOpFrame::getInnerCode(txResult.result.results()[0]); REQUIRE(innerCode == result); REQUIRE(txResult.feeCharged == app.getLedgerManager().getTxFee()); AccountFrame toAccountAfter; bool afterToExists = AccountFrame::loadAccount( to.getPublicKey(), toAccountAfter, app.getDatabase()); if (!(innerCode == PAYMENT_SUCCESS || innerCode == PAYMENT_SUCCESS_MULTI)) { // check that the target account didn't change REQUIRE(beforeToExists == afterToExists); if (beforeToExists && afterToExists) { REQUIRE(memcmp(&toAccount.getAccount(), &toAccountAfter.getAccount(), sizeof(AccountEntry)) == 0); } } else { REQUIRE(afterToExists); } }
OperationResult applyInflation(Application& app, SecretKey& from, SequenceNumber seq, InflationResultCode result) { TransactionFramePtr txFrame = createInflation(from, seq); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); bool res = txFrame->apply(delta, app); checkTransaction(*txFrame); REQUIRE(InflationOpFrame::getInnerCode( txFrame->getResult().result.results()[0]) == result); if (res) { delta.commit(); } return getFirstResult(*txFrame); }
bool operator()(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) { if (tx1->getSourceID() == tx2->getSourceID()) return tx1->getSeqNum() < tx2->getSeqNum(); float fee1 = mAccountFeeMap[tx1->getSourceID()]; float fee2 = mAccountFeeMap[tx2->getSourceID()]; if (fee1 == fee2) return tx1->getSourceID() < tx2->getSourceID(); return fee1 > fee2; }
TransactionFramePtr transactionFromOperation(SecretKey& from, SequenceNumber seq, Operation const& op) { TransactionEnvelope e; e.tx.sourceAccount = from.getPublicKey(); e.tx.maxLedger = UINT32_MAX; e.tx.minLedger = 0; e.tx.fee = 10; e.tx.seqNum = seq; e.tx.operations.push_back(op); TransactionFramePtr res = TransactionFrame::makeTransactionFromWire(e); res->addSignature(from); return res; }
PaymentResult applyCreditPaymentTx(Application& app, SecretKey& from, SecretKey& to, Currency& ci, SequenceNumber seq, int64_t amount, PaymentResultCode result, std::vector<Currency>* path) { TransactionFramePtr txFrame; txFrame = createCreditPaymentTx(from, to, ci, seq, amount, path); LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); txFrame->apply(delta, app); checkTransaction(*txFrame); auto& firstResult = getFirstResult(*txFrame); PaymentResult res = firstResult.tr().paymentResult(); auto resCode = res.code(); REQUIRE(resCode == result); return res; }
void CommandHandler::testTx(std::string const& params, std::string& retStr) { std::map<std::string, std::string> retMap; http::server::server::parseParams(params, retMap); auto to = retMap.find("to"); auto from = retMap.find("from"); auto amount = retMap.find("amount"); auto create = retMap.find("create"); Json::Value root; if (to != retMap.end() && from != retMap.end() && amount != retMap.end()) { Hash const& networkID = mApp.getNetworkID(); SecretKey toKey, fromKey; if (to->second == "root") { toKey = getRoot(networkID); } else { toKey = getAccount(to->second.c_str()); } if (from->second == "root") { fromKey = getRoot(networkID); } else { fromKey = getAccount(from->second.c_str()); } uint64_t paymentAmount = 0; std::istringstream iss(amount->second); iss >> paymentAmount; root["from_name"] = from->second; root["to_name"] = to->second; root["from_id"] = PubKeyUtils::toStrKey(fromKey.getPublicKey()); root["to_id"] = PubKeyUtils::toStrKey(toKey.getPublicKey()); ; root["amount"] = (Json::UInt64)paymentAmount; SequenceNumber fromSeq = getSeq(fromKey, mApp) + 1; TransactionFramePtr txFrame; if (create != retMap.end() && create->second == "true") { txFrame = createCreateAccountTx(networkID, fromKey, toKey, fromSeq, paymentAmount); } else { txFrame = createPaymentTx(networkID, fromKey, toKey, fromSeq, paymentAmount); } switch (mApp.getHerder().recvTransaction(txFrame)) { case Herder::TX_STATUS_PENDING: root["status"] = "pending"; break; case Herder::TX_STATUS_DUPLICATE: root["status"] = "duplicate"; break; case Herder::TX_STATUS_ERROR: root["status"] = "error"; root["detail"] = xdr::xdr_to_string(txFrame->getResult().result.code()); break; default: assert(false); } }
static bool SeqSorter(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) { return tx1->getSeqNum() < tx2->getSeqNum(); }
size_t TransactionFrame::copyTransactionsToStream(Database& db, soci::session& sess, uint32_t ledgerSeq, uint32_t ledgerCount, XDROutputFileStream& txOut, XDROutputFileStream& txResultOut) { auto timer = db.getSelectTimer("txhistory"); std::string txBody, txResult, txMeta; uint32_t begin = ledgerSeq, end = ledgerSeq + ledgerCount; size_t n = 0; TransactionEnvelope tx; uint32_t curLedgerSeq; assert(begin <= end); soci::statement st = (sess.prepare << "SELECT ledgerseq, txbody, txresult FROM txhistory " "WHERE ledgerseq >= :begin AND ledgerseq < :end ORDER " "BY ledgerseq ASC, txindex ASC", soci::into(curLedgerSeq), soci::into(txBody), soci::into(txResult), soci::use(begin), soci::use(end)); Hash h; TxSetFrame txSet(h); // we're setting the hash later TransactionHistoryResultEntry results; st.execute(true); uint32_t lastLedgerSeq = curLedgerSeq; results.ledgerSeq = curLedgerSeq; while (st.got_data()) { if (curLedgerSeq != lastLedgerSeq) { saveTransactionHelper(db, sess, lastLedgerSeq, txSet, results, txOut, txResultOut); // reset state txSet.mTransactions.clear(); results.ledgerSeq = ledgerSeq; results.txResultSet.results.clear(); lastLedgerSeq = curLedgerSeq; } std::string body = base64::decode(txBody); std::string result = base64::decode(txResult); xdr::xdr_get g1(body.data(), body.data() + body.size()); xdr_argpack_archive(g1, tx); TransactionFramePtr txFrame = make_shared<TransactionFrame>(tx); txSet.add(txFrame); xdr::xdr_get g2(result.data(), result.data() + result.size()); results.txResultSet.results.emplace_back(); TransactionResultPair& p = results.txResultSet.results.back(); xdr_argpack_archive(g2, p); if (p.transactionHash != txFrame->getContentsHash()) { throw std::runtime_error("transaction mismatch"); } ++n; st.fetch(); } if (n != 0) { saveTransactionHelper(db, sess, lastLedgerSeq, txSet, results, txOut, txResultOut); } return n; }
bool operator()(TransactionFramePtr const& tx1, TransactionFramePtr const& tx2) { return tx1->getFeeRatio(mApp) < tx2->getFeeRatio(mApp); }
// 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; }
static CreateOfferResult applyCreateOfferHelper(Application& app, LedgerDelta& delta, uint64 offerId, SecretKey& source, Currency& takerGets, Currency& takerPays, Price const& price, int64_t amount, SequenceNumber seq) { uint64_t expectedOfferID = delta.getHeaderFrame().getLastGeneratedID() + 1; if (offerId != 0) { expectedOfferID = offerId; } TransactionFramePtr txFrame; txFrame = createOfferOp(offerId, source, takerGets, takerPays, price, amount, seq); txFrame->apply(delta, app); checkTransaction(*txFrame); auto& results = txFrame->getResult().result.results(); REQUIRE(results.size() == 1); auto& createOfferResult = results[0].tr().createOfferResult(); if (createOfferResult.code() == CREATE_OFFER_SUCCESS) { OfferFrame offer; auto& offerResult = createOfferResult.success().offer; auto& offerEntry = offer.getOffer(); switch (offerResult.effect()) { case CREATE_OFFER_CREATED: case CREATE_OFFER_UPDATED: REQUIRE(OfferFrame::loadOffer(source.getPublicKey(), expectedOfferID, offer, app.getDatabase())); REQUIRE(memcmp(&offerEntry, &offerResult.offer(), sizeof(OfferEntry)) == 0); REQUIRE(offerEntry.price == price); REQUIRE(memcmp(&offerEntry.takerGets, &takerGets, sizeof(Currency)) == 0); REQUIRE(memcmp(&offerEntry.takerPays, &takerPays, sizeof(Currency)) == 0); break; case CREATE_OFFER_DELETED: REQUIRE(!OfferFrame::loadOffer(source.getPublicKey(), expectedOfferID, offer, app.getDatabase())); break; default: abort(); } } return createOfferResult; }
Application::pointer appPtr = Application::create(clock, cfg); Application& app = *appPtr; app.start(); // set up world SecretKey root = getRoot(); SecretKey a1 = getAccount("A"); SequenceNumber rootSeq = getAccountSeqNum(root, app) + 1; ; const uint64_t paymentAmount = app.getLedgerManager().getCurrentLedgerHeader().baseReserve * 10; SECTION("outer envelope") { TransactionFramePtr txFrame; LedgerDelta delta(app.getLedgerManager().getCurrentLedgerHeader()); SECTION("no signature") { txFrame = createPaymentTx(root, a1, rootSeq++, paymentAmount); txFrame->getEnvelope().signatures.clear(); txFrame->apply(delta, app); REQUIRE(txFrame->getResultCode() == txBAD_AUTH); } SECTION("bad signature") { txFrame = createPaymentTx(root, a1, rootSeq++, paymentAmount); txFrame->getEnvelope().signatures[0].signature.fill(123);
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; }
static void doInflation(Application& app, int nbAccounts, std::function<int64(int)> getBalance, std::function<int(int)> getVote, int expectedWinnerCount) { // simulate the expected inflation based off the current ledger state std::map<int, int64> balances; // load account balances for (int i = 0; i < nbAccounts; i++) { AccountFrame act; if (getBalance(i) < 0) { balances[i] = -1; REQUIRE(!AccountFrame::loadAccount(getTestAccount(i).getPublicKey(), act, app.getDatabase())); } else { REQUIRE(AccountFrame::loadAccount(getTestAccount(i).getPublicKey(), act, app.getDatabase())); balances[i] = act.getBalance(); // double check that inflationDest is setup properly if (act.getAccount().inflationDest) { REQUIRE(getTestAccount(getVote(i)).getPublicKey() == *act.getAccount().inflationDest); } else { REQUIRE(getVote(i) < 0); } } } LedgerManager& lm = app.getLedgerManager(); LedgerHeader& cur = lm.getCurrentLedgerHeader(); cur.feePool = 10000; int64 expectedTotcoins = cur.totalCoins; int64 expectedFees = cur.feePool; std::vector<int64> expectedBalances; auto root = getRoot(); TransactionFramePtr txFrame = createInflation(root, getAccountSeqNum(root, app) + 1); expectedFees += txFrame->getFee(app); expectedBalances = simulateInflation(nbAccounts, expectedTotcoins, expectedFees, [&](int i) { return balances[i]; }, getVote); // perform actual inflation { LedgerDelta delta(lm.getCurrentLedgerHeader()); REQUIRE(txFrame->apply(delta, app)); delta.commit(); } // verify ledger state LedgerHeader& cur2 = lm.getCurrentLedgerHeader(); REQUIRE(cur2.totalCoins == expectedTotcoins); REQUIRE(cur2.feePool == expectedFees); // verify balances InflationResult const& infResult = getFirstResult(*txFrame).tr().inflationResult(); auto const& payouts = infResult.payouts(); int actualChanges = 0; for (int i = 0; i < nbAccounts; i++) { AccountFrame act; auto const& pk = getTestAccount(i).getPublicKey(); if (expectedBalances[i] < 0) { REQUIRE(!AccountFrame::loadAccount(pk, act, app.getDatabase())); REQUIRE(balances[i] < 0); // account didn't get deleted } else { REQUIRE(AccountFrame::loadAccount(pk, act, app.getDatabase())); REQUIRE(expectedBalances[i] == act.getBalance()); if (expectedBalances[i] != balances[i]) { REQUIRE(balances[i] >= 0); actualChanges++; bool found = false; for (auto const& p : payouts) { if (p.destination == pk) { int64 computedFromResult = balances[i] + p.amount; REQUIRE(computedFromResult == expectedBalances[i]); found = true; break; } } REQUIRE(found); } } } REQUIRE(actualChanges == expectedWinnerCount); REQUIRE(expectedWinnerCount == payouts.size()); }