mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 22:45:52 +00:00
Moved cpp code to src/cpp and js code to src/js.
This commit is contained in:
496
src/cpp/ripple/TransactionEngine.cpp
Normal file
496
src/cpp/ripple/TransactionEngine.cpp
Normal file
@@ -0,0 +1,496 @@
|
||||
//
|
||||
// XXX Make sure all fields are recognized in transactions.
|
||||
//
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "TransactionEngine.h"
|
||||
|
||||
#include "../json/writer.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "Log.h"
|
||||
#include "TransactionFormats.h"
|
||||
#include "utils.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(TransactionEngine);
|
||||
|
||||
void TransactionEngine::txnWrite()
|
||||
{
|
||||
// Write back the account states
|
||||
typedef std::pair<const uint256, LedgerEntrySetEntry> u256_LES_pair;
|
||||
BOOST_FOREACH(u256_LES_pair& it, mNodes)
|
||||
{
|
||||
const SLE::pointer& sleEntry = it.second.mEntry;
|
||||
|
||||
switch (it.second.mAction)
|
||||
{
|
||||
case taaNONE:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case taaCACHED:
|
||||
break;
|
||||
|
||||
case taaCREATE:
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: taaCREATE: " << sleEntry->getText();
|
||||
|
||||
if (mLedger->writeBack(lepCREATE, sleEntry) & lepERROR)
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case taaMODIFY:
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: taaMODIFY: " << sleEntry->getText();
|
||||
|
||||
if (mLedger->writeBack(lepNONE, sleEntry) & lepERROR)
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case taaDELETE:
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: taaDELETE: " << sleEntry->getText();
|
||||
|
||||
if (!mLedger->peekAccountStateMap()->delItem(it.first))
|
||||
assert(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, TransactionEngineParams params)
|
||||
{
|
||||
cLog(lsTRACE) << "applyTransaction>";
|
||||
assert(mLedger);
|
||||
mNodes.init(mLedger, txn.getTransactionID(), mLedger->getLedgerSeq());
|
||||
|
||||
#ifdef DEBUG
|
||||
if (1)
|
||||
{
|
||||
Serializer ser;
|
||||
txn.add(ser);
|
||||
SerializerIterator sit(ser);
|
||||
SerializedTransaction s2(sit);
|
||||
if (!s2.isEquivalent(txn))
|
||||
{
|
||||
cLog(lsFATAL) << "Transaction serdes mismatch";
|
||||
Json::StyledStreamWriter ssw;
|
||||
cLog(lsINFO) << txn.getJson(0);
|
||||
cLog(lsFATAL) << s2.getJson(0);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TER terResult = tesSUCCESS;
|
||||
uint256 txID = txn.getTransactionID();
|
||||
if (!txID)
|
||||
{
|
||||
cLog(lsWARNING) << "applyTransaction: invalid transaction id";
|
||||
|
||||
terResult = temINVALID;
|
||||
}
|
||||
|
||||
//
|
||||
// Verify transaction is signed properly.
|
||||
//
|
||||
|
||||
// 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.
|
||||
RippleAddress naSigningPubKey;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
naSigningPubKey = RippleAddress::createAccountPublic(txn.getSigningPubKey());
|
||||
|
||||
// Consistency: really signed.
|
||||
if ((tesSUCCESS == terResult) && !isSetBit(params, tapNO_CHECK_SIGN) && !txn.checkSign(naSigningPubKey))
|
||||
{
|
||||
cLog(lsWARNING) << "applyTransaction: Invalid transaction: bad signature";
|
||||
|
||||
terResult = temINVALID;
|
||||
}
|
||||
|
||||
STAmount saCost = theConfig.FEE_DEFAULT;
|
||||
|
||||
// Customize behavior based on transaction type.
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
switch (txn.getTxnType())
|
||||
{
|
||||
case ttCLAIM:
|
||||
case ttPASSWORD_SET:
|
||||
saCost = 0;
|
||||
break;
|
||||
|
||||
case ttPAYMENT:
|
||||
if (txn.getFlags() & tfCreateAccount)
|
||||
{
|
||||
saCost = theConfig.FEE_ACCOUNT_CREATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case ttNICKNAME_SET:
|
||||
{
|
||||
SLE::pointer sleNickname = entryCache(ltNICKNAME, txn.getFieldH256(sfNickname));
|
||||
|
||||
if (!sleNickname)
|
||||
saCost = theConfig.FEE_NICKNAME_CREATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case ttACCOUNT_SET:
|
||||
case ttCREDIT_SET:
|
||||
case ttOFFER_CREATE:
|
||||
case ttOFFER_CANCEL:
|
||||
case ttPASSWORD_FUND:
|
||||
case ttWALLET_ADD:
|
||||
nothing();
|
||||
break;
|
||||
|
||||
case ttINVALID:
|
||||
cLog(lsWARNING) << "applyTransaction: Invalid transaction: ttINVALID transaction type";
|
||||
terResult = temINVALID;
|
||||
break;
|
||||
|
||||
default:
|
||||
cLog(lsWARNING) << "applyTransaction: Invalid transaction: unknown transaction type";
|
||||
terResult = temUNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
STAmount saPaid = txn.getTransactionFee();
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
if (saCost)
|
||||
{
|
||||
// Only check fee is sufficient when the ledger is open.
|
||||
if (isSetBit(params, tapOPEN_LEDGER) && saPaid < saCost)
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: insufficient fee";
|
||||
|
||||
terResult = telINSUF_FEE_P;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (saPaid)
|
||||
{
|
||||
// Transaction is malformed.
|
||||
cLog(lsWARNING) << "applyTransaction: fee not allowed";
|
||||
|
||||
terResult = temINSUF_FEE_P;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get source account ID.
|
||||
mTxnAccountID = txn.getSourceAccount().getAccountID();
|
||||
if (tesSUCCESS == terResult && !mTxnAccountID)
|
||||
{
|
||||
cLog(lsWARNING) << "applyTransaction: bad source id";
|
||||
|
||||
terResult = temINVALID;
|
||||
}
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
return terResult;
|
||||
|
||||
boost::recursive_mutex::scoped_lock sl(mLedger->mLock);
|
||||
|
||||
mTxnAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(mTxnAccountID));
|
||||
|
||||
// Find source account
|
||||
// If are only forwarding, due to resource limitations, we might verifying only some transactions, this would be probablistic.
|
||||
|
||||
STAmount saSrcBalance;
|
||||
uint32 t_seq = txn.getSequence();
|
||||
bool bHaveAuthKey = false;
|
||||
|
||||
if (!mTxnAccount)
|
||||
{
|
||||
cLog(lsTRACE) << boost::str(boost::format("applyTransaction: Delay transaction: source account does not exist: %s") %
|
||||
txn.getSourceAccount().humanAccountID());
|
||||
|
||||
terResult = terNO_ACCOUNT;
|
||||
}
|
||||
else
|
||||
{
|
||||
saSrcBalance = mTxnAccount->getFieldAmount(sfBalance);
|
||||
bHaveAuthKey = mTxnAccount->isFieldPresent(sfAuthorizedKey);
|
||||
}
|
||||
|
||||
// Check if account claimed.
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
switch (txn.getTxnType())
|
||||
{
|
||||
case ttCLAIM:
|
||||
if (bHaveAuthKey)
|
||||
{
|
||||
cLog(lsWARNING) << "applyTransaction: Account already claimed.";
|
||||
|
||||
terResult = tefCLAIMED;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
nothing();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Consistency: Check signature
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
switch (txn.getTxnType())
|
||||
{
|
||||
case ttCLAIM:
|
||||
// Transaction's signing public key must be for the source account.
|
||||
// To prove the master private key made this transaction.
|
||||
if (naSigningPubKey.getAccountID() != mTxnAccountID)
|
||||
{
|
||||
// Signing Pub Key must be for Source Account ID.
|
||||
cLog(lsWARNING) << "sourceAccountID: " << naSigningPubKey.humanAccountID();
|
||||
cLog(lsWARNING) << "txn accountID: " << txn.getSourceAccount().humanAccountID();
|
||||
|
||||
terResult = tefBAD_CLAIM_ID;
|
||||
}
|
||||
break;
|
||||
|
||||
case ttPASSWORD_SET:
|
||||
// Transaction's signing public key must be for the source account.
|
||||
// To prove the master private key made this transaction.
|
||||
if (naSigningPubKey.getAccountID() != mTxnAccountID)
|
||||
{
|
||||
// Signing Pub Key must be for Source Account ID.
|
||||
cLog(lsWARNING) << "sourceAccountID: " << naSigningPubKey.humanAccountID();
|
||||
cLog(lsWARNING) << "txn accountID: " << txn.getSourceAccount().humanAccountID();
|
||||
|
||||
terResult = temBAD_SET_ID;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Verify the transaction's signing public key is the key authorized for signing.
|
||||
if (bHaveAuthKey && naSigningPubKey.getAccountID() == mTxnAccount->getFieldAccount(sfAuthorizedKey).getAccountID())
|
||||
{
|
||||
// Authorized to continue.
|
||||
nothing();
|
||||
}
|
||||
else if (naSigningPubKey.getAccountID() == mTxnAccountID)
|
||||
{
|
||||
// Authorized to continue.
|
||||
nothing();
|
||||
}
|
||||
else if (bHaveAuthKey)
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: Delay: Not authorized to use account.";
|
||||
|
||||
terResult = tefBAD_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: Invalid: Not authorized to use account.";
|
||||
|
||||
terResult = temBAD_AUTH_MASTER;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deduct the fee, so it's not available during the transaction.
|
||||
// Will only write the account back, if the transaction succeeds.
|
||||
if (tesSUCCESS != terResult || !saCost)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (saSrcBalance < saPaid)
|
||||
{
|
||||
cLog(lsINFO)
|
||||
<< boost::str(boost::format("applyTransaction: Delay: insufficient balance: balance=%s paid=%s")
|
||||
% saSrcBalance.getText()
|
||||
% saPaid.getText());
|
||||
|
||||
terResult = terINSUF_FEE_B;
|
||||
}
|
||||
else
|
||||
{
|
||||
mTxnAccount->setFieldAmount(sfBalance, saSrcBalance - saPaid);
|
||||
}
|
||||
|
||||
// Validate sequence
|
||||
if (tesSUCCESS != terResult)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (saCost)
|
||||
{
|
||||
uint32 a_seq = mTxnAccount->getFieldU32(sfSequence);
|
||||
|
||||
cLog(lsTRACE) << "Aseq=" << a_seq << ", Tseq=" << t_seq;
|
||||
|
||||
if (t_seq != a_seq)
|
||||
{
|
||||
if (a_seq < t_seq)
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: future sequence number";
|
||||
|
||||
terResult = terPRE_SEQ;
|
||||
}
|
||||
else if (mLedger->hasTransaction(txID))
|
||||
terResult = tefALREADY;
|
||||
else
|
||||
{
|
||||
cLog(lsWARNING) << "applyTransaction: past sequence number";
|
||||
|
||||
terResult = tefPAST_SEQ;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mTxnAccount->setFieldU32(sfSequence, t_seq + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: Zero cost transaction";
|
||||
|
||||
if (t_seq)
|
||||
{
|
||||
cLog(lsINFO) << "applyTransaction: bad sequence for pre-paid transaction";
|
||||
|
||||
terResult = tefPAST_SEQ;
|
||||
}
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
entryModify(mTxnAccount);
|
||||
|
||||
switch (txn.getTxnType())
|
||||
{
|
||||
case ttACCOUNT_SET:
|
||||
terResult = doAccountSet(txn);
|
||||
break;
|
||||
|
||||
case ttCLAIM:
|
||||
terResult = doClaim(txn);
|
||||
break;
|
||||
|
||||
case ttCREDIT_SET:
|
||||
terResult = doCreditSet(txn);
|
||||
break;
|
||||
|
||||
case ttINVALID:
|
||||
cLog(lsINFO) << "applyTransaction: invalid type";
|
||||
terResult = temINVALID;
|
||||
break;
|
||||
|
||||
//case ttINVOICE:
|
||||
// terResult = doInvoice(txn);
|
||||
// break;
|
||||
|
||||
case ttOFFER_CREATE:
|
||||
terResult = doOfferCreate(txn);
|
||||
break;
|
||||
|
||||
case ttOFFER_CANCEL:
|
||||
terResult = doOfferCancel(txn);
|
||||
break;
|
||||
|
||||
case ttNICKNAME_SET:
|
||||
terResult = doNicknameSet(txn);
|
||||
break;
|
||||
|
||||
case ttPASSWORD_FUND:
|
||||
terResult = doPasswordFund(txn);
|
||||
break;
|
||||
|
||||
case ttPASSWORD_SET:
|
||||
terResult = doPasswordSet(txn);
|
||||
break;
|
||||
|
||||
case ttPAYMENT:
|
||||
terResult = doPayment(txn, params);
|
||||
break;
|
||||
|
||||
case ttWALLET_ADD:
|
||||
terResult = doWalletAdd(txn);
|
||||
break;
|
||||
|
||||
case ttCONTRACT:
|
||||
terResult = doContractAdd(txn);
|
||||
break;
|
||||
case ttCONTRACT_REMOVE:
|
||||
terResult = doContractRemove(txn);
|
||||
break;
|
||||
|
||||
default:
|
||||
terResult = temUNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
transResultInfo(terResult, strToken, strHuman);
|
||||
|
||||
cLog(lsINFO) << "applyTransaction: terResult=" << strToken << " : " << terResult << " : " << strHuman;
|
||||
|
||||
if (isTepPartial(terResult) && isSetBit(params, tapRETRY))
|
||||
{
|
||||
// Partial result and allowed to retry, reclassify as a retry.
|
||||
terResult = terRETRY;
|
||||
}
|
||||
|
||||
if ((tesSUCCESS == terResult) || isTepPartial(terResult))
|
||||
{
|
||||
// Transaction succeeded fully or (retries are not allowed and the transaction succeeded partially).
|
||||
Serializer m;
|
||||
mNodes.calcRawMeta(m, terResult);
|
||||
|
||||
txnWrite();
|
||||
|
||||
Serializer s;
|
||||
txn.add(s);
|
||||
|
||||
if (isSetBit(params, tapOPEN_LEDGER))
|
||||
{
|
||||
if (!mLedger->addTransaction(txID, s))
|
||||
assert(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!mLedger->addTransaction(txID, s, m))
|
||||
assert(false);
|
||||
|
||||
// Charge whatever fee they specified.
|
||||
mLedger->destroyCoins(saPaid.getNValue());
|
||||
}
|
||||
}
|
||||
|
||||
mTxnAccount.reset();
|
||||
mNodes.clear();
|
||||
|
||||
if (!isSetBit(params, tapOPEN_LEDGER)
|
||||
&& (isTemMalformed(terResult) || isTefFailure(terResult)))
|
||||
{
|
||||
// XXX Malformed or failed transaction in closed ledger must bow out.
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// vim:ts=4
|
||||
Reference in New Issue
Block a user