void AmendmentTableImpl::doVoting (Ledger::ref lastClosedLedger, SHAMap::ref initialPosition) { // LCL must be flag ledger assert((lastClosedLedger->getLedgerSeq () % 256) == 0); AmendmentSet amendmentSet (lastClosedLedger->getParentCloseTimeNC ()); // get validations for ledger before flag ledger ValidationSet valSet = getApp().getValidations ().getValidations (lastClosedLedger->getParentHash ()); for (auto const& entry : valSet) { auto const& val = *entry.second; if (val.isTrusted ()) { amendmentSet.addVoter (); if (val.isFieldPresent (sfAmendments)) { for (auto const& amendment : val.getFieldV256 (sfAmendments)) { amendmentSet.addVote (amendment); } } } } reportValidations (amendmentSet); amendmentList_t lAmendments = getToEnable (lastClosedLedger->getCloseTimeNC ()); for (auto const& uAmendment : lAmendments) { if (m_journal.warning) m_journal.warning << "Voting for amendment: " << uAmendment; // Create the transaction to enable the amendment STTx trans (ttAMENDMENT); trans.setFieldAccount (sfAccount, Account ()); trans.setFieldH256 (sfAmendment, uAmendment); uint256 txID = trans.getTransactionID (); if (m_journal.warning) m_journal.warning << "Vote ID: " << txID; // Inject the transaction into our initial proposal Serializer s; trans.add (s, true); #if RIPPLE_PROPOSE_AMENDMENTS SHAMapItem::pointer tItem = std::make_shared<SHAMapItem> (txID, s.peekData ()); if (!initialPosition->addGiveItem (tItem, true, false)) { if (m_journal.warning) m_journal.warning << "Ledger already had amendment transaction"; } #endif } }
static Blob getSigningData (STTx const& that) { Serializer s; s.add32 (HashPrefix::txSign); that.addWithoutSigningFields (s); return s.getData(); }
std::tuple<TER, std::uint32_t, std::vector<SignerEntries::SignerEntry>, SetSignerList::Operation> SetSignerList::determineOperation(STTx const& tx, ApplyFlags flags, beast::Journal j) { // Check the quorum. A non-zero quorum means we're creating or replacing // the list. A zero quorum means we're destroying the list. auto const quorum = tx[sfSignerQuorum]; std::vector<SignerEntries::SignerEntry> sign; Operation op = unknown; bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries)); if (quorum && hasSignerEntries) { auto signers = SignerEntries::deserialize(tx, j, "transaction"); if (signers.second != tesSUCCESS) return std::make_tuple(signers.second, quorum, sign, op); std::sort(signers.first.begin(), signers.first.end()); // Save deserialized list for later. sign = std::move(signers.first); op = set; } else if ((quorum == 0) && !hasSignerEntries) { op = destroy; } return std::make_tuple(tesSUCCESS, quorum, sign, op); }
static TxConsequences invoke_calculateConsequences(STTx const& tx) { switch (tx.getTxnType()) { case ttACCOUNT_SET: return invoke_calculateConsequences<SetAccount>(tx); case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx); case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx); case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx); case ttSUSPAY_CREATE: return invoke_calculateConsequences<SusPayCreate>(tx); case ttSUSPAY_FINISH: return invoke_calculateConsequences<SusPayFinish>(tx); case ttSUSPAY_CANCEL: return invoke_calculateConsequences<SusPayCancel>(tx); case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx); case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx); case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx); case ttTICKET_CREATE: return invoke_calculateConsequences<CreateTicket>(tx); case ttTRUST_SET: return invoke_calculateConsequences<SetTrust>(tx); case ttAMENDMENT: case ttFEE: // fall through to default default: assert(false); return { TxConsequences::blocker, Transactor::calculateFeePaid(tx), beast::zero }; } }
static Blob getSigningData (STTx const& that) { Serializer s; s.add32 (HashPrefix::txSign); that.add (s, false); return s.getData(); }
std::pair<Validity, std::string> checkValidity(HashRouter& router, STTx const& tx, Rules const& rules, Config const& config) { auto const allowMultiSign = rules.enabled(featureMultiSign); auto const id = tx.getTransactionID(); auto const flags = router.getFlags(id); if (flags & SF_SIGBAD) // Signature is known bad return {Validity::SigBad, "Transaction has bad signature."}; if (!(flags & SF_SIGGOOD)) { // Don't know signature state. Check it. auto const sigVerify = tx.checkSign(allowMultiSign); if (! sigVerify.first) { router.setFlags(id, SF_SIGBAD); return {Validity::SigBad, sigVerify.second}; } router.setFlags(id, SF_SIGGOOD); } // Signature is now known good if (flags & SF_LOCALBAD) // ...but the local checks // are known bad. return {Validity::SigGoodOnly, "Local checks failed."}; if (flags & SF_LOCALGOOD) // ...and the local checks // are known good. return {Validity::Valid, ""}; // Do the local checks std::string reason; if (!passesLocalChecks(tx, reason)) { router.setFlags(id, SF_LOCALBAD); return {Validity::SigGoodOnly, reason}; } router.setFlags(id, SF_LOCALGOOD); return {Validity::Valid, ""}; }
std::shared_ptr<STTx const> sterilize (STTx const& stx) { Serializer s; stx.add(s); SerialIter sit(s.slice()); return std::make_shared<STTx const>(std::ref(sit)); }
std::uint64_t Transactor::calculateBaseFee ( ReadView const& view, STTx const& tx) { // Returns the fee in fee units. // The computation has two parts: // * The base fee, which is the same for most transactions. // * The additional cost of each multisignature on the transaction. std::uint64_t baseFee = view.fees().units; // Each signer adds one more baseFee to the minimum required fee // for the transaction. std::uint32_t signerCount = 0; if (tx.isFieldPresent (sfSigners)) signerCount = tx.getFieldArray (sfSigners).size(); return baseFee + (signerCount * baseFee); }
ApplyResult applyTransaction (Application& app, OpenView& view, STTx const& txn, bool retryAssured, ApplyFlags flags, beast::Journal j) { // Returns false if the transaction has need not be retried. if (retryAssured) flags = flags | tapRETRY; JLOG (j.debug()) << "TXN " << txn.getTransactionID () //<< (engine.view().open() ? " open" : " closed") // because of the optional in engine << (retryAssured ? "/retry" : "/final"); try { auto const result = apply(app, view, txn, flags, j); if (result.second) { JLOG (j.debug()) << "Transaction applied: " << transHuman (result.first); return ApplyResult::Success; } if (isTefFailure (result.first) || isTemMalformed (result.first) || isTelLocal (result.first)) { // failure JLOG (j.debug()) << "Transaction failure: " << transHuman (result.first); return ApplyResult::Fail; } JLOG (j.debug()) << "Transaction retry: " << transHuman (result.first); return ApplyResult::Retry; } catch (std::exception const&) { JLOG (j.warn()) << "Throws"; return ApplyResult::Fail; } }
std::pair<TER, bool> apply (OpenView& view, STTx const& tx, ApplyFlags flags, Config const& config, beast::Journal j) { try { return invoke (tx.getTxnType(), view, tx, flags, config, j); } catch(std::exception const& e) { JLOG(j.fatal) << "Caught exception: " << e.what(); return { tefEXCEPTION, false }; } catch(...) { JLOG(j.fatal) << "Caught unknown exception"; return { tefEXCEPTION, false }; } }
static std::uint64_t getFeeLevelPaid( STTx const& tx, std::uint64_t baseRefLevel, std::uint64_t refTxnCostDrops) { // Compute the minimum XRP fee the transaction could pay auto requiredFee = getRequiredFeeLevel(tx.getTxnType()); if (requiredFee == 0 || refTxnCostDrops == 0) // If nothing is required, or the cost is 0, // the level is effectively infinite. return std::numeric_limits<std::uint64_t>::max(); // TODO: getRequiredFeeLevel(ttREFERENCE)? auto referenceFee = ripple::getRequiredFeeLevel(ttACCOUNT_SET); // Don't care about the overflow flag return mulDiv(tx[sfFee].xrp().drops(), baseRefLevel * referenceFee, refTxnCostDrops * requiredFee).second; }
void FeeVoteImpl::doVoting (Ledger::ref lastClosedLedger, std::shared_ptr<SHAMap> const& initialPosition) { // LCL must be flag ledger assert ((lastClosedLedger->getLedgerSeq () % 256) == 0); detail::VotableInteger<std::uint64_t> baseFeeVote ( lastClosedLedger->getBaseFee (), target_.reference_fee); detail::VotableInteger<std::uint32_t> baseReserveVote ( lastClosedLedger->getReserve (0), target_.account_reserve); detail::VotableInteger<std::uint32_t> incReserveVote ( lastClosedLedger->getReserveInc (), target_.owner_reserve); // get validations for ledger before flag ValidationSet const set = getApp().getValidations ().getValidations ( lastClosedLedger->getParentHash ()); for (auto const& e : set) { STValidation const& val = *e.second; if (val.isTrusted ()) { if (val.isFieldPresent (sfBaseFee)) { baseFeeVote.addVote (val.getFieldU64 (sfBaseFee)); } else { baseFeeVote.noVote (); } if (val.isFieldPresent (sfReserveBase)) { baseReserveVote.addVote (val.getFieldU32 (sfReserveBase)); } else { baseReserveVote.noVote (); } if (val.isFieldPresent (sfReserveIncrement)) { incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement)); } else { incReserveVote.noVote (); } } } // choose our positions std::uint64_t const baseFee = baseFeeVote.getVotes (); std::uint32_t const baseReserve = baseReserveVote.getVotes (); std::uint32_t const incReserve = incReserveVote.getVotes (); // add transactions to our position if ((baseFee != lastClosedLedger->getBaseFee ()) || (baseReserve != lastClosedLedger->getReserve (0)) || (incReserve != lastClosedLedger->getReserveInc ())) { if (journal_.warning) journal_.warning << "We are voting for a fee change: " << baseFee << "/" << baseReserve << "/" << incReserve; STTx trans (ttFEE); trans.setFieldAccount (sfAccount, AccountID ()); trans.setFieldU64 (sfBaseFee, baseFee); trans.setFieldU32 (sfReferenceFeeUnits, 10); trans.setFieldU32 (sfReserveBase, baseReserve); trans.setFieldU32 (sfReserveIncrement, incReserve); uint256 txID = trans.getTransactionID (); if (journal_.warning) journal_.warning << "Vote: " << txID; Serializer s; trans.add (s); auto tItem = std::make_shared<SHAMapItem> (txID, s.peekData ()); if (!initialPosition->addGiveItem (tItem, true, false)) { if (journal_.warning) journal_.warning << "Ledger already had fee change"; } } }
void FeeVoteImpl::doVoting( std::shared_ptr<ReadView const> const& lastClosedLedger, ValidationSet const& set, std::shared_ptr<SHAMap> const& initialPosition) { // LCL must be flag ledger assert ((lastClosedLedger->info().seq % 256) == 0); detail::VotableInteger<std::uint64_t> baseFeeVote ( lastClosedLedger->fees().base, target_.reference_fee); detail::VotableInteger<std::uint32_t> baseReserveVote ( lastClosedLedger->fees().accountReserve(0).drops(), target_.account_reserve); detail::VotableInteger<std::uint32_t> incReserveVote ( lastClosedLedger->fees().increment, target_.owner_reserve); for (auto const& e : set) { STValidation const& val = *e.second; if (val.isTrusted ()) { if (val.isFieldPresent (sfBaseFee)) { baseFeeVote.addVote (val.getFieldU64 (sfBaseFee)); } else { baseFeeVote.noVote (); } if (val.isFieldPresent (sfReserveBase)) { baseReserveVote.addVote (val.getFieldU32 (sfReserveBase)); } else { baseReserveVote.noVote (); } if (val.isFieldPresent (sfReserveIncrement)) { incReserveVote.addVote (val.getFieldU32 (sfReserveIncrement)); } else { incReserveVote.noVote (); } } } // choose our positions std::uint64_t const baseFee = baseFeeVote.getVotes (); std::uint32_t const baseReserve = baseReserveVote.getVotes (); std::uint32_t const incReserve = incReserveVote.getVotes (); std::uint32_t const feeUnits = target_.reference_fee_units; auto const seq = lastClosedLedger->info().seq + 1; // add transactions to our position if ((baseFee != lastClosedLedger->fees().base) || (baseReserve != lastClosedLedger->fees().accountReserve(0)) || (incReserve != lastClosedLedger->fees().increment)) { if (journal_.warning) journal_.warning << "We are voting for a fee change: " << baseFee << "/" << baseReserve << "/" << incReserve; STTx feeTx (ttFEE, [seq,baseFee,baseReserve,incReserve,feeUnits](auto& obj) { obj[sfAccount] = AccountID(); obj[sfLedgerSequence] = seq; obj[sfBaseFee] = baseFee; obj[sfReserveBase] = baseReserve; obj[sfReserveIncrement] = incReserve; obj[sfReferenceFeeUnits] = feeUnits; }); uint256 txID = feeTx.getTransactionID (); if (journal_.warning) journal_.warning << "Vote: " << txID; Serializer s; feeTx.add (s); auto tItem = std::make_shared<SHAMapItem> (txID, s.peekData ()); if (!initialPosition->addGiveItem (tItem, true, false)) { if (journal_.warning) journal_.warning << "Ledger already had fee change"; } } }
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; }
void run() { RippleAddress seed; seed.setSeedRandom (); RippleAddress generator = RippleAddress::createGeneratorPublic (seed); RippleAddress publicAcct = RippleAddress::createAccountPublic (generator, 1); RippleAddress privateAcct = RippleAddress::createAccountPrivate (generator, seed, 1); STTx j (ttACCOUNT_SET); j.setSourceAccount (publicAcct); j.setSigningPubKey (publicAcct); j.setFieldVL (sfMessageKey, publicAcct.getAccountPublic ()); j.sign (privateAcct); unexpected (!j.checkSign (), "Transaction fails signature test"); Serializer rawTxn; j.add (rawTxn); SerialIter sit (rawTxn); STTx copy (sit); if (copy != j) { log << j.getJson (0); log << copy.getJson (0); fail ("Transaction fails serialize/deserialize test"); } else { pass (); } STParsedJSONObject parsed ("test", j.getJson (0)); if (!parsed.object) fail ("Unable to build object from json"); if (STObject (j) != parsed.object) { log << "ORIG: " << j.getJson (0); log << "BUILT " << parsed.object->getJson (0); fail ("Built a different transaction"); } else { pass (); } }