Files
xahaud/src/cpp/ripple/Transactor.cpp
JoelKatz 36a84484e0 Implement clearing the regular key, setting and clearing the no master
flag and enforcing the no master flag.
2013-06-17 18:53:42 -07:00

257 lines
7.4 KiB
C++

//------------------------------------------------------------------------------
/*
Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================
SETUP_LOG (Transactor)
UPTR_T<Transactor> Transactor::makeTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine)
{
switch (txn.getTxnType ())
{
case ttPAYMENT:
return UPTR_T<Transactor> (new PaymentTransactor (txn, params, engine));
case ttACCOUNT_SET:
return UPTR_T<Transactor> (new AccountSetTransactor (txn, params, engine));
case ttREGULAR_KEY_SET:
return UPTR_T<Transactor> (new RegularKeySetTransactor (txn, params, engine));
case ttTRUST_SET:
return UPTR_T<Transactor> (new TrustSetTransactor (txn, params, engine));
case ttOFFER_CREATE:
return UPTR_T<Transactor> (new OfferCreateTransactor (txn, params, engine));
case ttOFFER_CANCEL:
return UPTR_T<Transactor> (new OfferCancelTransactor (txn, params, engine));
case ttWALLET_ADD:
return UPTR_T<Transactor> (new WalletAddTransactor (txn, params, engine));
case ttFEATURE:
case ttFEE:
return UPTR_T<Transactor> (new ChangeTransactor (txn, params, engine));
default:
return UPTR_T<Transactor> ();
}
}
Transactor::Transactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) : mTxn (txn), mEngine (engine), mParams (params)
{
mHasAuthKey = false;
mSigMaster = false;
}
void Transactor::calculateFee ()
{
mFeeDue = STAmount (mEngine->getLedger ()->scaleFeeLoad (calculateBaseFee (), isSetBit (mParams, tapADMIN)));
}
uint64 Transactor::calculateBaseFee ()
{
return theConfig.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 (isSetBit (mParams, tapOPEN_LEDGER) && saPaid < mFeeDue)
{
WriteLog (lsINFO, Transactor) << boost::str (boost::format ("applyTransaction: Insufficient fee paid: %s/%s")
% saPaid.getText ()
% mFeeDue.getText ());
return telINSUF_FEE_P;
}
if (saPaid.isNegative () || !saPaid.isNative ())
return temBAD_FEE;
if (!saPaid) return tesSUCCESS;
// Deduct the fee, so it's not available during the transaction.
// Will only write the account back, if the transaction succeeds.
if (mSourceBalance < saPaid)
{
WriteLog (lsINFO, Transactor)
<< boost::str (boost::format ("applyTransaction: Delay: insufficient balance: balance=%s paid=%s")
% mSourceBalance.getText ()
% saPaid.getText ());
return terINSUF_FEE_B;
}
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.
nothing ();
}
else if (mHasAuthKey)
{
WriteLog (lsINFO, Transactor) << "applyTransaction: Delay: Not authorized to use account.";
return tefBAD_AUTH;
}
else
{
WriteLog (lsINFO, Transactor) << "applyTransaction: Invalid: Not authorized to use account.";
return temBAD_AUTH_MASTER;
}
return tesSUCCESS;
}
TER Transactor::checkSeq ()
{
uint32 t_seq = mTxn.getSequence ();
uint32 a_seq = mTxnAccount->getFieldU32 (sfSequence);
WriteLog (lsTRACE, Transactor) << "Aseq=" << a_seq << ", Tseq=" << t_seq;
if (t_seq != a_seq)
{
if (a_seq < t_seq)
{
WriteLog (lsINFO, Transactor) << "applyTransaction: future sequence number";
return terPRE_SEQ;
}
else
{
uint256 txID = mTxn.getTransactionID ();
if (mEngine->getLedger ()->hasTransaction (txID))
return tefALREADY;
}
WriteLog (lsWARNING, Transactor) << "applyTransaction: past sequence number";
return tefPAST_SEQ;
}
if (mTxn.isFieldPresent (sfPreviousTxnID) &&
(mTxnAccount->getFieldH256 (sfPreviousTxnID) != mTxn.getFieldH256 (sfPreviousTxnID)))
return tefWRONG_PRIOR;
mTxnAccount->setFieldU32 (sfSequence, t_seq + 1);
return tesSUCCESS;
}
// check stuff before you bother to lock the ledger
TER Transactor::preCheck ()
{
mTxnAccountID = mTxn.getSourceAccount ().getAccountID ();
if (!mTxnAccountID)
{
WriteLog (lsWARNING, Transactor) << "applyTransaction: bad 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 () || (!isSetBit (mParams, tapNO_CHECK_SIGN) && !mTxn.checkSign (mSigningPubKey)))
{
mTxn.setBad ();
WriteLog (lsWARNING, Transactor) << "applyTransaction: Invalid transaction: bad signature";
return temINVALID;
}
mTxn.setGood ();
}
return tesSUCCESS;
}
TER Transactor::apply ()
{
TER terResult = tesSUCCESS;
terResult = preCheck ();
if (terResult != tesSUCCESS) return (terResult);
calculateFee ();
boost::recursive_mutex::scoped_lock sl (mEngine->getLedger ()->mLock);
mTxnAccount = mEngine->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 probabilistic.
if (!mTxnAccount)
{
if (mustHaveValidAccount ())
{
WriteLog (lsTRACE, Transactor) << boost::str (boost::format ("applyTransaction: Delay transaction: source account does not exist: %s") %
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 ();
}
// vim:ts=4