refactor Transaction. compiling. still need to test

This commit is contained in:
jed
2012-11-14 13:05:59 -08:00
parent c1611f3b07
commit 5bbdd90a4c
23 changed files with 1515 additions and 1516 deletions

View File

@@ -6,6 +6,7 @@
#include <boost/foreach.hpp>
#include "TransactionEngine.h"
#include "Transactor.h"
#include "../json/writer.h"
@@ -88,401 +89,74 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa
}
#endif
TER terResult = tesSUCCESS;
uint256 txID = txn.getTransactionID();
if (!txID)
Transactor::pointer transactor=Transactor::makeTransactor(txn,params,shared_from_this());
if(transactor)
{
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())
uint256 txID = txn.getTransactionID();
if (!txID)
{
case ttCLAIM:
case ttREGULAR_KEY_SET:
saCost = 0;
break;
cLog(lsWARNING) << "applyTransaction: invalid transaction id";
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 ttTRUST_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;
return temINVALID;
}
}
STAmount saPaid = txn.getTransactionFee();
TER terResult= transactor->apply();
std::string strToken;
std::string strHuman;
if (tesSUCCESS == terResult)
{
if (saCost)
transResultInfo(terResult, strToken, strHuman);
cLog(lsINFO) << "applyTransaction: terResult=" << strToken << " : " << terResult << " : " << strHuman;
if (isTepPartial(terResult) && isSetBit(params, tapRETRY))
{
// Only check fee is sufficient when the ledger is open.
if (isSetBit(params, tapOPEN_LEDGER) && saPaid < saCost)
// 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))
{
cLog(lsINFO) << "applyTransaction: insufficient fee";
terResult = telINSUF_FEE_P;
if (!mLedger->addTransaction(txID, s))
assert(false);
}
}
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 ttREGULAR_KEY_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";
if (!mLedger->addTransaction(txID, s, m))
assert(false);
terResult = tefPAST_SEQ;
STAmount saPaid = txn.getTransactionFee();
// Charge whatever fee they specified.
mLedger->destroyCoins(saPaid.getNValue());
}
}
else
mTxnAccount.reset();
mNodes.clear();
if (!isSetBit(params, tapOPEN_LEDGER)
&& (isTemMalformed(terResult) || isTefFailure(terResult)))
{
mTxnAccount->setFieldU32(sfSequence, t_seq + 1);
// XXX Malformed or failed transaction in closed ledger must bow out.
}
}
else
return terResult;
}else
{
cLog(lsINFO) << "applyTransaction: Zero cost transaction";
if (t_seq)
{
cLog(lsINFO) << "applyTransaction: bad sequence for pre-paid transaction";
terResult = tefPAST_SEQ;
}
cLog(lsWARNING) << "applyTransaction: Invalid transaction: unknown transaction type";
return temUNKNOWN;
}
if (tesSUCCESS == terResult)
{
entryModify(mTxnAccount);
switch (txn.getTxnType())
{
case ttACCOUNT_SET:
terResult = doAccountSet(txn);
break;
case ttCLAIM:
terResult = doClaim(txn);
break;
case ttTRUST_SET:
terResult = doTrustSet(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 ttREGULAR_KEY_SET:
terResult = doRegularKeySet(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