From cc467bf1d32d46b313b81d7154181c7d347980b3 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 10 Sep 2012 12:51:56 -0700 Subject: [PATCH] Move TransactionEngine do* to TransactionAction.cpp --- src/TransactionAction.cpp | 1171 +++++++++++++++++++++++++++++++++++++ src/TransactionEngine.cpp | 1162 +----------------------------------- 2 files changed, 1174 insertions(+), 1159 deletions(-) create mode 100644 src/TransactionAction.cpp diff --git a/src/TransactionAction.cpp b/src/TransactionAction.cpp new file mode 100644 index 0000000000..f60ae0af57 --- /dev/null +++ b/src/TransactionAction.cpp @@ -0,0 +1,1171 @@ +// +// XXX Make sure all fields are recognized in transactions. +// + +#include +#include +#include +#include + +#include "TransactionEngine.h" + +#include "../json/writer.h" + +#include "Config.h" +#include "Contract.h" +#include "Interpreter.h" +#include "Log.h" +#include "RippleCalc.h" +#include "TransactionFormats.h" +#include "utils.h" + +#define RIPPLE_PATHS_MAX 3 + +// Set the authorized public key for an account. May also set the generator map. +TER TransactionEngine::setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator) +{ + // + // Verify that submitter knows the private key for the generator. + // Otherwise, people could deny access to generators. + // + + std::vector vucCipher = txn.getITFieldVL(sfGenerator); + std::vector vucPubKey = txn.getITFieldVL(sfPubKey); + std::vector vucSignature = txn.getITFieldVL(sfSignature); + NewcoinAddress naAccountPublic = NewcoinAddress::createAccountPublic(vucPubKey); + + if (!naAccountPublic.accountPublicVerify(Serializer::getSHA512Half(vucCipher), vucSignature)) + { + Log(lsWARNING) << "createGenerator: bad signature unauthorized generator claim"; + + return tefBAD_GEN_AUTH; + } + + // Create generator. + uint160 hGeneratorID = naAccountPublic.getAccountID(); + + SLE::pointer sleGen = entryCache(ltGENERATOR_MAP, Ledger::getGeneratorIndex(hGeneratorID)); + if (!sleGen) + { + // Create the generator. + Log(lsTRACE) << "createGenerator: creating generator"; + + sleGen = entryCreate(ltGENERATOR_MAP, Ledger::getGeneratorIndex(hGeneratorID)); + + sleGen->setIFieldVL(sfGenerator, vucCipher); + } + else if (bMustSetGenerator) + { + // Doing a claim. Must set generator. + // Generator is already in use. Regular passphrases limited to one wallet. + Log(lsWARNING) << "createGenerator: generator already in use"; + + return tefGEN_IN_USE; + } + + // Set the public key needed to use the account. + uint160 uAuthKeyID = bMustSetGenerator + ? hGeneratorID // Claim + : txn.getITFieldAccount(sfAuthorizedKey); // PasswordSet + + mTxnAccount->setIFieldAccount(sfAuthorizedKey, uAuthKeyID); + + return tesSUCCESS; +} + +TER TransactionEngine::doAccountSet(const SerializedTransaction& txn) +{ + Log(lsINFO) << "doAccountSet>"; + + // + // EmailHash + // + + if (txn.getITFieldPresent(sfEmailHash)) + { + uint128 uHash = txn.getITFieldH128(sfEmailHash); + + if (!uHash) + { + Log(lsINFO) << "doAccountSet: unset email hash"; + + mTxnAccount->makeIFieldAbsent(sfEmailHash); + } + else + { + Log(lsINFO) << "doAccountSet: set email hash"; + + mTxnAccount->setIFieldH128(sfEmailHash, uHash); + } + } + + // + // WalletLocator + // + + if (txn.getITFieldPresent(sfWalletLocator)) + { + uint256 uHash = txn.getITFieldH256(sfWalletLocator); + + if (!uHash) + { + Log(lsINFO) << "doAccountSet: unset wallet locator"; + + mTxnAccount->makeIFieldAbsent(sfEmailHash); + } + else + { + Log(lsINFO) << "doAccountSet: set wallet locator"; + + mTxnAccount->setIFieldH256(sfWalletLocator, uHash); + } + } + + // + // MessageKey + // + + if (!txn.getITFieldPresent(sfMessageKey)) + { + nothing(); + } + else + { + Log(lsINFO) << "doAccountSet: set message key"; + + mTxnAccount->setIFieldVL(sfMessageKey, txn.getITFieldVL(sfMessageKey)); + } + + // + // Domain + // + + if (txn.getITFieldPresent(sfDomain)) + { + std::vector vucDomain = txn.getITFieldVL(sfDomain); + + if (vucDomain.empty()) + { + Log(lsINFO) << "doAccountSet: unset domain"; + + mTxnAccount->makeIFieldAbsent(sfDomain); + } + else + { + Log(lsINFO) << "doAccountSet: set domain"; + + mTxnAccount->setIFieldVL(sfDomain, vucDomain); + } + } + + // + // TransferRate + // + + if (txn.getITFieldPresent(sfTransferRate)) + { + uint32 uRate = txn.getITFieldU32(sfTransferRate); + + if (!uRate) + { + Log(lsINFO) << "doAccountSet: unset transfer rate"; + + mTxnAccount->makeIFieldAbsent(sfTransferRate); + } + else + { + Log(lsINFO) << "doAccountSet: set transfer rate"; + + mTxnAccount->setIFieldU32(sfTransferRate, uRate); + } + } + + // + // PublishHash && PublishSize + // + + bool bPublishHash = txn.getITFieldPresent(sfPublishHash); + bool bPublishSize = txn.getITFieldPresent(sfPublishSize); + + if (bPublishHash ^ bPublishSize) + { + Log(lsINFO) << "doAccountSet: bad publish"; + + return temBAD_PUBLISH; + } + else if (bPublishHash && bPublishSize) + { + uint256 uHash = txn.getITFieldH256(sfPublishHash); + uint32 uSize = txn.getITFieldU32(sfPublishSize); + + if (!uHash) + { + Log(lsINFO) << "doAccountSet: unset publish"; + + mTxnAccount->makeIFieldAbsent(sfPublishHash); + mTxnAccount->makeIFieldAbsent(sfPublishSize); + } + else + { + Log(lsINFO) << "doAccountSet: set publish"; + + mTxnAccount->setIFieldH256(sfPublishHash, uHash); + mTxnAccount->setIFieldU32(sfPublishSize, uSize); + } + } + + Log(lsINFO) << "doAccountSet<"; + + return tesSUCCESS; +} + +TER TransactionEngine::doClaim(const SerializedTransaction& txn) +{ + Log(lsINFO) << "doClaim>"; + + TER terResult = setAuthorized(txn, true); + + Log(lsINFO) << "doClaim<"; + + return terResult; +} + +TER TransactionEngine::doCreditSet(const SerializedTransaction& txn) +{ + TER terResult = tesSUCCESS; + Log(lsINFO) << "doCreditSet>"; + + // Check if destination makes sense. + uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); + + if (!uDstAccountID) + { + Log(lsINFO) << "doCreditSet: Invalid transaction: Destination account not specifed."; + + return temDST_NEEDED; + } + else if (mTxnAccountID == uDstAccountID) + { + Log(lsINFO) << "doCreditSet: Invalid transaction: Can not extend credit to self."; + + return temDST_IS_SRC; + } + + SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + if (!sleDst) + { + Log(lsINFO) << "doCreditSet: Delay transaction: Destination account does not exist."; + + return terNO_DST; + } + + const bool bFlipped = mTxnAccountID > uDstAccountID; // true, iff current is not lowest. + const bool bLimitAmount = txn.getITFieldPresent(sfLimitAmount); + const STAmount saLimitAmount = bLimitAmount ? txn.getITFieldAmount(sfLimitAmount) : STAmount(); + const bool bQualityIn = txn.getITFieldPresent(sfQualityIn); + const uint32 uQualityIn = bQualityIn ? txn.getITFieldU32(sfQualityIn) : 0; + const bool bQualityOut = txn.getITFieldPresent(sfQualityOut); + const uint32 uQualityOut = bQualityIn ? txn.getITFieldU32(sfQualityOut) : 0; + const uint160 uCurrencyID = saLimitAmount.getCurrency(); + bool bDelIndex = false; + + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); + if (sleRippleState) + { + // A line exists in one or more directions. +#if 0 + if (!saLimitAmount) + { + // Zeroing line. + uint160 uLowID = sleRippleState->getIValueFieldAccount(sfLowID).getAccountID(); + uint160 uHighID = sleRippleState->getIValueFieldAccount(sfHighID).getAccountID(); + bool bLow = uLowID == uSrcAccountID; + bool bHigh = uLowID == uDstAccountID; + bool bBalanceZero = !sleRippleState->getIValueFieldAmount(sfBalance); + STAmount saDstLimit = sleRippleState->getIValueFieldAmount(bSendLow ? sfLowLimit : sfHighLimit); + bool bDstLimitZero = !saDstLimit; + + assert(bLow || bHigh); + + if (bBalanceZero && bDstLimitZero) + { + // Zero balance and eliminating last limit. + + bDelIndex = true; + terResult = dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex(), false); + } + } +#endif + + if (!bDelIndex) + { + if (bLimitAmount) + sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit: sfLowLimit , saLimitAmount); + + if (!bQualityIn) + { + nothing(); + } + else if (uQualityIn) + { + sleRippleState->setIFieldU32(bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn); + } + else + { + sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityIn : sfHighQualityIn); + } + + if (!bQualityOut) + { + nothing(); + } + else if (uQualityOut) + { + sleRippleState->setIFieldU32(bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut); + } + else + { + sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityOut : sfHighQualityOut); + } + + entryModify(sleRippleState); + } + + Log(lsINFO) << "doCreditSet: Modifying ripple line: bDelIndex=" << bDelIndex; + } + // Line does not exist. + else if (!saLimitAmount) + { + Log(lsINFO) << "doCreditSet: Redundant: Setting non-existant ripple line to 0."; + + return terNO_LINE_NO_ZERO; + } + else + { + // Create a new ripple line. + sleRippleState = entryCreate(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); + + Log(lsINFO) << "doCreditSet: Creating ripple line: " << sleRippleState->getIndex().ToString(); + + sleRippleState->setIFieldAmount(sfBalance, STAmount(uCurrencyID, ACCOUNT_ONE)); // Zero balance in currency. + sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit : sfLowLimit, saLimitAmount); + sleRippleState->setIFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, ACCOUNT_ONE)); + sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, mTxnAccountID); + sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uDstAccountID); + if (uQualityIn) + sleRippleState->setIFieldU32(bFlipped ? sfHighQualityIn : sfLowQualityIn, uQualityIn); + if (uQualityOut) + sleRippleState->setIFieldU32(bFlipped ? sfHighQualityOut : sfLowQualityOut, uQualityOut); + + uint64 uSrcRef; // Ignored, dirs never delete. + + terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex()); + + if (tesSUCCESS == terResult) + terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(uDstAccountID), sleRippleState->getIndex()); + } + + Log(lsINFO) << "doCreditSet<"; + + return terResult; +} + +TER TransactionEngine::doNicknameSet(const SerializedTransaction& txn) +{ + std::cerr << "doNicknameSet>" << std::endl; + + const uint256 uNickname = txn.getITFieldH256(sfNickname); + const bool bMinOffer = txn.getITFieldPresent(sfMinimumOffer); + const STAmount saMinOffer = bMinOffer ? txn.getITFieldAmount(sfAmount) : STAmount(); + + SLE::pointer sleNickname = entryCache(ltNICKNAME, uNickname); + + if (sleNickname) + { + // Edit old entry. + sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); + + if (bMinOffer && saMinOffer) + { + sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); + } + else + { + sleNickname->makeIFieldAbsent(sfMinimumOffer); + } + + entryModify(sleNickname); + } + else + { + // Make a new entry. + // XXX Need to include authorization limiting for first year. + + sleNickname = entryCreate(ltNICKNAME, Ledger::getNicknameIndex(uNickname)); + + std::cerr << "doNicknameSet: Creating nickname node: " << sleNickname->getIndex().ToString() << std::endl; + + sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); + + if (bMinOffer && saMinOffer) + sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); + } + + std::cerr << "doNicknameSet<" << std::endl; + + return tesSUCCESS; +} + +TER TransactionEngine::doPasswordFund(const SerializedTransaction& txn) +{ + std::cerr << "doPasswordFund>" << std::endl; + + const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); + SLE::pointer sleDst = mTxnAccountID == uDstAccountID + ? mTxnAccount + : entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + if (!sleDst) + { + // Destination account does not exist. + std::cerr << "doPasswordFund: Delay transaction: Destination account does not exist." << std::endl; + + return terSET_MISSING_DST; + } + + if (sleDst->getFlags() & lsfPasswordSpent) + { + sleDst->clearFlag(lsfPasswordSpent); + + std::cerr << "doPasswordFund: Clearing spent." << sleDst->getFlags() << std::endl; + + if (mTxnAccountID != uDstAccountID) { + std::cerr << "doPasswordFund: Destination modified." << std::endl; + + entryModify(sleDst); + } + } + + std::cerr << "doPasswordFund<" << std::endl; + + return tesSUCCESS; +} + +TER TransactionEngine::doPasswordSet(const SerializedTransaction& txn) +{ + std::cerr << "doPasswordSet>" << std::endl; + + if (mTxnAccount->getFlags() & lsfPasswordSpent) + { + std::cerr << "doPasswordSet: Delay transaction: Funds already spent." << std::endl; + + return terFUNDS_SPENT; + } + + mTxnAccount->setFlag(lsfPasswordSpent); + + TER terResult = setAuthorized(txn, false); + + std::cerr << "doPasswordSet<" << std::endl; + + return terResult; +} + + +// XXX Need to audit for things like setting accountID not having memory. +TER TransactionEngine::doPayment(const SerializedTransaction& txn, const TransactionEngineParams params) +{ + // Ripple if source or destination is non-native or if there are paths. + const uint32 uTxFlags = txn.getFlags(); + const bool bCreate = isSetBit(uTxFlags, tfCreateAccount); + const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); + const bool bLimitQuality = isSetBit(uTxFlags, tfLimitQuality); + const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); + const bool bPaths = txn.getITFieldPresent(sfPaths); + const bool bMax = txn.getITFieldPresent(sfSendMax); + const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); + const STAmount saDstAmount = txn.getITFieldAmount(sfAmount); + const STAmount saMaxAmount = bMax ? txn.getITFieldAmount(sfSendMax) : saDstAmount; + const uint160 uSrcCurrency = saMaxAmount.getCurrency(); + const uint160 uDstCurrency = saDstAmount.getCurrency(); + + Log(lsINFO) << boost::str(boost::format("doPayment> saMaxAmount=%s saDstAmount=%s") + % saMaxAmount.getFullText() + % saDstAmount.getFullText()); + + if (!uDstAccountID) + { + Log(lsINFO) << "doPayment: Invalid transaction: Payment destination account not specifed."; + + return temDST_NEEDED; + } + else if (!saDstAmount.isPositive()) + { + Log(lsINFO) << "doPayment: Invalid transaction: bad amount: " << saDstAmount.getHumanCurrency() << " " << saDstAmount.getText(); + + return temBAD_AMOUNT; + } + else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) + { + Log(lsINFO) << boost::str(boost::format("doPayment: Invalid transaction: Redunant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") + % mTxnAccountID.ToString() + % uDstAccountID.ToString() + % uSrcCurrency.ToString() + % uDstCurrency.ToString()); + + return temREDUNDANT; + } + else if (bMax + && ((saMaxAmount == saDstAmount && saMaxAmount.getCurrency() == saDstAmount.getCurrency()) + || (saDstAmount.isNative() && saMaxAmount.isNative()))) + { + Log(lsINFO) << "doPayment: Invalid transaction: bad SendMax."; + + return temINVALID; + } + + SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + if (!sleDst) + { + // Destination account does not exist. + if (bCreate && !saDstAmount.isNative()) + { + // This restriction could be relaxed. + Log(lsINFO) << "doPayment: Invalid transaction: Create account may only fund XNS."; + + return temCREATEXNS; + } + else if (!bCreate) + { + Log(lsINFO) << "doPayment: Delay transaction: Destination account does not exist."; + + return terNO_DST; + } + + // Create the account. + sleDst = entryCreate(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + + sleDst->setIFieldAccount(sfAccount, uDstAccountID); + sleDst->setIFieldU32(sfSequence, 1); + } + else + { + entryModify(sleDst); + } + + TER terResult; + // XXX Should bMax be sufficient to imply ripple? + const bool bRipple = bPaths || bMax || !saDstAmount.isNative(); + + if (bRipple) + { + // Ripple payment + + STPathSet spsPaths = txn.getITFieldPathSet(sfPaths); + STAmount saMaxAmountAct; + STAmount saDstAmountAct; + + terResult = isSetBit(params, tapOPEN_LEDGER) && spsPaths.getPathCount() > RIPPLE_PATHS_MAX + ? telBAD_PATH_COUNT + : RippleCalc::rippleCalc( + mNodes, + saMaxAmountAct, + saDstAmountAct, + saMaxAmount, + saDstAmount, + uDstAccountID, + mTxnAccountID, + spsPaths, + bPartialPayment, + bLimitQuality, + bNoRippleDirect); + } + else + { + // Direct XNS payment. + + STAmount saSrcXNSBalance = mTxnAccount->getIValueFieldAmount(sfBalance); + + if (saSrcXNSBalance < saDstAmount) + { + // Transaction might succeed, if applied in a different order. + Log(lsINFO) << "doPayment: Delay transaction: Insufficent funds."; + + terResult = terUNFUNDED; + } + else + { + mTxnAccount->setIFieldAmount(sfBalance, saSrcXNSBalance - saDstAmount); + sleDst->setIFieldAmount(sfBalance, sleDst->getIValueFieldAmount(sfBalance) + saDstAmount); + + terResult = tesSUCCESS; + } + } + + std::string strToken; + std::string strHuman; + + if (transResultInfo(terResult, strToken, strHuman)) + { + Log(lsINFO) << boost::str(boost::format("doPayment: %s: %s") % strToken % strHuman); + } + else + { + assert(false); + } + + return terResult; +} + +TER TransactionEngine::doWalletAdd(const SerializedTransaction& txn) +{ + std::cerr << "WalletAdd>" << std::endl; + + const std::vector vucPubKey = txn.getITFieldVL(sfPubKey); + const std::vector vucSignature = txn.getITFieldVL(sfSignature); + const uint160 uAuthKeyID = txn.getITFieldAccount(sfAuthorizedKey); + const NewcoinAddress naMasterPubKey = NewcoinAddress::createAccountPublic(vucPubKey); + const uint160 uDstAccountID = naMasterPubKey.getAccountID(); + + if (!naMasterPubKey.accountPublicVerify(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature)) + { + std::cerr << "WalletAdd: unauthorized: bad signature " << std::endl; + + return tefBAD_ADD_AUTH; + } + + SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + + if (sleDst) + { + std::cerr << "WalletAdd: account already created" << std::endl; + + return tefCREATED; + } + + STAmount saAmount = txn.getITFieldAmount(sfAmount); + STAmount saSrcBalance = mTxnAccount->getIValueFieldAmount(sfBalance); + + if (saSrcBalance < saAmount) + { + std::cerr + << boost::str(boost::format("WalletAdd: Delay transaction: insufficent balance: balance=%s amount=%s") + % saSrcBalance.getText() + % saAmount.getText()) + << std::endl; + + return terUNFUNDED; + } + + // Deduct initial balance from source account. + mTxnAccount->setIFieldAmount(sfBalance, saSrcBalance-saAmount); + + // Create the account. + sleDst = entryCreate(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); + + sleDst->setIFieldAccount(sfAccount, uDstAccountID); + sleDst->setIFieldU32(sfSequence, 1); + sleDst->setIFieldAmount(sfBalance, saAmount); + sleDst->setIFieldAccount(sfAuthorizedKey, uAuthKeyID); + + std::cerr << "WalletAdd<" << std::endl; + + return tesSUCCESS; +} + +TER TransactionEngine::doInvoice(const SerializedTransaction& txn) +{ + return temUNKNOWN; +} + +// Take as much as possible. Adjusts account balances. Charges fees on top to taker. +// --> uBookBase: The order book to take against. +// --> saTakerPays: What the taker offers (w/ issuer) +// --> saTakerGets: What the taker wanted (w/ issuer) +// <-- saTakerPaid: What taker paid not including fees. To reduce an offer. +// <-- saTakerGot: What taker got not including fees. To reduce an offer. +// <-- terResult: tesSUCCESS or terNO_ACCOUNT +// XXX: Fees should be paid by the source of the currency. +TER TransactionEngine::takeOffers( + bool bPassive, + const uint256& uBookBase, + const uint160& uTakerAccountID, + const SLE::pointer& sleTakerAccount, + const STAmount& saTakerPays, + const STAmount& saTakerGets, + STAmount& saTakerPaid, + STAmount& saTakerGot) +{ + assert(saTakerPays && saTakerGets); + + Log(lsINFO) << "takeOffers: against book: " << uBookBase.ToString(); + + uint256 uTipIndex = uBookBase; + const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); + const uint64 uTakeQuality = STAmount::getRate(saTakerGets, saTakerPays); + const uint160 uTakerPaysAccountID = saTakerPays.getIssuer(); + const uint160 uTakerGetsAccountID = saTakerGets.getIssuer(); + TER terResult = temUNCERTAIN; + + boost::unordered_set usOfferUnfundedFound; // Offers found unfunded. + boost::unordered_set usOfferUnfundedBecame; // Offers that became unfunded. + boost::unordered_set usAccountTouched; // Accounts touched. + + saTakerPaid = 0; + saTakerGot = 0; + + while (temUNCERTAIN == terResult) + { + SLE::pointer sleOfferDir; + uint64 uTipQuality; + + // Figure out next offer to take, if needed. + if (saTakerGets != saTakerGot && saTakerPays != saTakerPaid) + { + // Taker has needs. + + sleOfferDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); + if (sleOfferDir) + { + Log(lsINFO) << "takeOffers: possible counter offer found"; + + uTipIndex = sleOfferDir->getIndex(); + uTipQuality = Ledger::getQuality(uTipIndex); + } + else + { + Log(lsINFO) << "takeOffers: counter offer book is empty: " + << uTipIndex.ToString() + << " ... " + << uBookEnd.ToString(); + } + } + + if (!sleOfferDir // No offer directory to take. + || uTakeQuality < uTipQuality // No offer's of sufficient quality available. + || (bPassive && uTakeQuality == uTipQuality)) + { + // Done. + Log(lsINFO) << "takeOffers: done"; + + terResult = tesSUCCESS; + } + else + { + // Have an offer directory to consider. + Log(lsINFO) << "takeOffers: considering dir : " << sleOfferDir->getJson(0); + + SLE::pointer sleBookNode; + unsigned int uBookEntry; + uint256 uOfferIndex; + + mNodes.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); + + SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); + + Log(lsINFO) << "takeOffers: considering offer : " << sleOffer->getJson(0); + + const uint160 uOfferOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); + STAmount saOfferPays = sleOffer->getIValueFieldAmount(sfTakerGets); + STAmount saOfferGets = sleOffer->getIValueFieldAmount(sfTakerPays); + + if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) + { + // Offer is expired. Expired offers are considered unfunded. Delete it. + Log(lsINFO) << "takeOffers: encountered expired offer"; + + usOfferUnfundedFound.insert(uOfferIndex); + } + else if (uOfferOwnerID == uTakerAccountID) + { + // Would take own offer. Consider old offer expired. Delete it. + Log(lsINFO) << "takeOffers: encountered taker's own old offer"; + + usOfferUnfundedFound.insert(uOfferIndex); + } + else + { + // Get offer funds available. + + Log(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText(); + + STAmount saOfferFunds = mNodes.accountFunds(uOfferOwnerID, saOfferPays); + STAmount saTakerFunds = mNodes.accountFunds(uTakerAccountID, saTakerPays); + SLE::pointer sleOfferAccount; // Owner of offer. + + if (!saOfferFunds.isPositive()) + { + // Offer is unfunded, possibly due to previous balance action. + Log(lsINFO) << "takeOffers: offer unfunded: delete"; + + boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID); + if (account != usAccountTouched.end()) + { + // Previously touched account. + usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. + } + else + { + // Never touched source account. + usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible. + } + } + else + { + STAmount saPay = saTakerPays - saTakerPaid; + if (saTakerFunds < saPay) + saPay = saTakerFunds; + STAmount saSubTakerPaid; + STAmount saSubTakerGot; + + Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText(); + + bool bOfferDelete = STAmount::applyOffer( + saOfferFunds, + saPay, // Driver XXX need to account for fees. + saOfferPays, + saOfferGets, + saTakerPays, + saTakerGets, + saSubTakerPaid, + saSubTakerGot); + + Log(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText(); + Log(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); + + // Adjust offer + + // Offer owner will pay less. Subtract what taker just got. + sleOffer->setIFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot); + + // Offer owner will get less. Subtract what owner just paid. + sleOffer->setIFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid); + + entryModify(sleOffer); + + if (bOfferDelete) + { + // Offer now fully claimed or now unfunded. + Log(lsINFO) << "takeOffers: offer claimed: delete"; + + usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. + + // Offer owner's account is no longer pristine. + usAccountTouched.insert(uOfferOwnerID); + } + else + { + Log(lsINFO) << "takeOffers: offer partial claim."; + } + + // Offer owner pays taker. + saSubTakerGot.setIssuer(uTakerGetsAccountID); // XXX Move this earlier? + + mNodes.accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); + + saTakerGot += saSubTakerGot; + + // Taker pays offer owner. + saSubTakerPaid.setIssuer(uTakerPaysAccountID); + + mNodes.accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); + + saTakerPaid += saSubTakerPaid; + } + } + } + } + + // On storing meta data, delete offers that were found unfunded to prevent encountering them in future. + if (tesSUCCESS == terResult) + { + BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound) + { + terResult = mNodes.offerDelete(uOfferIndex); + if (tesSUCCESS != terResult) + break; + } + } + + if (tesSUCCESS == terResult) + { + // On success, delete offers that became unfunded. + BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedBecame) + { + terResult = mNodes.offerDelete(uOfferIndex); + if (tesSUCCESS != terResult) + break; + } + } + + return terResult; +} + +TER TransactionEngine::doOfferCreate(const SerializedTransaction& txn) +{ +Log(lsWARNING) << "doOfferCreate> " << txn.getJson(0); + const uint32 txFlags = txn.getFlags(); + const bool bPassive = isSetBit(txFlags, tfPassive); + STAmount saTakerPays = txn.getITFieldAmount(sfTakerPays); + STAmount saTakerGets = txn.getITFieldAmount(sfTakerGets); +Log(lsWARNING) << "doOfferCreate: saTakerPays=" << saTakerPays.getFullText(); +Log(lsWARNING) << "doOfferCreate: saTakerGets=" << saTakerGets.getFullText(); + const uint160 uPaysIssuerID = saTakerPays.getIssuer(); + const uint160 uGetsIssuerID = saTakerGets.getIssuer(); + const uint32 uExpiration = txn.getITFieldU32(sfExpiration); + const bool bHaveExpiration = txn.getITFieldPresent(sfExpiration); + const uint32 uSequence = txn.getSequence(); + + const uint256 uLedgerIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); + SLE::pointer sleOffer = entryCreate(ltOFFER, uLedgerIndex); + + Log(lsINFO) << "doOfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence; + + const uint160 uPaysCurrency = saTakerPays.getCurrency(); + const uint160 uGetsCurrency = saTakerGets.getCurrency(); + const uint64 uRate = STAmount::getRate(saTakerGets, saTakerPays); + + TER terResult = tesSUCCESS; + uint256 uDirectory; // Delete hints. + uint64 uOwnerNode; + uint64 uBookNode; + + if (bHaveExpiration && !uExpiration) + { + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad expiration"; + + terResult = temBAD_EXPIRATION; + } + else if (bHaveExpiration && mLedger->getParentCloseTimeNC() >= uExpiration) + { + Log(lsWARNING) << "doOfferCreate: Expired transaction: offer expired"; + + // XXX CHARGE FEE ONLY. + terResult = tesSUCCESS; + } + else if (saTakerPays.isNative() && saTakerGets.isNative()) + { + Log(lsWARNING) << "doOfferCreate: Malformed offer: XNS for XNS"; + + terResult = temBAD_OFFER; + } + else if (!saTakerPays || !saTakerGets) + { + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad amount"; + + terResult = temBAD_OFFER; + } + else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID) + { + Log(lsWARNING) << "doOfferCreate: Malformed offer: redundant offer"; + + terResult = temREDUNDANT; + } + else if (saTakerPays.isNative() != !uPaysIssuerID || saTakerGets.isNative() != !uGetsIssuerID) + { + Log(lsWARNING) << "doOfferCreate: Malformed offer: bad issuer"; + + terResult = temBAD_ISSUER; + } + else if (!mNodes.accountFunds(mTxnAccountID, saTakerGets).isPositive()) + { + Log(lsWARNING) << "doOfferCreate: delay: Offers must be at least partially funded."; + + terResult = terUNFUNDED; + } + + if (tesSUCCESS == terResult && !saTakerPays.isNative()) + { + SLE::pointer sleTakerPays = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uPaysIssuerID)); + + if (!sleTakerPays) + { + Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existant issuer: " << NewcoinAddress::createHumanAccountID(uPaysIssuerID); + + terResult = terNO_ACCOUNT; + } + } + + if (tesSUCCESS == terResult) + { + STAmount saOfferPaid; + STAmount saOfferGot; + const uint256 uTakeBookBase = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); + + Log(lsINFO) << boost::str(boost::format("doOfferCreate: take against book: %s : %s/%s -> %s/%s") + % uTakeBookBase.ToString() + % saTakerGets.getHumanCurrency() + % NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer()) + % saTakerPays.getHumanCurrency() + % NewcoinAddress::createHumanAccountID(saTakerPays.getIssuer())); + + // Take using the parameters of the offer. + terResult = takeOffers( + bPassive, + uTakeBookBase, + mTxnAccountID, + mTxnAccount, + saTakerGets, + saTakerPays, + saOfferPaid, // How much was spent. + saOfferGot // How much was got. + ); + + Log(lsWARNING) << "doOfferCreate: takeOffers=" << terResult; + Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText(); + Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText(); + Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); + Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); + + if (tesSUCCESS == terResult) + { + saTakerPays -= saOfferGot; // Reduce payin from takers by what offer just got. + saTakerGets -= saOfferPaid; // Reduce payout to takers by what srcAccount just paid. + } + } + + Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); + Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); + Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer()); + Log(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << NewcoinAddress::createHumanAccountID(mTxnAccountID); + Log(lsWARNING) << "doOfferCreate: takeOffers: funds=" << mNodes.accountFunds(mTxnAccountID, saTakerGets).getFullText(); + + // Log(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); + // Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); + + if (tesSUCCESS == terResult + && saTakerPays // Still wanting something. + && saTakerGets // Still offering something. + && mNodes.accountFunds(mTxnAccountID, saTakerGets).isPositive()) // Still funded. + { + // We need to place the remainder of the offer into its order book. + + // Add offer to owner's directory. + terResult = mNodes.dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex); + + if (tesSUCCESS == terResult) + { + uint256 uBookBase = Ledger::getBookBase(uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID); + + Log(lsINFO) << boost::str(boost::format("doOfferCreate: adding to book: %s : %s/%s -> %s/%s") + % uBookBase.ToString() + % saTakerPays.getHumanCurrency() + % NewcoinAddress::createHumanAccountID(saTakerPays.getIssuer()) + % saTakerGets.getHumanCurrency() + % NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer())); + + uDirectory = Ledger::getQualityIndex(uBookBase, uRate); // Use original rate. + + // Add offer to order book. + terResult = mNodes.dirAdd(uBookNode, uDirectory, uLedgerIndex); + } + + if (tesSUCCESS == terResult) + { + // Log(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); + // Log(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); + // Log(lsWARNING) << "doOfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative(); + // Log(lsWARNING) << "doOfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative(); + // Log(lsWARNING) << "doOfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency(); + // Log(lsWARNING) << "doOfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency(); + + sleOffer->setIFieldAccount(sfAccount, mTxnAccountID); + sleOffer->setIFieldU32(sfSequence, uSequence); + sleOffer->setIFieldH256(sfBookDirectory, uDirectory); + sleOffer->setIFieldAmount(sfTakerPays, saTakerPays); + sleOffer->setIFieldAmount(sfTakerGets, saTakerGets); + sleOffer->setIFieldU64(sfOwnerNode, uOwnerNode); + sleOffer->setIFieldU64(sfBookNode, uBookNode); + + if (uExpiration) + sleOffer->setIFieldU32(sfExpiration, uExpiration); + + if (bPassive) + sleOffer->setFlag(lsfPassive); + } + } + + return terResult; +} + +TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn) +{ + TER terResult; + const uint32 uSequence = txn.getITFieldU32(sfOfferSequence); + const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); + SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); + + if (sleOffer) + { + Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence; + + terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); + } + else + { + Log(lsWARNING) << "doOfferCancel: offer not found: " + << NewcoinAddress::createHumanAccountID(mTxnAccountID) + << " : " << uSequence + << " : " << uOfferIndex.ToString(); + + terResult = terOFFER_NOT_FOUND; + } + + return terResult; +} + +TER TransactionEngine::doContractAdd(const SerializedTransaction& txn) +{ + Log(lsWARNING) << "doContractAdd> " << txn.getJson(0); + + const uint32 expiration = txn.getITFieldU32(sfExpiration); +// const uint32 bondAmount = txn.getITFieldU32(sfBondAmount); +// const uint32 stampEscrow = txn.getITFieldU32(sfStampEscrow); + STAmount rippleEscrow = txn.getITFieldAmount(sfRippleEscrow); + std::vector createCode = txn.getITFieldVL(sfCreateCode); + std::vector fundCode = txn.getITFieldVL(sfFundCode); + std::vector removeCode = txn.getITFieldVL(sfRemoveCode); + std::vector expireCode = txn.getITFieldVL(sfExpireCode); + + // make sure + // expiration hasn't passed + // bond amount is enough + // they have the stamps for the bond + + // place contract in ledger + // run create code + + + if (mLedger->getParentCloseTimeNC() >= expiration) + { + Log(lsWARNING) << "doContractAdd: Expired transaction: offer expired"; + return(tefALREADY); + } + //TODO: check bond + //if( txn.getSourceAccount() ) + + Contract contract; + Script::Interpreter interpreter; + TER terResult=interpreter.interpret(&contract,txn,createCode); + if(tesSUCCESS != terResult) + { + + } + + return(terResult); +} + +TER TransactionEngine::doContractRemove(const SerializedTransaction& txn) +{ + // TODO: + return(tesSUCCESS); +} + +// vim:ts=4 diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 526716ec81..2e5e22bd88 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1,77 +1,17 @@ // -// XXX Should make sure all fields and are recognized on a transactions. +// XXX Make sure all fields are recognized in transactions. // +#include + #include "TransactionEngine.h" -#include -#include -#include -#include - #include "../json/writer.h" #include "Config.h" #include "Log.h" #include "TransactionFormats.h" #include "utils.h" -#include "Interpreter.h" -#include "Contract.h" -#include "RippleCalc.h" - -#define RIPPLE_PATHS_MAX 3 - -// Set the authorized public key for an account. May also set the generator map. -TER TransactionEngine::setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator) -{ - // - // Verify that submitter knows the private key for the generator. - // Otherwise, people could deny access to generators. - // - - std::vector vucCipher = txn.getITFieldVL(sfGenerator); - std::vector vucPubKey = txn.getITFieldVL(sfPubKey); - std::vector vucSignature = txn.getITFieldVL(sfSignature); - NewcoinAddress naAccountPublic = NewcoinAddress::createAccountPublic(vucPubKey); - - if (!naAccountPublic.accountPublicVerify(Serializer::getSHA512Half(vucCipher), vucSignature)) - { - Log(lsWARNING) << "createGenerator: bad signature unauthorized generator claim"; - - return tefBAD_GEN_AUTH; - } - - // Create generator. - uint160 hGeneratorID = naAccountPublic.getAccountID(); - - SLE::pointer sleGen = entryCache(ltGENERATOR_MAP, Ledger::getGeneratorIndex(hGeneratorID)); - if (!sleGen) - { - // Create the generator. - Log(lsTRACE) << "createGenerator: creating generator"; - - sleGen = entryCreate(ltGENERATOR_MAP, Ledger::getGeneratorIndex(hGeneratorID)); - - sleGen->setIFieldVL(sfGenerator, vucCipher); - } - else if (bMustSetGenerator) - { - // Doing a claim. Must set generator. - // Generator is already in use. Regular passphrases limited to one wallet. - Log(lsWARNING) << "createGenerator: generator already in use"; - - return tefGEN_IN_USE; - } - - // Set the public key needed to use the account. - uint160 uAuthKeyID = bMustSetGenerator - ? hGeneratorID // Claim - : txn.getITFieldAccount(sfAuthorizedKey); // PasswordSet - - mTxnAccount->setIFieldAccount(sfAuthorizedKey, uAuthKeyID); - - return tesSUCCESS; -} void TransactionEngine::txnWrite() { @@ -543,1100 +483,4 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa return terResult; } - -TER TransactionEngine::doAccountSet(const SerializedTransaction& txn) -{ - Log(lsINFO) << "doAccountSet>"; - - // - // EmailHash - // - - if (txn.getITFieldPresent(sfEmailHash)) - { - uint128 uHash = txn.getITFieldH128(sfEmailHash); - - if (!uHash) - { - Log(lsINFO) << "doAccountSet: unset email hash"; - - mTxnAccount->makeIFieldAbsent(sfEmailHash); - } - else - { - Log(lsINFO) << "doAccountSet: set email hash"; - - mTxnAccount->setIFieldH128(sfEmailHash, uHash); - } - } - - // - // WalletLocator - // - - if (txn.getITFieldPresent(sfWalletLocator)) - { - uint256 uHash = txn.getITFieldH256(sfWalletLocator); - - if (!uHash) - { - Log(lsINFO) << "doAccountSet: unset wallet locator"; - - mTxnAccount->makeIFieldAbsent(sfEmailHash); - } - else - { - Log(lsINFO) << "doAccountSet: set wallet locator"; - - mTxnAccount->setIFieldH256(sfWalletLocator, uHash); - } - } - - // - // MessageKey - // - - if (!txn.getITFieldPresent(sfMessageKey)) - { - nothing(); - } - else - { - Log(lsINFO) << "doAccountSet: set message key"; - - mTxnAccount->setIFieldVL(sfMessageKey, txn.getITFieldVL(sfMessageKey)); - } - - // - // Domain - // - - if (txn.getITFieldPresent(sfDomain)) - { - std::vector vucDomain = txn.getITFieldVL(sfDomain); - - if (vucDomain.empty()) - { - Log(lsINFO) << "doAccountSet: unset domain"; - - mTxnAccount->makeIFieldAbsent(sfDomain); - } - else - { - Log(lsINFO) << "doAccountSet: set domain"; - - mTxnAccount->setIFieldVL(sfDomain, vucDomain); - } - } - - // - // TransferRate - // - - if (txn.getITFieldPresent(sfTransferRate)) - { - uint32 uRate = txn.getITFieldU32(sfTransferRate); - - if (!uRate) - { - Log(lsINFO) << "doAccountSet: unset transfer rate"; - - mTxnAccount->makeIFieldAbsent(sfTransferRate); - } - else - { - Log(lsINFO) << "doAccountSet: set transfer rate"; - - mTxnAccount->setIFieldU32(sfTransferRate, uRate); - } - } - - // - // PublishHash && PublishSize - // - - bool bPublishHash = txn.getITFieldPresent(sfPublishHash); - bool bPublishSize = txn.getITFieldPresent(sfPublishSize); - - if (bPublishHash ^ bPublishSize) - { - Log(lsINFO) << "doAccountSet: bad publish"; - - return temBAD_PUBLISH; - } - else if (bPublishHash && bPublishSize) - { - uint256 uHash = txn.getITFieldH256(sfPublishHash); - uint32 uSize = txn.getITFieldU32(sfPublishSize); - - if (!uHash) - { - Log(lsINFO) << "doAccountSet: unset publish"; - - mTxnAccount->makeIFieldAbsent(sfPublishHash); - mTxnAccount->makeIFieldAbsent(sfPublishSize); - } - else - { - Log(lsINFO) << "doAccountSet: set publish"; - - mTxnAccount->setIFieldH256(sfPublishHash, uHash); - mTxnAccount->setIFieldU32(sfPublishSize, uSize); - } - } - - Log(lsINFO) << "doAccountSet<"; - - return tesSUCCESS; -} - -TER TransactionEngine::doClaim(const SerializedTransaction& txn) -{ - Log(lsINFO) << "doClaim>"; - - TER terResult = setAuthorized(txn, true); - - Log(lsINFO) << "doClaim<"; - - return terResult; -} - -TER TransactionEngine::doCreditSet(const SerializedTransaction& txn) -{ - TER terResult = tesSUCCESS; - Log(lsINFO) << "doCreditSet>"; - - // Check if destination makes sense. - uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); - - if (!uDstAccountID) - { - Log(lsINFO) << "doCreditSet: Invalid transaction: Destination account not specifed."; - - return temDST_NEEDED; - } - else if (mTxnAccountID == uDstAccountID) - { - Log(lsINFO) << "doCreditSet: Invalid transaction: Can not extend credit to self."; - - return temDST_IS_SRC; - } - - SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - if (!sleDst) - { - Log(lsINFO) << "doCreditSet: Delay transaction: Destination account does not exist."; - - return terNO_DST; - } - - const bool bFlipped = mTxnAccountID > uDstAccountID; // true, iff current is not lowest. - const bool bLimitAmount = txn.getITFieldPresent(sfLimitAmount); - const STAmount saLimitAmount = bLimitAmount ? txn.getITFieldAmount(sfLimitAmount) : STAmount(); - const bool bQualityIn = txn.getITFieldPresent(sfQualityIn); - const uint32 uQualityIn = bQualityIn ? txn.getITFieldU32(sfQualityIn) : 0; - const bool bQualityOut = txn.getITFieldPresent(sfQualityOut); - const uint32 uQualityOut = bQualityIn ? txn.getITFieldU32(sfQualityOut) : 0; - const uint160 uCurrencyID = saLimitAmount.getCurrency(); - bool bDelIndex = false; - - SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); - if (sleRippleState) - { - // A line exists in one or more directions. -#if 0 - if (!saLimitAmount) - { - // Zeroing line. - uint160 uLowID = sleRippleState->getIValueFieldAccount(sfLowID).getAccountID(); - uint160 uHighID = sleRippleState->getIValueFieldAccount(sfHighID).getAccountID(); - bool bLow = uLowID == uSrcAccountID; - bool bHigh = uLowID == uDstAccountID; - bool bBalanceZero = !sleRippleState->getIValueFieldAmount(sfBalance); - STAmount saDstLimit = sleRippleState->getIValueFieldAmount(bSendLow ? sfLowLimit : sfHighLimit); - bool bDstLimitZero = !saDstLimit; - - assert(bLow || bHigh); - - if (bBalanceZero && bDstLimitZero) - { - // Zero balance and eliminating last limit. - - bDelIndex = true; - terResult = dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex(), false); - } - } -#endif - - if (!bDelIndex) - { - if (bLimitAmount) - sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit: sfLowLimit , saLimitAmount); - - if (!bQualityIn) - { - nothing(); - } - else if (uQualityIn) - { - sleRippleState->setIFieldU32(bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn); - } - else - { - sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityIn : sfHighQualityIn); - } - - if (!bQualityOut) - { - nothing(); - } - else if (uQualityOut) - { - sleRippleState->setIFieldU32(bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut); - } - else - { - sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityOut : sfHighQualityOut); - } - - entryModify(sleRippleState); - } - - Log(lsINFO) << "doCreditSet: Modifying ripple line: bDelIndex=" << bDelIndex; - } - // Line does not exist. - else if (!saLimitAmount) - { - Log(lsINFO) << "doCreditSet: Redundant: Setting non-existant ripple line to 0."; - - return terNO_LINE_NO_ZERO; - } - else - { - // Create a new ripple line. - sleRippleState = entryCreate(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); - - Log(lsINFO) << "doCreditSet: Creating ripple line: " << sleRippleState->getIndex().ToString(); - - sleRippleState->setIFieldAmount(sfBalance, STAmount(uCurrencyID, ACCOUNT_ONE)); // Zero balance in currency. - sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit : sfLowLimit, saLimitAmount); - sleRippleState->setIFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, ACCOUNT_ONE)); - sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, mTxnAccountID); - sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uDstAccountID); - if (uQualityIn) - sleRippleState->setIFieldU32(bFlipped ? sfHighQualityIn : sfLowQualityIn, uQualityIn); - if (uQualityOut) - sleRippleState->setIFieldU32(bFlipped ? sfHighQualityOut : sfLowQualityOut, uQualityOut); - - uint64 uSrcRef; // Ignored, dirs never delete. - - terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex()); - - if (tesSUCCESS == terResult) - terResult = mNodes.dirAdd(uSrcRef, Ledger::getOwnerDirIndex(uDstAccountID), sleRippleState->getIndex()); - } - - Log(lsINFO) << "doCreditSet<"; - - return terResult; -} - -TER TransactionEngine::doNicknameSet(const SerializedTransaction& txn) -{ - std::cerr << "doNicknameSet>" << std::endl; - - const uint256 uNickname = txn.getITFieldH256(sfNickname); - const bool bMinOffer = txn.getITFieldPresent(sfMinimumOffer); - const STAmount saMinOffer = bMinOffer ? txn.getITFieldAmount(sfAmount) : STAmount(); - - SLE::pointer sleNickname = entryCache(ltNICKNAME, uNickname); - - if (sleNickname) - { - // Edit old entry. - sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); - - if (bMinOffer && saMinOffer) - { - sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); - } - else - { - sleNickname->makeIFieldAbsent(sfMinimumOffer); - } - - entryModify(sleNickname); - } - else - { - // Make a new entry. - // XXX Need to include authorization limiting for first year. - - sleNickname = entryCreate(ltNICKNAME, Ledger::getNicknameIndex(uNickname)); - - std::cerr << "doNicknameSet: Creating nickname node: " << sleNickname->getIndex().ToString() << std::endl; - - sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); - - if (bMinOffer && saMinOffer) - sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); - } - - std::cerr << "doNicknameSet<" << std::endl; - - return tesSUCCESS; -} - -TER TransactionEngine::doPasswordFund(const SerializedTransaction& txn) -{ - std::cerr << "doPasswordFund>" << std::endl; - - const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); - SLE::pointer sleDst = mTxnAccountID == uDstAccountID - ? mTxnAccount - : entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - if (!sleDst) - { - // Destination account does not exist. - std::cerr << "doPasswordFund: Delay transaction: Destination account does not exist." << std::endl; - - return terSET_MISSING_DST; - } - - if (sleDst->getFlags() & lsfPasswordSpent) - { - sleDst->clearFlag(lsfPasswordSpent); - - std::cerr << "doPasswordFund: Clearing spent." << sleDst->getFlags() << std::endl; - - if (mTxnAccountID != uDstAccountID) { - std::cerr << "doPasswordFund: Destination modified." << std::endl; - - entryModify(sleDst); - } - } - - std::cerr << "doPasswordFund<" << std::endl; - - return tesSUCCESS; -} - -TER TransactionEngine::doPasswordSet(const SerializedTransaction& txn) -{ - std::cerr << "doPasswordSet>" << std::endl; - - if (mTxnAccount->getFlags() & lsfPasswordSpent) - { - std::cerr << "doPasswordSet: Delay transaction: Funds already spent." << std::endl; - - return terFUNDS_SPENT; - } - - mTxnAccount->setFlag(lsfPasswordSpent); - - TER terResult = setAuthorized(txn, false); - - std::cerr << "doPasswordSet<" << std::endl; - - return terResult; -} - - -// XXX Need to audit for things like setting accountID not having memory. -TER TransactionEngine::doPayment(const SerializedTransaction& txn, const TransactionEngineParams params) -{ - // Ripple if source or destination is non-native or if there are paths. - const uint32 uTxFlags = txn.getFlags(); - const bool bCreate = isSetBit(uTxFlags, tfCreateAccount); - const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); - const bool bLimitQuality = isSetBit(uTxFlags, tfLimitQuality); - const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); - const bool bPaths = txn.getITFieldPresent(sfPaths); - const bool bMax = txn.getITFieldPresent(sfSendMax); - const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); - const STAmount saDstAmount = txn.getITFieldAmount(sfAmount); - const STAmount saMaxAmount = bMax ? txn.getITFieldAmount(sfSendMax) : saDstAmount; - const uint160 uSrcCurrency = saMaxAmount.getCurrency(); - const uint160 uDstCurrency = saDstAmount.getCurrency(); - - Log(lsINFO) << boost::str(boost::format("doPayment> saMaxAmount=%s saDstAmount=%s") - % saMaxAmount.getFullText() - % saDstAmount.getFullText()); - - if (!uDstAccountID) - { - Log(lsINFO) << "doPayment: Invalid transaction: Payment destination account not specifed."; - - return temDST_NEEDED; - } - else if (!saDstAmount.isPositive()) - { - Log(lsINFO) << "doPayment: Invalid transaction: bad amount: " << saDstAmount.getHumanCurrency() << " " << saDstAmount.getText(); - - return temBAD_AMOUNT; - } - else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) - { - Log(lsINFO) << boost::str(boost::format("doPayment: Invalid transaction: Redunant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") - % mTxnAccountID.ToString() - % uDstAccountID.ToString() - % uSrcCurrency.ToString() - % uDstCurrency.ToString()); - - return temREDUNDANT; - } - else if (bMax - && ((saMaxAmount == saDstAmount && saMaxAmount.getCurrency() == saDstAmount.getCurrency()) - || (saDstAmount.isNative() && saMaxAmount.isNative()))) - { - Log(lsINFO) << "doPayment: Invalid transaction: bad SendMax."; - - return temINVALID; - } - - SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - if (!sleDst) - { - // Destination account does not exist. - if (bCreate && !saDstAmount.isNative()) - { - // This restriction could be relaxed. - Log(lsINFO) << "doPayment: Invalid transaction: Create account may only fund XNS."; - - return temCREATEXNS; - } - else if (!bCreate) - { - Log(lsINFO) << "doPayment: Delay transaction: Destination account does not exist."; - - return terNO_DST; - } - - // Create the account. - sleDst = entryCreate(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - - sleDst->setIFieldAccount(sfAccount, uDstAccountID); - sleDst->setIFieldU32(sfSequence, 1); - } - else - { - entryModify(sleDst); - } - - TER terResult; - // XXX Should bMax be sufficient to imply ripple? - const bool bRipple = bPaths || bMax || !saDstAmount.isNative(); - - if (bRipple) - { - // Ripple payment - - STPathSet spsPaths = txn.getITFieldPathSet(sfPaths); - STAmount saMaxAmountAct; - STAmount saDstAmountAct; - - terResult = isSetBit(params, tapOPEN_LEDGER) && spsPaths.getPathCount() > RIPPLE_PATHS_MAX - ? telBAD_PATH_COUNT - : RippleCalc::rippleCalc( - mNodes, - saMaxAmountAct, - saDstAmountAct, - saMaxAmount, - saDstAmount, - uDstAccountID, - mTxnAccountID, - spsPaths, - bPartialPayment, - bLimitQuality, - bNoRippleDirect); - } - else - { - // Direct XNS payment. - - STAmount saSrcXNSBalance = mTxnAccount->getIValueFieldAmount(sfBalance); - - if (saSrcXNSBalance < saDstAmount) - { - // Transaction might succeed, if applied in a different order. - Log(lsINFO) << "doPayment: Delay transaction: Insufficent funds."; - - terResult = terUNFUNDED; - } - else - { - mTxnAccount->setIFieldAmount(sfBalance, saSrcXNSBalance - saDstAmount); - sleDst->setIFieldAmount(sfBalance, sleDst->getIValueFieldAmount(sfBalance) + saDstAmount); - - terResult = tesSUCCESS; - } - } - - std::string strToken; - std::string strHuman; - - if (transResultInfo(terResult, strToken, strHuman)) - { - Log(lsINFO) << boost::str(boost::format("doPayment: %s: %s") % strToken % strHuman); - } - else - { - assert(false); - } - - return terResult; -} - -TER TransactionEngine::doWalletAdd(const SerializedTransaction& txn) -{ - std::cerr << "WalletAdd>" << std::endl; - - const std::vector vucPubKey = txn.getITFieldVL(sfPubKey); - const std::vector vucSignature = txn.getITFieldVL(sfSignature); - const uint160 uAuthKeyID = txn.getITFieldAccount(sfAuthorizedKey); - const NewcoinAddress naMasterPubKey = NewcoinAddress::createAccountPublic(vucPubKey); - const uint160 uDstAccountID = naMasterPubKey.getAccountID(); - - if (!naMasterPubKey.accountPublicVerify(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature)) - { - std::cerr << "WalletAdd: unauthorized: bad signature " << std::endl; - - return tefBAD_ADD_AUTH; - } - - SLE::pointer sleDst = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - - if (sleDst) - { - std::cerr << "WalletAdd: account already created" << std::endl; - - return tefCREATED; - } - - STAmount saAmount = txn.getITFieldAmount(sfAmount); - STAmount saSrcBalance = mTxnAccount->getIValueFieldAmount(sfBalance); - - if (saSrcBalance < saAmount) - { - std::cerr - << boost::str(boost::format("WalletAdd: Delay transaction: insufficent balance: balance=%s amount=%s") - % saSrcBalance.getText() - % saAmount.getText()) - << std::endl; - - return terUNFUNDED; - } - - // Deduct initial balance from source account. - mTxnAccount->setIFieldAmount(sfBalance, saSrcBalance-saAmount); - - // Create the account. - sleDst = entryCreate(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uDstAccountID)); - - sleDst->setIFieldAccount(sfAccount, uDstAccountID); - sleDst->setIFieldU32(sfSequence, 1); - sleDst->setIFieldAmount(sfBalance, saAmount); - sleDst->setIFieldAccount(sfAuthorizedKey, uAuthKeyID); - - std::cerr << "WalletAdd<" << std::endl; - - return tesSUCCESS; -} - -TER TransactionEngine::doInvoice(const SerializedTransaction& txn) -{ - return temUNKNOWN; -} - -// Take as much as possible. Adjusts account balances. Charges fees on top to taker. -// --> uBookBase: The order book to take against. -// --> saTakerPays: What the taker offers (w/ issuer) -// --> saTakerGets: What the taker wanted (w/ issuer) -// <-- saTakerPaid: What taker paid not including fees. To reduce an offer. -// <-- saTakerGot: What taker got not including fees. To reduce an offer. -// <-- terResult: tesSUCCESS or terNO_ACCOUNT -// XXX: Fees should be paid by the source of the currency. -TER TransactionEngine::takeOffers( - bool bPassive, - const uint256& uBookBase, - const uint160& uTakerAccountID, - const SLE::pointer& sleTakerAccount, - const STAmount& saTakerPays, - const STAmount& saTakerGets, - STAmount& saTakerPaid, - STAmount& saTakerGot) -{ - assert(saTakerPays && saTakerGets); - - Log(lsINFO) << "takeOffers: against book: " << uBookBase.ToString(); - - uint256 uTipIndex = uBookBase; - const uint256 uBookEnd = Ledger::getQualityNext(uBookBase); - const uint64 uTakeQuality = STAmount::getRate(saTakerGets, saTakerPays); - const uint160 uTakerPaysAccountID = saTakerPays.getIssuer(); - const uint160 uTakerGetsAccountID = saTakerGets.getIssuer(); - TER terResult = temUNCERTAIN; - - boost::unordered_set usOfferUnfundedFound; // Offers found unfunded. - boost::unordered_set usOfferUnfundedBecame; // Offers that became unfunded. - boost::unordered_set usAccountTouched; // Accounts touched. - - saTakerPaid = 0; - saTakerGot = 0; - - while (temUNCERTAIN == terResult) - { - SLE::pointer sleOfferDir; - uint64 uTipQuality; - - // Figure out next offer to take, if needed. - if (saTakerGets != saTakerGot && saTakerPays != saTakerPaid) - { - // Taker has needs. - - sleOfferDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uTipIndex, uBookEnd)); - if (sleOfferDir) - { - Log(lsINFO) << "takeOffers: possible counter offer found"; - - uTipIndex = sleOfferDir->getIndex(); - uTipQuality = Ledger::getQuality(uTipIndex); - } - else - { - Log(lsINFO) << "takeOffers: counter offer book is empty: " - << uTipIndex.ToString() - << " ... " - << uBookEnd.ToString(); - } - } - - if (!sleOfferDir // No offer directory to take. - || uTakeQuality < uTipQuality // No offer's of sufficient quality available. - || (bPassive && uTakeQuality == uTipQuality)) - { - // Done. - Log(lsINFO) << "takeOffers: done"; - - terResult = tesSUCCESS; - } - else - { - // Have an offer directory to consider. - Log(lsINFO) << "takeOffers: considering dir : " << sleOfferDir->getJson(0); - - SLE::pointer sleBookNode; - unsigned int uBookEntry; - uint256 uOfferIndex; - - mNodes.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex); - - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - - Log(lsINFO) << "takeOffers: considering offer : " << sleOffer->getJson(0); - - const uint160 uOfferOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); - STAmount saOfferPays = sleOffer->getIValueFieldAmount(sfTakerGets); - STAmount saOfferGets = sleOffer->getIValueFieldAmount(sfTakerPays); - - if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) - { - // Offer is expired. Expired offers are considered unfunded. Delete it. - Log(lsINFO) << "takeOffers: encountered expired offer"; - - usOfferUnfundedFound.insert(uOfferIndex); - } - else if (uOfferOwnerID == uTakerAccountID) - { - // Would take own offer. Consider old offer expired. Delete it. - Log(lsINFO) << "takeOffers: encountered taker's own old offer"; - - usOfferUnfundedFound.insert(uOfferIndex); - } - else - { - // Get offer funds available. - - Log(lsINFO) << "takeOffers: saOfferPays=" << saOfferPays.getFullText(); - - STAmount saOfferFunds = mNodes.accountFunds(uOfferOwnerID, saOfferPays); - STAmount saTakerFunds = mNodes.accountFunds(uTakerAccountID, saTakerPays); - SLE::pointer sleOfferAccount; // Owner of offer. - - if (!saOfferFunds.isPositive()) - { - // Offer is unfunded, possibly due to previous balance action. - Log(lsINFO) << "takeOffers: offer unfunded: delete"; - - boost::unordered_set::iterator account = usAccountTouched.find(uOfferOwnerID); - if (account != usAccountTouched.end()) - { - // Previously touched account. - usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. - } - else - { - // Never touched source account. - usOfferUnfundedFound.insert(uOfferIndex); // Delete found unfunded offer when possible. - } - } - else - { - STAmount saPay = saTakerPays - saTakerPaid; - if (saTakerFunds < saPay) - saPay = saTakerFunds; - STAmount saSubTakerPaid; - STAmount saSubTakerGot; - - Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saTakerPays: " << saTakerPays.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saTakerGets: " << saTakerGets.getFullText(); - - bool bOfferDelete = STAmount::applyOffer( - saOfferFunds, - saPay, // Driver XXX need to account for fees. - saOfferPays, - saOfferGets, - saTakerPays, - saTakerGets, - saSubTakerPaid, - saSubTakerGot); - - Log(lsINFO) << "takeOffers: applyOffer: saSubTakerPaid: " << saSubTakerPaid.getFullText(); - Log(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); - - // Adjust offer - - // Offer owner will pay less. Subtract what taker just got. - sleOffer->setIFieldAmount(sfTakerGets, saOfferPays -= saSubTakerGot); - - // Offer owner will get less. Subtract what owner just paid. - sleOffer->setIFieldAmount(sfTakerPays, saOfferGets -= saSubTakerPaid); - - entryModify(sleOffer); - - if (bOfferDelete) - { - // Offer now fully claimed or now unfunded. - Log(lsINFO) << "takeOffers: offer claimed: delete"; - - usOfferUnfundedBecame.insert(uOfferIndex); // Delete unfunded offer on success. - - // Offer owner's account is no longer pristine. - usAccountTouched.insert(uOfferOwnerID); - } - else - { - Log(lsINFO) << "takeOffers: offer partial claim."; - } - - // Offer owner pays taker. - saSubTakerGot.setIssuer(uTakerGetsAccountID); // XXX Move this earlier? - - mNodes.accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); - - saTakerGot += saSubTakerGot; - - // Taker pays offer owner. - saSubTakerPaid.setIssuer(uTakerPaysAccountID); - - mNodes.accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); - - saTakerPaid += saSubTakerPaid; - } - } - } - } - - // On storing meta data, delete offers that were found unfunded to prevent encountering them in future. - if (tesSUCCESS == terResult) - { - BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedFound) - { - terResult = mNodes.offerDelete(uOfferIndex); - if (tesSUCCESS != terResult) - break; - } - } - - if (tesSUCCESS == terResult) - { - // On success, delete offers that became unfunded. - BOOST_FOREACH(const uint256& uOfferIndex, usOfferUnfundedBecame) - { - terResult = mNodes.offerDelete(uOfferIndex); - if (tesSUCCESS != terResult) - break; - } - } - - return terResult; -} - -TER TransactionEngine::doOfferCreate(const SerializedTransaction& txn) -{ -Log(lsWARNING) << "doOfferCreate> " << txn.getJson(0); - const uint32 txFlags = txn.getFlags(); - const bool bPassive = isSetBit(txFlags, tfPassive); - STAmount saTakerPays = txn.getITFieldAmount(sfTakerPays); - STAmount saTakerGets = txn.getITFieldAmount(sfTakerGets); -Log(lsWARNING) << "doOfferCreate: saTakerPays=" << saTakerPays.getFullText(); -Log(lsWARNING) << "doOfferCreate: saTakerGets=" << saTakerGets.getFullText(); - const uint160 uPaysIssuerID = saTakerPays.getIssuer(); - const uint160 uGetsIssuerID = saTakerGets.getIssuer(); - const uint32 uExpiration = txn.getITFieldU32(sfExpiration); - const bool bHaveExpiration = txn.getITFieldPresent(sfExpiration); - const uint32 uSequence = txn.getSequence(); - - const uint256 uLedgerIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); - SLE::pointer sleOffer = entryCreate(ltOFFER, uLedgerIndex); - - Log(lsINFO) << "doOfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence; - - const uint160 uPaysCurrency = saTakerPays.getCurrency(); - const uint160 uGetsCurrency = saTakerGets.getCurrency(); - const uint64 uRate = STAmount::getRate(saTakerGets, saTakerPays); - - TER terResult = tesSUCCESS; - uint256 uDirectory; // Delete hints. - uint64 uOwnerNode; - uint64 uBookNode; - - if (bHaveExpiration && !uExpiration) - { - Log(lsWARNING) << "doOfferCreate: Malformed offer: bad expiration"; - - terResult = temBAD_EXPIRATION; - } - else if (bHaveExpiration && mLedger->getParentCloseTimeNC() >= uExpiration) - { - Log(lsWARNING) << "doOfferCreate: Expired transaction: offer expired"; - - // XXX CHARGE FEE ONLY. - terResult = tesSUCCESS; - } - else if (saTakerPays.isNative() && saTakerGets.isNative()) - { - Log(lsWARNING) << "doOfferCreate: Malformed offer: XNS for XNS"; - - terResult = temBAD_OFFER; - } - else if (!saTakerPays || !saTakerGets) - { - Log(lsWARNING) << "doOfferCreate: Malformed offer: bad amount"; - - terResult = temBAD_OFFER; - } - else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID) - { - Log(lsWARNING) << "doOfferCreate: Malformed offer: redundant offer"; - - terResult = temREDUNDANT; - } - else if (saTakerPays.isNative() != !uPaysIssuerID || saTakerGets.isNative() != !uGetsIssuerID) - { - Log(lsWARNING) << "doOfferCreate: Malformed offer: bad issuer"; - - terResult = temBAD_ISSUER; - } - else if (!mNodes.accountFunds(mTxnAccountID, saTakerGets).isPositive()) - { - Log(lsWARNING) << "doOfferCreate: delay: Offers must be at least partially funded."; - - terResult = terUNFUNDED; - } - - if (tesSUCCESS == terResult && !saTakerPays.isNative()) - { - SLE::pointer sleTakerPays = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uPaysIssuerID)); - - if (!sleTakerPays) - { - Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existant issuer: " << NewcoinAddress::createHumanAccountID(uPaysIssuerID); - - terResult = terNO_ACCOUNT; - } - } - - if (tesSUCCESS == terResult) - { - STAmount saOfferPaid; - STAmount saOfferGot; - const uint256 uTakeBookBase = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); - - Log(lsINFO) << boost::str(boost::format("doOfferCreate: take against book: %s : %s/%s -> %s/%s") - % uTakeBookBase.ToString() - % saTakerGets.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer()) - % saTakerPays.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerPays.getIssuer())); - - // Take using the parameters of the offer. - terResult = takeOffers( - bPassive, - uTakeBookBase, - mTxnAccountID, - mTxnAccount, - saTakerGets, - saTakerPays, - saOfferPaid, // How much was spent. - saOfferGot // How much was got. - ); - - Log(lsWARNING) << "doOfferCreate: takeOffers=" << terResult; - Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); - - if (tesSUCCESS == terResult) - { - saTakerPays -= saOfferGot; // Reduce payin from takers by what offer just got. - saTakerGets -= saOfferPaid; // Reduce payout to takers by what srcAccount just paid. - } - } - - Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer()); - Log(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << NewcoinAddress::createHumanAccountID(mTxnAccountID); - Log(lsWARNING) << "doOfferCreate: takeOffers: funds=" << mNodes.accountFunds(mTxnAccountID, saTakerGets).getFullText(); - - // Log(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); - // Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); - - if (tesSUCCESS == terResult - && saTakerPays // Still wanting something. - && saTakerGets // Still offering something. - && mNodes.accountFunds(mTxnAccountID, saTakerGets).isPositive()) // Still funded. - { - // We need to place the remainder of the offer into its order book. - - // Add offer to owner's directory. - terResult = mNodes.dirAdd(uOwnerNode, Ledger::getOwnerDirIndex(mTxnAccountID), uLedgerIndex); - - if (tesSUCCESS == terResult) - { - uint256 uBookBase = Ledger::getBookBase(uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID); - - Log(lsINFO) << boost::str(boost::format("doOfferCreate: adding to book: %s : %s/%s -> %s/%s") - % uBookBase.ToString() - % saTakerPays.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerPays.getIssuer()) - % saTakerGets.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer())); - - uDirectory = Ledger::getQualityIndex(uBookBase, uRate); // Use original rate. - - // Add offer to order book. - terResult = mNodes.dirAdd(uBookNode, uDirectory, uLedgerIndex); - } - - if (tesSUCCESS == terResult) - { - // Log(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); - // Log(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); - // Log(lsWARNING) << "doOfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative(); - // Log(lsWARNING) << "doOfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative(); - // Log(lsWARNING) << "doOfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency(); - // Log(lsWARNING) << "doOfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency(); - - sleOffer->setIFieldAccount(sfAccount, mTxnAccountID); - sleOffer->setIFieldU32(sfSequence, uSequence); - sleOffer->setIFieldH256(sfBookDirectory, uDirectory); - sleOffer->setIFieldAmount(sfTakerPays, saTakerPays); - sleOffer->setIFieldAmount(sfTakerGets, saTakerGets); - sleOffer->setIFieldU64(sfOwnerNode, uOwnerNode); - sleOffer->setIFieldU64(sfBookNode, uBookNode); - - if (uExpiration) - sleOffer->setIFieldU32(sfExpiration, uExpiration); - - if (bPassive) - sleOffer->setFlag(lsfPassive); - } - } - - return terResult; -} - -TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn) -{ - TER terResult; - const uint32 uSequence = txn.getITFieldU32(sfOfferSequence); - const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - - if (sleOffer) - { - Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence; - - terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); - } - else - { - Log(lsWARNING) << "doOfferCancel: offer not found: " - << NewcoinAddress::createHumanAccountID(mTxnAccountID) - << " : " << uSequence - << " : " << uOfferIndex.ToString(); - - terResult = terOFFER_NOT_FOUND; - } - - return terResult; -} - -TER TransactionEngine::doContractAdd(const SerializedTransaction& txn) -{ - Log(lsWARNING) << "doContractAdd> " << txn.getJson(0); - - const uint32 expiration = txn.getITFieldU32(sfExpiration); -// const uint32 bondAmount = txn.getITFieldU32(sfBondAmount); -// const uint32 stampEscrow = txn.getITFieldU32(sfStampEscrow); - STAmount rippleEscrow = txn.getITFieldAmount(sfRippleEscrow); - std::vector createCode = txn.getITFieldVL(sfCreateCode); - std::vector fundCode = txn.getITFieldVL(sfFundCode); - std::vector removeCode = txn.getITFieldVL(sfRemoveCode); - std::vector expireCode = txn.getITFieldVL(sfExpireCode); - - // make sure - // expiration hasn't passed - // bond amount is enough - // they have the stamps for the bond - - // place contract in ledger - // run create code - - - if (mLedger->getParentCloseTimeNC() >= expiration) - { - Log(lsWARNING) << "doContractAdd: Expired transaction: offer expired"; - return(tefALREADY); - } - //TODO: check bond - //if( txn.getSourceAccount() ) - - Contract contract; - Script::Interpreter interpreter; - TER terResult=interpreter.interpret(&contract,txn,createCode); - if(tesSUCCESS != terResult) - { - - } - - return(terResult); -} - -TER TransactionEngine::doContractRemove(const SerializedTransaction& txn) -{ - // TODO: - return(tesSUCCESS); -} - // vim:ts=4