Files
rippled/src/ripple/app/transactors/Transactor.cpp
Vinnie Falco df54b47cd0 Tidy up includes and add modules to the classic build:
An alternative to the unity build, the classic build compiles each
translation unit individually. This adds more modules to the classic build:

* Remove unity header app.h
* Add missing includes as needed
* Remove obsolete NodeStore backend code
* Add app/, core/, crypto/, json/, net/, overlay/, peerfinder/ to classic build
2015-01-05 13:35:57 -08:00

320 lines
9.6 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 <BeastConfig.h>
#include <ripple/core/Config.h>
#include <ripple/app/transactors/Transactor.h>
#include <ripple/protocol/Indexes.h>
namespace ripple {
TER transact_Payment (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetAccount (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetRegularKey (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_SetTrust (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CreateOffer (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CancelOffer (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_AddWallet (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_Change (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CreateTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER transact_CancelTicket (STTx const& txn, TransactionEngineParams params, TransactionEngine* engine);
TER
Transactor::transact (
STTx 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);
case ttTICKET_CREATE:
return transact_CreateTicket (txn, params, engine);
case ttTICKET_CANCEL:
return transact_CancelTicket (txn, params, engine);
default:
return temUNKNOWN;
}
}
Transactor::Transactor (
STTx 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 ()
{
// Returns the fee in fee units
return getConfig ().TRANSACTION_FEE_BASE;
}
TER Transactor::payFee ()
{
STAmount saPaid = mTxn.getTransactionFee ();
if (!isLegalNet (saPaid))
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
{
if (mEngine->getLedger ()->hasTransaction (mTxn.getTransactionID ()))
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()))
{
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,
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 ();
}
}