diff --git a/js/events.js b/js/events.js new file mode 100644 index 0000000000..7a2098f98a --- /dev/null +++ b/js/events.js @@ -0,0 +1,57 @@ +var EventEmitter = function () { + this._events = {}; +}; + +EventEmitter.prototype.on = function (e, f) { + console.log('on', e, f) + if (e in this._events) { + if (this._events[e].indexOf(f) < 0) { + this._events[e].push(f); + } + } else { + this._events[e] = [f]; + } + return this; +}; + +EventEmitter.prototype.off = function (e, f) { + if (f) { + function eq(x) { return function (y) { return x === y; } } + this._events[e] = this.listeners(e).filter(eq(f)); + } else { + delete this._events[e]; + } +}; + +EventEmitter.prototype.removeListener = function (e, f) { + this.off(e, f); +}; + +EventEmitter.prototype.removeAllListeners = function (e) { + this.off(e); +}; + +EventEmitter.prototype.emit = function (e) { + var args = Array.prototype.slice.call(arguments, 1), + fs = this.listeners(e); + console.log('emit', e, args) + + for (var i = 0; i < fs.length; i++) { + fs[i].apply(e, args); + } +}; + +EventEmitter.prototype.listeners = function (e) { + return this._events[e] || []; +}; + +EventEmitter.prototype.once = function (e, f) { + var that = this; + this.on(e, function g() { + f.apply(e, arguments); + that.off(e, g); + }); + return this; +}; + +exports.EventEmitter = EventEmitter; \ No newline at end of file diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 3109a074aa..3a318d339c 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -171,6 +171,7 @@ + @@ -205,15 +206,20 @@ + + + + + @@ -233,8 +239,12 @@ + + + + @@ -250,19 +260,29 @@ + + + + + + + + + + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 8aa2cf9ad0..090e299712 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -309,6 +309,15 @@ Source Files + + Source Files + + + Source Files + + + Source Files + @@ -566,6 +575,63 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + diff --git a/src/Amount.cpp b/src/Amount.cpp index bedc61b71f..8f22491711 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -16,7 +16,7 @@ SETUP_LOG(); uint64 STAmount::uRateOne = STAmount::getRate(STAmount(1), STAmount(1)); -// --> sCurrency: "", "XNS", or three letter ISO code. +// --> sCurrency: "", "XRP", or three letter ISO code. bool STAmount::currencyFromString(uint160& uDstCurrency, const std::string& sCurrency) { bool bSuccess = true; @@ -1365,19 +1365,19 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test ) if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "60") + if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60") BOOST_FAIL("STAmount multiply fail"); if (STAmount::multiply(STAmount(20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::multiply(STAmount(20), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "60") + if (STAmount::multiply(STAmount(20), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "60") BOOST_FAIL("STAmount multiply fail"); if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20") BOOST_FAIL("STAmount divide fail"); - if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), uint160(), ACCOUNT_XRP).getText() != "20") BOOST_FAIL("STAmount divide fail"); if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20") BOOST_FAIL("STAmount divide fail"); - if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 3), uint160(), ACCOUNT_XNS).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 3), uint160(), ACCOUNT_XRP).getText() != "20") BOOST_FAIL("STAmount divide fail"); STAmount a1(CURRENCY_ONE, ACCOUNT_ONE, 60), a2 (CURRENCY_ONE, ACCOUNT_ONE, 10, -1); diff --git a/src/Application.cpp b/src/Application.cpp index 37f497827a..aa15bc6539 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -42,7 +42,7 @@ Application::Application() : mNetOps(mIOService, &mMasterLedger), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), mSNTPClient(mAuxService), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), - mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService) + mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService), mRPCHandler(&mNetOps) { RAND_bytes(mNonce256.begin(), mNonce256.size()); RAND_bytes(reinterpret_cast(&mNonceST), sizeof(mNonceST)); diff --git a/src/Application.h b/src/Application.h index aeca10973f..da956fae28 100644 --- a/src/Application.h +++ b/src/Application.h @@ -19,7 +19,7 @@ #include "SNTPClient.h" #include "../database/database.h" #include "JobQueue.h" - +#include "RPCHandler.h" class RPCDoor; class PeerDoor; @@ -55,6 +55,7 @@ class Application HashedObjectStore mHashedObjectStore; SNTPClient mSNTPClient; JobQueue mJobQueue; + RPCHandler mRPCHandler; DatabaseCon *mRpcDB, *mTxnDB, *mLedgerDB, *mWalletDB, *mHashNodeDB, *mNetNodeDB; @@ -96,9 +97,12 @@ public: ValidationCollection& getValidations() { return mValidations; } JobQueue& getJobQueue() { return mJobQueue; } SuppressionTable& getSuppression() { return mSuppressions; } + RPCHandler& getRPCHandler() { return mRPCHandler; } + bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); } bool isNew(const uint256& s, uint64 p) { return mSuppressions.addSuppressionPeer(s, p); } + bool isNew(const uint256& s, uint64 p, int& f) { return mSuppressions.addSuppressionPeer(s, p, f); } bool isNewFlag(const uint256& s, int f) { return mSuppressions.setFlag(s, f); } bool running() { return mTxnDB != NULL; } bool getSystemTimeOffset(int& offset) { return mSNTPClient.getOffset(offset); } diff --git a/src/CanonicalTXSet.h b/src/CanonicalTXSet.h index c75fb6b1b1..fead540db7 100644 --- a/src/CanonicalTXSet.h +++ b/src/CanonicalTXSet.h @@ -39,6 +39,13 @@ public: CanonicalTXSet(const uint256& lclHash) : mSetHash(lclHash) { ; } void push_back(SerializedTransaction::ref txn); + + void reset(const uint256& newLCL) + { + mSetHash = newLCL; + mMap.clear(); + } + iterator erase(const iterator& it); iterator begin() { return mMap.begin(); } diff --git a/src/Config.cpp b/src/Config.cpp index 2faed71d30..f69e58446b 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -35,7 +35,7 @@ #define SECTION_VALIDATORS "validators" #define SECTION_VALIDATORS_SITE "validators_site" -// Fees are in XNS. +// Fees are in XRP. #define DEFAULT_FEE_DEFAULT 10 #define DEFAULT_FEE_ACCOUNT_CREATE 1000*SYSTEM_CURRENCY_PARTS #define DEFAULT_FEE_NICKNAME_CREATE 1000 @@ -165,6 +165,8 @@ void Config::setup(const std::string& strConf) void Config::load() { + std::cout << "Loading: " << CONFIG_FILE << std::endl; + std::ifstream ifsConfig(CONFIG_FILE.c_str(), std::ios::in); if (!ifsConfig) diff --git a/src/Config.h b/src/Config.h index 2bc0662e5f..cb17b46ed6 100644 --- a/src/Config.h +++ b/src/Config.h @@ -10,9 +10,9 @@ #include #define SYSTEM_NAME "ripple" -#define SYSTEM_CURRENCY_CODE "XNS" +#define SYSTEM_CURRENCY_CODE "XRP" #define SYSTEM_CURRENCY_PRECISION 6 -#define SYSTEM_CURRENCY_CODE_RIPPLE "XNR" +#define SYSTEM_CURRENCY_CODE_RIPPLE "XRR" #define SYSTEM_CURRENCY_GIFT 1000ull #define SYSTEM_CURRENCY_USERS 100000000ull diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 9fc95a825d..ee992abdb2 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -21,7 +21,6 @@ HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) : bool HashedObjectStore::store(HashedObjectType type, uint32 index, const std::vector& data, const uint256& hash) { // return: false = already in cache, true = added to cache - assert(hash == Serializer::getSHA512Half(data)); if (!theApp->getHashNodeDB()) { cLog(lsTRACE) << "HOS: no db"; @@ -32,6 +31,7 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, cLog(lsTRACE) << "HOS: " << hash << " store: incache"; return false; } + assert(hash == Serializer::getSHA512Half(data)); HashedObject::pointer object = boost::make_shared(type, index, data, hash); if (!mCache.canonicalize(hash, object)) diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index dabbb692d1..6fce0827b6 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -46,7 +46,7 @@ Interpreter::Interpreter() mFunctionTable[CANCEL_OP]=new SubOp(); mFunctionTable[BLOCK_OP]=new SubOp(); mFunctionTable[BLOCK_END_OP]=new SubOp(); - mFunctionTable[SEND_XNS_OP]=new SendXNSOp(); + mFunctionTable[SEND_XRP_OP]=new SendXRPOp(); /* mFunctionTable[SEND_OP]=new SendOp(); mFunctionTable[REMOVE_CONTRACT_OP]=new SubOp(); @@ -63,7 +63,7 @@ Interpreter::Interpreter() mFunctionTable[GET_LEDGER_TIME_OP]=new SubOp(); mFunctionTable[GET_LEDGER_NUM_OP]=new SubOp(); mFunctionTable[GET_RAND_FLOAT_OP]=new SubOp(); - mFunctionTable[GET_XNS_ESCROWED_OP]=new SubOp(); + mFunctionTable[GET_XRP_ESCROWED_OP]=new SubOp(); mFunctionTable[GET_RIPPLE_ESCROWED_OP]=new SubOp(); mFunctionTable[GET_RIPPLE_ESCROWED_CURRENCY_OP]=new SubOp(); mFunctionTable[GET_RIPPLE_ESCROWED_ISSUER]=new GetRippleEscrowedIssuerOp(); diff --git a/src/Interpreter.h b/src/Interpreter.h index ad7d2cb855..5b43017bf5 100644 --- a/src/Interpreter.h +++ b/src/Interpreter.h @@ -39,12 +39,12 @@ public: STOP_OP, CANCEL_OP, BLOCK_OP, BLOCK_END_OP, - SEND_XNS_OP,SEND_OP,REMOVE_CONTRACT_OP,FEE_OP,CHANGE_CONTRACT_OWNER_OP, + SEND_XRP_OP,SEND_OP,REMOVE_CONTRACT_OP,FEE_OP,CHANGE_CONTRACT_OWNER_OP, STOP_REMOVE_OP, SET_DATA_OP,GET_DATA_OP, GET_NUM_DATA_OP, SET_REGISTER_OP,GET_REGISTER_OP, GET_ISSUER_ID_OP, GET_OWNER_ID_OP, GET_LEDGER_TIME_OP, GET_LEDGER_NUM_OP, GET_RAND_FLOAT_OP, - GET_XNS_ESCROWED_OP, GET_RIPPLE_ESCROWED_OP, GET_RIPPLE_ESCROWED_CURRENCY_OP, GET_RIPPLE_ESCROWED_ISSUER, + GET_XRP_ESCROWED_OP, GET_RIPPLE_ESCROWED_OP, GET_RIPPLE_ESCROWED_CURRENCY_OP, GET_RIPPLE_ESCROWED_ISSUER, GET_ACCEPT_DATA_OP, GET_ACCEPTOR_ID_OP, GET_CONTRACT_ID_OP, NUM_OF_OPS }; diff --git a/src/Ledger.cpp b/src/Ledger.cpp index f05e1245a5..632ced3728 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -563,7 +563,7 @@ Json::Value Ledger::getJson(int options) } else ledger["closed"] = false; - if (mTransactionMap && (full || ((options & LEDGER_JSON_DUMP_TXNS) != 0))) + if (mTransactionMap && (full || ((options & LEDGER_JSON_DUMP_TXRP) != 0))) { Json::Value txns(Json::arrayValue); SHAMapTreeNode::TNType type; diff --git a/src/Ledger.h b/src/Ledger.h index c033c59c4d..d89fcbba42 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -35,7 +35,7 @@ enum LedgerStateParms lepERROR = 32, // error }; -#define LEDGER_JSON_DUMP_TXNS 0x10000000 +#define LEDGER_JSON_DUMP_TXRP 0x10000000 #define LEDGER_JSON_DUMP_STATE 0x20000000 #define LEDGER_JSON_FULL 0x40000000 diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index f9d9f1bef9..50497b3cda 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -170,7 +170,7 @@ void LedgerAcquire::addPeers() if (!found) { BOOST_FOREACH(Peer::ref peer, peerList) - peerHas(peer); + peerHas(peer); } } diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 876a14cc6e..48939b5cde 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -35,16 +35,43 @@ void TransactionAcquire::done() { if (mFailed) { - cLog(lsWARNING) << "Failed to acquire TXs " << mHash; + cLog(lsWARNING) << "Failed to acquire TX set " << mHash; theApp->getOPs().mapComplete(mHash, SHAMap::pointer()); } else { + cLog(lsINFO) << "Acquired TX set " << mHash; mMap->setImmutable(); theApp->getOPs().mapComplete(mHash, mMap); } } +void TransactionAcquire::onTimer() +{ + if (!getPeerCount()) + { // out of peers + cLog(lsWARNING) << "Out of peers for TX set " << getHash(); + + bool found = false; + std::vector peerList = theApp->getConnectionPool().getPeerVector(); + BOOST_FOREACH(Peer::ref peer, peerList) + { + if (peer->hasTxSet(getHash())) + { + found = true; + peerHas(peer); + } + } + if (!found) + { + BOOST_FOREACH(Peer::ref peer, peerList) + peerHas(peer); + } + } + else + trigger(Peer::pointer(), true); +} + boost::weak_ptr TransactionAcquire::pmDowncast() { return boost::shared_polymorphic_downcast(shared_from_this()); @@ -59,6 +86,7 @@ void TransactionAcquire::trigger(Peer::ref peer, bool timer) } if (!mHaveRoot) { + cLog(lsTRACE) << "TransactionAcquire::trigger " << (peer ? "havePeer" : "noPeer") << " no root"; ripple::TMGetLedger tmGL; tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_itype(ripple::liTS_CANDIDATE); @@ -98,9 +126,15 @@ bool TransactionAcquire::takeNodes(const std::list& nodeIDs, const std::list< std::vector >& data, Peer::ref peer) { if (mComplete) + { + cLog(lsTRACE) << "TX set complete"; return true; + } if (mFailed) + { + cLog(lsTRACE) << "TX set failed"; return false; + } try { std::list::const_iterator nodeIDit = nodeIDs.begin(); @@ -116,12 +150,18 @@ bool TransactionAcquire::takeNodes(const std::list& nodeIDs, return false; } if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE, NULL)) + { + cLog(lsWARNING) << "TX acquire got bad root node"; return false; + } else mHaveRoot = true; } else if (!mMap->addKnownNode(*nodeIDit, *nodeDatait, &sf)) + { + cLog(lsWARNING) << "TX acquire got bad non-root node"; return false; + } ++nodeIDit; ++nodeDatait; } @@ -323,7 +363,6 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) mProposing = false; mValidating = false; mPeerPositions.clear(); - mPeerData.clear(); mDisputes.clear(); mCloseTimes.clear(); mDeadNodes.clear(); @@ -537,7 +576,7 @@ void LedgerConsensus::closeLedger() mCloseTime = theApp->getOPs().getCloseTimeNC(); theApp->getOPs().setLastCloseTime(mCloseTime); statusChange(ripple::neCLOSING_LEDGER, *mPreviousLedger); - takeInitialPosition(*theApp->getMasterLedger().closeLedger()); + takeInitialPosition(*theApp->getMasterLedger().closeLedger(true)); } void LedgerConsensus::stateEstablish() @@ -842,7 +881,7 @@ void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vec { boost::unordered_map::const_iterator cit = mAcquired.find(pit.second->getCurrentHash()); - if (cit != mAcquired.end() && cit->second) + if ((cit != mAcquired.end()) && cit->second) txn->setVote(pit.first, cit->second->hasItem(txID)); } @@ -927,7 +966,10 @@ bool LedgerConsensus::peerGaveNodes(Peer::ref peer, const uint256& setHash, { boost::unordered_map::iterator acq = mAcquiring.find(setHash); if (acq == mAcquiring.end()) + { + cLog(lsINFO) << "Got TX data for set not acquiring: " << setHash; return false; + } TransactionAcquire::pointer set = acq->second; // We must keep the set around during the function return set->takeNodes(nodeIDs, nodeData, peer); } @@ -956,6 +998,7 @@ void LedgerConsensus::playbackProposals() for (boost::unordered_map< uint160, std::list >::iterator it = storedProposals.begin(), end = storedProposals.end(); it != end; ++it) { + bool relay = false; BOOST_FOREACH(const LedgerProposal::pointer& proposal, it->second) { if (proposal->hasSignature()) @@ -964,11 +1007,33 @@ void LedgerConsensus::playbackProposals() if (proposal->checkSign()) { cLog(lsINFO) << "Applying stored proposal"; - peerPosition(proposal); + relay = peerPosition(proposal); } } else if (proposal->isPrevLedger(mPrevLedgerHash)) - peerPosition(proposal); + relay = peerPosition(proposal); + + if (relay) + { + cLog(lsWARNING) << "We should do delayed relay of this proposal, but we cannot"; + } +#if 0 // FIXME: We can't do delayed relay because we don't have the signature + std::set peers + if (relay && theApp->getSuppression().swapSet(proposal.getSuppress(), set, SF_RELAYED)) + { + cLog(lsDEBUG) << "Stored proposal delayed relay"; + ripple::TMProposeSet set; + set.set_proposeseq + set.set_currenttxhash(, 256 / 8); + previousledger + closetime + nodepubkey + signature + PackedMessage::pointer message = boost::make_shared(set, ripple::mtPROPOSE_LEDGER); + theApp->getConnectionPool().relayMessageBut(peers, message); + } +#endif + } } } @@ -1117,7 +1182,7 @@ void LedgerConsensus::accept(SHAMap::ref set) { Log(lsTRACE) << "newLCL"; Json::Value p; - newLCL->addJson(p, LEDGER_JSON_DUMP_TXNS | LEDGER_JSON_DUMP_STATE); + newLCL->addJson(p, LEDGER_JSON_DUMP_TXRP | LEDGER_JSON_DUMP_STATE); Log(lsTRACE) << p; } diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index 6eae257506..8ac72634a1 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -30,7 +30,7 @@ protected: SHAMap::pointer mMap; bool mHaveRoot; - void onTimer() { trigger(Peer::pointer(), true); } + void onTimer(); void newPeer(Peer::ref peer) { trigger(peer, false); } void done(); diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index 60ee99a1c0..3b3017b744 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -447,9 +447,8 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) it != end; ++it) entryModify(it->second); - cLog(lsTRACE) << "Metadata:" << mSet.getJson(0); - mSet.addRaw(s, result); + cLog(lsTRACE) << "Metadata:" << mSet.getJson(0); } // <-- uNodeDir: For deletion, present to make dirDelete efficient. diff --git a/src/LedgerMaster.cpp b/src/LedgerMaster.cpp index cd477b21cc..6c3a2ebc97 100644 --- a/src/LedgerMaster.cpp +++ b/src/LedgerMaster.cpp @@ -14,10 +14,10 @@ uint32 LedgerMaster::getCurrentLedgerIndex() return mCurrentLedger->getLedgerSeq(); } -bool LedgerMaster::addHeldTransaction(const Transaction::pointer& transaction) +void LedgerMaster::addHeldTransaction(const Transaction::pointer& transaction) { // returns true if transaction was added boost::recursive_mutex::scoped_lock ml(mLock); - return mHeldTransactionsByID.insert(std::make_pair(transaction->getID(), transaction)).second; + mHeldTransactions.push_back(transaction->getSTransaction()); } void LedgerMaster::pushLedger(Ledger::ref newLedger) @@ -79,19 +79,41 @@ void LedgerMaster::storeLedger(Ledger::ref ledger) } -Ledger::pointer LedgerMaster::closeLedger() +Ledger::pointer LedgerMaster::closeLedger(bool recover) { boost::recursive_mutex::scoped_lock sl(mLock); Ledger::pointer closingLedger = mCurrentLedger; + + if (recover) + { + int recovers = 0; + for (CanonicalTXSet::iterator it = mHeldTransactions.begin(), end = mHeldTransactions.end(); it != end; ++it) + { + try + { + TER result = mEngine.applyTransaction(*it->second, tapOPEN_LEDGER); + if (isTepSuccess(result)) + ++recovers; + } + catch (...) + { + cLog(lsWARNING) << "Held transaction throws"; + } + } + tLog(recovers != 0, lsINFO) << "Recovered " << recovers << " held transactions"; + mHeldTransactions.reset(closingLedger->getHash()); + } + mCurrentLedger = boost::make_shared(boost::ref(*closingLedger), true); mEngine.setLedger(mCurrentLedger); + return closingLedger; } TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params) { TER result = mEngine.applyTransaction(txn, params); - theApp->getOPs().pubTransaction(mEngine.getLedger(), txn, result); + theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result); return result; } diff --git a/src/LedgerMaster.h b/src/LedgerMaster.h index e3270710e5..152952b50b 100644 --- a/src/LedgerMaster.h +++ b/src/LedgerMaster.h @@ -9,6 +9,7 @@ #include "Transaction.h" #include "TransactionEngine.h" #include "RangeSet.h" +#include "CanonicalTXSet.h" // Tracks the current ledger and any ledgers in the process of closing // Tracks ledger history @@ -25,7 +26,7 @@ class LedgerMaster LedgerHistory mLedgerHistory; - std::map mHeldTransactionsByID; + CanonicalTXSet mHeldTransactions; RangeSet mCompleteLedgers; LedgerAcquire::pointer mMissingLedger; @@ -40,7 +41,7 @@ class LedgerMaster public: - LedgerMaster() : mMissingSeq(0) { ; } + LedgerMaster() : mHeldTransactions(uint256()), mMissingSeq(0) { ; } uint32 getCurrentLedgerIndex(); @@ -64,7 +65,7 @@ public: std::string getCompleteLedgers() { return mCompleteLedgers.toString(); } - Ledger::pointer closeLedger(); + Ledger::pointer closeLedger(bool recoverHeldTransactions); Ledger::pointer getLedgerBySeq(uint32 index) { @@ -90,7 +91,7 @@ public: void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); } - bool addHeldTransaction(const Transaction::pointer& trans); + void addHeldTransaction(const Transaction::pointer& trans); void sweep(void) { mLedgerHistory.sweep(); } }; diff --git a/src/LedgerProposal.cpp b/src/LedgerProposal.cpp index 75e286e9e0..5f5f058735 100644 --- a/src/LedgerProposal.cpp +++ b/src/LedgerProposal.cpp @@ -10,8 +10,9 @@ DECLARE_INSTANCE(LedgerProposal); LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, uint32 closeTime, - const RippleAddress& naPeerPublic) : - mPreviousLedger(pLgr), mCurrentHash(tx), mCloseTime(closeTime), mProposeSeq(seq), mPublicKey(naPeerPublic) + const RippleAddress& naPeerPublic, const uint256& suppression) : + mPreviousLedger(pLgr), mCurrentHash(tx), mSuppression(suppression), mCloseTime(closeTime), + mProposeSeq(seq), mPublicKey(naPeerPublic) { // XXX Validate key. // if (!mKey->SetPubKey(pubKey)) @@ -26,7 +27,7 @@ LedgerProposal::LedgerProposal(const RippleAddress& naPub, const RippleAddress& const uint256& prevLgr, const uint256& position, uint32 closeTime) : mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0), mPublicKey(naPub), mPrivateKey(naPriv) -{ // OPTIMIZEME: This is expensive. We create both the public and private keys separately each time +{ mPeerID = mPublicKey.getNodeID(); mTime = boost::posix_time::second_clock::universal_time(); } diff --git a/src/LedgerProposal.h b/src/LedgerProposal.h index a531de214c..b5176c7c90 100644 --- a/src/LedgerProposal.h +++ b/src/LedgerProposal.h @@ -18,7 +18,7 @@ class LedgerProposal : private IS_INSTANCE(LedgerProposal) { protected: - uint256 mPreviousLedger, mCurrentHash; + uint256 mPreviousLedger, mCurrentHash, mSuppression; uint32 mCloseTime, mProposeSeq; uint160 mPeerID; @@ -35,7 +35,7 @@ public: // proposal from peer LedgerProposal(const uint256& prevLgr, uint32 proposeSeq, const uint256& propose, - uint32 closeTime, const RippleAddress& naPeerPublic); + uint32 closeTime, const RippleAddress& naPeerPublic, const uint256& suppress); // our first proposal LedgerProposal(const RippleAddress& pubKey, const RippleAddress& privKey, @@ -52,6 +52,7 @@ public: const uint160& getPeerID() const { return mPeerID; } const uint256& getCurrentHash() const { return mCurrentHash; } const uint256& getPrevLedger() const { return mPreviousLedger; } + const uint256& getSuppression() const { return mSuppression; } uint32 getProposeSeq() const { return mProposeSeq; } uint32 getCloseTime() const { return mCloseTime; } const RippleAddress& peekPublic() const { return mPublicKey; } diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index ce52fde7b3..21ce906666 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -115,19 +115,36 @@ Transaction::pointer NetworkOPs::submitTransaction(const Transaction::pointer& t Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans) { Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true); - if (dbtx) return dbtx; + if (dbtx) + return dbtx; - if (!trans->checkSign()) - { - cLog(lsINFO) << "Transaction has bad signature"; + int newFlags = theApp->getSuppression().getFlags(trans->getID()); + if ((newFlags & SF_BAD) != 0) + { // cached bad trans->setStatus(INVALID); return trans; } - TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER); + if ((newFlags & SF_SIGGOOD) == 0) + { // signature not checked + if (!trans->checkSign()) + { + cLog(lsINFO) << "Transaction has bad signature"; + trans->setStatus(INVALID); + theApp->isNewFlag(trans->getID(), SF_BAD); + return trans; + } + theApp->isNewFlag(trans->getID(), SF_SIGGOOD); + } + TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN); trans->setResult(r); + if (isTemMalformed(r)) // malformed, cache bad + theApp->isNewFlag(trans->getID(), SF_BAD); + else if(isTelLocal(r) || isTerRetry(r)) // can be retried + theApp->isNewFlag(trans->getID(), SF_RETRY); + #ifdef DEBUG if (r != tesSUCCESS) { @@ -139,9 +156,9 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans) if (r == tefFAILURE) throw Fault(IO_ERROR); - if (r == terPRE_SEQ) + if (isTerRetry(r)) { // transaction should be held - cLog(lsDEBUG) << "Transaction should be held"; + cLog(lsDEBUG) << "Transaction should be held: " << r; trans->setStatus(HELD); theApp->getMasterTransaction().canonicalize(trans, true); mLedgerMaster->addHeldTransaction(trans); @@ -154,12 +171,24 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans) return trans; } + bool relay = true; + if (r == tesSUCCESS) { - cLog(lsINFO) << "Transaction is now included"; + cLog(lsINFO) << "Transaction is now included in open ledger"; trans->setStatus(INCLUDED); theApp->getMasterTransaction().canonicalize(trans, true); + } + else + { + cLog(lsDEBUG) << "Status other than success " << r; + if (mMode == omFULL) + relay = false; + trans->setStatus(INVALID); + } + if (relay) + { std::set peers; if (theApp->getSuppression().swapSet(trans->getID(), peers, SF_RELAYED)) { @@ -168,32 +197,13 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans) trans->getSTransaction()->add(s); tx.set_rawtransaction(&s.getData().front(), s.getLength()); tx.set_status(ripple::tsCURRENT); - tx.set_receivetimestamp(getNetworkTimeNC()); + tx.set_receivetimestamp(getNetworkTimeNC()); // FIXME: This should be when we received it PackedMessage::pointer packet = boost::make_shared(tx, ripple::mtTRANSACTION); theApp->getConnectionPool().relayMessageBut(peers, packet); } - - return trans; } - cLog(lsDEBUG) << "Status other than success " << r; - std::set peers; - - if ((mMode != omFULL) && (mMode != omTRACKING) && - theApp->getSuppression().swapSet(trans->getID(), peers, SF_RELAYED)) - { - ripple::TMTransaction tx; - Serializer s; - trans->getSTransaction()->add(s); - tx.set_rawtransaction(&s.getData().front(), s.getLength()); - tx.set_status(ripple::tsCURRENT); - tx.set_receivetimestamp(getNetworkTimeNC()); - PackedMessage::pointer packet = boost::make_shared(tx, ripple::mtTRANSACTION); - theApp->getConnectionPool().relayMessageTo(peers, packet); - } - - trans->setStatus(INVALID); return trans; } @@ -694,60 +704,55 @@ bool NetworkOPs::haveConsensusObject() return mConsensus; } -// <-- bool: true to relay -bool NetworkOPs::recvPropose(const uint256& suppression, uint32 proposeSeq, const uint256& proposeHash, - const uint256& prevLedger, uint32 closeTime, const std::string& signature, - const RippleAddress& nodePublic) +uint256 NetworkOPs::getConsensusLCL() { - // JED: does mConsensus need to be locked? + if (!haveConsensusObject()) + return uint256(); + return mConsensus->getLCL(); +} - // XXX Validate key. - // XXX Take a vuc for pubkey. +void NetworkOPs::processTrustedProposal(LedgerProposal::pointer proposal, + boost::shared_ptr set, RippleAddress nodePublic, uint256 checkLedger, bool sigGood) +{ + bool relay = true; if (!haveConsensusObject()) { cLog(lsINFO) << "Received proposal outside consensus window"; - return mMode != omFULL; + if (mMode == omFULL) + relay = false; } - - if (mConsensus->isOurPubKey(nodePublic)) + else { - cLog(lsTRACE) << "Received our own validation"; - return false; - } + storeProposal(proposal, nodePublic); - // Is this node on our UNL? - if (!theApp->getUNL().nodeInUNL(nodePublic)) - { - cLog(lsINFO) << "Untrusted proposal: " << nodePublic.humanNodePublic() << " " << proposeHash; - return true; - } + uint256 consensusLCL = mConsensus->getLCL(); - if (prevLedger.isNonZero()) - { // proposal includes a previous ledger - LedgerProposal::pointer proposal = - boost::make_shared(prevLedger, proposeSeq, proposeHash, closeTime, nodePublic); - if (!proposal->checkSign(signature)) + if (!set->has_previousledger() && (checkLedger != consensusLCL)) { - cLog(lsWARNING) << "New-style ledger proposal fails signature check"; - return false; + cLog(lsWARNING) << "Have to re-check proposal signature due to consensus view change"; + assert(proposal->hasSignature()); + proposal->setPrevLedger(consensusLCL); + if (proposal->checkSign()) + sigGood = true; + } + + if (sigGood && (consensusLCL == proposal->getPrevLedger())) + { + relay = mConsensus->peerPosition(proposal); + cLog(lsTRACE) << "Proposal processing finished, relay=" << relay; } - if (prevLedger == mConsensus->getLCL()) - return mConsensus->peerPosition(proposal); - storeProposal(proposal, nodePublic); - return false; } - LedgerProposal::pointer proposal = - boost::make_shared(mConsensus->getLCL(), proposeSeq, proposeHash, closeTime, nodePublic); - if (!proposal->checkSign(signature)) - { // Note that if the LCL is different, the signature check will fail - cLog(lsWARNING) << "Ledger proposal fails signature check"; - proposal->setSignature(signature); - storeProposal(proposal, nodePublic); - return false; + if (relay) + { + std::set peers; + theApp->getSuppression().swapSet(proposal->getSuppression(), peers, SF_RELAYED); + PackedMessage::pointer message = boost::make_shared(*set, ripple::mtPROPOSE_LEDGER); + theApp->getConnectionPool().relayMessageBut(peers, message); } - return mConsensus->peerPosition(proposal); + else + cLog(lsINFO) << "Not relaying trusted proposal"; } SHAMap::pointer NetworkOPs::getTXMap(const uint256& hash) @@ -761,7 +766,10 @@ bool NetworkOPs::gotTXData(const boost::shared_ptr& peer, const uint256& h const std::list& nodeIDs, const std::list< std::vector >& nodeData) { if (!haveConsensusObject()) + { + cLog(lsWARNING) << "Got TX data with no consensus object"; return false; + } return mConsensus->peerGaveNodes(peer, hash, nodeIDs, nodeData); } @@ -777,7 +785,7 @@ bool NetworkOPs::hasTXSet(const boost::shared_ptr& peer, const uint256& se void NetworkOPs::mapComplete(const uint256& hash, SHAMap::ref map) { - if (!haveConsensusObject()) + if (haveConsensusObject()) mConsensus->mapComplete(hash, map, true); } @@ -919,30 +927,25 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri return jvObj; } -void NetworkOPs::pubAccountInfo(const RippleAddress& naAccountID, const Json::Value& jvObj) +void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) { - boost::interprocess::sharable_lock sl(mMonitorLock); - - subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); - - if (simIterator == mSubAccountInfo.end()) + Json::Value jvObj = transJson(stTxn, terResult, false, lpCurrent, "transaction"); + { - // Address not found do nothing. - nothing(); - } - else - { - // Found it. - BOOST_FOREACH(InfoSub* ispListener, simIterator->second) + boost::interprocess::sharable_lock sl(mMonitorLock); + BOOST_FOREACH(InfoSub* ispListener, mSubRTTransactions) { ispListener->send(jvObj); } } + + pubAccountTransaction(lpCurrent,stTxn,terResult,false); } void NetworkOPs::pubLedger(Ledger::ref lpAccepted) { // Don't publish to clients ledgers we don't trust. + // TODO: we need to publish old transactions when we get reconnected to the network otherwise clients can miss transactions if (NetworkOPs::omDISCONNECTED == getOperatingMode()) return; @@ -964,39 +967,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) } } } - + { - boost::interprocess::sharable_lock sl(mMonitorLock); - if (!mSubAccountTransaction.empty()) - { - Json::Value jvAccounts(Json::arrayValue); - - BOOST_FOREACH(const RippleAddress& naAccountID, getLedgerAffectedAccounts(lpAccepted->getLedgerSeq())) - { - jvAccounts.append(Json::Value(naAccountID.humanAccountID())); - } - - Json::Value jvObj(Json::objectValue); - - jvObj["type"] = "ledgerClosedAccounts"; - jvObj["ledger_closed_index"] = lpAccepted->getLedgerSeq(); - jvObj["ledger_closed"] = lpAccepted->getHash().ToString(); - jvObj["ledger_closed_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC())); - jvObj["accounts"] = jvAccounts; - - BOOST_FOREACH(InfoSub* ispListener, mSubLedgerAccounts) - { - ispListener->send(jvObj); - } - } - } - - { - boost::interprocess::sharable_lock sl(mMonitorLock); - bool bAll = !mSubTransaction.empty(); - bool bAccounts = !mSubAccountTransaction.empty(); - - if (bAll || bAccounts) + // we don't lock since pubAcceptedTransaction is locking + if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty() || !mSubmitMap.empty() ) { SHAMap& txSet = *lpAccepted->peekTransactionMap(); @@ -1007,36 +981,13 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) // XXX Need to give failures too. TER terResult = tesSUCCESS; - if (bAll) - { - pubTransactionAll(lpAccepted, *stTxn, terResult, true); - } - - if (bAccounts) - { - pubTransactionAccounts(lpAccepted, *stTxn, terResult, true); - } + pubAcceptedTransaction(lpAccepted, *stTxn, terResult); } + // TODO: remove old entries from the submit map } } - // Publish bootstrap information for accounts. - { - boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(const subInfoMapType::iterator::value_type& it, mBootAccountInfo) - { - Json::Value jvObj = pubBootstrapAccountInfo(lpAccepted, RippleAddress::createAccountID(it.first)); - - BOOST_FOREACH(InfoSub* ispListener, it.second) - { - ispListener->send(jvObj); - } - } - mBootAccountInfo.clear(); - } - - // XXX Publish delta information for accounts. } Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType) @@ -1065,103 +1016,100 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes return jvObj; } -void NetworkOPs::pubTransactionAll(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted) +void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) { - Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "transaction"); + Json::Value jvObj = transJson(stTxn, terResult, true, lpCurrent, "transaction"); - BOOST_FOREACH(InfoSub* ispListener, mSubTransaction) { - ispListener->send(jvObj); + boost::interprocess::sharable_lock sl(mMonitorLock); + BOOST_FOREACH(InfoSub* ispListener, mSubTransactions) + { + ispListener->send(jvObj); + } + + BOOST_FOREACH(InfoSub* ispListener, mSubRTTransactions) + { + ispListener->send(jvObj); + } } + + pubAccountTransaction(lpCurrent,stTxn,terResult,true); } -void NetworkOPs::pubTransactionAccounts(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted) + +// TODO: tell the mSubmitMap people +void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted) { - boost::unordered_set usisNotify; + boost::unordered_set notify; { boost::interprocess::sharable_lock sl(mMonitorLock); - if (!mSubAccountTransaction.empty()) + if(!bAccepted && mSubRTAccount.empty()) return; + + if (!mSubAccount.empty() || (!mSubRTAccount.empty()) ) { BOOST_FOREACH(const RippleAddress& naAccountPublic, stTxn.getAffectedAccounts()) { - subInfoMapIterator simiIt = mSubAccountTransaction.find(naAccountPublic.getAccountID()); + subInfoMapIterator simiIt = mSubRTAccount.find(naAccountPublic.getAccountID()); - if (simiIt != mSubAccountTransaction.end()) + if (simiIt != mSubRTAccount.end()) { BOOST_FOREACH(InfoSub* ispListener, simiIt->second) { - usisNotify.insert(ispListener); + notify.insert(ispListener); + } + } + if(bAccepted) + { + simiIt = mSubAccount.find(naAccountPublic.getAccountID()); + + if (simiIt != mSubAccount.end()) + { + BOOST_FOREACH(InfoSub* ispListener, simiIt->second) + { + notify.insert(ispListener); + } } } } } } - if (!usisNotify.empty()) + if (!notify.empty()) { Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "account"); - BOOST_FOREACH(InfoSub* ispListener, usisNotify) + BOOST_FOREACH(InfoSub* ispListener, notify) { ispListener->send(jvObj); } } } -void NetworkOPs::pubTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) -{ - boost::interprocess::sharable_lock sl(mMonitorLock); - - if (!mSubTransaction.empty()) - { - pubTransactionAll(lpCurrent, stTxn, terResult, false); - } - - if (!mSubAccountTransaction.empty()) - { - pubTransactionAccounts(lpCurrent, stTxn, terResult, false); - } -} - // // Monitoring // -void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) + + +void NetworkOPs::subAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt) { + subInfoMapType& subMap=mSubAccount; + if(rt) subMap=mSubRTAccount; + boost::interprocess::scoped_lock sl(mMonitorLock); BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { - // Register for bootstrap info. - subInfoMapType::iterator simIterator; - - simIterator = mBootAccountInfo.find(naAccountID.getAccountID()); - if (simIterator == mBootAccountInfo.end()) + subInfoMapType::iterator simIterator = subMap.find(naAccountID.getAccountID()); + if (simIterator == subMap.end()) { // Not found boost::unordered_set usisElement; usisElement.insert(ispListener); - mBootAccountInfo.insert(simIterator, make_pair(naAccountID.getAccountID(), usisElement)); - } - else - { - // Found - simIterator->second.insert(ispListener); - } - - // Register for messages. - simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); - if (simIterator == mSubAccountInfo.end()) - { - // Not found - boost::unordered_set usisElement; - - usisElement.insert(ispListener); - mSubAccountInfo.insert(simIterator, make_pair(naAccountID.getAccountID(), usisElement)); + mSubAccount.insert(simIterator, make_pair(naAccountID.getAccountID(), usisElement)); } else { @@ -1171,14 +1119,16 @@ void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set } } -void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) +void NetworkOPs::unsubAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt) { + subInfoMapType& subMap= rt ? mSubRTAccount : mSubAccount; + boost::interprocess::scoped_lock sl(mMonitorLock); BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { - subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); - if (simIterator == mSubAccountInfo.end()) + subInfoMapType::iterator simIterator = subMap.find(naAccountID.getAccountID()); + if (simIterator == mSubAccount.end()) { // Not found. Done. nothing(); @@ -1191,56 +1141,7 @@ void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_s if (simIterator->second.empty()) { // Don't need hash entry. - mSubAccountInfo.erase(simIterator); - } - } - } -} - -void NetworkOPs::subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) -{ - boost::interprocess::scoped_lock sl(mMonitorLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) - { - subInfoMapType::iterator simIterator = mSubAccountTransaction.find(naAccountID.getAccountID()); - if (simIterator == mSubAccountTransaction.end()) - { - // Not found - boost::unordered_set usisElement; - - usisElement.insert(ispListener); - mSubAccountTransaction.insert(simIterator, make_pair(naAccountID.getAccountID(), usisElement)); - } - else - { - // Found - simIterator->second.insert(ispListener); - } - } -} - -void NetworkOPs::unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) -{ - boost::interprocess::scoped_lock sl(mMonitorLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) - { - subInfoMapType::iterator simIterator = mSubAccountTransaction.find(naAccountID.getAccountID()); - if (simIterator == mSubAccountTransaction.end()) - { - // Not found. Done. - nothing(); - } - else - { - // Found - simIterator->second.erase(ispListener); - - if (simIterator->second.empty()) - { - // Don't need hash entry. - mSubAccountTransaction.erase(simIterator); + subMap.erase(simIterator); } } } @@ -1292,27 +1193,39 @@ bool NetworkOPs::unsubLedger(InfoSub* ispListener) } // <-- bool: true=added, false=already there -bool NetworkOPs::subLedgerAccounts(InfoSub* ispListener) +bool NetworkOPs::subServer(InfoSub* ispListener) { - return mSubLedgerAccounts.insert(ispListener).second; + return mSubServer.insert(ispListener).second; } // <-- bool: true=erased, false=was not there -bool NetworkOPs::unsubLedgerAccounts(InfoSub* ispListener) +bool NetworkOPs::unsubServer(InfoSub* ispListener) { - return !!mSubLedgerAccounts.erase(ispListener); + return !!mSubServer.erase(ispListener); } // <-- bool: true=added, false=already there -bool NetworkOPs::subTransaction(InfoSub* ispListener) +bool NetworkOPs::subTransactions(InfoSub* ispListener) { - return mSubTransaction.insert(ispListener).second; + return mSubTransactions.insert(ispListener).second; } // <-- bool: true=erased, false=was not there -bool NetworkOPs::unsubTransaction(InfoSub* ispListener) +bool NetworkOPs::unsubTransactions(InfoSub* ispListener) { - return !!mSubTransaction.erase(ispListener); + return !!mSubTransactions.erase(ispListener); +} + +// <-- bool: true=added, false=already there +bool NetworkOPs::subRTTransactions(InfoSub* ispListener) +{ + return mSubTransactions.insert(ispListener).second; +} + +// <-- bool: true=erased, false=was not there +bool NetworkOPs::unsubRTTransactions(InfoSub* ispListener) +{ + return !!mSubTransactions.erase(ispListener); } // vim:ts=4 diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index cd5243b262..b14b06e09a 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -51,6 +51,8 @@ protected: typedef boost::unordered_map >::value_type subInfoMapValue; typedef boost::unordered_map >::iterator subInfoMapIterator; + typedef boost::unordered_map > subSubmitMapType; + OperatingMode mMode; bool mNeedNetworkLedger; boost::posix_time::ptime mConnectTime; @@ -72,24 +74,26 @@ protected: // XXX Split into more locks. boost::interprocess::interprocess_upgradable_mutex mMonitorLock; - subInfoMapType mBootAccountInfo; - subInfoMapType mSubAccountInfo; - subInfoMapType mSubAccountTransaction; - boost::unordered_set mSubLedger; // ledger accepteds - boost::unordered_set mSubLedgerAccounts; // ledger accepteds + affected accounts - boost::unordered_set mSubTransaction; // all transactions - boost::unordered_set mSubTxMeta; // all transaction meta -// subInfoMapType mSubTransactionAccounts; + subInfoMapType mSubAccount; + subInfoMapType mSubRTAccount; + subSubmitMapType mSubmitMap; + + boost::unordered_set mSubLedger; // accepted ledgers + boost::unordered_set mSubServer; // when server changes connectivity state + boost::unordered_set mSubTransactions; // all accepted transactions + boost::unordered_set mSubRTTransactions; // all proposed and accepted transactions + void setMode(OperatingMode); Json::Value transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType); - void pubTransactionAll(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted); - void pubTransactionAccounts(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted); bool haveConsensusObject(); Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID); + void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult); + void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted); + public: NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster); @@ -168,8 +172,8 @@ public: const std::vector& myNode, std::list< std::vector >& newNodes); // ledger proposal/close functions - bool recvPropose(const uint256& suppression, uint32 proposeSeq, const uint256& proposeHash, - const uint256& prevLedger, uint32 closeTime, const std::string& signature, const RippleAddress& nodePublic); + void processTrustedProposal(LedgerProposal::pointer proposal, boost::shared_ptr set, + RippleAddress nodePublic, uint256 checkLedger, bool sigGood); bool gotTXData(const boost::shared_ptr& peer, const uint256& hash, const std::list& nodeIDs, const std::list< std::vector >& nodeData); bool recvValidation(const SerializedValidation::pointer& val); @@ -199,6 +203,7 @@ public: boost::unordered_map >& peekStoredProposals() { return mStoredProposals; } void storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic); + uint256 getConsensusLCL(); // client information retrieval functions std::vector< std::pair > @@ -209,33 +214,27 @@ public: // // Monitoring: publisher side // - - void pubAccountInfo(const RippleAddress& naAccountID, const Json::Value& jvObj); void pubLedger(Ledger::ref lpAccepted); - void pubTransaction(Ledger::ref lpLedger, const SerializedTransaction& stTxn, TER terResult); + void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult); + // // Monitoring: subscriber side // - - // --> vnaAddress: empty = all - void subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - - void subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - - // void subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash); - // void unsubAccountChanges(InfoSub* ispListener); + void subAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt); + void unsubAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt); bool subLedger(InfoSub* ispListener); bool unsubLedger(InfoSub* ispListener); - bool subLedgerAccounts(InfoSub* ispListener); - bool unsubLedgerAccounts(InfoSub* ispListener); + bool subServer(InfoSub* ispListener); + bool unsubServer(InfoSub* ispListener); - bool subTransaction(InfoSub* ispListener); - bool unsubTransaction(InfoSub* ispListener); + bool subTransactions(InfoSub* ispListener); + bool unsubTransactions(InfoSub* ispListener); + + bool subRTTransactions(InfoSub* ispListener); + bool unsubRTTransactions(InfoSub* ispListener); }; #endif diff --git a/src/Operation.h b/src/Operation.h index b2946f2945..5c0c6b8d1a 100644 --- a/src/Operation.h +++ b/src/Operation.h @@ -276,7 +276,7 @@ public: } }; -class SendXNSOp : public Operation +class SendXRPOp : public Operation { public: bool work(Interpreter* interpreter) diff --git a/src/OrderBookDB.cpp b/src/OrderBookDB.cpp index 4f49f01338..ca06acd9b5 100644 --- a/src/OrderBookDB.cpp +++ b/src/OrderBookDB.cpp @@ -20,8 +20,8 @@ OrderBookDB::OrderBookDB(Ledger::pointer ledger) mKnownMap[book->getBookBase()]=true; if(!book->getCurrencyIn()) - { // XNS - mXNSOrders.push_back(book); + { // XRP + mXRPOrders.push_back(book); }else { mIssuerMap[book->getIssuerIn()].push_back(book); diff --git a/src/OrderBookDB.h b/src/OrderBookDB.h index c8e598d186..206898dadc 100644 --- a/src/OrderBookDB.h +++ b/src/OrderBookDB.h @@ -9,7 +9,7 @@ But for now it is probably faster to just generate it each time class OrderBookDB { std::vector mEmptyVector; - std::vector mXNSOrders; + std::vector mXRPOrders; std::map > mIssuerMap; std::map mKnownMap; @@ -17,8 +17,8 @@ class OrderBookDB public: OrderBookDB(Ledger::pointer ledger); - // return list of all orderbooks that want XNS - std::vector& getXNSInBooks(){ return mXNSOrders; } + // return list of all orderbooks that want XRP + std::vector& getXRPInBooks(){ return mXRPOrders; } // return list of all orderbooks that want IssuerID std::vector& getBooks(const uint160& issuerID); // return list of all orderbooks that want this issuerID and currencyID diff --git a/src/Pathfinder.cpp b/src/Pathfinder.cpp index 407d2f894f..318d763719 100644 --- a/src/Pathfinder.cpp +++ b/src/Pathfinder.cpp @@ -21,18 +21,18 @@ TODO: what is a good way to come up with multiple paths? OrderDB: - getXNSOffers(); + getXRPOffers(); - // return list of all orderbooks that want XNS + // return list of all orderbooks that want XRP // return list of all orderbooks that want IssuerID // return list of all orderbooks that want this issuerID and currencyID */ /* -Test sending to XNS -Test XNS to XNS +Test sending to XRP +Test XRP to XRP Test offer in middle -Test XNS to USD +Test XRP to USD Test USD to EUR */ @@ -113,7 +113,7 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet // found the destination if (!ele.mCurrencyID) { - BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXNSInBooks()) + BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks()) { //if (!path.hasSeen(line->getAccountIDPeer().getAccountID())) { @@ -188,8 +188,8 @@ bool Pathfinder::checkComplete(STPathSet& retPathSet) // get all the options from this accountID -// if source is XNS -// every offer that wants XNS +// if source is XRP +// every offer that wants XRP // else // every ripple line that starts with the source currency // every offer that we can take that wants the source currency @@ -197,8 +197,8 @@ bool Pathfinder::checkComplete(STPathSet& retPathSet) void Pathfinder::addOptions(PathOption::pointer tail) { if(!tail->mCurrencyID) - { // source XNS - BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXNSInBooks()) + { // source XRP + BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXRPInBooks()) { PathOption::pointer pathOption(new PathOption(tail)); diff --git a/src/Peer.cpp b/src/Peer.cpp index c9c6df5dbf..5e4b121288 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -500,8 +500,8 @@ void Peer::processReadBuffer() case ripple::mtPROPOSE_LEDGER: { - ripple::TMProposeSet msg; - if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) + boost::shared_ptr msg = boost::make_shared(); + if (msg->ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvPropose(msg); else cLog(lsWARNING) << "parse error: " << type; @@ -694,6 +694,42 @@ void Peer::recvHello(ripple::TMHello& packet) } } +static void checkTransaction(Job&, int flags, SerializedTransaction::pointer stx, boost::weak_ptr peer) +{ + +#ifndef TRUST_NETWORK + try + { +#endif + Transaction::pointer tx; + + if ((flags & SF_SIGGOOD) != 0) + { + tx = boost::make_shared(stx, true); + if (tx->getStatus() == INVALID) + { + theApp->getSuppression().setFlag(stx->getTransactionID(), SF_BAD); + Peer::punishPeer(peer, PP_BAD_SIGNATURE); + return; + } + else + theApp->getSuppression().setFlag(stx->getTransactionID(), SF_SIGGOOD); + } + else + tx = boost::make_shared(stx, false); + + theApp->getIOService().post(boost::bind(&NetworkOPs::processTransaction, &theApp->getOPs(), tx)); + +#ifndef TRUST_NETWORK + } + catch (...) + { + theApp->getSuppression().setFlags(stx->getTransactionID(), SF_BAD); + punishPeer(peer, PP_INVALID_REQUEST); + } +#endif +} + void Peer::recvTransaction(ripple::TMTransaction& packet) { cLog(lsDEBUG) << "Got transaction from peer"; @@ -708,12 +744,22 @@ void Peer::recvTransaction(ripple::TMTransaction& packet) SerializerIterator sit(s); SerializedTransaction::pointer stx = boost::make_shared(boost::ref(sit)); - if (!theApp->isNew(stx->getTransactionID(), mPeerId)) - return; + int flags; + if (!theApp->isNew(stx->getTransactionID(), mPeerId, flags)) + { // we have seen this transaction recently + if ((flags & SF_BAD) != 0) + { + punishPeer(PP_BAD_SIGNATURE); + return; + } + + if ((flags & SF_RETRY) == 0) + return; + } + + theApp->getJobQueue().addJob(jtTRANSACTION, + boost::bind(&checkTransaction, _1, flags, stx, boost::weak_ptr(shared_from_this()))); - tx = boost::make_shared(stx, true); - if (tx->getStatus() == INVALID) - throw(0); #ifndef TRUST_NETWORK } catch (...) @@ -727,52 +773,122 @@ void Peer::recvTransaction(ripple::TMTransaction& packet) } #endif - tx = theApp->getOPs().processTransaction(tx); - - if(tx->getStatus() != INCLUDED) - { // transaction wasn't accepted into ledger -#ifdef DEBUG - std::cerr << "Transaction from peer won't go in ledger" << std::endl; -#endif - } } -void Peer::recvPropose(ripple::TMProposeSet& packet) +static void checkPropose(Job& job, boost::shared_ptr packet, + LedgerProposal::pointer proposal, uint256 consensusLCL, RippleAddress nodePublic, boost::weak_ptr peer) { - if ((packet.currenttxhash().size() != 32) || (packet.nodepubkey().size() < 28) || - (packet.signature().size() < 56) || (packet.nodepubkey().size() > 128) || (packet.signature().size() > 128)) + bool sigGood = false; + bool isTrusted = (job.getType() == jtPROPOSAL_t); + + cLog(lsTRACE) << "Checking " << (isTrusted ? "trusted" : "UNtrusted") << " proposal"; + + assert(packet); + ripple::TMProposeSet& set = *packet; + + uint256 prevLedger; + if (set.has_previousledger()) + { // proposal includes a previous ledger + cLog(lsTRACE) << "proposal with previous ledger"; + memcpy(prevLedger.begin(), set.previousledger().data(), 256 / 8); + if (!proposal->checkSign(set.signature())) + { + cLog(lsWARNING) << "proposal with previous ledger fails signature check"; + Peer::punishPeer(peer, PP_BAD_SIGNATURE); + return; + } + else + sigGood = true; + } + else + { + if (consensusLCL.isNonZero() && proposal->checkSign(set.signature())) + { + prevLedger = consensusLCL; + sigGood = true; + } + else + { + cLog(lsWARNING) << "Ledger proposal fails signature check"; + proposal->setSignature(set.signature()); + } + } + + if (isTrusted) + { + theApp->getIOService().post(boost::bind(&NetworkOPs::processTrustedProposal, &theApp->getOPs(), + proposal, packet, nodePublic, prevLedger, sigGood)); + } + else if (sigGood && (prevLedger == consensusLCL)) + { // relay untrusted proposal + cLog(lsTRACE) << "relaying untrusted proposal"; + std::set peers; + theApp->getSuppression().swapSet(proposal->getSuppression(), peers, SF_RELAYED); + PackedMessage::pointer message = boost::make_shared(set, ripple::mtPROPOSE_LEDGER); + theApp->getConnectionPool().relayMessageBut(peers, message); + } + else + cLog(lsDEBUG) << "Not relaying untrusted proposal"; +} + +void Peer::recvPropose(const boost::shared_ptr& packet) +{ + assert(packet); + ripple::TMProposeSet& set = *packet; + + if ((set.currenttxhash().size() != 32) || (set.nodepubkey().size() < 28) || + (set.signature().size() < 56) || (set.nodepubkey().size() > 128) || (set.signature().size() > 128)) { cLog(lsWARNING) << "Received proposal is malformed"; + punishPeer(PP_INVALID_REQUEST); return; } - uint256 currentTxHash, prevLedger; - memcpy(currentTxHash.begin(), packet.currenttxhash().data(), 32); + if (set.has_previousledger() && (set.previousledger().size() != 32)) + { + cLog(lsWARNING) << "Received proposal is malformed"; + punishPeer(PP_INVALID_REQUEST); + return; + } - if ((packet.has_previousledger()) && (packet.previousledger().size() == 32)) - memcpy(prevLedger.begin(), packet.previousledger().data(), 32); + uint256 proposeHash, prevLedger; + memcpy(proposeHash.begin(), set.currenttxhash().data(), 32); + if (set.has_previousledger()) + memcpy(prevLedger.begin(), set.previousledger().data(), 32); Serializer s(512); - s.add256(currentTxHash); - s.add256(prevLedger); - s.add32(packet.proposeseq()); - s.add32(packet.closetime()); - s.addVL(packet.nodepubkey()); - s.addVL(packet.signature()); + s.add256(proposeHash); + s.add32(set.proposeseq()); + s.add32(set.closetime()); + s.addVL(set.nodepubkey()); + s.addVL(set.signature()); + if (set.has_previousledger()) + s.add256(prevLedger); uint256 suppression = s.getSHA512Half(); if (!theApp->isNew(suppression, mPeerId)) + { + cLog(lsTRACE) << "Received duplicate proposal from peer " << mPeerId; return; - - RippleAddress nodePublic = RippleAddress::createNodePublic(strCopy(packet.nodepubkey())); -// bool isTrusted = theApp->getUNL().nodeInUNL(nodePublic); - - if(theApp->getOPs().recvPropose(suppression, packet.proposeseq(), currentTxHash, prevLedger, packet.closetime(), - packet.signature(), nodePublic)) - { // FIXME: Not all nodes will want proposals - PackedMessage::pointer message = boost::make_shared(packet, ripple::mtPROPOSE_LEDGER); - theApp->getConnectionPool().relayMessage(this, message); } + + RippleAddress signerPublic = RippleAddress::createNodePublic(strCopy(set.nodepubkey())); + if (signerPublic == theConfig.VALIDATION_PUB) + { + cLog(lsTRACE) << "Received our own proposal from peer " << mPeerId; + return; + } + bool isTrusted = theApp->getUNL().nodeInUNL(signerPublic); + cLog(lsTRACE) << "Received " << (isTrusted ? "trusted" : "UNtrusted") << " proposal from " << mPeerId; + + uint256 consensusLCL = theApp->getOPs().getConsensusLCL(); + LedgerProposal::pointer proposal = boost::make_shared( + prevLedger.isNonZero() ? prevLedger : consensusLCL, + set.proposeseq(), proposeHash, set.closetime(), signerPublic, suppression); + + theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, + boost::bind(&checkPropose, _1, packet, proposal, consensusLCL, + mNodePublic, boost::weak_ptr(shared_from_this()))); } void Peer::recvHaveTxSet(ripple::TMHaveTransactionSet& packet) @@ -1043,11 +1159,12 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) reply.set_requestcookie(packet.requestcookie()); if (packet.itype() == ripple::liTS_CANDIDATE) - { // Request is for a transaction candidate set + { // Request is for a transaction candidate set cLog(lsINFO) << "Received request for TX candidate set data " << getIP(); if ((!packet.has_ledgerhash() || packet.ledgerhash().size() != 32)) { punishPeer(PP_INVALID_REQUEST); + cLog(lsWARNING) << "invalid request"; return; } uint256 txHash; @@ -1171,6 +1288,7 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) SHAMapNode mn(packet.nodeids(i).data(), packet.nodeids(i).size()); if(!mn.isValid()) { + cLog(lsWARNING) << "Request for invalid node"; punishPeer(PP_INVALID_REQUEST); return; } @@ -1178,6 +1296,8 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) std::list< std::vector > rawNodes; if(map->getNodeFat(mn, nodeIDs, rawNodes, fatRoot, fatLeaves)) { + assert(nodeIDs.size() == rawNodes.size()); + cLog(lsDEBUG) << "getNodeFat got " << rawNodes.size() << " nodes"; std::vector::iterator nodeIDIterator; std::list< std::vector >::iterator rawNodeIterator; for(nodeIDIterator = nodeIDs.begin(), rawNodeIterator = rawNodes.begin(); @@ -1190,6 +1310,8 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) node->set_nodedata(&rawNodeIterator->front(), rawNodeIterator->size()); } } + else + cLog(lsWARNING) << "getNodeFat returns false"; } PackedMessage::pointer oPacket = boost::make_shared(reply, ripple::mtLEDGER_DATA); sendPacket(oPacket); @@ -1199,7 +1321,7 @@ void Peer::recvLedger(ripple::TMLedgerData& packet) { if (packet.nodes().size() <= 0) { - cLog(lsWARNING) << "Ledger data with no nodes"; + cLog(lsWARNING) << "Ledger/TXset data with no nodes"; punishPeer(PP_INVALID_REQUEST); return; } @@ -1209,6 +1331,7 @@ void Peer::recvLedger(ripple::TMLedgerData& packet) uint256 hash; if(packet.ledgerhash().size() != 32) { + cLog(lsWARNING) << "TX candidate reply with invalid hash size"; punishPeer(PP_INVALID_REQUEST); return; } @@ -1368,7 +1491,7 @@ Json::Value Peer::getJson() if (mHello.has_protoversion() && (mHello.protoversion() != MAKE_VERSION_INT(PROTO_VERSION_MAJOR, PROTO_VERSION_MINOR))) - ret["protocol"] = boost::lexical_cast(GET_VERSION_MAJOR(mHello.protoversion())) + "." + + ret["protocol"] = boost::lexical_cast(GET_VERSION_MAJOR(mHello.protoversion())) + "." + boost::lexical_cast(GET_VERSION_MINOR(mHello.protoversion())); if (!!mClosedLedgerHash) diff --git a/src/Peer.h b/src/Peer.h index 256319c831..e70d702fb0 100644 --- a/src/Peer.h +++ b/src/Peer.h @@ -19,6 +19,7 @@ enum PeerPunish PP_INVALID_REQUEST = 1, // The peer sent a request that makes no sense PP_UNKNOWN_REQUEST = 2, // The peer sent a request that might be garbage PP_UNWANTED_DATA = 3, // The peer sent us data we didn't want/need + PP_BAD_SIGNATURE = 4, // Object had bad signature }; typedef std::pair ipPort; @@ -116,7 +117,7 @@ protected: void recvGetLedger(ripple::TMGetLedger& packet); void recvLedger(ripple::TMLedgerData& packet); void recvStatus(ripple::TMStatusChange& packet); - void recvPropose(ripple::TMProposeSet& packet); + void recvPropose(const boost::shared_ptr& packet); void recvHaveTxSet(ripple::TMHaveTransactionSet& packet); void getSessionCookie(std::string& strDst); diff --git a/src/RPCDoor.cpp b/src/RPCDoor.cpp index 5fb00a3ffc..9d98a24624 100644 --- a/src/RPCDoor.cpp +++ b/src/RPCDoor.cpp @@ -14,6 +14,10 @@ RPCDoor::RPCDoor(boost::asio::io_service& io_service) : Log(lsINFO) << "RPC port: " << theConfig.RPC_IP << " " << theConfig.RPC_PORT << " allow remote: " << theConfig.RPC_ALLOW_REMOTE; startListening(); } +RPCDoor::~RPCDoor() +{ + Log(lsINFO) << "RPC port: " << theConfig.RPC_IP << " " << theConfig.RPC_PORT << " allow remote: " << theConfig.RPC_ALLOW_REMOTE; +} void RPCDoor::startListening() { diff --git a/src/RPCDoor.h b/src/RPCDoor.h index f91f864855..ab60539c67 100644 --- a/src/RPCDoor.h +++ b/src/RPCDoor.h @@ -15,4 +15,5 @@ class RPCDoor bool isClientAllowed(const std::string& ip); public: RPCDoor(boost::asio::io_service& io_service); + ~RPCDoor(); }; diff --git a/src/RPCHandler.cpp b/src/RPCHandler.cpp index a609e3567a..fe98e650cd 100644 --- a/src/RPCHandler.cpp +++ b/src/RPCHandler.cpp @@ -248,7 +248,7 @@ Json::Value RPCHandler::authorize(const uint256& uLedger, if (saSrcBalance < saFee) { - cLog(lsINFO) << "authorize: Insufficent funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); + cLog(lsINFO) << "authorize: Insufficient funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); return rpcError(rpcINSUF_FUNDS); } @@ -326,125 +326,7 @@ Json::Value RPCHandler::doAcceptLedger(const Json::Value ¶ms) return obj; } -// account_domain_set [] -Json::Value RPCHandler::doAccountDomainSet(const Json::Value ¶ms) -{ - RippleAddress naSrcAccountID; - RippleAddress naSeed; - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - 0, - 0, - RippleAddress(), - true, - strCopy(params[2u].asString()), - false, - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return Json::Value(Json::objectValue); -} - -// account_email_set [] -Json::Value RPCHandler::doAccountEmailSet(const Json::Value ¶ms) -{ - RippleAddress naSrcAccountID; - RippleAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - // Hash as per: http://en.gravatar.com/site/implement/hash/ - std::string strEmail = 3 == params.size() ? params[2u].asString() : ""; - boost::trim(strEmail); - boost::to_lower(strEmail); - - std::vector vucMD5(128/8, 0); - MD5(reinterpret_cast(strEmail.data()), strEmail.size(), &vucMD5.front()); - - uint128 uEmailHash(vucMD5); - std::vector vucDomain; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - true, - strEmail.empty() ? uint128() : uEmailHash, - false, - uint256(), - 0, - RippleAddress(), - false, - vucDomain, - false, - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - if (!strEmail.empty()) - { - obj["Email"] = strEmail; - obj["EmailHash"] = strHex(vucMD5); - obj["UrlGravatar"] = AccountState::createGravatarUrl(uEmailHash); - } - - return obj; -} // account_info || // account_info || [] @@ -498,177 +380,6 @@ Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) -// account_message_set -Json::Value RPCHandler::doAccountMessageSet(const Json::Value& params) { - RippleAddress naSrcAccountID; - RippleAddress naSeed; - RippleAddress naMessagePubKey; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!naMessagePubKey.setAccountPublic(params[2u].asString())) - { - return rpcError(rpcPUBLIC_MALFORMED); - } - - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - std::vector vucDomain; - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - uint256(), - 0, - naMessagePubKey, - false, - vucDomain, - false, - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["MessageKey"] = naMessagePubKey.humanAccountPublic(); - - return obj; -} - -// account_rate_set -Json::Value RPCHandler::doAccountRateSet(const Json::Value ¶ms) -{ - RippleAddress naSrcAccountID; - RippleAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - uint32 uRate = lexical_cast_s(params[2u].asString()); - std::vector vucDomain; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - 0, - 0, - RippleAddress(), - false, - vucDomain, - true, - uRate); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return Json::Value(Json::objectValue); -} - -// account_wallet_set [] -Json::Value RPCHandler::doAccountWalletSet(const Json::Value& params) { - RippleAddress naSrcAccountID; - RippleAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - std::vector vucDomain; - - if (!obj.empty()) - return obj; - - std::string strWalletLocator = params.size() == 3 ? params[2u].asString() : ""; - uint256 uWalletLocator; - uint32 uWalletSize = 0; // XXX Broken should be an argument. - - if (!strWalletLocator.empty()) - uWalletLocator.SetHex(strWalletLocator); - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - true, - uWalletLocator, - uWalletSize, - RippleAddress(), - false, - vucDomain, - false, - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - if (!strWalletLocator.empty()) - obj["WalletLocator"] = uWalletLocator.GetHex(); - - return obj; -} Json::Value RPCHandler::doConnect(const Json::Value& params) { @@ -782,192 +493,6 @@ Json::Value RPCHandler::doNicknameInfo(const Json::Value& params) return ret; } -// nickname_set [] [] -Json::Value RPCHandler::doNicknameSet(const Json::Value& params) -{ - RippleAddress naSrcAccountID; - RippleAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - STAmount saMinimumOffer; - bool bSetOffer = params.size() >= 4; - std::string strOfferCurrency; - std::string strNickname = params[2u].asString(); - boost::trim(strNickname); - - if (strNickname.empty()) - { - return rpcError(rpcNICKNAME_MALFORMED); - } - else if (params.size() >= 4 && !saMinimumOffer.setFullValue(params[3u].asString(), strOfferCurrency)) - { - return rpcError(rpcDST_AMT_MALFORMED); - } - - STAmount saFee; - NicknameState::pointer nsSrc = mNetOps->getNicknameState(uint256(0), strNickname); - - if (!nsSrc) - { - // Creating nickname. - saFee = theConfig.FEE_NICKNAME_CREATE; - } - else if (naSrcAccountID != nsSrc->getAccountID()) - { - // We don't own the nickname. - return rpcError(rpcNICKNAME_PERM); - } - else - { - // Setting the minimum offer. - saFee = theConfig.FEE_DEFAULT; - } - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could verify nickname does not exist or points to paying account. - // XXX Adjust fee for nickname create. - - Transaction::pointer trans = Transaction::sharedNicknameSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - Ledger::getNicknameHash(strNickname), - bSetOffer, - saMinimumOffer); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// offer_create [passive] -// *offering* for *wants* -Json::Value RPCHandler::doOfferCreate(const Json::Value ¶ms) -{ - RippleAddress naSeed; - RippleAddress naSrcAccountID; - STAmount saTakerPays; - STAmount saTakerGets; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!saTakerGets.setFullValue(params[2u].asString(), params[3u].asString(), params[4u].asString())) - { - return rpcError(rpcGETS_AMT_MALFORMED); - } - else if (!saTakerPays.setFullValue(params[5u].asString(), params[6u].asString(), params[7u].asString())) - { - return rpcError(rpcPAYS_AMT_MALFORMED); - } - else if (params.size() == 10 && params[9u].asString() != "passive") - { - return rpcError(rpcINVALID_PARAMS); - } - - uint32 uExpiration = lexical_cast_s(params[8u].asString()); - bool bPassive = params.size() == 10; - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedOfferCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - bPassive, - saTakerPays, - saTakerGets, - uExpiration); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// offer_cancel -Json::Value RPCHandler::doOfferCancel(const Json::Value ¶ms) -{ - RippleAddress naSeed; - RippleAddress naSrcAccountID; - uint32 uSequence = lexical_cast_s(params[2u].asString()); - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedOfferCancel( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - uSequence); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} // owner_info || // owner_info || [] @@ -993,150 +518,7 @@ Json::Value RPCHandler::doOwnerInfo(const Json::Value& params) return ret; } -// password_fund [] -// YYY Make making account default to first account for seed. -Json::Value RPCHandler::doPasswordFund(const Json::Value ¶ms) -{ - RippleAddress naSrcAccountID; - RippleAddress naDstAccountID; - RippleAddress naSeed; - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[params.size() == 3 ? 2u : 1u].asString())) - { - return rpcError(rpcDST_ACT_MALFORMED); - } - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could verify dst exists and isn't already funded. - - Transaction::pointer trans = Transaction::sharedPasswordFund( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - naDstAccountID); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// password_set [] -Json::Value RPCHandler::doPasswordSet(const Json::Value& params) -{ - RippleAddress naMasterSeed; - RippleAddress naRegularSeed; - RippleAddress naAccountID; - - if (!naMasterSeed.setSeedGeneric(params[0u].asString())) - { - // Should also not allow account id's as seeds. - return rpcError(rpcBAD_SEED); - } - else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) - { - // Should also not allow account id's as seeds. - return rpcError(rpcBAD_SEED); - } - // YYY Might use account from string to be more flexible. - else if (params.size() >= 3 && !naAccountID.setAccountID(params[2u].asString())) - { - return rpcError(rpcACT_MALFORMED); - } - else - { - RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); - RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); - RippleAddress naRegular0Public; - RippleAddress naRegular0Private; - - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - - naAccountPublic.setAccountPublic(naMasterGenerator, 0); - naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); - - naRegular0Public.setAccountPublic(naRegularGenerator, 0); - naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); - - // Hash of regular account #0 public key. - // uint160 uGeneratorID = naRegular0Public.getAccountID(); - std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); - std::vector vucGeneratorSig; - - // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. - // XXX Check result. - naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); - - RippleAddress naMasterXPublic; - RippleAddress naRegularXPublic; - unsigned int iIndex = -1; // Compensate for initial increment. - int iMax = theConfig.ACCOUNT_PROBE_MAX; - - // YYY Could probe periodically to see if accounts exists. - // YYY Max could be set randomly. - // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are - // related. - do { - ++iIndex; - naMasterXPublic.setAccountPublic(naMasterGenerator, iIndex); - naRegularXPublic.setAccountPublic(naRegularGenerator, iIndex); - - std::cerr << iIndex << ": " << naRegularXPublic.humanAccountID() << std::endl; - - } while (naAccountID.getAccountID() != naMasterXPublic.getAccountID() && --iMax); - - if (!iMax) - { - return rpcError(rpcACT_NOT_FOUND); - } - - Transaction::pointer trans = Transaction::sharedPasswordSet( - naAccountPublic, naAccountPrivate, - 0, - naRegularXPublic, - vucGeneratorCipher, - naRegular0Public.getAccountPublic(), - vucGeneratorSig); - - trans = mNetOps->submitTransaction(trans); - - Json::Value obj(Json::objectValue); - - // We "echo" the seeds so they can be checked. - obj["master_seed"] = naMasterSeed.humanSeed(); - obj["master_key"] = naMasterSeed.humanSeed1751(); - obj["regular_seed"] = naRegularSeed.humanSeed(); - obj["regular_key"] = naRegularSeed.humanSeed1751(); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; - } -} Json::Value RPCHandler::doPeers(const Json::Value& params) { @@ -1236,310 +618,6 @@ Json::Value RPCHandler::doProfile(const Json::Value ¶ms) return obj; } -// ripple -// [] -// + -// full|partial limit|average [] -// -// path: -// path + -// -// path_element: -// account [] [] -// offer [] -Json::Value RPCHandler::doRipple(const Json::Value ¶ms) -{ - RippleAddress naSeed; - STAmount saSrcAmountMax; - uint160 uSrcCurrencyID; - RippleAddress naSrcAccountID; - RippleAddress naSrcIssuerID; - bool bPartial; - bool bFull; - bool bLimit; - bool bAverage; - RippleAddress naDstAccountID; - STAmount saDstAmount; - uint160 uDstCurrencyID; - - STPathSet spsPaths; - - naSrcIssuerID.setAccountID(params[4u].asString()); // - - if (!naSeed.setSeedGeneric(params[0u].asString())) // - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) // - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - // [] - else if (!saSrcAmountMax.setFullValue(params[2u].asString(), params[3u].asString(), params[naSrcIssuerID.isValid() ? 4u : 1u].asString())) - { - // Log(lsINFO) << "naSrcIssuerID.isValid(): " << naSrcIssuerID.isValid(); - // Log(lsINFO) << "source_max: " << params[2u].asString(); - // Log(lsINFO) << "source_currency: " << params[3u].asString(); - // Log(lsINFO) << "source_issuer: " << params[naSrcIssuerID.isValid() ? 4u : 2u].asString(); - - return rpcError(rpcSRC_AMT_MALFORMED); - } - - int iArg = 4 + naSrcIssuerID.isValid(); - - // XXX bSrcRedeem & bSrcIssue not used. - STPath spPath; - - while (params.size() != iArg && params[iArg].asString() == "path") // path - { - Log(lsINFO) << "Path>"; - ++iArg; - - while (params.size() != iArg - && (params[iArg].asString() == "offer" || params[iArg].asString() == "account")) - { - if (params.size() >= iArg + 3 && params[iArg].asString() == "offer") // offer - { - Log(lsINFO) << "Offer>"; - uint160 uCurrencyID; - RippleAddress naIssuerID; - - ++iArg; - - if (!STAmount::currencyFromString(uCurrencyID, params[iArg++].asString())) // - { - return rpcError(rpcINVALID_PARAMS); - } - else if (naIssuerID.setAccountID(params[iArg].asString())) // [] - { - ++iArg; - } - - spPath.addElement(STPathElement( - uint160(0), - uCurrencyID, - naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); - } - else if (params.size() >= iArg + 2 && params[iArg].asString() == "account") // account - { - Log(lsINFO) << "Account>"; - RippleAddress naAccountID; - uint160 uCurrencyID; - RippleAddress naIssuerID; - - ++iArg; - - if (!naAccountID.setAccountID(params[iArg++].asString())) // - { - return rpcError(rpcINVALID_PARAMS); - } - - if (params.size() != iArg && STAmount::currencyFromString(uCurrencyID, params[iArg].asString())) // [] - { - ++iArg; - } - - if (params.size() != iArg && naIssuerID.setAccountID(params[iArg].asString())) // [] - { - ++iArg; - } - - spPath.addElement(STPathElement( - naAccountID.getAccountID(), - uCurrencyID, - naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); - } - else - { - return rpcError(rpcINVALID_PARAMS); - } - } - - if (spPath.isEmpty()) - { - return rpcError(rpcINVALID_PARAMS); - } - else - { - spsPaths.addPath(spPath); - spPath.clear(); - } - } - - // full|partial - bPartial = params.size() != iArg ? params[iArg].asString() == "partial" : false; - bFull = params.size() != iArg ? params[iArg].asString() == "full" : false; - - if (!bPartial && !bFull) - { - return rpcError(rpcINVALID_PARAMS); - } - else - { - ++iArg; - } - - // limit|average - bLimit = params.size() != iArg ? params[iArg].asString() == "limit" : false; - bAverage = params.size() != iArg ? params[iArg].asString() == "average" : false; - - if (!bLimit && !bAverage) - { - return rpcError(rpcINVALID_PARAMS); - } - else - { - ++iArg; - } - - if (params.size() != iArg && !naDstAccountID.setAccountID(params[iArg++].asString())) // - { - return rpcError(rpcDST_ACT_MALFORMED); - } - - const unsigned int uDstIssuer = params.size() == iArg + 3 ? iArg+2 : iArg-1; - - // - if (params.size() != iArg + 2 && params.size() != iArg + 3) - { - // Log(lsINFO) << "params.size(): " << params.size(); - - return rpcError(rpcDST_AMT_MALFORMED); - } - else if (!saDstAmount.setFullValue(params[iArg].asString(), params[iArg+1].asString(), params[uDstIssuer].asString())) - { - // Log(lsINFO) << " Amount: " << params[iArg].asString(); - // Log(lsINFO) << "Currency: " << params[iArg+1].asString(); - // Log(lsINFO) << " Issuer: " << params[uDstIssuer].asString(); - - return rpcError(rpcDST_AMT_MALFORMED); - } - - AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); - STAmount saFee = theConfig.FEE_DEFAULT; - - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could do some checking: source has funds or credit, dst exists and has sufficent credit limit. - // YYY Currency from same source or loops not allowed. - // YYY Limit paths length and count. - if (!asDst) - { - Log(lsINFO) << "naDstAccountID: " << naDstAccountID.humanAccountID(); - - return rpcError(rpcDST_ACT_MISSING); - } - - Transaction::pointer trans = Transaction::sharedPayment( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount, - saSrcAmountMax, - spsPaths, - bPartial, - bLimit); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["fee"] = saFee.getText(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - obj["srcAmountMax"] = saSrcAmountMax.getText(); - obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); - obj["dstAmount"] = saDstAmount.getText(); - obj["dstISO"] = saDstAmount.getHumanCurrency(); - obj["paths"] = spsPaths.getText(); - - return obj; -} - -// ripple_line_set [] [] [] -Json::Value RPCHandler::doRippleLineSet(const Json::Value& params) -{ - RippleAddress naSeed; - RippleAddress naSrcAccountID; - RippleAddress naDstAccountID; - STAmount saLimitAmount; - bool bQualityIn = params.size() >= 6; - bool bQualityOut = params.size() >= 7; - uint32 uQualityIn = 0; - uint32 uQualityOut = 0; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return rpcError(rpcDST_ACT_MALFORMED); - } - else if (!saLimitAmount.setFullValue(params[3u].asString(), params.size() >= 5 ? params[4u].asString() : "", params[2u].asString())) - { - return rpcError(rpcSRC_AMT_MALFORMED); - } - else if (bQualityIn && !parseQuality(params[5u].asString(), uQualityIn)) - { - return rpcError(rpcQUALITY_MALFORMED); - } - else if (bQualityOut && !parseQuality(params[6u].asString(), uQualityOut)) - { - return rpcError(rpcQUALITY_MALFORMED); - } - else - { - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedCreditSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - saLimitAmount, - bQualityIn, uQualityIn, - bQualityOut, uQualityOut); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - - return obj; - } -} - // ripple_lines_get || [] Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) { @@ -1609,162 +687,251 @@ Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) } // submit any transaction to the network +// submit private_key json Json::Value RPCHandler::doSubmit(const Json::Value& params) { - // TODO + Json::Value txJSON; + Json::Reader reader; + if(reader.parse(params[1u].asString(),txJSON)) + { + return handleJSONSubmit(params[0u].asString(), txJSON ); + } + return rpcError(rpcSRC_ACT_MALFORMED); } -// send regular_seed paying_account account_id amount [currency] [issuer] [send_max] [send_currency] [send_issuer] -Json::Value RPCHandler::doSend(const Json::Value& params) + +Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) { + Json::Value jvResult; RippleAddress naSeed; - RippleAddress naSrcAccountID; - RippleAddress naDstAccountID; - STAmount saSrcAmountMax; - STAmount saDstAmount; - std::string sSrcCurrency; - std::string sDstCurrency; - std::string sSrcIssuer; - std::string sDstIssuer; + RippleAddress srcAddress; - if (params.size() >= 5) - sDstCurrency = params[4u].asString(); - - if (params.size() >= 6) - sDstIssuer = params[5u].asString(); - - if (params.size() >= 8) - sSrcCurrency = params[7u].asString(); - - if (params.size() >= 9) - sSrcIssuer = params[8u].asString(); - - if (!naSeed.setSeedGeneric(params[0u].asString())) + if (!naSeed.setSeedGeneric(key)) { return rpcError(rpcBAD_SEED); } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) + if (!txJSON.isMember("Account")) + { + return rpcError(rpcSRC_ACT_MISSING); + } + if (!srcAddress.setAccountID(txJSON["Account"].asString())) { return rpcError(rpcSRC_ACT_MALFORMED); } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return rpcError(rpcDST_ACT_MALFORMED); - } - else if (!saDstAmount.setFullValue(params[3u].asString(), sDstCurrency, sDstIssuer)) - { - return rpcError(rpcDST_AMT_MALFORMED); - } - else if (params.size() >= 7 && !saSrcAmountMax.setFullValue(params[6u].asString(), sSrcCurrency, sSrcIssuer)) - { - return rpcError(rpcSRC_AMT_MALFORMED); - } - else - { - AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); - bool bCreate = !asDst; - STAmount saFee = bCreate ? theConfig.FEE_ACCOUNT_CREATE : theConfig.FEE_DEFAULT; - RippleAddress naVerifyGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naVerifyGenerator); + AccountState::pointer asSrc = mNetOps->getAccountState(uint256(0), srcAddress); - // Log(lsINFO) << boost::str(boost::format("doSend: sSrcIssuer=%s sDstIssuer=%s saSrcAmountMax=%s saDstAmount=%s") - // % sSrcIssuer - // % sDstIssuer - // % saSrcAmountMax.getFullText() - // % saDstAmount.getFullText()); + if( txJSON["type"]=="Payment") + { + txJSON["TransactionType"]=0; - if (!obj.empty()) - return obj; - - if (params.size() < 7) - saSrcAmountMax = saDstAmount; - - // Do a few simple checks. - if (!saSrcAmountMax.isNative()) + RippleAddress dstAccountID; + if (!dstAccountID.setAccountID(txJSON["Destination"].asString())) { - Log(lsINFO) << "doSend: Ripple"; - - nothing(); + return rpcError(rpcDST_ACT_MALFORMED); } - else if (!saSrcBalance.isPositive()) + + if(!txJSON.isMember("Fee")) { - // No native currency to send. - Log(lsINFO) << "doSend: No native currency to send: " << saSrcBalance.getText(); - - return rpcError(rpcINSUF_FUNDS); + if(mNetOps->getAccountState(uint256(0), dstAccountID)) + txJSON["Fee"]=(int)theConfig.FEE_DEFAULT; + else txJSON["Fee"]=(int)theConfig.FEE_ACCOUNT_CREATE; } - else if (saDstAmount.isNative() && saSrcAmountMax < saDstAmount) + if(!txJSON.isMember("Paths")) { - // Not enough native currency. + if(txJSON["Amount"].isObject() || txJSON.isMember("SendMax") ) + { // we need a ripple path + STPathSet spsPaths; + uint160 srcCurrencyID; + if(txJSON.isMember("SendMax") && txJSON["SendMax"].isMember("currency")) + { + STAmount::currencyFromString(srcCurrencyID, "XRP"); + }else STAmount::currencyFromString(srcCurrencyID, txJSON["SendMax"]["currency"].asString()); - Log(lsINFO) << "doSend: Insufficient funds: src=" << saSrcAmountMax.getText() << " dst=" << saDstAmount.getText(); + STAmount dstAmount; + if(txJSON["Amount"].isObject()) + { + std::string issuerStr; + if( txJSON["Amount"].isMember("issuer")) issuerStr=txJSON["Amount"]["issuer"].asString(); + if( !txJSON["Amount"].isMember("value") || !txJSON["Amount"].isMember("currency")) return rpcError(rpcDST_AMT_MALFORMED); + if (!dstAmount.setFullValue(txJSON["Amount"]["value"].asString(), txJSON["Amount"]["currency"].asString(), issuerStr)) + { + return rpcError(rpcDST_AMT_MALFORMED); + } + }else if (!dstAmount.setFullValue(txJSON["Amount"].asString())) + { + return rpcError(rpcDST_AMT_MALFORMED); + } - return rpcError(rpcINSUF_FUNDS); - } - // XXX Don't allow send to self of same currency. - - Transaction::pointer trans; - if (asDst) { - // Destination exists, ordinary send. - - STPathSet spsPaths; - uint160 srcCurrencyID; - - if (!saSrcAmountMax.isNative() || !saDstAmount.isNative()) - { - STAmount::currencyFromString(srcCurrencyID, sSrcCurrency); - Pathfinder pf(naSrcAccountID, naDstAccountID, srcCurrencyID, saDstAmount); + Pathfinder pf(srcAddress, dstAccountID, srcCurrencyID, dstAmount); pf.findPaths(5, 1, spsPaths); + txJSON["Paths"]=spsPaths.getJson(0); + if(txJSON.isMember("Flags")) txJSON["Flags"]=txJSON["Flags"].asUInt() | 2; + else txJSON["Flags"]=2; } - - trans = Transaction::sharedPayment( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount, - saSrcAmountMax, - spsPaths); } - else - { - // Create destination and send. - - trans = Transaction::sharedCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount); // Initial funds in XNS. - } - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["fee"] = saFee.getText(); - obj["create"] = bCreate; - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - obj["srcAmountMax"] = saSrcAmountMax.getText(); - obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); - obj["dstAmount"] = saDstAmount.getText(); - obj["dstISO"] = saDstAmount.getHumanCurrency(); - - return obj; + + }else if( txJSON["type"]=="OfferCreate" ) + { + txJSON["TransactionType"]=7; + if(!txJSON.isMember("Fee")) txJSON["Fee"]=(int)theConfig.FEE_DEFAULT; + }else if( txJSON["type"]=="TrustSet") + { + txJSON["TransactionType"]=20; + if(!txJSON.isMember("Fee")) txJSON["Fee"]=(int)theConfig.FEE_DEFAULT; + }else if( txJSON["type"]=="OfferCancel") + { + txJSON["TransactionType"]=8; + if(!txJSON.isMember("Fee")) txJSON["Fee"]=(int)theConfig.FEE_DEFAULT; } + + txJSON.removeMember("type"); + + if(!txJSON.isMember("Sequence")) txJSON["Sequence"]=asSrc->getSeq(); + if(!txJSON.isMember("Flags")) txJSON["Flags"]=0; + + Ledger::pointer lpCurrent = mNetOps->getCurrentLedger(); + SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(srcAddress.getAccountID())); + + if (!sleAccountRoot) + { + // XXX Ignore transactions for accounts not created. + return rpcError(rpcSRC_ACT_MISSING); + } + + bool bHaveAuthKey = false; + RippleAddress naAuthorizedPublic; + + + RippleAddress naSecret = RippleAddress::createSeedGeneric(key); + RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret); + + // Find the index of Account from the master generator, so we can generate the public and private keys. + RippleAddress naMasterAccountPublic; + unsigned int iIndex = 0; + bool bFound = false; + + // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are + // related. + while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX) + { + naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); + + Log(lsWARNING) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << srcAddress.humanAccountID(); + + bFound = srcAddress.getAccountID() == naMasterAccountPublic.getAccountID(); + if (!bFound) + ++iIndex; + } + + if (!bFound) + { + return rpcError(rpcSRC_ACT_MISSING); + } + + // Use the generator to determine the associated public and private keys. + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret); + RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex); + RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex); + + if (bHaveAuthKey + // The generated pair must match authorized... + && naAuthorizedPublic.getAccountID() != naAccountPublic.getAccountID() + // ... or the master key must have been used. + && srcAddress.getAccountID() != naAccountPublic.getAccountID()) + { + // std::cerr << "iIndex: " << iIndex << std::endl; + // std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl; + // std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl; + + return rpcError(rpcSRC_ACT_MISSING); + } + + std::auto_ptr sopTrans; + + try + { + sopTrans = STObject::parseJson(txJSON); + } + catch (std::exception& e) + { + jvResult["error"] = "malformedTransaction"; + jvResult["error_exception"] = e.what(); + return(jvResult); + } + + sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic()); + + SerializedTransaction::pointer stpTrans; + + try + { + stpTrans = boost::make_shared(*sopTrans); + } + catch (std::exception& e) + { + jvResult["error"] = "invalidTransaction"; + jvResult["error_exception"] = e.what(); + return jvResult; + } + + stpTrans->sign(naAccountPrivate); + + Transaction::pointer tpTrans; + + try + { + tpTrans = boost::make_shared(stpTrans, false); + } + catch (std::exception& e) + { + jvResult["error"] = "internalTransaction"; + jvResult["error_exception"] = e.what(); + return(jvResult); + } + + try + { + tpTrans = mNetOps->submitTransaction(tpTrans); + + if (!tpTrans) { + jvResult["error"] = "invalidTransaction"; + jvResult["error_exception"] = "Unable to sterilize transaction."; + return(jvResult); + } + } + catch (std::exception& e) + { + jvResult["error"] = "internalSubmit"; + jvResult["error_exception"] = e.what(); + return(jvResult); + } + + try + { + jvResult["transaction"] = tpTrans->getJson(0); + + if (temUNCERTAIN != tpTrans->getResult()) + { + std::string sToken; + std::string sHuman; + + transResultInfo(tpTrans->getResult(), sToken, sHuman); + + jvResult["engine_result"] = sToken; + jvResult["engine_result_code"] = tpTrans->getResult(); + jvResult["engine_result_message"] = sHuman; + } + return(jvResult); + } + catch (std::exception& e) + { + jvResult["error"] = "internalJson"; + jvResult["error_exception"] = e.what(); + return(jvResult); + } + } Json::Value RPCHandler::doServerInfo(const Json::Value& params) @@ -1835,6 +1002,24 @@ Json::Value RPCHandler::doTx(const Json::Value& params) return rpcError(rpcNOT_IMPL); } +Json::Value RPCHandler::doLedgerClosed(const Json::Value& params) +{ + Json::Value jvResult; + uint256 uLedger = mNetOps->getClosedLedger(); + + jvResult["ledger_closed_index"] = mNetOps->getLedgerID(uLedger); + jvResult["ledger_closed"] = uLedger.ToString(); + //jvResult["ledger_closed_time"] = uLedger. + return jvResult; +} + +Json::Value RPCHandler::doLedgerCurrent(const Json::Value& params) +{ + Json::Value jvResult; + jvResult["ledger_current_index"] = mNetOps->getCurrentLedgerID(); + return jvResult; +} + // ledger [id|current|lastclosed] [full] Json::Value RPCHandler::doLedger(const Json::Value& params) { @@ -2088,255 +1273,6 @@ Json::Value RPCHandler::doWalletAccounts(const Json::Value& params) } } -// wallet_add [] [] -Json::Value RPCHandler::doWalletAdd(const Json::Value& params) -{ - RippleAddress naMasterSeed; - RippleAddress naRegularSeed; - RippleAddress naSrcAccountID; - STAmount saAmount; - std::string sDstCurrency; - - if (!naRegularSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!naMasterSeed.setSeedGeneric(params[2u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (params.size() >= 4 && !saAmount.setFullValue(params[3u].asString(), sDstCurrency)) - { - return rpcError(rpcDST_AMT_MALFORMED); - } - else - { - RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); - RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); - - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naRegularSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - if (saSrcBalance < saAmount) - { - return rpcError(rpcINSUF_FUNDS); - } - else - { - RippleAddress naNewAccountPublic; - RippleAddress naNewAccountPrivate; - RippleAddress naAuthKeyID; - uint160 uAuthKeyID; - AccountState::pointer asNew; - std::vector vucSignature; - bool bAgain = true; - int iIndex = -1; - - // Find an unmade account. - do { - ++iIndex; - naNewAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - - asNew = mNetOps->getAccountState(uint256(0), naNewAccountPublic); - if (!asNew) - bAgain = false; - } while (bAgain); - - // XXX Have a maximum number of accounts per wallet? - - // Determine corrisponding master private key. - naNewAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, iIndex); - - // Determine new accounts authorized regular key. - naAuthKeyID.setAccountPublic(naRegularGenerator, iIndex); - - uAuthKeyID = naAuthKeyID.getAccountID(); - - // Sign anything (naAuthKeyID) to prove we know new master private key. - naNewAccountPrivate.accountPrivateSign(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature); - - Transaction::pointer trans = Transaction::sharedWalletAdd( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_ACCOUNT_CREATE, - 0, // YYY No source tag - saAmount, - naAuthKeyID, - naNewAccountPublic, - vucSignature); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["newAccountID"] = naNewAccountPublic.humanAccountID(); - obj["amount"] = saAmount.getText(); - - return obj; - } - } -} - -// wallet_claim [] [] -// -// To provide an example to client writers, we do everything we expect a client to do here. -Json::Value RPCHandler::doWalletClaim(const Json::Value& params) -{ - RippleAddress naMasterSeed; - RippleAddress naRegularSeed; - - if (!naMasterSeed.setSeedGeneric(params[0u].asString())) - { - // Should also not allow account id's as seeds. - return rpcError(rpcBAD_SEED); - } - else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) - { - // Should also not allow account id's as seeds. - return rpcError(rpcBAD_SEED); - } - else - { - // Building: - // peer_wallet_claim - // [] - // - // - // Which has no confidential information. - - // XXX Need better parsing. - uint32 uSourceTag = (params.size() == 2) ? 0 : lexical_cast_s(params[2u].asString()); - // XXX Annotation is ignored. - std::string strAnnotation = (params.size() == 3) ? "" : params[3u].asString(); - - RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); - RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); - RippleAddress naRegular0Public; - RippleAddress naRegular0Private; - - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - - naAccountPublic.setAccountPublic(naMasterGenerator, 0); - naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); - - naRegular0Public.setAccountPublic(naRegularGenerator, 0); - naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); - - // Hash of regular account #0 public key. - uint160 uGeneratorID = naRegular0Public.getAccountID(); - std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); - std::vector vucGeneratorSig; - - // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. - // XXX Check result. - naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); - - Transaction::pointer trans = Transaction::sharedClaim( - naAccountPublic, naAccountPrivate, - uSourceTag, - vucGeneratorCipher, - naRegular0Public.getAccountPublic(), - vucGeneratorSig); - - trans = mNetOps->submitTransaction(trans); - - Json::Value obj(Json::objectValue); - - // We "echo" the seeds so they can be checked. - obj["master_seed"] = naMasterSeed.humanSeed(); - obj["master_key"] = naMasterSeed.humanSeed1751(); - obj["regular_seed"] = naRegularSeed.humanSeed(); - obj["regular_key"] = naRegularSeed.humanSeed1751(); - - obj["account_id"] = naAccountPublic.humanAccountID(); - obj["generator_id"] = strHex(uGeneratorID); - obj["generator"] = strHex(vucGeneratorCipher); - obj["annotation"] = strAnnotation; - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; - } -} - -// wallet_create regular_seed paying_account account_id [initial_funds] -// We don't allow creating an account_id by default here because we want to make sure the person has a chance to write down the -// master seed of the account to be created. -// YYY Need annotation and source tag -Json::Value RPCHandler::doWalletCreate(const Json::Value& params) -{ - RippleAddress naSrcAccountID; - RippleAddress naDstAccountID; - RippleAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return rpcError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return rpcError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return rpcError(rpcDST_ACT_MALFORMED); - } - else if (mNetOps->getAccountState(uint256(0), naDstAccountID)) - { - return rpcError(rpcACT_EXISTS); - } - - // Trying to build: - // peer_wallet_create [] [] - - RippleAddress naMasterGenerator; - RippleAddress naAccountPublic; - RippleAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - STAmount saInitialFunds = (params.size() < 4) ? 0 : lexical_cast_s(params[3u].asString()); - - if (saSrcBalance < saInitialFunds) - return rpcError(rpcINSUF_FUNDS); - - Transaction::pointer trans = Transaction::sharedCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_ACCOUNT_CREATE, - 0, // YYY No source tag - naDstAccountID, - saInitialFunds); // Initial funds in XNC. - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - Json::Value RPCHandler::doLogRotate(const Json::Value& params) { @@ -2356,13 +1292,8 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param unsigned int iOptions; } commandsA[] = { { "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true }, - { "account_domain_set", &RPCHandler::doAccountDomainSet, 2, 3, false, optCurrent }, - { "account_email_set", &RPCHandler::doAccountEmailSet, 2, 3, false, optCurrent }, { "account_info", &RPCHandler::doAccountInfo, 1, 2, false, optCurrent }, - { "account_message_set", &RPCHandler::doAccountMessageSet, 3, 3, false, optCurrent }, - { "account_rate_set", &RPCHandler::doAccountRateSet, 3, 3, false, optCurrent }, { "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, optNetwork }, - { "account_wallet_set", &RPCHandler::doAccountWalletSet, 2, 3, false, optCurrent }, { "connect", &RPCHandler::doConnect, 1, 2, true }, { "data_delete", &RPCHandler::doDataDelete, 1, 1, true }, { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true }, @@ -2372,18 +1303,11 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { "log_level", &RPCHandler::doLogLevel, 0, 2, true }, { "logrotate", &RPCHandler::doLogRotate, 0, 0, true }, { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent }, - { "nickname_set", &RPCHandler::doNicknameSet, 2, 3, false, optCurrent }, - { "offer_create", &RPCHandler::doOfferCreate, 9, 10, false, optCurrent }, - { "offer_cancel", &RPCHandler::doOfferCancel, 3, 3, false, optCurrent }, { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, optCurrent }, - { "password_fund", &RPCHandler::doPasswordFund, 2, 3, false, optCurrent }, - { "password_set", &RPCHandler::doPasswordSet, 2, 3, false, optNetwork }, { "peers", &RPCHandler::doPeers, 0, 0, true }, { "profile", &RPCHandler::doProfile, 1, 9, false, optCurrent }, - { "ripple", &RPCHandler::doRipple, 9, -1, false, optCurrent|optClosed }, { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, optCurrent }, - { "ripple_line_set", &RPCHandler::doRippleLineSet, 4, 7, false, optCurrent }, - { "send", &RPCHandler::doSend, 3, 9, false, optCurrent }, + { "submit", &RPCHandler::doSubmit, 2, 2, false, optCurrent }, { "server_info", &RPCHandler::doServerInfo, 0, 0, true }, { "stop", &RPCHandler::doStop, 0, 0, true }, { "tx", &RPCHandler::doTx, 1, 1, true }, @@ -2401,9 +1325,6 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false }, { "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, optCurrent }, - { "wallet_add", &RPCHandler::doWalletAdd, 3, 5, false, optCurrent }, - { "wallet_claim", &RPCHandler::doWalletClaim, 2, 4, false, optNetwork }, - { "wallet_create", &RPCHandler::doWalletCreate, 3, 4, false, optCurrent }, { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, }, { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, }, @@ -2666,4 +1587,328 @@ Json::Value RPCHandler::doUnlLoad(const Json::Value& params) return "loading"; } +Json::Value RPCHandler::doLedgerAccept(const Json::Value& ) +{ + Json::Value jvResult; + if (!theConfig.RUN_STANDALONE) + { + jvResult["error"] = "notStandAlone"; + } + else + { + mNetOps->acceptLedger(); + + jvResult["ledger_current_index"] = mNetOps->getCurrentLedgerID(); + } + return(jvResult); +} + + +Json::Value RPCHandler::doTransactionEntry(const Json::Value& params) +{ + Json::Value jvResult; + Json::Value jvRequest; + Json::Reader reader; + if(!reader.parse(params[0u].asString(),jvRequest)) + return rpcError(rpcINVALID_PARAMS); + + if (!jvRequest.isMember("tx_hash")) + { + jvResult["error"] = "fieldNotFoundTransaction"; + } + if (!jvRequest.isMember("ledger_hash")) + { + jvResult["error"] = "notYetImplemented"; // XXX We don't support any transaction yet. + } + else + { + uint256 uTransID; + // XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure. + uTransID.SetHex(jvRequest["tx_hash"].asString()); + + uint256 uLedgerID; + // XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure. + uLedgerID.SetHex(jvRequest["ledger_hash"].asString()); + + Ledger::pointer lpLedger = theApp->getMasterLedger().getLedgerByHash(uLedgerID); + + if (!lpLedger) { + jvResult["error"] = "ledgerNotFound"; + } + else + { + Transaction::pointer tpTrans; + TransactionMetaSet::pointer tmTrans; + + if (!lpLedger-> getTransaction(uTransID, tpTrans, tmTrans)) + { + jvResult["error"] = "transactionNotFound"; + } + else + { + jvResult["transaction"] = tpTrans->getJson(0); + jvResult["metadata"] = tmTrans->getJson(0); + // 'accounts' + // 'engine_...' + // 'ledger_...' + } + } + } + + return jvResult; +} + + + +Json::Value RPCHandler::doLedgerEntry(const Json::Value& params) +{ + Json::Value jvResult; + Json::Value jvRequest; + Json::Reader reader; + if(!reader.parse(params[0u].asString(),jvRequest)) + return rpcError(rpcINVALID_PARAMS); + + + uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0; + uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0; + + Ledger::pointer lpLedger; + + if (!!uLedger) + { + // Ledger directly specified. + lpLedger = mNetOps->getLedgerByHash(uLedger); + + if (!lpLedger) + { + jvResult["error"] = "ledgerNotFound"; + return jvResult; + } + + uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index, override if needed. + } + else if (!!uLedgerIndex) + { + lpLedger = mNetOps->getLedgerBySeq(uLedgerIndex); + + if (!lpLedger) + { + jvResult["error"] = "ledgerNotFound"; // ledger_index from future? + return jvResult; + } + } + else + { + // Default to current ledger. + lpLedger = mNetOps->getCurrentLedger(); + uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index. + } + + if (lpLedger->isClosed()) + { + if (!!uLedger) + jvResult["ledger_closed"] = uLedger.ToString(); + + jvResult["ledger_closed_index"] = uLedgerIndex; + } + else + { + jvResult["ledger_current_index"] = uLedgerIndex; + } + + uint256 uNodeIndex; + bool bNodeBinary = false; + + if (jvRequest.isMember("index")) + { + // XXX Needs to provide proof. + uNodeIndex.SetHex(jvRequest["index"].asString()); + bNodeBinary = true; + } + else if (jvRequest.isMember("account_root")) + { + RippleAddress naAccount; + + if (!naAccount.setAccountID(jvRequest["account_root"].asString()) + || !naAccount.getAccountID()) + { + jvResult["error"] = "malformedAddress"; + } + else + { + uNodeIndex = Ledger::getAccountRootIndex(naAccount.getAccountID()); + } + } + else if (jvRequest.isMember("directory")) + { + + if (!jvRequest.isObject()) + { + uNodeIndex.SetHex(jvRequest["directory"].asString()); + } + else if (jvRequest["directory"].isMember("sub_index") + && !jvRequest["directory"]["sub_index"].isIntegral()) + { + jvResult["error"] = "malformedRequest"; + } + else + { + uint64 uSubIndex = jvRequest["directory"].isMember("sub_index") + ? jvRequest["directory"]["sub_index"].asUInt() + : 0; + + if (jvRequest["directory"].isMember("dir_root")) + { + uint256 uDirRoot; + + uDirRoot.SetHex(jvRequest["dir_root"].asString()); + + uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex); + } + else if (jvRequest["directory"].isMember("owner")) + { + RippleAddress naOwnerID; + + if (!naOwnerID.setAccountID(jvRequest["directory"]["owner"].asString())) + { + jvResult["error"] = "malformedAddress"; + } + else + { + uint256 uDirRoot = Ledger::getOwnerDirIndex(naOwnerID.getAccountID()); + + uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex); + } + } + else + { + jvResult["error"] = "malformedRequest"; + } + } + } + else if (jvRequest.isMember("generator")) + { + RippleAddress naGeneratorID; + + if (!jvRequest.isObject()) + { + uNodeIndex.SetHex(jvRequest["generator"].asString()); + } + else if (!jvRequest["generator"].isMember("regular_seed")) + { + jvResult["error"] = "malformedRequest"; + } + else if (!naGeneratorID.setSeedGeneric(jvRequest["generator"]["regular_seed"].asString())) + { + jvResult["error"] = "malformedAddress"; + } + else + { + RippleAddress na0Public; // To find the generator's index. + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naGeneratorID); + + na0Public.setAccountPublic(naGenerator, 0); + + uNodeIndex = Ledger::getGeneratorIndex(na0Public.getAccountID()); + } + } + else if (jvRequest.isMember("offer")) + { + RippleAddress naAccountID; + + if (!jvRequest.isObject()) + { + uNodeIndex.SetHex(jvRequest["offer"].asString()); + } + else if (!jvRequest["offer"].isMember("account") + || !jvRequest["offer"].isMember("seq") + || !jvRequest["offer"]["seq"].isIntegral()) + { + jvResult["error"] = "malformedRequest"; + } + else if (!naAccountID.setAccountID(jvRequest["offer"]["account"].asString())) + { + jvResult["error"] = "malformedAddress"; + } + else + { + uint32 uSequence = jvRequest["offer"]["seq"].asUInt(); + + uNodeIndex = Ledger::getOfferIndex(naAccountID.getAccountID(), uSequence); + } + } + else if (jvRequest.isMember("ripple_state")) + { + RippleAddress naA; + RippleAddress naB; + uint160 uCurrency; + Json::Value jvRippleState = jvRequest["ripple_state"]; + + if (!jvRippleState.isMember("currency") + || !jvRippleState.isMember("accounts") + || !jvRippleState["accounts"].isArray() + || 2 != jvRippleState["accounts"].size() + || !jvRippleState["accounts"][0u].isString() + || !jvRippleState["accounts"][1u].isString() + || jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString() + ) { + + cLog(lsINFO) + << boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array: %d size: %d equal: %d") + % jvRippleState.isMember("accounts") + % jvRippleState.isMember("currency") + % jvRippleState["accounts"].isArray() + % jvRippleState["accounts"].size() + % (jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString()) + ); + + jvResult["error"] = "malformedRequest"; + } + else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString()) + || !naB.setAccountID(jvRippleState["accounts"][1u].asString())) { + jvResult["error"] = "malformedAddress"; + } + else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) { + jvResult["error"] = "malformedCurrency"; + } + else + { + uNodeIndex = Ledger::getRippleStateIndex(naA, naB, uCurrency); + } + } + else + { + jvResult["error"] = "unknownOption"; + } + + if (!!uNodeIndex) + { + SLE::pointer sleNode = mNetOps->getSLE(lpLedger, uNodeIndex); + + if (!sleNode) + { + // Not found. + // XXX Should also provide proof. + jvResult["error"] = "entryNotFound"; + } + else if (bNodeBinary) + { + // XXX Should also provide proof. + Serializer s; + + sleNode->add(s); + + jvResult["node_binary"] = strHex(s.peekData()); + jvResult["index"] = uNodeIndex.ToString(); + } + else + { + jvResult["node"] = sleNode->getJson(0); + jvResult["index"] = uNodeIndex.ToString(); + } + } + + return jvResult; +} + // vim:ts=4 diff --git a/src/RPCHandler.h b/src/RPCHandler.h index c001e7dffa..6dc6275467 100644 --- a/src/RPCHandler.h +++ b/src/RPCHandler.h @@ -28,14 +28,9 @@ class RPCHandler Json::Value accountFromString(const uint256& uLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex); Json::Value doAcceptLedger(const Json::Value ¶ms); - Json::Value doAccountDomainSet(const Json::Value ¶ms); - Json::Value doAccountEmailSet(const Json::Value ¶ms); + Json::Value doAccountInfo(const Json::Value& params); - Json::Value doAccountMessageSet(const Json::Value ¶ms); - Json::Value doAccountPublishSet(const Json::Value ¶ms); - Json::Value doAccountRateSet(const Json::Value ¶ms); Json::Value doAccountTransactions(const Json::Value& params); - Json::Value doAccountWalletSet(const Json::Value ¶ms); Json::Value doConnect(const Json::Value& params); Json::Value doDataDelete(const Json::Value& params); Json::Value doDataFetch(const Json::Value& params); @@ -44,24 +39,18 @@ class RPCHandler Json::Value doLedger(const Json::Value& params); Json::Value doLogRotate(const Json::Value& params); Json::Value doNicknameInfo(const Json::Value& params); - Json::Value doNicknameSet(const Json::Value& params); - Json::Value doOfferCreate(const Json::Value& params); - Json::Value doOfferCancel(const Json::Value& params); + Json::Value doOwnerInfo(const Json::Value& params); - Json::Value doPasswordFund(const Json::Value& params); - Json::Value doPasswordSet(const Json::Value& params); + Json::Value doProfile(const Json::Value& params); Json::Value doPeers(const Json::Value& params); - Json::Value doRipple(const Json::Value ¶ms); + Json::Value doRippleLinesGet(const Json::Value ¶ms); - Json::Value doRippleLineSet(const Json::Value& params); - Json::Value doSend(const Json::Value& params); Json::Value doServerInfo(const Json::Value& params); Json::Value doSessionClose(const Json::Value& params); Json::Value doSessionOpen(const Json::Value& params); Json::Value doLogLevel(const Json::Value& params); Json::Value doStop(const Json::Value& params); - Json::Value doTransitSet(const Json::Value& params); Json::Value doTx(const Json::Value& params); Json::Value doTxHistory(const Json::Value& params); Json::Value doSubmit(const Json::Value& params); @@ -80,9 +69,6 @@ class RPCHandler Json::Value doValidationSeed(const Json::Value& params); Json::Value doWalletAccounts(const Json::Value& params); - Json::Value doWalletAdd(const Json::Value& params); - Json::Value doWalletClaim(const Json::Value& params); - Json::Value doWalletCreate(const Json::Value& params); Json::Value doWalletLock(const Json::Value& params); Json::Value doWalletPropose(const Json::Value& params); Json::Value doWalletSeed(const Json::Value& params); @@ -91,6 +77,14 @@ class RPCHandler Json::Value doLogin(const Json::Value& params); + Json::Value doLedgerAccept(const Json::Value& params); + Json::Value doLedgerClosed(const Json::Value& params); + Json::Value doLedgerCurrent(const Json::Value& params); + Json::Value doLedgerEntry(const Json::Value& params); + Json::Value doTransactionEntry(const Json::Value& params); + + + void addSubmitPath(Json::Value& txJSON); public: @@ -160,6 +154,8 @@ public: Json::Value doCommand(const std::string& command, Json::Value& params,int role); Json::Value rpcError(int iError); + Json::Value handleJSONSubmit(std::string& key, Json::Value& txJSON); + }; #endif diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index 1ac494c3a8..efd7cf0202 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -1536,7 +1536,7 @@ TER PathState::pushNode( terResult = pushImply( pnCur.uAccountID, // Current account. pnCur.uCurrencyID, // Wanted currency. - !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as wanted issuer. + !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer. // Note: pnPrv may no longer be the immediately previous node. } @@ -1603,7 +1603,7 @@ TER PathState::pushNode( terResult = pushImply( !!pnPrv.uCurrencyID ? ACCOUNT_ONE // Rippling, but offer's don't have an account. - : ACCOUNT_XNS, + : ACCOUNT_XRP, pnPrv.uCurrencyID, pnPrv.uIssuerID); } @@ -1636,8 +1636,8 @@ PathState::PathState( { const uint160 uInCurrencyID = saSendMax.getCurrency(); const uint160 uOutCurrencyID = saSend.getCurrency(); - const uint160 uInIssuerID = !!uInCurrencyID ? saSendMax.getIssuer() : ACCOUNT_XNS; - const uint160 uOutIssuerID = !!uOutCurrencyID ? saSend.getIssuer() : ACCOUNT_XNS; + const uint160 uInIssuerID = !!uInCurrencyID ? saSendMax.getIssuer() : ACCOUNT_XRP; + const uint160 uOutIssuerID = !!uOutCurrencyID ? saSend.getIssuer() : ACCOUNT_XRP; lesEntries = lesSource.duplicate(); @@ -1670,7 +1670,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currenc ? uOutIssuerID == uReceiverID ? uReceiverID : uOutIssuerID - : ACCOUNT_XNS; + : ACCOUNT_XRP; cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") % RippleAddress::createHumanAccountID(uNxtCurrencyID) @@ -1730,7 +1730,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: implied: account=%s curren | STPathElement::typeIssuer, uReceiverID, // Receive to output uOutCurrencyID, // Desired currency - !!uOutCurrencyID ? uReceiverID : ACCOUNT_XNS); + !!uOutCurrencyID ? uReceiverID : ACCOUNT_XRP); } if (tesSUCCESS == terStatus) @@ -2441,13 +2441,13 @@ void TransactionEngine::calcNodeOffer( { TER terResult = temUNKNOWN; - // Direct: not bridging via XNS + // Direct: not bridging via XRP bool bDirectNext = true; // True, if need to load. uint256 uDirectQuality; uint256 uDirectTip = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); - // Bridging: bridging via XNS + // Bridging: bridging via XRP bool bBridge = true; // True, if bridging active. False, missing an offer. uint256 uBridgeQuality; STAmount saBridgeIn; // Amount available. @@ -2475,10 +2475,10 @@ void TransactionEngine::calcNodeOffer( if (!uCurCurrencyID && !uPrvCurrencyID) { - // Bridging: Neither currency is XNS. - uInTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, CURRENCY_XNS, ACCOUNT_XNS); + // Bridging: Neither currency is XRP. + uInTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, CURRENCY_XRP, ACCOUNT_XRP); uInEnd = Ledger::getQualityNext(uInTip); - uOutTip = Ledger::getBookBase(CURRENCY_XNS, ACCOUNT_XNS, uCurCurrencyID, uCurIssuerID); + uOutTip = Ledger::getBookBase(CURRENCY_XRP, ACCOUNT_XRP, uCurCurrencyID, uCurIssuerID); uOutEnd = Ledger::getQualityNext(uInTip); } diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index e3de616d28..def66e4f39 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -716,23 +716,22 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui // Log(lsTRACE) << "fetchNodeExternal: missing " << hash; throw SHAMapMissingNode(mType, id, hash); } - assert(Serializer::getSHA512Half(obj->getData()) == hash); try { SHAMapTreeNode::pointer ret = boost::make_shared(id, obj->getData(), mSeq - 1, snfPREFIX); -#ifdef DEBUG if (id != *ret) { Log(lsFATAL) << "id:" << id << ", got:" << *ret; assert(false); + return SHAMapTreeNode::pointer(); } if (ret->getNodeHash() != hash) { Log(lsFATAL) << "Hashes don't match"; assert(false); + return SHAMapTreeNode::pointer(); } -#endif return ret; } catch (...) diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index b86df1ddb5..4151379c52 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -25,9 +25,9 @@ enum PathFlags PF_ISSUE = 0x80, }; -#define CURRENCY_XNS uint160(0) +#define CURRENCY_XRP uint160(0) #define CURRENCY_ONE uint160(1) // Used as a place holder -#define ACCOUNT_XNS uint160(0) +#define ACCOUNT_XRP uint160(0) #define ACCOUNT_ONE uint160(1) // Used as a place holder DEFINE_INSTANCE(SerializedValue); @@ -58,7 +58,7 @@ public: virtual std::string getFullText() const; virtual std::string getText() const // just the value { return std::string(); } - virtual Json::Value getJson(int) const + virtual Json::Value getJson(int /*options*/) const { return getText(); } virtual void add(Serializer& s) const { ; } @@ -209,11 +209,11 @@ class STAmount : public SerializedType protected: uint160 mCurrency; // Compared by ==. Always update mIsNative. - uint160 mIssuer; // Not compared by ==. 0 for XNS. + uint160 mIssuer; // Not compared by ==. 0 for XRP. uint64 mValue; int mOffset; - bool mIsNative; // Always !mCurrency. Native is XNS. + bool mIsNative; // Always !mCurrency. Native is XRP. bool mIsNegative; void canonicalize(); @@ -274,7 +274,7 @@ public: int getExponent() const { return mOffset; } uint64 getMantissa() const { return mValue; } - // When the currency is XNS, the value in raw units. S=signed + // When the currency is XRP, the value in raw units. S=signed uint64 getNValue() const { if (!mIsNative) throw std::runtime_error("not native"); return mValue; } void setNValue(uint64 v) { if (!mIsNative) throw std::runtime_error("not native"); mValue = v; } int64 getSNValue() const; diff --git a/src/Suppression.cpp b/src/Suppression.cpp index ad96d4b152..5eee0ab7f3 100644 --- a/src/Suppression.cpp +++ b/src/Suppression.cpp @@ -58,6 +58,25 @@ bool SuppressionTable::addSuppressionPeer(const uint256& index, uint64 peer) return created; } +bool SuppressionTable::addSuppressionPeer(const uint256& index, uint64 peer, int& flags) +{ + boost::mutex::scoped_lock sl(mSuppressionMutex); + + bool created; + Suppression &s = findCreateEntry(index, created); + s.addPeer(peer); + flags = s.getFlags(); + return created; +} + +int SuppressionTable::getFlags(const uint256& index) +{ + boost::mutex::scoped_lock sl(mSuppressionMutex); + + bool created; + return findCreateEntry(index, created).getFlags(); +} + bool SuppressionTable::addSuppressionFlags(const uint256& index, int flag) { boost::mutex::scoped_lock sl(mSuppressionMutex); diff --git a/src/Suppression.h b/src/Suppression.h index 14b611ede9..fe07e522b6 100644 --- a/src/Suppression.h +++ b/src/Suppression.h @@ -14,10 +14,11 @@ DEFINE_INSTANCE(Suppression); -#define SF_RELAYED 0x01 -#define SF_SIGBAD 0x02 -#define SF_SIGGOOD 0x04 +#define SF_RELAYED 0x01 // Has already been relayed to other nodes +#define SF_BAD 0x02 // Signature/format is bad +#define SF_SIGGOOD 0x04 // Signature is good #define SF_SAVED 0x08 +#define SF_RETRY 0x10 // Transaction can be retried class Suppression : private IS_INSTANCE(Suppression) { @@ -61,12 +62,15 @@ public: bool addSuppression(const uint256& index); bool addSuppressionPeer(const uint256& index, uint64 peer); + bool addSuppressionPeer(const uint256& index, uint64 peer, int& flags); bool addSuppressionFlags(const uint256& index, int flag); bool setFlag(const uint256& index, int flag); + int getFlags(const uint256& index); Suppression getEntry(const uint256&); bool swapSet(const uint256& index, std::set& peers, int flag); + bool swapSet(const uint256& index, std::set& peers); }; #endif diff --git a/src/TransactionAction.cpp b/src/TransactionAction.cpp index 8ff5c1f341..8b30023167 100644 --- a/src/TransactionAction.cpp +++ b/src/TransactionAction.cpp @@ -518,9 +518,9 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn, const Transac if (bCreate && !saDstAmount.isNative()) { // This restriction could be relaxed. - Log(lsINFO) << "doPayment: Invalid transaction: Create account may only fund XNS."; + Log(lsINFO) << "doPayment: Invalid transaction: Create account may only fund XRP."; - return temCREATEXNS; + return temCREATEXRP; } else if (!bCreate) { @@ -569,11 +569,11 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn, const Transac } else { - // Direct XNS payment. + // Direct XRP payment. - STAmount saSrcXNSBalance = mTxnAccount->getFieldAmount(sfBalance); + STAmount saSrcXRPBalance = mTxnAccount->getFieldAmount(sfBalance); - if (saSrcXNSBalance < saDstAmount) + if (saSrcXRPBalance < saDstAmount) { // Transaction might succeed, if applied in a different order. Log(lsINFO) << "doPayment: Delay transaction: Insufficent funds."; @@ -582,7 +582,7 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn, const Transac } else { - mTxnAccount->setFieldAmount(sfBalance, saSrcXNSBalance - saDstAmount); + mTxnAccount->setFieldAmount(sfBalance, saSrcXRPBalance - saDstAmount); sleDst->setFieldAmount(sfBalance, sleDst->getFieldAmount(sfBalance) + saDstAmount); terResult = tesSUCCESS; @@ -953,7 +953,7 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe } else if (saTakerPays.isNative() && saTakerGets.isNative()) { - Log(lsWARNING) << "doOfferCreate: Malformed offer: XNS for XNS"; + Log(lsWARNING) << "doOfferCreate: Malformed offer: XRP for XRP"; terResult = temBAD_OFFER; } diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 0535029396..3c6789ce5b 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -455,7 +455,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa terResult = terRETRY; } - if (tesSUCCESS == terResult || isTepPartial(terResult)) + if ((tesSUCCESS == terResult) || isTepPartial(terResult)) { // Transaction succeeded fully or (retries are not allowed and the transaction succeeded partially). Serializer m; diff --git a/src/TransactionErr.cpp b/src/TransactionErr.cpp index 9f50b4d3ab..93f3c86877 100644 --- a/src/TransactionErr.cpp +++ b/src/TransactionErr.cpp @@ -34,7 +34,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" }, { temBAD_SET_ID, "temBAD_SET_ID", "Malformed." }, { temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence in not in the past." }, - { temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." }, + { temCREATEXRP, "temCREATEXRP", "Can not specify non XRP for Create." }, { temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." }, { temDST_NEEDED, "temDST_NEEDED", "Destination not specified." }, { temINSUF_FEE_P, "temINSUF_FEE_P", "Fee not allowed." }, diff --git a/src/TransactionErr.h b/src/TransactionErr.h index f672caf7f3..93c03515f4 100644 --- a/src/TransactionErr.h +++ b/src/TransactionErr.h @@ -36,7 +36,7 @@ enum TER // aka TransactionEngineResult temBAD_TRANSFER_RATE, temBAD_SEQUENCE, temBAD_SET_ID, - temCREATEXNS, + temCREATEXRP, temDST_IS_SRC, temDST_NEEDED, temINSUF_FEE_P, diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index f8fdff08df..8866b039f9 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -121,6 +121,7 @@ static bool compare(const STObject& o1, const STObject& o2) STObject TransactionMetaSet::getAsObject() const { STObject metaData(sfTransactionMetaData); + assert(mResult != 255); metaData.setFieldU8(sfTransactionResult, mResult); metaData.addObject(mNodes); return metaData; diff --git a/src/WSConnection.cpp b/src/WSConnection.cpp new file mode 100644 index 0000000000..8e8328784a --- /dev/null +++ b/src/WSConnection.cpp @@ -0,0 +1,304 @@ +#include "WSConnection.h" +#include "WSHandler.h" + +#include "../json/reader.h" +#include "../json/writer.h" +// +// WSConnection +// + +SETUP_LOG(); + +WSConnection::~WSConnection() +{ + mNetwork.unsubTransactions(this); + mNetwork.unsubRTTransactions(this); + mNetwork.unsubLedger(this); + mNetwork.unsubServer(this); + mNetwork.unsubAccount(this, mSubAccountInfo,true); + mNetwork.unsubAccount(this, mSubAccountInfo,false); +} + +void WSConnection::send(const Json::Value& jvObj) +{ + mHandler->send(mConnection, jvObj); +} + +// +// Utilities +// + +Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) +{ + static struct { + const char* pCommand; + doFuncPtr dfpFunc; + } commandsA[] = { + // Request-Response Commands: + { "submit", &WSConnection::doSubmit }, + { "subscribe", &WSConnection::doSubscribe }, + { "unsubscribe", &WSConnection::doUnsubscribe }, + { "rpc", &WSConnection::doRPC }, + }; + + if (!jvRequest.isMember("command")) + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "response"; + jvResult["result"] = "error"; + jvResult["error"] = "missingCommand"; + jvResult["command"] = jvRequest; + + return jvResult; + } + + std::string strCommand = jvRequest["command"].asString(); + + int i = NUMBER(commandsA); + + while (i-- && strCommand != commandsA[i].pCommand) + ; + + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "response"; + + if (i < 0) + { + jvResult["error"] = "unknownCommand"; // Unknown command. + } + else + { + (this->*(commandsA[i].dfpFunc))(jvResult, jvRequest); + } + + if (jvRequest.isMember("id")) + { + jvResult["id"] = jvRequest["id"]; + } + + if (jvResult.isMember("error")) + { + jvResult["result"] = "error"; + jvResult["request"] = jvRequest; + } + else + { + jvResult["result"] = "success"; + } + + return jvResult; +} + +boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) +{ + boost::unordered_set usnaResult; + + for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) + { + RippleAddress naString; + + if (!(*it).isString() || !naString.setAccountID((*it).asString())) + { + usnaResult.clear(); + break; + } + else + { + (void) usnaResult.insert(naString); + } + } + + return usnaResult; +} + +// +// Commands +// + +/* +server : Sends a message anytime the server status changes such as network connectivity. +ledger : Sends a message at every ledger close. +transactions : Sends a message for every transaction that makes it into a ledger. +rt_transactions +accounts +rt_accounts +*/ +void WSConnection::doSubscribe(Json::Value& jvResult, Json::Value& jvRequest) +{ + if (jvRequest.isMember("streams")) + { + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + { + if ((*it).isString() ) + { + std::string streamName=(*it).asString(); + + if(streamName=="server") + { + mNetwork.subServer(this); + }else if(streamName=="ledger") + { + mNetwork.subLedger(this); + }else if(streamName=="transactions") + { + mNetwork.subTransactions(this); + }else if(streamName=="rt_transactions") + { + mNetwork.subRTTransactions(this); + }else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } + }else + { + jvResult["error"] = "malformedSteam"; + } + } + } + + if (jvRequest.isMember("rt_accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.subAccount(this, usnaAccoundIds,true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.subAccount(this, usnaAccoundIds,false); + } + } +} + +void WSConnection::doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest) +{ + if (jvRequest.isMember("streams")) + { + for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + { + if ((*it).isString() ) + { + std::string streamName=(*it).asString(); + + if(streamName=="server") + { + mNetwork.unsubServer(this); + }else if(streamName=="ledger") + { + mNetwork.unsubLedger(this); + }else if(streamName=="transactions") + { + mNetwork.unsubTransactions(this); + }else if(streamName=="rt_transactions") + { + mNetwork.unsubRTTransactions(this); + }else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } + }else + { + jvResult["error"] = "malformedSteam"; + } + } + } + + if (jvRequest.isMember("rt_accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.unsubAccount(this, usnaAccoundIds,true); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.unsubAccount(this, usnaAccoundIds,false); + } + } +} + + + +void WSConnection::doRPC(Json::Value& jvResult, Json::Value& jvRequest) +{ + if (jvRequest.isMember("rpc_command") ) + { + jvResult=theApp->getRPCHandler().doCommand(jvRequest["rpc_command"].asString(),jvRequest["params"],RPCHandler::GUEST); + + }else jvResult["error"] = "fieldNotCommand"; + +} + +// XXX Currently requires secret. Allow signed transaction as an alternative. +void WSConnection::doSubmit(Json::Value& jvResult, Json::Value& jvRequest) +{ + if (!jvRequest.isMember("tx_json")) + { + jvResult["error"] = "fieldNotFoundTransaction"; + }else if (!jvRequest.isMember("key")) + { + jvResult["error"] = "fieldNotFoundKey"; + }else + { + jvResult=theApp->getRPCHandler().handleJSONSubmit(jvRequest["key"].asString(),jvRequest["tx_json"]); + + // TODO: track the transaction mNetwork.subSubmit(this, jvResult["tx hash"] ); + } +} + diff --git a/src/WSConnection.h b/src/WSConnection.h new file mode 100644 index 0000000000..e93609c1e2 --- /dev/null +++ b/src/WSConnection.h @@ -0,0 +1,55 @@ +#include "../websocketpp/src/sockets/tls.hpp" +#include "../websocketpp/src/websocketpp.hpp" +#include "WSDoor.h" +#include "Application.h" + +#include "Log.h" +#include "NetworkOPs.h" + +template +class WSServerHandler; +// +// Storage for connection specific info +// - Subscriptions +// +class WSConnection : public InfoSub +{ +public: + typedef websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr; + typedef websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr; + +protected: + typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, Json::Value &jvRequest); + + boost::mutex mLock; + boost::unordered_set mSubAccountInfo; + boost::unordered_set mSubAccountTransaction; + + WSServerHandler* mHandler; + connection_ptr mConnection; + NetworkOPs& mNetwork; + +public: + // WSConnection() + // : mHandler((WSServerHandler*)(NULL)), + // mConnection(connection_ptr()) { ; } + + WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) + : mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()) { ; } + + virtual ~WSConnection(); + + // Implement overridden functions from base class: + void send(const Json::Value& jvObj); + + // Utilities + Json::Value invokeCommand(Json::Value& jvRequest); + boost::unordered_set parseAccountIds(const Json::Value& jvArray); + + // Commands + void doSubmit(Json::Value& jvResult, Json::Value& jvRequest); + void doRPC(Json::Value& jvResult, Json::Value& jvRequest); + void doSubscribe(Json::Value& jvResult, Json::Value& jvRequest); + void doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest); + +}; \ No newline at end of file diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 5d52859fbe..9629841590 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -1,12 +1,14 @@ #include "WSDoor.h" + #include "Application.h" #include "Config.h" #include "Log.h" #include "NetworkOPs.h" #include "utils.h" - +#include "WSConnection.h" +#include "WSHandler.h" #include #include @@ -14,8 +16,6 @@ #include #include -#include "../json/reader.h" -#include "../json/writer.h" SETUP_LOG(); @@ -39,192 +39,9 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength) return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024(); } -template -class WSServerHandler; - -// -// Storage for connection specific info -// - Subscriptions -// -class WSConnection : public InfoSub -{ -public: - typedef websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr; - typedef websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr; - -protected: - typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, const Json::Value &jvRequest); - - boost::mutex mLock; - boost::unordered_set mSubAccountInfo; - boost::unordered_set mSubAccountTransaction; - - WSServerHandler* mHandler; - connection_ptr mConnection; - NetworkOPs& mNetwork; - -public: -// WSConnection() -// : mHandler((WSServerHandler*)(NULL)), -// mConnection(connection_ptr()) { ; } - - WSConnection(WSServerHandler* wshpHandler, connection_ptr cpConnection) - : mHandler(wshpHandler), mConnection(cpConnection), mNetwork(theApp->getOPs()) { ; } - - virtual ~WSConnection(); - - // Implement overridden functions from base class: - void send(const Json::Value& jvObj); - - // Utilities - Json::Value invokeCommand(const Json::Value& jvRequest); - boost::unordered_set parseAccountIds(const Json::Value& jvArray); - - // Commands - void doSubmit(Json::Value& jvResult, const Json::Value& jvRequest); - void doRPC(Json::Value& jvResult, const Json::Value& jvRequest); - void doSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - // deprecated - void doLedgerAccept(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerClosed(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest); - void doTransactionEntry(Json::Value& jvResult, const Json::Value& jvRequest); - - void doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doAccountTransactionSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doAccountTransactionUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - - void doServerSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doServerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerAccountsSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerAccountsUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doTransactionSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doTransactionUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); -}; - -// A single instance of this object is made. -// This instance dispatches all events. There is no per connection persistence. -template -class WSServerHandler : public endpoint_type::handler -{ -public: - typedef typename endpoint_type::handler::connection_ptr connection_ptr; - typedef typename endpoint_type::handler::message_ptr message_ptr; - - // Private reasons to close. - enum { - crTooSlow = 4000, // Client is too slow. - }; - -private: - boost::shared_ptr mCtx; - -protected: - boost::mutex mMapLock; - // For each connection maintain an assoicated object to track subscriptions. - boost::unordered_map > mMap; - -public: - WSServerHandler(boost::shared_ptr spCtx) : mCtx(spCtx) {} - - boost::shared_ptr on_tls_init() - { - return mCtx; - } - - void send(connection_ptr cpClient, message_ptr mpMessage) - { - try - { - cpClient->send(mpMessage->get_payload(), mpMessage->get_opcode()); - } - catch (...) - { - cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow.")); - } - } - - void send(connection_ptr cpClient, const std::string& strMessage) - { - try - { - cLog(lsDEBUG) << "Ws:: Sending '" << strMessage << "'"; - - cpClient->send(strMessage); - } - catch (...) - { - cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow.")); - } - } - - void send(connection_ptr cpClient, const Json::Value& jvObj) - { - Json::FastWriter jfwWriter; - - // cLog(lsDEBUG) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'"; - - send(cpClient, jfwWriter.write(jvObj)); - } - - void on_open(connection_ptr cpClient) - { - boost::mutex::scoped_lock sl(mMapLock); - - mMap[cpClient] = boost::make_shared(this, cpClient); - } - - void on_close(connection_ptr cpClient) - { - boost::mutex::scoped_lock sl(mMapLock); - - mMap.erase(cpClient); - } - - void on_message(connection_ptr cpClient, message_ptr mpMessage) - { - Json::Value jvRequest; - Json::Reader jrReader; - - if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT) - { - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "error"; - jvResult["error"] = "wsTextRequired"; // We only accept text messages. - - send(cpClient, jvResult); - } - else if (!jrReader.parse(mpMessage->get_payload(), jvRequest) || jvRequest.isNull() || !jvRequest.isObject()) - { - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "error"; - jvResult["error"] = "jsonInvalid"; // Received invalid json. - jvResult["value"] = mpMessage->get_payload(); - - send(cpClient, jvResult); - } - else - { - send(cpClient, mMap[cpClient]->invokeCommand(jvRequest)); - } - } - - // Respond to http requests. - void http(connection_ptr cpClient) - { - cpClient->set_body( - "" SYSTEM_NAME " Test" - "

