TER CancelOffer::preflight (PreflightContext const& ctx) { auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; auto const uTxFlags = ctx.tx.getFlags(); if (uTxFlags & tfUniversalMask) { JLOG(ctx.j.trace) << "Malformed transaction: " << "Invalid flags set."; return temINVALID_FLAG; } auto const seq = ctx.tx.getFieldU32 (sfOfferSequence); if (! seq) { JLOG(ctx.j.trace) << "CancelOffer::preflight: missing sequence"; return temBAD_SEQUENCE; } return preflight2(ctx); }
/** Performs early sanity checks on the account and fee fields */ NotTEC preflight1 (PreflightContext const& ctx) { auto const ret = preflight0(ctx); if (!isTesSuccess(ret)) return ret; auto const id = ctx.tx.getAccountID(sfAccount); if (id == beast::zero) { JLOG(ctx.j.warn()) << "preflight1: bad account id"; return temBAD_SRC_ACCOUNT; } // No point in going any further if the transaction fee is malformed. auto const fee = ctx.tx.getFieldAmount (sfFee); if (!fee.native () || fee.negative () || !isLegalAmount (fee.xrp ())) { JLOG(ctx.j.debug()) << "preflight1: invalid fee"; return temBAD_FEE; } auto const spk = ctx.tx.getSigningPubKey(); if (!spk.empty () && !publicKeyType (makeSlice (spk))) { JLOG(ctx.j.debug()) << "preflight1: invalid signing key"; return temBAD_SIGNATURE; } return tesSUCCESS; }
NotTEC CreateTicket::preflight (PreflightContext const& ctx) { if (! ctx.rules.enabled(featureTickets)) return temDISABLED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; if (ctx.tx.isFieldPresent (sfExpiration)) { if (ctx.tx.getFieldU32 (sfExpiration) == 0) { JLOG(ctx.j.warn()) << "Malformed transaction: bad expiration"; return temBAD_EXPIRATION; } } return preflight2 (ctx); }
TER SetTrust::preflight (PreflightContext const& ctx) { auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; auto& tx = ctx.tx; auto& j = ctx.j; std::uint32_t const uTxFlags = tx.getFlags (); if (uTxFlags & tfTrustSetMask) { JLOG(j.trace) << "Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } STAmount const saLimitAmount (tx.getFieldAmount (sfLimitAmount)); if (!isLegalNet (saLimitAmount)) return temBAD_AMOUNT; if (saLimitAmount.native ()) { JLOG(j.trace) << "Malformed transaction: specifies native limit " << saLimitAmount.getFullText (); return temBAD_LIMIT; } if (badCurrency() == saLimitAmount.getCurrency ()) { JLOG(j.trace) << "Malformed transaction: specifies XRP as IOU"; return temBAD_CURRENCY; } if (saLimitAmount < zero) { JLOG(j.trace) << "Malformed transaction: Negative credit limit."; return temBAD_LIMIT; } // Check if destination makes sense. auto const& issuer = saLimitAmount.getIssuer (); if (!issuer || issuer == noAccount()) { JLOG(j.trace) << "Malformed transaction: no destination account."; return temDST_NEEDED; } return preflight2 (ctx); }
TER CancelTicket::preflight (PreflightContext const& ctx) { #if ! RIPPLE_ENABLE_TICKETS if (! (ctx.flags & tapENABLE_TESTING)) return temDISABLED; #endif auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; return preflight2 (ctx); }
NotTEC CancelTicket::preflight (PreflightContext const& ctx) { if (! ctx.rules.enabled(featureTickets)) return temDISABLED; if (ctx.tx.getFlags() & tfUniversalMask) return temINVALID_FLAG; auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; return preflight2 (ctx); }
TER SetRegularKey::preflight (PreflightContext const& ctx) { auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; std::uint32_t const uTxFlags = ctx.tx.getFlags (); if (uTxFlags & tfUniversalMask) { JLOG(ctx.j.trace) << "Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } return preflight2(ctx); }
TER SetSignerList::preflight (PreflightContext const& ctx) { if (! ctx.rules.enabled(featureMultiSign, ctx.app.config().features)) return temDISABLED; auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j); if (std::get<0>(result) != tesSUCCESS) return std::get<0>(result); if (std::get<3>(result) == unknown) { // Neither a set nor a destroy. Malformed. JLOG(ctx.j.trace) << "Malformed transaction: Invalid signer set list format."; return temMALFORMED; } if (std::get<3>(result) == set) { // Validate our settings. auto const account = ctx.tx.getAccountID(sfAccount); TER const ter = validateQuorumAndSignerEntries(std::get<1>(result), std::get<2>(result), account, ctx.j); if (ter != tesSUCCESS) { return ter; } } return preflight2 (ctx); }
TER TransactionEngine::applyTransaction ( STTx const& txn, TransactionEngineParams params, bool& didApply) { WriteLog (lsTRACE, TransactionEngine) << "applyTransaction>"; didApply = false; assert (mLedger); uint256 const& txID = txn.getTransactionID (); mNodes.init (mLedger, txID, mLedger->getLedgerSeq (), params); #ifdef BEAST_DEBUG if (1) { Serializer ser; txn.add (ser); SerializerIterator sit (ser); STTx s2 (sit); if (!s2.isEquivalent (txn)) { WriteLog (lsFATAL, TransactionEngine) << "Transaction serdes mismatch"; WriteLog (lsINFO, TransactionEngine) << txn.getJson (0); WriteLog (lsFATAL, TransactionEngine) << s2.getJson (0); assert (false); } } #endif if (!txID) { WriteLog (lsWARNING, TransactionEngine) << "applyTransaction: invalid transaction id"; return temINVALID; } TER terResult = Transactor::transact (txn, params, this); if (terResult == temUNKNOWN) { WriteLog (lsWARNING, TransactionEngine) << "applyTransaction: Invalid transaction: unknown transaction type"; return temUNKNOWN; } if (ShouldLog (lsDEBUG, TransactionEngine)) { std::string strToken; std::string strHuman; transResultInfo (terResult, strToken, strHuman); WriteLog (lsDEBUG, TransactionEngine) << "applyTransaction: terResult=" << strToken << " : " << terResult << " : " << strHuman; } if (isTesSuccess (terResult)) didApply = true; else if (isTecClaim (terResult) && !(params & tapRETRY)) { // only claim the transaction fee WriteLog (lsDEBUG, TransactionEngine) << "Reprocessing to only claim fee"; mNodes.clear (); SLE::pointer txnAcct = entryCache (ltACCOUNT_ROOT, getAccountRootIndex (txn.getSourceAccount ())); if (!txnAcct) terResult = terNO_ACCOUNT; else { std::uint32_t t_seq = txn.getSequence (); std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence); if (a_seq < t_seq) terResult = terPRE_SEQ; else if (a_seq > t_seq) terResult = tefPAST_SEQ; else { STAmount fee = txn.getTransactionFee (); STAmount balance = txnAcct->getFieldAmount (sfBalance); STAmount balanceVBC = txnAcct->getFieldAmount(sfBalanceVBC); // We retry/reject the transaction if the account // balance is zero or we're applying against an open // ledger and the balance is less than the fee if ((balance == zero) || (balanceVBC.getNValue() == 0) || ((params & tapOPEN_LEDGER) && (balance < fee))) { // Account has no funds or ledger is open terResult = terINSUF_FEE_B; } else { if (fee > balance) fee = balance; txnAcct->setFieldAmount (sfBalance, balance - fee); txnAcct->setFieldAmount(sfBalanceVBC, balanceVBC); txnAcct->setFieldU32 (sfSequence, t_seq + 1); entryModify (txnAcct); didApply = true; } } } } else WriteLog (lsDEBUG, TransactionEngine) << "Not applying transaction " << txID; if (didApply) { if (!checkInvariants (terResult, txn, params)) { WriteLog (lsFATAL, TransactionEngine) << "Transaction violates invariants"; WriteLog (lsFATAL, TransactionEngine) << txn.getJson (0); WriteLog (lsFATAL, TransactionEngine) << transToken (terResult) << ": " << transHuman (terResult); WriteLog (lsFATAL, TransactionEngine) << mNodes.getJson (0); didApply = false; terResult = tefINTERNAL; } else { // Transaction succeeded fully or (retries are not allowed and the // transaction could claim a fee) Serializer m; mNodes.calcRawMeta (m, terResult, mTxnSeq++); txnWrite (); Serializer s; txn.add (s); if (params & tapOPEN_LEDGER) { if (!mLedger->addTransaction (txID, s)) { WriteLog (lsFATAL, TransactionEngine) << "Tried to add transaction to open ledger that already had it"; assert (false); throw std::runtime_error ("Duplicate transaction applied"); } } else { if (!mLedger->addTransaction (txID, s, m)) { WriteLog (lsFATAL, TransactionEngine) << "Tried to add transaction to ledger that already had it"; assert (false); throw std::runtime_error ("Duplicate transaction applied to closed ledger"); } // Charge whatever fee they specified. STAmount saPaid = txn.getTransactionFee (); mLedger->destroyCoins (saPaid.getNValue ()); } } } mTxnAccount.reset (); mNodes.clear (); if (!(params & tapOPEN_LEDGER) && isTemMalformed (terResult)) { // XXX Malformed or failed transaction in closed ledger must bow out. } return terResult; }
//------------------------------------------------------------------------------ std::pair<TER, bool> Transactor::operator()() { JLOG(j_.trace()) << "apply: " << ctx_.tx.getTransactionID (); #ifdef BEAST_DEBUG { Serializer ser; ctx_.tx.add (ser); SerialIter sit(ser.slice()); STTx s2 (sit); if (! s2.isEquivalent(ctx_.tx)) { JLOG(j_.fatal()) << "Transaction serdes mismatch"; JLOG(j_.info()) << to_string(ctx_.tx.getJson (0)); JLOG(j_.fatal()) << s2.getJson (0); assert (false); } } #endif auto result = ctx_.preclaimResult; if (result == tesSUCCESS) result = apply(); // No transaction can return temUNKNOWN from apply, // and it can't be passed in from a preclaim. assert(result != temUNKNOWN); if (auto stream = j_.trace()) stream << "preclaim result: " << transToken(result); bool applied = isTesSuccess (result); auto fee = ctx_.tx.getFieldAmount(sfFee).xrp (); if (ctx_.size() > oversizeMetaDataCap) result = tecOVERSIZE; if ((result == tecOVERSIZE) || (isTecClaimHardFail (result, view().flags()))) { JLOG(j_.trace()) << "reapplying because of " << transToken(result); std::vector<uint256> removedOffers; if (result == tecOVERSIZE) { ctx_.visit ( [&removedOffers]( uint256 const& index, bool isDelete, std::shared_ptr <SLE const> const& before, std::shared_ptr <SLE const> const& after) { if (isDelete) { assert (before && after); if (before && after && (before->getType() == ltOFFER) && (before->getFieldAmount(sfTakerPays) == after->getFieldAmount(sfTakerPays))) { // Removal of offer found or made unfunded removedOffers.push_back (index); } } }); } // Reset the context, potentially adjusting the fee fee = reset(fee); // If necessary, remove any offers found unfunded during processing if (result == tecOVERSIZE) removeUnfundedOffers (view(), removedOffers, ctx_.app.journal ("View")); applied = true; } if (applied) { // Check invariants: if `tecINVARIANT_FAILED` is not returned, we can // proceed to apply the tx result = ctx_.checkInvariants(result, fee); if (result == tecINVARIANT_FAILED) { // if invariants checking failed again, reset the context and // attempt to only claim a fee. fee = reset(fee); // Check invariants again to ensure the fee claiming doesn't // violate invariants. result = ctx_.checkInvariants(result, fee); } // We ran through the invariant checker, which can, in some cases, // return a tef error code. Don't apply the transaction in that case. if (!isTecClaim(result) && !isTesSuccess(result)) applied = false; } if (applied) { // Transaction succeeded fully or (retries are not allowed and the // transaction could claim a fee) // The transactor and invariant checkers guarantee that this will // *never* trigger but if it, somehow, happens, don't allow a tx // that charges a negative fee. if (fee < beast::zero) Throw<std::logic_error> ("fee charged is negative!"); // Charge whatever fee they specified. The fee has already been // deducted from the balance of the account that issued the // transaction. We just need to account for it in the ledger // header. if (!view().open() && fee != beast::zero) ctx_.destroyXRP (fee); // Once we call apply, we will no longer be able to look at view() ctx_.apply(result); } JLOG(j_.trace()) << (applied ? "applied" : "not applied") << transToken(result); return { result, applied }; }
TER SetAccount::preflight (PreflightContext const& ctx) { auto const ret = preflight1 (ctx); if (!isTesSuccess (ret)) return ret; auto& tx = ctx.tx; auto& j = ctx.j; std::uint32_t const uTxFlags = tx.getFlags (); if (uTxFlags & tfAccountSetMask) { JLOG(j.trace) << "Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } std::uint32_t const uSetFlag = tx.getFieldU32 (sfSetFlag); std::uint32_t const uClearFlag = tx.getFieldU32 (sfClearFlag); if ((uSetFlag != 0) && (uSetFlag == uClearFlag)) { JLOG(j.trace) << "Malformed transaction: Set and clear same flag."; return temINVALID_FLAG; } // // RequireAuth // bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth); if (bSetRequireAuth && bClearRequireAuth) { JLOG(j.trace) << "Malformed transaction: Contradictory flags set."; return temINVALID_FLAG; } // // RequireDestTag // bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest); bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest); if (bSetRequireDest && bClearRequireDest) { JLOG(j.trace) << "Malformed transaction: Contradictory flags set."; return temINVALID_FLAG; } // // DisallowXRP // bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP); bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP); if (bSetDisallowXRP && bClearDisallowXRP) { JLOG(j.trace) << "Malformed transaction: Contradictory flags set."; return temINVALID_FLAG; } // TransferRate if (tx.isFieldPresent (sfTransferRate)) { std::uint32_t uRate = tx.getFieldU32 (sfTransferRate); if (uRate && (uRate < QUALITY_ONE)) { JLOG(j.trace) << "Malformed transaction: Bad transfer rate."; return temBAD_TRANSFER_RATE; } } auto const messageKey = tx[~sfMessageKey]; if (messageKey && messageKey->size() > PUBLIC_BYTES_MAX) { JLOG(j.trace) << "message key too long"; return telBAD_PUBLIC_KEY; } auto const domain = tx[~sfDomain]; if (domain&& domain->size() > DOMAIN_BYTES_MAX) { JLOG(j.trace) << "domain too long"; return telBAD_DOMAIN; } return preflight2(ctx); }