Files
xahaud/src/ripple/module/app/transactors/Transactor.cpp
Nik Bougalis 889c0a0d0f Transactor refactor:
* Allocate transactors on the stack instead of the heap.
* Remove header files and reduce transactor public interface.
2014-09-04 16:11:44 -07:00

310 lines
9.3 KiB
C++

//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/module/app/transactors/Transactor.h>
namespace ripple {
TER transact_Payment (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetAccount (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetRegularKey (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetTrust (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CreateOffer (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CancelOffer (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_AddWallet (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_Change (SerializedTransaction const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER
Transactor::transact (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine)
{
switch (txn.getTxnType ())
{
case ttPAYMENT:
return transact_Payment (txn, params, engine);
case ttACCOUNT_SET:
return transact_SetAccount (txn, params, engine);
case ttREGULAR_KEY_SET:
return transact_SetRegularKey (txn, params, engine);
case ttTRUST_SET:
return transact_SetTrust (txn, params, engine);
case ttOFFER_CREATE:
return transact_CreateOffer (txn, params, engine);
case ttOFFER_CANCEL:
return transact_CancelOffer (txn, params, engine);
case ttWALLET_ADD:
return transact_AddWallet (txn, params, engine);
case ttAMENDMENT:
case ttFEE:
return transact_Change (txn, params, engine);
default:
return temUNKNOWN;
}
}
Transactor::Transactor (
SerializedTransaction const& txn,
TransactionEngineParams params,
TransactionEngine* engine,
beast::Journal journal)
: mTxn (txn)
, mEngine (engine)
, mParams (params)
, mHasAuthKey (false)
, mSigMaster (false)
, m_journal (journal)
{
}
void Transactor::calculateFee ()
{
mFeeDue = STAmount (mEngine->getLedger ()->scaleFeeLoad (
calculateBaseFee (), mParams & tapADMIN));
}
std::uint64_t Transactor::calculateBaseFee ()
{
return getConfig ().FEE_DEFAULT;
}
TER Transactor::payFee ()
{
STAmount saPaid = mTxn.getTransactionFee ();
if (!saPaid.isLegalNet ())
return temBAD_AMOUNT;
// Only check fee is sufficient when the ledger is open.
if ((mParams & tapOPEN_LEDGER) && saPaid < mFeeDue)
{
m_journal.trace << "Insufficient fee paid: " <<
saPaid.getText () << "/" << mFeeDue.getText ();
return telINSUF_FEE_P;
}
if (saPaid < zero || !saPaid.isNative ())
return temBAD_FEE;
if (!saPaid)
return tesSUCCESS;
if (mSourceBalance < saPaid)
{
m_journal.trace << "Insufficient balance:" <<
" balance=" << mSourceBalance.getText () <<
" paid=" << saPaid.getText ();
if ((mSourceBalance > zero) && (!(mParams & tapOPEN_LEDGER)))
{
// Closed ledger, non-zero balance, less than fee
mSourceBalance.clear ();
mTxnAccount->setFieldAmount (sfBalance, mSourceBalance);
return tecINSUFF_FEE;
}
return terINSUF_FEE_B;
}
// Deduct the fee, so it's not available during the transaction.
// Will only write the account back, if the transaction succeeds.
mSourceBalance -= saPaid;
mTxnAccount->setFieldAmount (sfBalance, mSourceBalance);
return tesSUCCESS;
}
TER Transactor::checkSig ()
{
// Consistency: Check signature
// Verify the transaction's signing public key is the key authorized for signing.
if (mSigningPubKey.getAccountID () == mTxnAccountID)
{
// Authorized to continue.
mSigMaster = true;
if (mTxnAccount->isFlag(lsfDisableMaster))
return tefMASTER_DISABLED;
}
else if (mHasAuthKey && mSigningPubKey.getAccountID () == mTxnAccount->getFieldAccount160 (sfRegularKey))
{
// Authorized to continue.
}
else if (mHasAuthKey)
{
m_journal.trace << "applyTransaction: Delay: Not authorized to use account.";
return tefBAD_AUTH;
}
else
{
m_journal.trace << "applyTransaction: Invalid: Not authorized to use account.";
return temBAD_AUTH_MASTER;
}
return tesSUCCESS;
}
TER Transactor::checkSeq ()
{
std::uint32_t t_seq = mTxn.getSequence ();
std::uint32_t a_seq = mTxnAccount->getFieldU32 (sfSequence);
m_journal.trace << "Aseq=" << a_seq << ", Tseq=" << t_seq;
if (t_seq != a_seq)
{
if (a_seq < t_seq)
{
m_journal.trace << "apply: transaction has future sequence number";
return terPRE_SEQ;
}
else
{
uint256 txID = mTxn.getTransactionID ();
if (mEngine->getLedger ()->hasTransaction (txID))
return tefALREADY;
}
m_journal.warning << "apply: transaction has past sequence number";
return tefPAST_SEQ;
}
// Deprecated: Do not use
if (mTxn.isFieldPresent (sfPreviousTxnID) &&
(mTxnAccount->getFieldH256 (sfPreviousTxnID) != mTxn.getFieldH256 (sfPreviousTxnID)))
return tefWRONG_PRIOR;
if (mTxn.isFieldPresent (sfAccountTxnID) &&
(mTxnAccount->getFieldH256 (sfAccountTxnID) != mTxn.getFieldH256 (sfAccountTxnID)))
return tefWRONG_PRIOR;
if (mTxn.isFieldPresent (sfLastLedgerSequence) &&
(mEngine->getLedger()->getLedgerSeq() > mTxn.getFieldU32 (sfLastLedgerSequence)))
return tefMAX_LEDGER;
mTxnAccount->setFieldU32 (sfSequence, t_seq + 1);
if (mTxnAccount->isFieldPresent (sfAccountTxnID))
mTxnAccount->setFieldH256 (sfAccountTxnID, mTxn.getTransactionID ());
return tesSUCCESS;
}
// check stuff before you bother to lock the ledger
TER Transactor::preCheck ()
{
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
if (!mTxnAccountID)
{
m_journal.warning << "apply: bad transaction source id";
return temBAD_SRC_ACCOUNT;
}
// Extract signing key
// Transactions contain a signing key. This allows us to trivially verify a
// transaction has at least been properly signed without going to disk.
// Each transaction also notes a source account id. This is used to verify
// that the signing key is associated with the account.
// XXX This could be a lot cleaner to prevent unnecessary copying.
mSigningPubKey = RippleAddress::createAccountPublic (mTxn.getSigningPubKey ());
// Consistency: really signed.
if (!mTxn.isKnownGood ())
{
if (mTxn.isKnownBad () ||
(!(mParams & tapNO_CHECK_SIGN) && !mTxn.checkSign (mSigningPubKey)))
{
mTxn.setBad ();
m_journal.warning << "apply: Invalid transaction (bad signature)";
return temINVALID;
}
mTxn.setGood ();
}
return tesSUCCESS;
}
TER Transactor::apply ()
{
TER terResult (preCheck ());
if (terResult != tesSUCCESS)
return (terResult);
mTxnAccount = mEngine->entryCache (ltACCOUNT_ROOT,
Ledger::getAccountRootIndex (mTxnAccountID));
calculateFee ();
// Find source account
// If are only forwarding, due to resource limitations, we might verifying
// only some transactions, this would be probabilistic.
if (!mTxnAccount)
{
if (mustHaveValidAccount ())
{
m_journal.trace <<
"apply: delay transaction: source account does not exist " <<
mTxn.getSourceAccount ().humanAccountID ();
return terNO_ACCOUNT;
}
}
else
{
mPriorBalance = mTxnAccount->getFieldAmount (sfBalance);
mSourceBalance = mPriorBalance;
mHasAuthKey = mTxnAccount->isFieldPresent (sfRegularKey);
}
terResult = checkSeq ();
if (terResult != tesSUCCESS) return (terResult);
terResult = payFee ();
if (terResult != tesSUCCESS) return (terResult);
terResult = checkSig ();
if (terResult != tesSUCCESS) return (terResult);
if (mTxnAccount)
mEngine->entryModify (mTxnAccount);
return doApply ();
}
}