//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== SETUP_LOG (Transactor) UPTR_T Transactor::makeTransactor (const SerializedTransaction& txn, TransactionEngineParams params, TransactionEngine* engine) { switch (txn.getTxnType ()) { case ttPAYMENT: return UPTR_T (new PaymentTransactor (txn, params, engine)); case ttACCOUNT_SET: return UPTR_T (new AccountSetTransactor (txn, params, engine)); case ttREGULAR_KEY_SET: return UPTR_T (new RegularKeySetTransactor (txn, params, engine)); case ttTRUST_SET: return UPTR_T (new TrustSetTransactor (txn, params, engine)); case ttOFFER_CREATE: return UPTR_T (new OfferCreateTransactor (txn, params, engine)); case ttOFFER_CANCEL: return UPTR_T (new OfferCancelTransactor (txn, params, engine)); case ttWALLET_ADD: return UPTR_T (new WalletAddTransactor (txn, params, engine)); case ttFEATURE: case ttFEE: return UPTR_T (new ChangeTransactor (txn, params, engine)); default: return UPTR_T (); } } 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