diff --git a/src/Application.cpp b/src/Application.cpp index daac98dd54..af3b17f9a0 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -70,11 +70,11 @@ void Application::run() if (!theConfig.DEBUG_LOGFILE.empty()) Log::setLogFile(theConfig.DEBUG_LOGFILE); - mSNTPClient.init(theConfig.SNTP_SERVERS); - boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService)); auxThread.detach(); + mSNTPClient.init(theConfig.SNTP_SERVERS); + // // Construct databases. // diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index 9dc7e6e92c..71730bfb19 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -21,7 +21,9 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index, if (!theApp->getHashNodeDB()) return true; if (mCache.touch(hash)) { +#ifdef HS_DEBUG Log(lsTRACE) << "HOS: " << hash.GetHex() << " store: incache"; +#endif return false; } diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 131e7d3b91..0cd6e2346d 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -57,7 +57,7 @@ Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mL } -Ledger::Ledger(bool dummy, Ledger& prevLedger) : +Ledger::Ledger(bool /* dummy */, Ledger& prevLedger) : mTotCoins(prevLedger.mTotCoins), mLedgerSeq(prevLedger.mLedgerSeq + 1), mParentCloseTime(prevLedger.mCloseTime), mCloseResolution(prevLedger.mCloseResolution), mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index 2407616bd6..6e13a883c8 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -11,7 +11,7 @@ #include "HashPrefixes.h" // #define LA_DEBUG -#define LEDGER_ACQUIRE_TIMEOUT 2 +#define LEDGER_ACQUIRE_TIMEOUT 1 #define TRUST_NETWORK PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterval(interval), mTimeouts(0), diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index ede0804cc4..aba77ea75f 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -26,7 +26,10 @@ TransactionAcquire::TransactionAcquire(const uint256& hash) : PeerSet(hash, 1), void TransactionAcquire::done() { if (mFailed) + { + Log(lsWARNING) << "Failed to acqiure TXs " << mHash.GetHex(); theApp->getOPs().mapComplete(mHash, SHAMap::pointer()); + } else theApp->getOPs().mapComplete(mHash, mMap); } @@ -48,7 +51,7 @@ void TransactionAcquire::trigger(Peer::pointer peer, bool timer) *(tmGL.add_nodeids()) = SHAMapNode().getRawString(); sendRequest(tmGL, peer); } - if (mHaveRoot) + else { std::vector nodeIDs; std::vector nodeHashes; ConsensusTransSetSF sf; @@ -67,10 +70,7 @@ void TransactionAcquire::trigger(Peer::pointer peer, bool timer) tmGL.set_itype(newcoin::liTS_CANDIDATE); for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it) *(tmGL.add_nodeids()) = it->getRawString(); - if (peer) - sendRequest(tmGL, peer); - else - sendRequest(tmGL); + sendRequest(tmGL, peer); return; } } @@ -224,7 +224,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::pointer pre } else { - Log(lsINFO) << "Entering consensus process, proposing"; + Log(lsINFO) << "Entering consensus process, watching"; mHaveCorrectLCL = true; mProposing = mValidating = false; } @@ -249,6 +249,9 @@ void LedgerConsensus::checkLCL() mPrevLedgerHash = netLgr; mAcquiringLedger = theApp->getMasterLedgerAcquire().findCreate(mPrevLedgerHash); std::vector peerList = theApp->getConnectionPool().getPeerVector(); + mHaveCorrectLCL = false; + mProposing = false; + mValidating = false; bool found = false; for (std::vector::const_iterator it = peerList.begin(), end = peerList.end(); it != end; ++it) if ((*it)->hasLedger(mPrevLedgerHash)) @@ -405,7 +408,12 @@ void LedgerConsensus::statePreClose() int proposersClosed = mPeerPositions.size(); // This ledger is open. This computes how long since the last ledger closed - int sinceClose = 1000 * (theApp->getOPs().getCloseTimeNC() - theApp->getOPs().getLastCloseNetTime()); + int lastCloseTime; + if (!anyTransactions && mPreviousLedger->getCloseAgree()) + lastCloseTime = mPreviousLedger->getCloseTimeNC(); + else + lastCloseTime = theApp->getOPs().getLastCloseNetTime(); + int sinceClose = 1000 * (theApp->getOPs().getCloseTimeNC() - lastCloseTime); if (sinceClose >= ContinuousLedgerTiming::shouldClose(anyTransactions, mPreviousProposers, proposersClosed, mPreviousMSeconds, sinceClose)) @@ -418,6 +426,8 @@ void LedgerConsensus::statePreClose() statusChange(newcoin::neCLOSING_LEDGER, *mPreviousLedger); takeInitialPosition(*theApp->getMasterLedger().closeLedger()); } + else + checkLCL(); } void LedgerConsensus::stateEstablish() @@ -441,9 +451,7 @@ void LedgerConsensus::stateEstablish() void LedgerConsensus::stateFinished() { // we are processing the finished ledger // logic of calculating next ledger advances us out of this state - - // CHECKME: Should we count proposers that didn't converge to our consensus set? - theApp->getOPs().newLCL(mPeerPositions.size(), mCurrentMSeconds, mNewLedgerHash); + // nothing to do } void LedgerConsensus::stateAccepted() @@ -544,6 +552,8 @@ void LedgerConsensus::updateOurPositions() ++thresh; } thresh = thresh * neededWeight / 100; + if (thresh == 0) + thresh = 1; for (std::map::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it) { @@ -553,6 +563,7 @@ void LedgerConsensus::updateOurPositions() Log(lsINFO) << "Close time consensus reached: " << closeTime; mHaveCloseTimeConsensus = true; closeTime = it->first; + thresh = it->second; } } } @@ -767,6 +778,7 @@ void LedgerConsensus::beginAccept() return; } + theApp->getOPs().newLCL(mPeerPositions.size(), mCurrentMSeconds, mNewLedgerHash); boost::thread thread(boost::bind(&LedgerConsensus::Saccept, shared_from_this(), consensusSet)); thread.detach(); } @@ -876,7 +888,8 @@ void LedgerConsensus::accept(SHAMap::pointer set) if (mHaveCorrectLCL) { Log(lsINFO) << "CNF tx " << mOurPosition->getCurrentHash().GetHex() << ", close " << closeTime; - Log(lsINFO) << "CNF oldLCL " << mPrevLedgerHash.GetHex(); + Log(lsINFO) << "CNF mode " << theApp->getOPs().getOperatingMode() + << ", oldLCL " << mPrevLedgerHash.GetHex(); } Ledger::pointer newLCL = boost::make_shared(false, boost::ref(*mPreviousLedger)); @@ -903,12 +916,12 @@ void LedgerConsensus::accept(SHAMap::pointer set) SerializedValidation::pointer v = boost::make_shared (newLCLHash, newLCL->getCloseTimeNC(), mValSeed, mProposing); v->setTrusted(); + Log(lsINFO) << "CNF Val " << newLCLHash.GetHex(); theApp->getValidations().addValidation(v); std::vector validation = v->getSigned(); newcoin::TMValidation val; val.set_validation(&validation[0], validation.size()); theApp->getConnectionPool().relayMessage(NULL, boost::make_shared(val, newcoin::mtVALIDATION)); - Log(lsINFO) << "CNF Val " << newLCLHash.GetHex(); } else Log(lsINFO) << "CNF newLCL " << newLCLHash.GetHex(); diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp index ecff1f441a..9243dd6ce0 100644 --- a/src/LedgerTiming.cpp +++ b/src/LedgerTiming.cpp @@ -17,13 +17,13 @@ int ContinuousLedgerTiming::shouldClose( int previousProposers, // proposers in the last closing int proposersClosed, // proposers who have currently closed this ledgers int previousMSeconds, // seconds the previous ledger took to reach consensus - int currentMSeconds) // seconds since the previous ledger closed + int currentMSeconds) // seconds since the previous ledger closed { if ((previousMSeconds < -1000) || (previousMSeconds > 600000) || (currentMSeconds < -1000) || (currentMSeconds > 600000)) { - Log(lsFATAL) << - boost::str(boost::format("CLC::shouldClose range error Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") + Log(lsWARNING) << + boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") % (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentMSeconds % previousMSeconds); return currentMSeconds; diff --git a/src/LedgerTiming.h b/src/LedgerTiming.h index 41e85762a1..eb8f2d56c8 100644 --- a/src/LedgerTiming.h +++ b/src/LedgerTiming.h @@ -4,8 +4,14 @@ // The number of seconds a ledger may remain idle before closing # define LEDGER_IDLE_INTERVAL 15 -// The number of seconds a validation remains current -# define LEDGER_MAX_INTERVAL (LEDGER_IDLE_INTERVAL * 4) +// The number of seconds a validation remains current after its ledger's close time +// This is a safety to protect against very old validations and the time it takes to adjust +// the close time accuracy window +# define LEDGER_VAL_INTERVAL 600 + +// The number of seconds before a close time that we consider a validation acceptable +// This protects against extreme clock errors +# define LEDGER_EARLY_INTERVAL 240 // The number of milliseconds we wait minimum to ensure participation # define LEDGER_MIN_CONSENSUS 2000 diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 875834a1e5..afbf87e32a 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -322,8 +322,11 @@ public: { if (trustedValidations > v.trustedValidations) return true; if (trustedValidations < v.trustedValidations) return false; - if (nodesUsing > v.nodesUsing) return true; - if (nodesUsing < v.nodesUsing) return false; + if (trustedValidations == 0) + { + if (nodesUsing > v.nodesUsing) return true; + if (nodesUsing < v.nodesUsing) return false; + } return highNode > v.highNode; } }; @@ -332,6 +335,7 @@ void NetworkOPs::checkState(const boost::system::error_code& result) { // Network state machine if (result == boost::asio::error::operation_aborted) return; + setStateTimer(); std::vector peerList = theApp->getConnectionPool().getPeerVector(); @@ -344,7 +348,6 @@ void NetworkOPs::checkState(const boost::system::error_code& result) Log(lsWARNING) << "Node count (" << peerList.size() << ") has fallen below quorum (" << theConfig.NETWORK_QUORUM << ")."; } - setStateTimer(); return; } if (mMode == omDISCONNECTED) @@ -356,7 +359,6 @@ void NetworkOPs::checkState(const boost::system::error_code& result) if (mConsensus) { mConsensus->timerEntry(); - setStateTimer(); return; } @@ -383,15 +385,13 @@ void NetworkOPs::checkState(const boost::system::error_code& result) // check if the ledger is good enough to go to omFULL // Note: Do not go to omFULL if we don't have the previous ledger // check if the ledger is bad enough to go to omCONNECTED -- TODO - if (theApp->getOPs().getNetworkTimeNC() < - (theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC() + 4)) + if (theApp->getOPs().getNetworkTimeNC() < theApp->getMasterLedger().getCurrentLedger()->getCloseTimeNC()) setMode(omFULL); - else - Log(lsINFO) << "Will try to go to FULL in consensus window"; } if (mMode == omFULL) { + // WRITEME // check if the ledger is bad enough to go to omTRACKING } @@ -399,7 +399,6 @@ void NetworkOPs::checkState(const boost::system::error_code& result) beginConsensus(networkClosed, theApp->getMasterLedger().getCurrentLedger()); if (mConsensus) mConsensus->timerEntry(); - setStateTimer(); } bool NetworkOPs::checkLastClosedLedger(const std::vector& peerList, uint256& networkClosed) @@ -458,9 +457,10 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis for (boost::unordered_map::iterator it = ledgers.begin(), end = ledgers.end(); it != end; ++it) { - Log(lsTRACE) << "L: " << it->first.GetHex() << - " t=" << it->second.trustedValidations << ", n=" << it->second.nodesUsing; - if ((it->second > bestVC) && !theApp->getValidations().isDeadLedger(it->first)) + bool isDead = theApp->getValidations().isDeadLedger(it->first); + Log(lsTRACE) << "L: " << it->first.GetHex() << ((isDead) ? " dead" : " live") << + " t=" << it->second.trustedValidations << ", n=" << it->second.nodesUsing; + if ((it->second > bestVC) && !isDead) { bestVC = it->second; closedLedger = it->first; @@ -632,20 +632,23 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, uint SHAMap::pointer NetworkOPs::getTXMap(const uint256& hash) { - if (!mConsensus) return SHAMap::pointer(); + if (!mConsensus) + return SHAMap::pointer(); return mConsensus->getTransactionTree(hash, false); } bool NetworkOPs::gotTXData(boost::shared_ptr peer, const uint256& hash, const std::list& nodeIDs, const std::list< std::vector >& nodeData) { - if (!mConsensus) return false; + if (!mConsensus) + return false; return mConsensus->peerGaveNodes(peer, hash, nodeIDs, nodeData); } bool NetworkOPs::hasTXSet(boost::shared_ptr peer, const uint256& set, newcoin::TxSetStatus status) { - if (!mConsensus) return false; + if (!mConsensus) + return false; return mConsensus->peerHasSet(peer, set, status); } @@ -676,10 +679,14 @@ void NetworkOPs::setMode(OperatingMode om) if ((om >= omCONNECTED) && (mMode == omDISCONNECTED)) mConnectTime = boost::posix_time::second_clock::universal_time(); Log l((om < mMode) ? lsWARNING : lsINFO); - if (om == omDISCONNECTED) l << "STATE->Disonnected"; - else if (om == omCONNECTED) l << "STATE->Connected"; - else if (om == omTRACKING) l << "STATE->Tracking"; - else l << "STATE->Full"; + if (om == omDISCONNECTED) + l << "STATE->Disonnected"; + else if (om == omCONNECTED) + l << "STATE->Connected"; + else if (om == omTRACKING) + l << "STATE->Tracking"; + else + l << "STATE->Full"; mMode = om; } diff --git a/src/Peer.cpp b/src/Peer.cpp index 99abf149f3..9b88610265 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -928,7 +928,7 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet) { SHAMap::pointer map; newcoin::TMLedgerData reply; - bool fatLeaves = true; + bool fatLeaves = true, fatRoot = false; if (packet.itype() == newcoin::liTS_CANDIDATE) { // Request is for a transaction candidate set @@ -952,6 +952,7 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet) reply.set_ledgerhash(txHash.begin(), txHash.size()); reply.set_type(newcoin::liTS_CANDIDATE); fatLeaves = false; // We'll already have most transactions + fatRoot = true; // Save a pass } else { // Figure out what ledger they want @@ -1057,7 +1058,7 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet) } std::vector nodeIDs; std::list< std::vector > rawNodes; - if(map->getNodeFat(mn, nodeIDs, rawNodes, fatLeaves)) + if(map->getNodeFat(mn, nodeIDs, rawNodes, fatRoot, fatLeaves)) { std::vector::iterator nodeIDIterator; std::list< std::vector >::iterator rawNodeIterator; @@ -1074,7 +1075,8 @@ void Peer::recvGetLedger(newcoin::TMGetLedger& packet) } } } - if (packet.has_requestcookie()) reply.set_requestcookie(packet.requestcookie()); + if (packet.has_requestcookie()) + reply.set_requestcookie(packet.requestcookie()); PackedMessage::pointer oPacket = boost::make_shared(reply, newcoin::mtLEDGER_DATA); sendPacket(oPacket); } diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 0ac81e6b54..46ea9eda7d 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -44,6 +44,7 @@ Json::Value RPCServer::RPCError(int iError) { rpcBAD_SEED, "badSeed", "Disallowed seed." }, { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency is malformed." }, + { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exists." }, { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, { rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets ammount malformed." }, @@ -70,8 +71,8 @@ Json::Value RPCServer::RPCError(int iError) { rpcPORT_MALFORMED, "portMalformed", "Port is malformed." }, { rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." }, { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, + { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency is malformed." }, - { rpcSRC_MISSING, "srcMissing", "Source account does not exist." }, { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, { rpcSUCCESS, "success", "Success." }, { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, @@ -279,7 +280,7 @@ Json::Value RPCServer::authorize(const uint256& uLedger, asSrc = mNetOps->getAccountState(uLedger, naSrcAccountID); if (!asSrc) { - return RPCError(rpcSRC_MISSING); + return RPCError(rpcSRC_ACT_MISSING); } NewcoinAddress naMasterGenerator; @@ -546,7 +547,7 @@ Json::Value RPCServer::doAccountInfo(const Json::Value ¶ms) { std::string strIdent = params[0u].asString(); bool bIndex; - int iIndex = 2 == params.size()? lexical_cast_s(params[1u].asString()) : 0; + int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; NewcoinAddress naAccount; Json::Value ret; @@ -1385,6 +1386,239 @@ Json::Value RPCServer::doRippleLineSet(const Json::Value& params) } } + +// ripple +// [noredeem] [noissue] +// + +// full|partial +// +// path: +// path + +// +// path_element: +// account [] [] [noredeem] [noissue] +// offer [] +Json::Value RPCServer::doRipple(const Json::Value ¶ms) +{ + NewcoinAddress naSeed; + STAmount saSrcAmountMax; + uint160 uSrcCurrencyID; + NewcoinAddress naSrcAccountID; + bool bSrcRedeem = true; + bool bSrcIssue = true; + bool bPartial; + bool bFull; + NewcoinAddress naDstAccountID; + STAmount saDstAmount; + uint160 uDstCurrencyID; + NewcoinAddress naDstIssuerID; + + std::vector vpnPath; + STPathSet spsPaths; + + 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[4u].asString())) + { + return RPCError(rpcSRC_AMT_MALFORMED); + } + + int iArg = 5; + + if (params[iArg].asString() == "noredeem") // [noredeem] + { + ++iArg; + bSrcRedeem = false; + } + + if (params[iArg].asString() == "noissue") // [noissue] + { + ++iArg; + bSrcIssue = false; + } + + // 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; + NewcoinAddress 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>"; + NewcoinAddress naAccountID; + uint160 uCurrencyID; + NewcoinAddress naIssuerID; + bool bRedeem = true; + bool bIssue = true; + + ++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; + } + + if (params.size() != iArg && params[iArg].asString() == "noredeem") // [noredeem] + { + ++iArg; + bRedeem = false; + } + + if (params.size() != iArg && params[iArg].asString() == "noissue") // [noissue] + { + ++iArg; + bIssue = false; + } + + spPath.addElement(STPathElement( + naAccountID.getAccountID(), + uCurrencyID, + naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0), + bRedeem, + bIssue)); + } + 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; + } + + if (params.size() != iArg && !naDstAccountID.setAccountID(params[iArg++].asString())) // + { + return RPCError(rpcDST_ACT_MALFORMED); + } + // + else if (params.size() != iArg + 3 || !saDstAmount.setFullValue(params[iArg].asString(), params[iArg+1].asString(), params[iArg+2].asString())) + { + Log(lsINFO) << "params.size(): " << params.size(); + Log(lsINFO) << " iArg: " << iArg; + Log(lsINFO) << " Amount: " << params[iArg].asString(); + Log(lsINFO) << "Currency: " << params[iArg+1].asString(); + Log(lsINFO) << " Issuer: " << params[iArg+2].asString(); + + return RPCError(rpcDST_AMT_MALFORMED); + } + + uint256 uLedger = mNetOps->getCurrentLedger(); + AccountState::pointer asDst = mNetOps->getAccountState(uLedger, naDstAccountID); + STAmount saFee = theConfig.FEE_DEFAULT; + + NewcoinAddress naVerifyGenerator; + NewcoinAddress naAccountPublic; + NewcoinAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uLedger, 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) + { + 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); + + 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_lines_get || [] Json::Value RPCServer::doRippleLinesGet(const Json::Value ¶ms) { @@ -1393,7 +1627,7 @@ Json::Value RPCServer::doRippleLinesGet(const Json::Value ¶ms) std::string strIdent = params[0u].asString(); bool bIndex; - int iIndex = 2 == params.size()? lexical_cast_s(params[1u].asString()) : 0; + int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; NewcoinAddress naAccount; @@ -1501,11 +1735,10 @@ Json::Value RPCServer::doSend(const Json::Value& params) NewcoinAddress naSeed; NewcoinAddress naSrcAccountID; NewcoinAddress naDstAccountID; - STAmount saSrcAmount; + STAmount saSrcAmountMax; STAmount saDstAmount; std::string sSrcCurrency; std::string sDstCurrency; - uint256 uLedger = mNetOps->getCurrentLedger(); if (params.size() >= 5) sDstCurrency = params[4u].asString(); @@ -1529,12 +1762,13 @@ Json::Value RPCServer::doSend(const Json::Value& params) { return RPCError(rpcDST_AMT_MALFORMED); } - else if (params.size() >= 6 && !saSrcAmount.setFullValue(params[5u].asString(), sSrcCurrency)) + else if (params.size() >= 6 && !saSrcAmountMax.setFullValue(params[5u].asString(), sSrcCurrency)) { return RPCError(rpcSRC_AMT_MALFORMED); } else { + uint256 uLedger = mNetOps->getCurrentLedger(); AccountState::pointer asDst = mNetOps->getAccountState(uLedger, naDstAccountID); bool bCreate = !asDst; STAmount saFee = bCreate ? theConfig.FEE_ACCOUNT_CREATE : theConfig.FEE_DEFAULT; @@ -1551,10 +1785,10 @@ Json::Value RPCServer::doSend(const Json::Value& params) return obj; if (params.size() < 6) - saSrcAmount = saDstAmount; + saSrcAmountMax = saDstAmount; // Do a few simple checks. - if (!saSrcAmount.isNative()) + if (!saSrcAmountMax.isNative()) { Log(lsINFO) << "doSend: Ripple"; @@ -1567,11 +1801,11 @@ Json::Value RPCServer::doSend(const Json::Value& params) return RPCError(rpcINSUF_FUNDS); } - else if (saDstAmount.isNative() && saSrcAmount < saDstAmount) + else if (saDstAmount.isNative() && saSrcAmountMax < saDstAmount) { // Not enough native currency. - Log(lsINFO) << "doSend: Insufficient funds: src=" << saSrcAmount.getText() << " dst=" << saDstAmount.getText(); + Log(lsINFO) << "doSend: Insufficient funds: src=" << saSrcAmountMax.getText() << " dst=" << saDstAmount.getText(); return RPCError(rpcINSUF_FUNDS); } @@ -1582,7 +1816,7 @@ Json::Value RPCServer::doSend(const Json::Value& params) if (asDst) { // Destination exists, ordinary send. - STPathSet spPaths; + STPathSet spsPaths; trans = Transaction::sharedPayment( naAccountPublic, naAccountPrivate, @@ -1592,8 +1826,8 @@ Json::Value RPCServer::doSend(const Json::Value& params) 0, // YYY No source tag naDstAccountID, saDstAmount, - saSrcAmount, - spPaths); + saSrcAmountMax, + spsPaths); } else { @@ -1618,8 +1852,8 @@ Json::Value RPCServer::doSend(const Json::Value& params) obj["create"] = bCreate; obj["srcAccountID"] = naSrcAccountID.humanAccountID(); obj["dstAccountID"] = naDstAccountID.humanAccountID(); - obj["srcAmount"] = saSrcAmount.getText(); - obj["srcISO"] = saSrcAmount.getHumanCurrency(); + obj["srcAmountMax"] = saSrcAmountMax.getText(); + obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); obj["dstAmount"] = saDstAmount.getText(); obj["dstISO"] = saDstAmount.getHumanCurrency(); @@ -2156,13 +2390,21 @@ Json::Value RPCServer::doWalletCreate(const Json::Value& params) return obj; } -// wallet_propose +// wallet_propose [] +// is only for testing. Master seeds should only be generated randomly. Json::Value RPCServer::doWalletPropose(const Json::Value& params) { NewcoinAddress naSeed; NewcoinAddress naAccount; - naSeed.setSeedRandom(); + if (params.empty()) + { + naSeed.setSeedRandom(); + } + else + { + naSeed = NewcoinAddress::createSeedGeneric(params[0u].asString()); + } NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSeed); naAccount.setAccountPublic(naGenerator, 0); @@ -2332,7 +2574,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { "password_fund", &RPCServer::doPasswordFund, 2, 3, false, optCurrent }, { "password_set", &RPCServer::doPasswordSet, 2, 3, false, optNetwork }, { "peers", &RPCServer::doPeers, 0, 0, true }, - { "ripple_lines_get", &RPCServer::doRippleLinesGet, 1, 2, false, optCurrent|optClosed }, + { "ripple", &RPCServer::doRipple, 10, -1, false, optCurrent|optClosed }, + { "ripple_lines_get", &RPCServer::doRippleLinesGet, 1, 2, false, optCurrent }, { "ripple_line_set", &RPCServer::doRippleLineSet, 4, 7, false, optCurrent }, { "send", &RPCServer::doSend, 3, 7, false, optCurrent }, { "server_info", &RPCServer::doServerInfo, 0, 0, true }, @@ -2354,7 +2597,7 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { "wallet_add", &RPCServer::doWalletAdd, 3, 5, false, optCurrent }, { "wallet_claim", &RPCServer::doWalletClaim, 2, 4, false, optNetwork }, { "wallet_create", &RPCServer::doWalletCreate, 3, 4, false, optCurrent }, - { "wallet_propose", &RPCServer::doWalletPropose, 0, 0, false, }, + { "wallet_propose", &RPCServer::doWalletPropose, 0, 1, false, }, { "wallet_seed", &RPCServer::doWalletSeed, 0, 1, false, }, { "login", &RPCServer::doLogin, 2, 2, true }, @@ -2373,7 +2616,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { return RPCError(rpcNO_PERMISSION); } - else if (params.size() < commandsA[i].iMinParams || params.size() > commandsA[i].iMaxParams) + else if (params.size() < commandsA[i].iMinParams + || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) { return RPCError(rpcINVALID_PARAMS); } diff --git a/src/RPCServer.h b/src/RPCServer.h index 539dcf2e7d..eb847b2e07 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -50,6 +50,7 @@ public: rpcACT_MALFORMED, rpcBAD_SEED, rpcDST_ACT_MALFORMED, + rpcDST_ACT_MISSING, rpcDST_AMT_MALFORMED, rpcGETS_ACT_MALFORMED, rpcGETS_AMT_MALFORMED, @@ -63,6 +64,7 @@ public: rpcPORT_MALFORMED, rpcPUBLIC_MALFORMED, rpcSRC_ACT_MALFORMED, + rpcSRC_ACT_MISSING, rpcSRC_AMT_MALFORMED, // Internal error (should never happen) @@ -146,6 +148,7 @@ private: Json::Value doPasswordFund(const Json::Value& params); Json::Value doPasswordSet(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); diff --git a/src/SHAMap.h b/src/SHAMap.h index 4bfd7999db..2f9e73d529 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -327,7 +327,7 @@ public: void getMissingNodes(std::vector& nodeIDs, std::vector& hashes, int max, SHAMapSyncFilter* filter); bool getNodeFat(const SHAMapNode& node, std::vector& nodeIDs, - std::list >& rawNode, bool fatLeaves); + std::list >& rawNode, bool fatRoot, bool fatLeaves); bool getRootNode(Serializer& s, SHANodeFormat format); bool addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format); bool addRootNode(const std::vector& rootNode, SHANodeFormat format); diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index 25c2d019a0..ed76558906 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -86,7 +86,7 @@ void SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector& nodeIDs, - std::list >& rawNodes, bool fatLeaves) + std::list >& rawNodes, bool fatRoot, bool fatLeaves) { // Gets a node and some of its children boost::recursive_mutex::scoped_lock sl(mLock); @@ -102,7 +102,7 @@ bool SHAMap::getNodeFat(const SHAMapNode& wanted, std::vector& nodeI node->addRaw(s, snfWIRE); rawNodes.push_back(s.peekData()); - if (node->isRoot() || node->isLeaf()) // don't get a fat root, can't get a fat leaf + if ((!fatRoot && node->isRoot()) || node->isLeaf()) // don't get a fat root, can't get a fat leaf return true; for (int i = 0; i < 16; ++i) @@ -141,7 +141,8 @@ bool SHAMap::addRootNode(const std::vector& rootNode, SHANodeForm } SHAMapTreeNode::pointer node = boost::make_shared(SHAMapNode(), rootNode, 0, format); - if (!node) return false; + if (!node) + return false; #ifdef DEBUG node->dump(); @@ -444,7 +445,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test ) destination.setSynching(); - if (!source.getNodeFat(SHAMapNode(), nodeIDs, gotNodes, (rand() % 2) == 0)) + if (!source.getNodeFat(SHAMapNode(), nodeIDs, gotNodes, (rand() % 2) == 0, (rand() % 2) == 0)) { Log(lsFATAL) << "GetNodeFat(root) fails"; BOOST_FAIL("GetNodeFat"); @@ -481,7 +482,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test ) // get as many nodes as possible based on this information for (nodeIDIterator = nodeIDs.begin(); nodeIDIterator != nodeIDs.end(); ++nodeIDIterator) { - if (!source.getNodeFat(*nodeIDIterator, gotNodeIDs, gotNodes, (rand() % 2) == 0)) + if (!source.getNodeFat(*nodeIDIterator, gotNodeIDs, gotNodes, (rand() % 2) == 0, (rand() % 2) == 0)) { Log(lsFATAL) << "GetNodeFat fails"; BOOST_FAIL("GetNodeFat"); diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index df263827e3..cd759e1ee2 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -346,6 +346,12 @@ STPathSet* STPathSet::construct(SerializerIterator& s, const char *name) } while(1); } +bool STPathSet::isEquivalent(const SerializedType& t) const +{ + const STPathSet* v = dynamic_cast(&t); + return v && (value == v->value); +} + int STPathSet::getLength() const { int ret = 0; diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index fc5e80d0dd..41bcafc33d 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -290,7 +290,7 @@ public: bool isNative() const { return mIsNative; } bool isZero() const { return mValue == 0; } - bool isNonZero() const { return mValue != 0; } + bool isNonZero() const { return mValue != 0; } bool isNegative() const { return mIsNegative && !isZero(); } bool isPositive() const { return !mIsNegative && !isZero(); } bool isGEZero() const { return !mIsNegative; } @@ -528,23 +528,31 @@ public: typeCurrency = 0x10, // Currency follows. typeIssuer = 0x20, // Issuer follows. typeBoundary = 0xFF, // Boundary between alternate paths. - typeValidBits = 0x3E, // Bits that may be non-zero. + typeValidBits = ( + typeAccount + | typeRedeem + | typeIssue + | typeCurrency + | typeIssuer + ), // Bits that may be non-zero. }; protected: int mType; uint160 mAccountID; - uint160 mCurrency; + uint160 mCurrencyID; uint160 mIssuerID; public: - STPathElement(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID) - : mAccountID(uAccountID), mCurrency(uCurrency), mIssuerID(uIssuerID) + STPathElement(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bRedeem=false, bool bIssue=false) + : mAccountID(uAccountID), mCurrencyID(uCurrencyID), mIssuerID(uIssuerID) { mType = (uAccountID.isZero() ? 0 : STPathElement::typeAccount) - | (uCurrency.isZero() ? 0 : STPathElement::typeCurrency) - | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer); + | (uCurrencyID.isZero() ? 0 : STPathElement::typeCurrency) + | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer) + | (bRedeem ? STPathElement::typeRedeem : 0) + | (bIssue ? STPathElement::typeIssue : 0); } int getNodeType() const { return mType; } @@ -553,8 +561,11 @@ public: // Nodes are either an account ID or a offer prefix. Offer prefixs denote a class of offers. const uint160& getAccountID() const { return mAccountID; } - const uint160& getCurrency() const { return mCurrency; } + const uint160& getCurrency() const { return mCurrencyID; } const uint160& getIssuerID() const { return mIssuerID; } + + bool operator==(const STPathElement& t) const + { return mType == t.mType && mAccountID == t.mAccountID && mCurrencyID == t.mCurrencyID && mIssuerID == mIssuerID; } }; class STPath @@ -580,6 +591,8 @@ public: std::vector::iterator end() { return mPath.end(); } std::vector::const_iterator begin() const { return mPath.begin(); } std::vector::const_iterator end() const { return mPath.end(); } + + bool operator==(const STPath& t) const { return mPath == t.mPath; } }; inline std::vector::iterator range_begin(STPath & x) @@ -647,6 +660,8 @@ public: void clear() { value.clear(); } void addPath(const STPath& e) { value.push_back(e); } + virtual bool isEquivalent(const SerializedType& t) const; + std::vector::iterator begin() { return value.begin(); } std::vector::iterator end() { return value.end(); } std::vector::const_iterator begin() const { return value.begin(); } diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 466b3d7f2e..1502e4b52d 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -5,16 +5,18 @@ #include "LedgerTiming.h" #include "Log.h" -bool ValidationCollection::addValidation(SerializedValidation::pointer val) +// #define VC_DEBUG + +bool ValidationCollection::addValidation(SerializedValidation::pointer& val) { NewcoinAddress signer = val->getSignerPublic(); bool isCurrent = false; - if (theApp->getUNL().nodeInUNL(signer)) + if (theApp->getUNL().nodeInUNL(signer) || val->isTrusted()) { val->setTrusted(); uint32 now = theApp->getOPs().getCloseTimeNC(); uint32 valClose = val->getCloseTime(); - if ((now > (valClose - 4)) && (now < (valClose + LEDGER_MAX_INTERVAL))) + if ((now > (valClose - LEDGER_EARLY_INTERVAL)) && (now < (valClose + LEDGER_VAL_INTERVAL))) isCurrent = true; else Log(lsWARNING) << "Received stale validation now=" << now << ", close=" << valClose; @@ -51,7 +53,7 @@ bool ValidationCollection::addValidation(SerializedValidation::pointer val) } Log(lsINFO) << "Val for " << hash.GetHex() << " from " << signer.humanNodePublic() - << " added " << (val->isTrusted() ? "trusted" : "UNtrusted"); + << " added " << (val->isTrusted() ? "trusted/" : "UNtrusted/") << (isCurrent ? "current" : "stale"); return isCurrent; } @@ -81,8 +83,14 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren if (isTrusted && currentOnly) { uint32 closeTime = vit->second->getCloseTime(); - if ((now < closeTime) || (now > (closeTime + 2 * LEDGER_MAX_INTERVAL))) + if ((now < (closeTime - LEDGER_EARLY_INTERVAL)) || (now > (closeTime + LEDGER_VAL_INTERVAL))) isTrusted = false; + else + { +#ifdef VC_DEBUG + Log(lsINFO) << "VC: Untrusted due to time " << ledger.GetHex(); +#endif + } } if (isTrusted) ++trusted; @@ -90,6 +98,9 @@ void ValidationCollection::getValidationCount(const uint256& ledger, bool curren ++untrusted; } } +#ifdef VC_DEBUG + Log(lsINFO) << "VC: " << ledger.GetHex() << "t:" << trusted << " u:" << untrusted; +#endif } int ValidationCollection::getTrustedValidationCount(const uint256& ledger) @@ -129,22 +140,27 @@ boost::unordered_map ValidationCollection::getCurrentValidations() { boost::mutex::scoped_lock sl(mValidationLock); boost::unordered_map::iterator it = mCurrentValidations.begin(); - bool anyNew = false; while (it != mCurrentValidations.end()) { ValidationPair& pair = it->second; - if (pair.oldest && (now > (pair.oldest->getCloseTime() + LEDGER_MAX_INTERVAL))) + if (pair.oldest && (now > (pair.oldest->getCloseTime() + LEDGER_VAL_INTERVAL))) { +#ifdef VC_DEBUG + Log(lsINFO) << "VC: " << it->first.GetHex() << " removeOldestStale"; +#endif mStaleValidations.push_back(pair.oldest); pair.oldest = SerializedValidation::pointer(); - anyNew = true; + condWrite(); } - if (pair.newest && (now > (pair.newest->getCloseTime() + LEDGER_MAX_INTERVAL))) + if (pair.newest && (now > (pair.newest->getCloseTime() + LEDGER_VAL_INTERVAL))) { +#ifdef VC_DEBUG + Log(lsINFO) << "VC: " << it->first.GetHex() << " removeNewestStale"; +#endif mStaleValidations.push_back(pair.newest); pair.newest = SerializedValidation::pointer(); - anyNew = true; + condWrite(); } if (!pair.newest && !pair.oldest) it = mCurrentValidations.erase(it); @@ -152,21 +168,23 @@ boost::unordered_map ValidationCollection::getCurrentValidations() { if (pair.oldest) { - Log(lsTRACE) << "OLD " << pair.oldest->getLedgerHash().GetHex() << " " << +#ifdef VC_DEBUG + Log(lsTRACE) << "VC: OLD " << pair.oldest->getLedgerHash().GetHex() << " " << boost::lexical_cast(pair.oldest->getCloseTime()); +#endif ++ret[pair.oldest->getLedgerHash()]; } if (pair.newest) { - Log(lsTRACE) << "NEW " << pair.newest->getLedgerHash().GetHex() << " " << +#ifdef VC_DEBUG + Log(lsTRACE) << "VC: NEW " << pair.newest->getLedgerHash().GetHex() << " " << boost::lexical_cast(pair.newest->getCloseTime()); +#endif ++ret[pair.newest->getLedgerHash()]; } ++it; } } - if (anyNew) - condWrite(); } return ret; @@ -227,8 +245,7 @@ void ValidationCollection::condWrite() void ValidationCollection::doWrite() { static boost::format insVal("INSERT INTO LedgerValidations " - "(LedgerHash,NodePubKey,Flags,CloseTime,Signature) VALUES " - "('%s','%s','%u','%u',%s);"); + "(LedgerHash,NodePubKey,Flags,CloseTime,Signature) VALUES ('%s','%s','%u','%u',%s);"); boost::mutex::scoped_lock sl(mValidationLock); assert(mWriting); diff --git a/src/ValidationCollection.h b/src/ValidationCollection.h index 2198a967fe..1ed522dc54 100644 --- a/src/ValidationCollection.h +++ b/src/ValidationCollection.h @@ -38,7 +38,7 @@ protected: public: ValidationCollection() : mWriting(false) { ; } - bool addValidation(SerializedValidation::pointer); + bool addValidation(SerializedValidation::pointer&); ValidationSet getValidations(const uint256& ledger); void getValidationCount(const uint256& ledger, bool currentOnly, int& trusted, int& untrusted); diff --git a/src/main.cpp b/src/main.cpp index 49d23edf31..155408170a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,6 +58,7 @@ void printHelp(const po::options_description& desc) cout << " password_fund []" << endl; cout << " password_set []" << endl; cout << " peers" << endl; + cout << " ripple ..." << endl; cout << " ripple_lines_get || []" << endl; cout << " ripple_line_set [] []" << endl; cout << " send [] [] []" << endl; @@ -75,7 +76,7 @@ void printHelp(const po::options_description& desc) cout << " wallet_accounts " << endl; cout << " wallet_claim [] []" << endl; cout << " wallet_seed [||]" << endl; - cout << " wallet_propose" << endl; + cout << " wallet_propose []" << endl; } int main(int argc, char* argv[])