" SYSTEM_NAME " Test

This page shows http(s) connectivity is working.

"); - } -}; void WSDoor::startListening() { @@ -284,893 +101,5 @@ void WSDoor::stop() } } -// -// WSConnection -// - -WSConnection::~WSConnection() -{ - theApp->getOPs().unsubTransaction(this); - theApp->getOPs().unsubLedger(this); - theApp->getOPs().unsubLedgerAccounts(this); - theApp->getOPs().unsubAccountInfo(this, mSubAccountInfo); - theApp->getOPs().unsubAccountTransaction(this, mSubAccountTransaction); -} - -void WSConnection::send(const Json::Value& jvObj) -{ - mHandler->send(mConnection, jvObj); -} - -// -// Utilities -// - -Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) -{ - static struct { - const char* pCommand; - doFuncPtr dfpFunc; - } commandsA[] = { - // Request-Response Commands: - { "ledger_accept", &WSConnection::doLedgerAccept }, - { "ledger_closed", &WSConnection::doLedgerClosed }, - { "ledger_current", &WSConnection::doLedgerCurrent }, - { "ledger_entry", &WSConnection::doLedgerEntry }, - { "submit", &WSConnection::doSubmit }, - { "transaction_entry", &WSConnection::doTransactionEntry }, - { "subscribe", &WSConnection::doSubscribe }, - { "unsubscribe", &WSConnection::doUnsubscribe }, - - // deprecated - { "account_info_subscribe", &WSConnection::doAccountInfoSubscribe }, - { "account_info_unsubscribe", &WSConnection::doAccountInfoUnsubscribe }, - { "account_transaction_subscribe", &WSConnection::doAccountTransactionSubscribe }, - { "account_transaction_unsubscribe", &WSConnection::doAccountTransactionUnsubscribe }, - { "ledger_accounts_subscribe", &WSConnection::doLedgerAccountsSubcribe }, - { "ledger_accounts_unsubscribe", &WSConnection::doLedgerAccountsUnsubscribe }, - { "server_subscribe", &WSConnection::doServerSubscribe }, - { "server_unsubscribe", &WSConnection::doServerUnsubscribe }, - { "transaction_subscribe", &WSConnection::doTransactionSubcribe }, - { "transaction_unsubscribe", &WSConnection::doTransactionUnsubscribe }, - }; - - if (!jvRequest.isMember("command")) - { - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "response"; - jvResult["result"] = "error"; - jvResult["error"] = "missingCommand"; - jvResult["command"] = jvRequest; - - return jvResult; - } - - std::string strCommand = jvRequest["command"].asString(); - - int i = NUMBER(commandsA); - - while (i-- && strCommand != commandsA[i].pCommand) - ; - - Json::Value jvResult(Json::objectValue); - - jvResult["type"] = "response"; - - if (i < 0) - { - jvResult["error"] = "unknownCommand"; // Unknown command. - } - else - { - (this->*(commandsA[i].dfpFunc))(jvResult, jvRequest); - } - - if (jvRequest.isMember("id")) - { - jvResult["id"] = jvRequest["id"]; - } - - if (jvResult.isMember("error")) - { - jvResult["result"] = "error"; - jvResult["request"] = jvRequest; - } - else - { - jvResult["result"] = "success"; - } - - return jvResult; -} - -boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) -{ - boost::unordered_set usnaResult; - - for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) - { - RippleAddress naString; - - if (!(*it).isString() || !naString.setAccountID((*it).asString())) - { - usnaResult.clear(); - break; - } - else - { - (void) usnaResult.insert(naString); - } - } - - return usnaResult; -} - -// -// Commands -// - -/* -server : Sends a message anytime the server status changes such as network connectivity. -ledger : Sends a message at every ledger close. -transactions : Sends a message for every transaction that makes it into a ledger. -rt_transactions -*/ -// TODO -void WSConnection::doSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (jvRequest.isMember("streams")) - { - for (Json::Value::const_iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) - { - if ((*it).isString() ) - { - std::string streamName=(*it).asString(); - - if(streamName=="server") - { - mNetwork.subLedgerAccounts(this); - }else if(streamName=="ledger") - { - mNetwork.subLedgerAccounts(this); - }else if(streamName=="transactions") - { - mNetwork.subTransaction(this); - }else if(streamName=="rt_transactions") - { - mNetwork.subTransaction(this); // TODO - }else - { - jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); - } - }else - { - jvResult["error"] = "malformedSteam"; - } - } - } - - if (jvRequest.isMember("rt_accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.subAccountInfo(this, usnaAccoundIds); - } - } - - if (jvRequest.isMember("accounts")) - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - }else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.subAccountInfo(this, usnaAccoundIds); - } - } -} - -void WSConnection::doUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - -} - -void WSConnection::doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!jvRequest.isMember("accounts")) - { - jvResult["error"] = "missingField"; - } - else if (jvRequest["accounts"].empty()) - { - jvResult["error"] = "emptySet"; - } - else - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - - } -} - -void WSConnection::doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!jvRequest.isMember("accounts")) - { - jvResult["error"] = "missingField"; - } - else if (jvRequest["accounts"].empty()) - { - jvResult["error"] = "emptySet"; - } - else - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - } - else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.erase(naAccountID); - } - - mNetwork.unsubAccountInfo(this, usnaAccoundIds); - } - } -} - -void WSConnection::doAccountTransactionSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!jvRequest.isMember("accounts")) - { - jvResult["error"] = "missingField"; - } - else if (jvRequest["accounts"].empty()) - { - jvResult["error"] = "emptySet"; - } - else - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - } - else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountTransaction.insert(naAccountID); - } - - mNetwork.subAccountTransaction(this, usnaAccoundIds); - } - } -} - -void WSConnection::doAccountTransactionUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!jvRequest.isMember("accounts")) - { - jvResult["error"] = "missingField"; - } - else if (jvRequest["accounts"].empty()) - { - jvResult["error"] = "emptySet"; - } - else - { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - } - else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) - { - mSubAccountTransaction.erase(naAccountID); - } - - mNetwork.unsubAccountTransaction(this, usnaAccoundIds); - } - } -} - -void WSConnection::doLedgerAccountsSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.subLedgerAccounts(this)) - { - jvResult["error"] = "ledgerAccountsSubscribed"; - } -} - -void WSConnection::doLedgerAccountsUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.unsubLedgerAccounts(this)) - { - jvResult["error"] = "ledgerAccountsNotSubscribed"; - } -} - -void WSConnection::doLedgerAccept(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!theConfig.RUN_STANDALONE) - { - jvResult["error"] = "notStandAlone"; - } - else - { - mNetwork.acceptLedger(); - - jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID(); - } -} - -void WSConnection::doLedgerClosed(Json::Value& jvResult, const Json::Value& jvRequest) -{ - uint256 uLedger = mNetwork.getClosedLedger(); - - jvResult["ledger_closed_index"] = mNetwork.getLedgerID(uLedger); - jvResult["ledger_closed"] = uLedger.ToString(); -} - -void WSConnection::doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvRequest) -{ - jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID(); -} - -void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest) -{ - NetworkOPs& noNetwork = mNetwork; - uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0; - uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0; - - Ledger::pointer lpLedger; - - if (!!uLedger) - { - // Ledger directly specified. - lpLedger = noNetwork.getLedgerByHash(uLedger); - - if (!lpLedger) - { - jvResult["error"] = "ledgerNotFound"; - return; - } - - uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index, override if needed. - } - else if (!!uLedgerIndex) - { - lpLedger = noNetwork.getLedgerBySeq(uLedgerIndex); - - if (!lpLedger) - { - jvResult["error"] = "ledgerNotFound"; // ledger_index from future? - return; - } - } - else - { - // Default to current ledger. - lpLedger = noNetwork.getCurrentLedger(); - uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index. - } - - if (lpLedger->isClosed()) - { - if (!!uLedger) - jvResult["ledger_closed"] = uLedger.ToString(); - - jvResult["ledger_closed_index"] = uLedgerIndex; - } - else - { - jvResult["ledger_current_index"] = uLedgerIndex; - } - - uint256 uNodeIndex; - bool bNodeBinary = false; - - if (jvRequest.isMember("index")) - { - // XXX Needs to provide proof. - uNodeIndex.SetHex(jvRequest["index"].asString()); - bNodeBinary = true; - } - else if (jvRequest.isMember("account_root")) - { - RippleAddress naAccount; - - if (!naAccount.setAccountID(jvRequest["account_root"].asString()) - || !naAccount.getAccountID()) - { - jvResult["error"] = "malformedAddress"; - } - else - { - uNodeIndex = Ledger::getAccountRootIndex(naAccount.getAccountID()); - } - } - else if (jvRequest.isMember("directory")) - { - - if (!jvRequest.isObject()) - { - uNodeIndex.SetHex(jvRequest["directory"].asString()); - } - else if (jvRequest["directory"].isMember("sub_index") - && !jvRequest["directory"]["sub_index"].isIntegral()) - { - jvResult["error"] = "malformedRequest"; - } - else - { - uint64 uSubIndex = jvRequest["directory"].isMember("sub_index") - ? jvRequest["directory"]["sub_index"].asUInt() - : 0; - - if (jvRequest["directory"].isMember("dir_root")) - { - uint256 uDirRoot; - - uDirRoot.SetHex(jvRequest["dir_root"].asString()); - - uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex); - } - else if (jvRequest["directory"].isMember("owner")) - { - RippleAddress naOwnerID; - - if (!naOwnerID.setAccountID(jvRequest["directory"]["owner"].asString())) - { - jvResult["error"] = "malformedAddress"; - } - else - { - uint256 uDirRoot = Ledger::getOwnerDirIndex(naOwnerID.getAccountID()); - - uNodeIndex = Ledger::getDirNodeIndex(uDirRoot, uSubIndex); - } - } - else - { - jvResult["error"] = "malformedRequest"; - } - } - } - else if (jvRequest.isMember("generator")) - { - RippleAddress naGeneratorID; - - if (!jvRequest.isObject()) - { - uNodeIndex.SetHex(jvRequest["generator"].asString()); - } - else if (!jvRequest["generator"].isMember("regular_seed")) - { - jvResult["error"] = "malformedRequest"; - } - else if (!naGeneratorID.setSeedGeneric(jvRequest["generator"]["regular_seed"].asString())) - { - jvResult["error"] = "malformedAddress"; - } - else - { - RippleAddress na0Public; // To find the generator's index. - RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naGeneratorID); - - na0Public.setAccountPublic(naGenerator, 0); - - uNodeIndex = Ledger::getGeneratorIndex(na0Public.getAccountID()); - } - } - else if (jvRequest.isMember("offer")) - { - RippleAddress naAccountID; - - if (!jvRequest.isObject()) - { - uNodeIndex.SetHex(jvRequest["offer"].asString()); - } - else if (!jvRequest["offer"].isMember("account") - || !jvRequest["offer"].isMember("seq") - || !jvRequest["offer"]["seq"].isIntegral()) - { - jvResult["error"] = "malformedRequest"; - } - else if (!naAccountID.setAccountID(jvRequest["offer"]["account"].asString())) - { - jvResult["error"] = "malformedAddress"; - } - else - { - uint32 uSequence = jvRequest["offer"]["seq"].asUInt(); - - uNodeIndex = Ledger::getOfferIndex(naAccountID.getAccountID(), uSequence); - } - } - else if (jvRequest.isMember("ripple_state")) - { - RippleAddress naA; - RippleAddress naB; - uint160 uCurrency; - Json::Value jvRippleState = jvRequest["ripple_state"]; - - if (!jvRippleState.isMember("currency") - || !jvRippleState.isMember("accounts") - || !jvRippleState["accounts"].isArray() - || 2 != jvRippleState["accounts"].size() - || !jvRippleState["accounts"][0u].isString() - || !jvRippleState["accounts"][1u].isString() - || jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString() - ) { - - cLog(lsINFO) - << boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array: %d size: %d equal: %d") - % jvRippleState.isMember("accounts") - % jvRippleState.isMember("currency") - % jvRippleState["accounts"].isArray() - % jvRippleState["accounts"].size() - % (jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString()) - ); - - jvResult["error"] = "malformedRequest"; - } - else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString()) - || !naB.setAccountID(jvRippleState["accounts"][1u].asString())) { - jvResult["error"] = "malformedAddress"; - } - else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) { - jvResult["error"] = "malformedCurrency"; - } - else - { - uNodeIndex = Ledger::getRippleStateIndex(naA, naB, uCurrency); - } - } - else - { - jvResult["error"] = "unknownOption"; - } - - if (!!uNodeIndex) - { - SLE::pointer sleNode = noNetwork.getSLE(lpLedger, uNodeIndex); - - if (!sleNode) - { - // Not found. - // XXX Should also provide proof. - jvResult["error"] = "entryNotFound"; - } - else if (bNodeBinary) - { - // XXX Should also provide proof. - Serializer s; - - sleNode->add(s); - - jvResult["node_binary"] = strHex(s.peekData()); - jvResult["index"] = uNodeIndex.ToString(); - } - else - { - jvResult["node"] = sleNode->getJson(0); - jvResult["index"] = uNodeIndex.ToString(); - } - } -} - -// The objective is to allow the client to know the server's status. The only thing that show the server is fully operating is the -// stream of ledger_closeds. Therefore, that is all that is provided. A client can drop servers that do not provide recent -// ledger_closeds. -void WSConnection::doServerSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.subLedger(this)) - { - jvResult["error"] = "serverSubscribed"; - } - else - { - if (theConfig.RUN_STANDALONE) - jvResult["stand_alone"] = 1; - - if (NetworkOPs::omDISCONNECTED != mNetwork.getOperatingMode()) { - jvResult["ledger_closed"] = mNetwork.getClosedLedger().ToString(); - jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID(); - } - } -} - -void WSConnection::doServerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.unsubLedger(this)) - { - jvResult["error"] = "serverNotSubscribed"; - } -} - -void WSConnection::doRPC(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (jvRequest.isMember("command")) - { - // TODO - }else jvResult["error"] = "fieldNotCommand"; - -} - -// XXX Current requires secret. Allow signed transaction as an alternative. -void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) -{ - RippleAddress naAccount; - - if (!jvRequest.isMember("transaction")) - { - jvResult["error"] = "fieldNotFoundTransaction"; - } - else if (!jvRequest["transaction"].isMember("Account")) - { - jvResult["error"] = "fieldNotFoundAccount"; - } - else if (!naAccount.setAccountID(jvRequest["transaction"]["Account"].asString())) - { - jvResult["error"] = "malformedAccount"; - } - else if (!jvRequest.isMember("secret")) - { - jvResult["error"] = "fieldNotFoundSecret"; - } - else - { - Ledger::pointer lpCurrent = mNetwork.getCurrentLedger(); - SLE::pointer sleAccountRoot = mNetwork.getSLE(lpCurrent, Ledger::getAccountRootIndex(naAccount.getAccountID())); - - if (!sleAccountRoot) - { - // XXX Ignore transactions for accounts not created. - - jvResult["error"] = "accountNotFound"; - return; - } - - bool bHaveAuthKey = false; - RippleAddress naAuthorizedPublic; -#if 0 - - if (sleAccountRoot->isFieldPresent(sfAuthorizedKey)) - { - naAuthorizedPublic = mLedgerEntry->getFieldAccount(sfAuthorizedKey); - // Json::Value obj = getMasterGenerator(uLedger, naRegularSeed, naMasterGenerator); - } -#endif - - RippleAddress naSecret = RippleAddress::createSeedGeneric(jvRequest["secret"].asString()); - RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret); - - // Find the index of Account from the master generator, so we can generate the public and private keys. - RippleAddress naMasterAccountPublic; - unsigned int iIndex = 0; - bool bFound = false; - - // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are - // related. - while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX) - { - naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - - Log(lsWARNING) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naAccount.humanAccountID(); - - bFound = naAccount.getAccountID() == naMasterAccountPublic.getAccountID(); - if (!bFound) - ++iIndex; - } - - if (!bFound) - { - jvResult["error"] = "accountNotMatched"; - return; - } - - // Use the generator to determine the associated public and private keys. - RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret); - RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex); - RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex); - - if (bHaveAuthKey - // The generated pair must match authorized... - && naAuthorizedPublic.getAccountID() != naAccountPublic.getAccountID() - // ... or the master key must have been used. - && naAccount.getAccountID() != naAccountPublic.getAccountID()) - { - // std::cerr << "iIndex: " << iIndex << std::endl; - // std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl; - // std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl; - - jvResult["error"] = "passwordChanged"; - return; - } - - std::auto_ptr sopTrans; - - try - { - sopTrans = STObject::parseJson(jvRequest["transaction"]); - } - catch (std::exception& e) - { - jvResult["error"] = "malformedTransaction"; - jvResult["error_exception"] = e.what(); - return; - } - - sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic()); - - SerializedTransaction::pointer stpTrans; - - try - { - stpTrans = boost::make_shared(*sopTrans); - } - catch (std::exception& e) - { - jvResult["error"] = "invalidTransaction"; - jvResult["error_exception"] = e.what(); - return; - } - - stpTrans->sign(naAccountPrivate); - - Transaction::pointer tpTrans; - - try - { - tpTrans = boost::make_shared(stpTrans, false); - } - catch (std::exception& e) - { - jvResult["error"] = "internalTransaction"; - jvResult["error_exception"] = e.what(); - return; - } - - try - { - tpTrans = mNetwork.submitTransaction(tpTrans); - - if (!tpTrans) { - jvResult["error"] = "invalidTransaction"; - jvResult["error_exception"] = "Unable to sterilize transaction."; - return; - } - } - catch (std::exception& e) - { - jvResult["error"] = "internalSubmit"; - jvResult["error_exception"] = e.what(); - return; - } - - try - { - jvResult["transaction"] = tpTrans->getJson(0); - - if (temUNCERTAIN != tpTrans->getResult()) - { - std::string sToken; - std::string sHuman; - - transResultInfo(tpTrans->getResult(), sToken, sHuman); - - jvResult["engine_result"] = sToken; - jvResult["engine_result_code"] = tpTrans->getResult(); - jvResult["engine_result_message"] = sHuman; - } - } - catch (std::exception& e) - { - jvResult["error"] = "internalJson"; - jvResult["error_exception"] = e.what(); - return; - } - } -} - -void WSConnection::doTransactionEntry(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!jvRequest.isMember("transaction")) - { - jvResult["error"] = "fieldNotFoundTransaction"; - } - if (!jvRequest.isMember("ledger_closed")) - { - jvResult["error"] = "notYetImplemented"; // XXX We don't support any transaction yet. - } - else - { - uint256 uTransID; - // XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure. - uTransID.SetHex(jvRequest["transaction"].asString()); - - uint256 uLedgerID; - // XXX Relying on trusted WSS client. Would be better to have a strict routine, returning success or failure. - uLedgerID.SetHex(jvRequest["ledger_closed"].asString()); - - Ledger::pointer lpLedger = theApp->getMasterLedger().getLedgerByHash(uLedgerID); - - if (!lpLedger) { - jvResult["error"] = "ledgerNotFound"; - } - else - { - Transaction::pointer tpTrans; - TransactionMetaSet::pointer tmTrans; - - if (!lpLedger-> getTransaction(uTransID, tpTrans, tmTrans)) - { - jvResult["error"] = "transactionNotFound"; - } - else - { - jvResult["transaction"] = tpTrans->getJson(0); - jvResult["metadata"] = tmTrans->getJson(0); - // 'accounts' - // 'engine_...' - // 'ledger_...' - } - } - } -} - -void WSConnection::doTransactionSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.subTransaction(this)) - { - jvResult["error"] = "TransactionsSubscribed"; - } -} - -void WSConnection::doTransactionUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!mNetwork.unsubTransaction(this)) - { - jvResult["error"] = "TransactionsNotSubscribed"; - } -} // vim:ts=4 diff --git a/src/WSHandler.h b/src/WSHandler.h new file mode 100644 index 0000000000..e400da35f7 --- /dev/null +++ b/src/WSHandler.h @@ -0,0 +1,124 @@ +#ifndef __WSHANDLER__ +#define __WSHANDLER__ + +class WSConnection; + +// A single instance of this object is made. +// This instance dispatches all events. There is no per connection persistence. +template +class WSServerHandler : public endpoint_type::handler +{ +public: + typedef typename endpoint_type::handler::connection_ptr connection_ptr; + typedef typename endpoint_type::handler::message_ptr message_ptr; + + // Private reasons to close. + enum { + crTooSlow = 4000, // Client is too slow. + }; + +private: + boost::shared_ptr mCtx; + +protected: + boost::mutex mMapLock; + // For each connection maintain an associated object to track subscriptions. + boost::unordered_map > mMap; + +public: + WSServerHandler(boost::shared_ptr spCtx) : mCtx(spCtx) {} + + boost::shared_ptr on_tls_init() + { + return mCtx; + } + + void send(connection_ptr cpClient, message_ptr mpMessage) + { + try + { + cpClient->send(mpMessage->get_payload(), mpMessage->get_opcode()); + } + catch (...) + { + cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow.")); + } + } + + void send(connection_ptr cpClient, const std::string& strMessage) + { + try + { + cLog(lsDEBUG) << "Ws:: Sending '" << strMessage << "'"; + + cpClient->send(strMessage); + } + catch (...) + { + cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow.")); + } + } + + void send(connection_ptr cpClient, const Json::Value& jvObj) + { + Json::FastWriter jfwWriter; + + // cLog(lsDEBUG) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'"; + + send(cpClient, jfwWriter.write(jvObj)); + } + + void on_open(connection_ptr cpClient) + { + boost::mutex::scoped_lock sl(mMapLock); + + mMap[cpClient] = boost::make_shared(this, cpClient); + } + + void on_close(connection_ptr cpClient) + { + boost::mutex::scoped_lock sl(mMapLock); + + mMap.erase(cpClient); + } + + void on_message(connection_ptr cpClient, message_ptr mpMessage) + { + Json::Value jvRequest; + Json::Reader jrReader; + + if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT) + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "error"; + jvResult["error"] = "wsTextRequired"; // We only accept text messages. + + send(cpClient, jvResult); + } + else if (!jrReader.parse(mpMessage->get_payload(), jvRequest) || jvRequest.isNull() || !jvRequest.isObject()) + { + Json::Value jvResult(Json::objectValue); + + jvResult["type"] = "error"; + jvResult["error"] = "jsonInvalid"; // Received invalid json. + jvResult["value"] = mpMessage->get_payload(); + + send(cpClient, jvResult); + } + else + { + send(cpClient, mMap[cpClient]->invokeCommand(jvRequest)); + } + } + + // Respond to http requests. + void http(connection_ptr cpClient) + { + cpClient->set_body( + "" SYSTEM_NAME " Test" + "

" SYSTEM_NAME " Test

This page shows http(s) connectivity is working.

"); + } +}; + +#endif