diff --git a/js/remote.js b/js/remote.js index 8919d6308..3bf421d69 100644 --- a/js/remote.js +++ b/js/remote.js @@ -299,16 +299,30 @@ Remote.prototype.server_subscribe = function (onDone, onFailure) { this.request( { 'command' : 'server_subscribe' }, - function (r) { - self.ledger_current_index = r.ledger_current_index; - self.ledger_closed = r.ledger_closed; - self.stand_alone = r.stand_alone; - onDone(); + function (r) { + self.ledger_current_index = r.ledger_current_index; + self.ledger_closed = r.ledger_closed; + self.stand_alone = r.stand_alone; + onDone(); }, onFailure ); }; +Remote.prototype.ledger_accept = function (onDone, onFailure) { + if (this.stand_alone) + { + this.request( + { 'command' : 'ledger_accept' }, + onDone, + onFailure + ); + } + else { + onFailure({ 'error' : 'notStandAlone' }); + } +}; + // Refresh accounts[account].seq // done(result); Remote.prototype.account_seq = function (account, advance, onDone, onFailure) { diff --git a/src/Amount.cpp b/src/Amount.cpp index 9012cf69f..78cde08d5 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -194,24 +194,30 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency) return sCurrency; } +// Assumes trusted input. bool STAmount::setValue(const std::string& sAmount) { // Note: mIsNative must be set already! uint64 uValue; int iOffset; size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : "."); - bool bInteger = uDecimal == std::string::npos; + size_t uExp = uDecimal == std::string::npos ? sAmount.find_first_of("e") : std::string::npos; + bool bInteger = uDecimal == std::string::npos && uExp == std::string::npos; mIsNegative = false; if (bInteger) { + // Integer input: does not necessarily mean native. + try { int64 a = sAmount.empty() ? 0 : lexical_cast_st(sAmount); if (a >= 0) - uValue = static_cast(a); + { + uValue = static_cast(a); + } else { - uValue = static_cast(-a); + uValue = static_cast(-a); mIsNegative = true; } @@ -224,36 +230,73 @@ bool STAmount::setValue(const std::string& sAmount) } iOffset = 0; } + else if (uExp != std::string::npos) + { + // e input + + try + { + int64 iInteger = uExp ? lexical_cast_st(sAmount.substr(0, uExp)) : 0; + if (iInteger >= 0) + { + uValue = static_cast(iInteger); + } + else + { + uValue = static_cast(-iInteger); + mIsNegative = true; + } + + iOffset = lexical_cast_st(sAmount.substr(uExp+1)); + } + catch (...) + { + Log(lsINFO) << "Bad e amount: " << sAmount; + + return false; + } + } else { + // Float input: has a decimal + // Example size decimal size-decimal offset // ^1 2 0 2 -1 // 123^ 4 3 1 0 // 1^23 4 1 3 -2 - iOffset = -int(sAmount.size() - uDecimal - 1); - - - // Issolate integer and fraction. - uint64 uInteger; - int64 iInteger = uDecimal ? lexical_cast_st(sAmount.substr(0, uDecimal)) : 0; - if (iInteger >= 0) - uInteger = static_cast(iInteger); - else + try { - uInteger = static_cast(-iInteger); - mIsNegative = true; + iOffset = -int(sAmount.size() - uDecimal - 1); + + // Issolate integer and fraction. + uint64 uInteger; + int64 iInteger = uDecimal ? lexical_cast_st(sAmount.substr(0, uDecimal)) : 0; + if (iInteger >= 0) + { + uInteger = static_cast(iInteger); + } + else + { + uInteger = static_cast(-iInteger); + mIsNegative = true; + } + + uint64 uFraction = iOffset ? lexical_cast_st(sAmount.substr(uDecimal+1)) : 0; + + // Scale the integer portion to the same offset as the fraction. + uValue = uInteger; + for (int i = -iOffset; i--;) + uValue *= 10; + + // Add in the fraction. + uValue += uFraction; } + catch (...) + { + Log(lsINFO) << "Bad float amount: " << sAmount; - - uint64 uFraction = iOffset ? lexical_cast_st(sAmount.substr(uDecimal+1)) : 0; - - // Scale the integer portion to the same offset as the fraction. - uValue = uInteger; - for (int i = -iOffset; i--;) - uValue *= 10; - - // Add in the fraction. - uValue += uFraction; + return false; + } } if (mIsNative) diff --git a/src/Application.cpp b/src/Application.cpp index 1a67e0b74..a5d458dc1 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -88,20 +88,24 @@ void Application::run() boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount)); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join(); - if(theConfig.START_UP==Config::FRESH) + if (theConfig.START_UP == Config::FRESH) { Log(lsINFO) << "Starting new Ledger"; startNewLedger(); - }else if(theConfig.START_UP==Config::LOAD) + } + else if (theConfig.START_UP == Config::LOAD) { Log(lsINFO) << "Loading Old Ledger"; loadOldLedger(); - }else - { // TODO: This should really not validate a ledger until it gets the current one from our peers - // but I'll let david make this change since a lot of code assumes we have a ledger - // for now just do what we always were doing + } + else if (theConfig.START_UP == Config::NETWORK) + { // This should probably become the default once we have a stable network + if (!theConfig.RUN_STANDALONE) + mNetOps.needNetworkLedger(); startNewLedger(); } + else + startNewLedger(); // // Begin validation and ip maintenance. @@ -173,6 +177,7 @@ Application::~Application() delete mHashNodeDB; delete mNetNodeDB; } + void Application::startNewLedger() { // New stuff. @@ -203,14 +208,17 @@ void Application::startNewLedger() void Application::loadOldLedger() { - Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;",true); + Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); - if(!lastLedger) + if (!lastLedger) { std::cout << "No Ledger found?" << std::endl; exit(-1); } - mMasterLedger.pushLedger(lastLedger); + + lastLedger->setClosed(); + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); + mMasterLedger.switchLedgers(lastLedger, openLedger); mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); } // vim:ts=4 diff --git a/src/Config.h b/src/Config.h index cc2b85ed4..f4c0d9a96 100644 --- a/src/Config.h +++ b/src/Config.h @@ -56,7 +56,7 @@ public: std::vector IPS; // Peer IPs from newcoind.cfg. std::vector SNTP_SERVERS; // SNTP servers from newcoind.cfg. - enum StartUpType {FRESH,NORMAL,LOAD}; + enum StartUpType { FRESH, NORMAL, LOAD, NETWORK }; StartUpType START_UP; // Network parameters diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 25a94db5c..f52e1f1e7 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -391,7 +391,7 @@ void Ledger::saveAcceptedLedger(Ledger::ref ledger) theApp->getOPs().pubLedger(ledger); } -Ledger::pointer Ledger::getSQL(const std::string& sql,bool isMutable) +Ledger::pointer Ledger::getSQL(const std::string& sql) { uint256 ledgerHash, prevHash, accountHash, transHash; uint64 totCoins; @@ -424,8 +424,8 @@ Ledger::pointer Ledger::getSQL(const std::string& sql,bool isMutable) db->endIterRows(); } - Ledger::pointer ret =Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, closingTime, prevClosingTime, - closeFlags, closeResolution, ledgerSeq,isMutable)); + Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, true)); if (ret->getHash() != ledgerHash) { if (sLog(lsERROR)) diff --git a/src/Ledger.h b/src/Ledger.h index 71a1bbb2a..41e30d2db 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -93,7 +93,7 @@ public: Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, - uint32 ledgerSeq,bool immutable); // used for database ledgers + uint32 ledgerSeq, bool immutable); // used for database ledgers Ledger(const std::vector& rawLedger); @@ -103,7 +103,7 @@ public: Ledger(Ledger& target, bool isMutable); // snapshot - static Ledger::pointer getSQL(const std::string& sqlStatement,bool immutable=false); + static Ledger::pointer getSQL(const std::string& sqlStatement); void updateHash(); void setClosed() { mClosed = true; } @@ -116,10 +116,6 @@ public: void armDirty() { mTransactionMap->armDirty(); mAccountStateMap->armDirty(); } void disarmDirty() { mTransactionMap->disarmDirty(); mAccountStateMap->disarmDirty(); } - // This ledger has closed, will never be accepted, and is accepting - // new transactions to be re-reprocessed when do accept a new last-closed ledger - void bumpSeq() { mClosed = true; mLedgerSeq++; } - // ledger signature operations void addRaw(Serializer &s) const; void setRaw(const Serializer& s); diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index f336e9463..855128bdf 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -26,8 +26,9 @@ SETUP_LOG(); NetworkOPs::NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster) : - mMode(omDISCONNECTED),mNetTimer(io_service), mLedgerMaster(pLedgerMaster), mCloseTimeOffset(0), - mLastCloseProposers(0), mLastCloseConvergeTime(1000 * LEDGER_IDLE_INTERVAL), mLastValidationTime(0) + mMode(omDISCONNECTED), mNeedNetworkLedger(false), mNetTimer(io_service), mLedgerMaster(pLedgerMaster), + mCloseTimeOffset(0), mLastCloseProposers(0), mLastCloseConvergeTime(1000 * LEDGER_IDLE_INTERVAL), + mLastValidationTime(0) { } @@ -424,7 +425,8 @@ void NetworkOPs::checkState(const boost::system::error_code& result) // If full or tracking, check only at wobble time! uint256 networkClosed; bool ledgerChange = checkLastClosedLedger(peerList, networkClosed); - if(networkClosed.isZero())return; + if(networkClosed.isZero()) + return; // WRITEME: Unless we are in omFULL and in the process of doing a consensus, // we must count how many nodes share our LCL, how many nodes disagree with our LCL, @@ -435,7 +437,8 @@ void NetworkOPs::checkState(const boost::system::error_code& result) if ((mMode == omCONNECTED) && !ledgerChange) { // count number of peers that agree with us and UNL nodes whose validations we have for LCL // if the ledger is good enough, go to omTRACKING - TODO - setMode(omTRACKING); + if (!mNeedNetworkLedger) + setMode(omTRACKING); } if ((mMode == omTRACKING) && !ledgerChange ) @@ -617,6 +620,7 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger, bool duringCo else cLog(lsERROR) << "JUMP last closed ledger to " << newLedger->getHash(); + mNeedNetworkLedger = false; newLedger->setClosed(); Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*newLedger)); mLedgerMaster->switchLedgers(newLedger, openLedger); @@ -931,10 +935,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) { Json::Value jvObj(Json::objectValue); - jvObj["type"] = "ledgerAccepted"; - jvObj["seq"] = lpAccepted->getLedgerSeq(); - jvObj["hash"] = lpAccepted->getHash().ToString(); - jvObj["time"] = Json::Value::UInt(lpAccepted->getCloseTimeNC()); + jvObj["type"] = "ledgerAccepted"; + jvObj["ledger_closed_index"] = lpAccepted->getLedgerSeq(); + jvObj["ledger_closed"] = lpAccepted->getHash().ToString(); + jvObj["time"] = Json::Value::UInt(lpAccepted->getCloseTimeNC()); BOOST_FOREACH(InfoSub* ispListener, mSubLedger) { diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index 8f4d3557d..043ae313b 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -52,6 +52,7 @@ protected: typedef boost::unordered_map >::iterator subInfoMapIterator; OperatingMode mMode; + bool mNeedNetworkLedger; boost::posix_time::ptime mConnectTime; boost::asio::deadline_timer mNetTimer; boost::shared_ptr mConsensus; @@ -184,6 +185,7 @@ public: void setStandAlone() { setMode(omFULL); } void setStateTimer(); void newLCL(int proposers, int convergeTime, const uint256& ledgerHash); + void needNetworkLedger() { mNeedNetworkLedger = true; } void consensusViewChange(); int getPreviousProposers() { return mLastCloseProposers; } int getPreviousConvergeTime() { return mLastCloseConvergeTime; } diff --git a/src/Pathfinder.cpp b/src/Pathfinder.cpp index 9290f921a..23e596aab 100644 --- a/src/Pathfinder.cpp +++ b/src/Pathfinder.cpp @@ -104,6 +104,9 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet if (ele.mAccountID == mDstAccountID) { path.mPath.erase(path.mPath.begin()); path.mPath.erase(path.mPath.begin() + path.mPath.size()-1); + if (path.mPath.size() == 0) { + continue; + } retPathSet.addPath(path); return true; } @@ -118,7 +121,11 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet STPath new_path(path); STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut()); new_path.mPath.push_back(new_ele); + new_path.mCurrencyID = book->getCurrencyOut(); + new_path.mCurrentAccount = book->getCurrencyOut(); + pqueue.push(new_path); + } } @@ -136,8 +143,26 @@ bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet new_path.mPath.push_back(new_ele); pqueue.push(new_path); } + } // BOOST_FOREACHE + + // every offer that wants the source currency + std::vector books; + mOrderBook.getBooks(path.mCurrentAccount, path.mCurrencyID, books); + + BOOST_FOREACH(OrderBook::pointer book,books) + { + STPath new_path(path); + STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut()); + + new_path.mPath.push_back(new_ele); + new_path.mCurrentAccount=book->getIssuerOut(); + new_path.mCurrencyID=book->getCurrencyOut(); + + pqueue.push(new_path); + } - } + + } // else // enumerate all adjacent nodes, construct a new path and push it into the queue } // While } // if there is a ledger diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 6d669e03f..0723e7936 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1828,9 +1828,6 @@ Json::Value RPCServer::doSend(const Json::Value& params) if (params.size() >= 9) sSrcIssuer = params[8u].asString(); - if (params.size() >= 9) - sSrcIssuer = params[8u].asString(); - if (!naSeed.setSeedGeneric(params[0u].asString())) { return RPCError(rpcBAD_SEED); diff --git a/src/SerializeProto.h b/src/SerializeProto.h index 736a83a3e..df7d2f6d5 100644 --- a/src/SerializeProto.h +++ b/src/SerializeProto.h @@ -24,6 +24,7 @@ // 8-bit integers FIELD(CloseResolution, UINT8, 1) + FIELD(TemplateEntryType, UINT8, 2) // 16-bit integers FIELD(LedgerEntryType, UINT16, 1) @@ -123,9 +124,13 @@ // inner object // OBJECT/1 is reserved for end of object + FIELD(TemplateEntry, OBJECT, 1) // array of objects // ARRAY/1 is reserved for end of array FIELD(SigningAccounts, ARRAY, 2) FIELD(TxnSignatures, ARRAY, 3) FIELD(Signatures, ARRAY, 4) + FIELD(Template, ARRAY, 5) + FIELD(Necessary, ARRAY, 6) + FIELD(Sufficient, ARRAY, 7) diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 9b502409f..90438442a 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -595,6 +595,9 @@ public: // std::string getText() const; Json::Value getJson(int) const; + uint160 mCurrencyID; + uint160 mCurrentAccount; // what account is at the end of the path + std::vector::iterator begin() { return mPath.begin(); } std::vector::iterator end() { return mPath.end(); } std::vector::const_iterator begin() const { return mPath.begin(); } diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 25eabb907..7aaa64009 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -79,6 +79,7 @@ public: boost::unordered_set parseAccountIds(const Json::Value& jvArray); // Request-Response Commands + 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); @@ -303,6 +304,7 @@ Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) doFuncPtr dfpFunc; } commandsA[] = { // Request-Response Commands: + { "ledger_accept", &WSConnection::doLedgerAccept }, { "ledger_closed", &WSConnection::doLedgerClosed }, { "ledger_current", &WSConnection::doLedgerCurrent }, { "ledger_entry", &WSConnection::doLedgerEntry }, @@ -541,6 +543,20 @@ void WSConnection::doLedgerAccountsUnsubscribe(Json::Value& jvResult, const Json } } +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(); diff --git a/src/main.cpp b/src/main.cpp index 63a781213..17a46eefa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -98,8 +98,9 @@ int main(int argc, char* argv[]) ("test,t", "Perform unit tests.") ("parameters", po::value< vector >(), "Specify comma separated parameters.") ("verbose,v", "Increase log level.") - ("load","Load the current ledger from the local DB.") - ("start","Start from a fresh Ledger.") + ("load", "Load the current ledger from the local DB.") + ("start", "Start from a fresh Ledger.") + ("net", "Get the initial ledger from the network.") ; // Interpret positional arguments as --parameters. @@ -156,8 +157,9 @@ int main(int argc, char* argv[]) } } - if(vm.count("start")) theConfig.START_UP=Config::FRESH; - else if(vm.count("load")) theConfig.START_UP=Config::LOAD; + if (vm.count("start")) theConfig.START_UP = Config::FRESH; + else if (vm.count("load")) theConfig.START_UP = Config::LOAD; + else if (vm.count("net")) theConfig.START_UP = Config::NETWORK; if (iResult) { diff --git a/test/standalone-test.js b/test/standalone-test.js index 572b0d0b5..68d86b784 100644 --- a/test/standalone-test.js +++ b/test/standalone-test.js @@ -1,9 +1,8 @@ -var fs = require("fs"); -var buster = require("buster"); +var fs = require("fs"); +var buster = require("buster"); var server = require("./server.js"); var remote = require("../js/remote.js"); -var utils = require("../js/utils.js"); var config = require("./config.js"); // How long to wait for server to start. @@ -11,23 +10,6 @@ var serverDelay = 1500; buster.testRunner.timeout = 5000; -buster.testCase("Utils", { - "hexToString and stringToHex" : { - "Even: 123456" : function () { - buster.assert.equals("123456", utils.stringToHex(utils.hexToString("123456"))); - }, - "Odd: 12345" : function () { - buster.assert.equals("012345", utils.stringToHex(utils.hexToString("12345"))); - }, - "Under 10: 0" : function () { - buster.assert.equals("00", utils.stringToHex(utils.hexToString("0"))); - }, - "Under 10: 1" : function () { - buster.assert.equals("01", utils.stringToHex(utils.hexToString("1"))); - } - } -}); - buster.testCase("Standalone server startup", { "server start and stop" : function (done) { server.start("alpha", diff --git a/test/utils-test.js b/test/utils-test.js new file mode 100644 index 000000000..5b22fb5cc --- /dev/null +++ b/test/utils-test.js @@ -0,0 +1,26 @@ +var fs = require("fs"); +var buster = require("buster"); + +var utils = require("../js/utils.js"); + +buster.testCase("Utils", { + "hexToString and stringToHex" : { + "Even: 123456" : function () { + buster.assert.equals("123456", utils.stringToHex(utils.hexToString("123456"))); + }, + "Odd: 12345" : function () { + buster.assert.equals("012345", utils.stringToHex(utils.hexToString("12345"))); + }, + "Under 10: 0" : function () { + buster.assert.equals("00", utils.stringToHex(utils.hexToString("0"))); + }, + "Under 10: 1" : function () { + buster.assert.equals("01", utils.stringToHex(utils.hexToString("1"))); + }, + "Empty" : function () { + buster.assert.equals("", utils.stringToHex(utils.hexToString(""))); + } + } +}); + +// vim:sw=2:sts=2:ts=8