From c43934ab6f2d329386c15042c5865c915e502c3f Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 05:06:24 -0700 Subject: [PATCH 01/20] Fix lots of places I used 'empty' instead of 'clear'. This is why I like 'isEmpty'. --- src/Application.cpp | 2 +- src/LedgerAcquire.cpp | 2 +- src/PubKeyCache.cpp | 2 +- src/SerializedObject.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index a5d458dc18..fa8861fa13 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -215,8 +215,8 @@ void Application::loadOldLedger() std::cout << "No Ledger found?" << std::endl; exit(-1); } - lastLedger->setClosed(); + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); mMasterLedger.switchLedgers(lastLedger, openLedger); mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index d9056fdad4..1eac7dc3f8 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -124,7 +124,7 @@ void LedgerAcquire::done() setComplete(); mLock.lock(); triggers = mOnComplete; - mOnComplete.empty(); + mOnComplete.clear(); mLock.unlock(); if (mLedger) diff --git a/src/PubKeyCache.cpp b/src/PubKeyCache.cpp index 503fefc3d1..4ca514039e 100644 --- a/src/PubKeyCache.cpp +++ b/src/PubKeyCache.cpp @@ -69,6 +69,6 @@ CKey::pointer PubKeyCache::store(const NewcoinAddress& id, const CKey::pointer& void PubKeyCache::clear() { boost::mutex::scoped_lock sl(mLock); - mCache.empty(); + mCache.clear(); } // vim:ts=4 diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index fb30248f64..8e6e9df605 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -120,8 +120,8 @@ std::auto_ptr STObject::makeDeserializedObject(SerializedTypeID void STObject::set(const std::vector& type) { - mData.empty(); - mType.empty(); + mData.clear(); + mType.clear(); BOOST_FOREACH(const SOElement::ptr& elem, type) { @@ -138,7 +138,7 @@ bool STObject::setType(const std::vector &type) boost::ptr_vector newData; bool valid = true; - mType.empty(); + mType.clear(); BOOST_FOREACH(const SOElement::ptr& elem, type) { bool match = false; @@ -204,7 +204,7 @@ bool STObject::isFieldAllowed(SField::ref field) bool STObject::set(SerializerIterator& sit, int depth) { // return true = terminated with end-of-object - mData.empty(); + mData.clear(); while (!sit.empty()) { int type, field; From 61c9d30732948c84900bf2c65c7558601e623021 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 05:13:29 -0700 Subject: [PATCH 02/20] Set up to fix the bug Jed reported and also support faulting in nodes under ledgers. --- src/Ledger.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 35ffc7dcfe..737579df73 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -24,7 +24,8 @@ SETUP_LOG(); Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1), mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), - mTransactionMap(new SHAMap(smtTRANSACTION)), mAccountStateMap(new SHAMap(smtFREE)) + mTransactionMap(boost::make_shared(smtTRANSACTION)), + mAccountStateMap(boost::make_shared(smtSTATE)) { // special case: put coins in root account AccountState::pointer startAccount = boost::make_shared(masterID); @@ -42,9 +43,19 @@ Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint25 : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins), mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime), mCloseResolution(closeResolution), mCloseFlags(closeFlags), - mClosed(false), mValidHash(false), mAccepted(false), mImmutable(isMutable) + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(isMutable), + mTransactionMap(boost::make_shared(smtTRANSACTION)), + mAccountStateMap(boost::make_shared(smtSTATE)) { updateHash(); + if (mTransHash.isNonZero()) + { + // WRITEME + } + if (mAccountHash.isNonZero()) + { + // WRITEME + } } Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq), @@ -62,7 +73,8 @@ 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), - mTransactionMap(new SHAMap(smtTRANSACTION)), mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true)) + mTransactionMap(boost::make_shared(smtTRANSACTION)), + mAccountStateMap(prevLedger.mAccountStateMap->snapShot(true)) { // Create a new ledger that follows this one prevLedger.updateHash(); mParentHash = prevLedger.getHash(); From 8c790b1d8824c44f5a3288985fc2c744fb428360 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 05:17:35 -0700 Subject: [PATCH 03/20] Remove dead code. --- src/NetworkOPs.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 855128bdf2..6a01d4268f 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -585,7 +585,6 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis if (!mAcquiringLedger->isComplete()) { // add more peers int count = 0; - std::vector peers=theApp->getConnectionPool().getPeerVector(); BOOST_FOREACH(Peer::ref it, peerList) { if (it->getClosedLedgerHash() == closedLedger) From 07cd8ad9bd22c605cda0b6bca7df384a31456962 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 06:04:06 -0700 Subject: [PATCH 04/20] Some extra debug on the dirty node flush code. It appears good. --- src/Ledger.cpp | 9 +++++---- src/SHAMap.cpp | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 737579df73..92f5621543 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -340,10 +340,11 @@ void Ledger::saveAcceptedLedger(Ledger::ref ledger) ledger->mAccountHash.GetHex() % ledger->mTransHash.GetHex())); // write out dirty nodes - while(ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) - { ; } - while(ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) - { ; } + int fc; + while ((fc = ledger->mTransactionMap->flushDirty(256, hotTRANSACTION_NODE, ledger->mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty transaction nodes"; } + while ((fc = ledger->mAccountStateMap->flushDirty(256, hotACCOUNT_NODE, ledger->mLedgerSeq)) > 0) + { cLog(lsINFO) << "Flushed " << fc << " dirty state nodes"; } ledger->disarmDirty(); SHAMap& txSet = *ledger->peekTransactionMap(); diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index efd97e2668..3d6f706202 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -725,6 +725,8 @@ int SHAMap::flushDirty(int maxNodes, HashedObjectType t, uint32 seq) boost::unordered_map::iterator it = dirtyNodes.begin(); while (it != dirtyNodes.end()) { + tLog(mType == smtTRANSACTION, lsDEBUG) << "TX node write " << it->first; + tLog(mType == smtSTATE, lsDEBUG) << "STATE node write " << it->first; s.erase(); it->second->addRaw(s, snfPREFIX); theApp->getHashedObjectStore().store(t, seq, s.peekData(), s.getSHA512Half()); From 55e38c40f72a9e7e60c54fa4bf69cb840c602bf9 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:01:03 -0700 Subject: [PATCH 05/20] Cleanups. --- src/SHAMap.cpp | 10 ++++++++++ src/SHAMap.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 3d6f706202..09877fa403 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -690,7 +690,10 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui HashedObject::pointer obj(theApp->getHashedObjectStore().retrieve(hash)); if (!obj) + { + Log(lsTRACE) << "fetchNodeExternal: missing " << hash; throw SHAMapMissingNode(mType, id, hash); + } assert(Serializer::getSHA512Half(obj->getData()) == hash); try @@ -708,6 +711,13 @@ SHAMapTreeNode::pointer SHAMap::fetchNodeExternal(const SHAMapNode& id, const ui } } +void SHAMap::fetchRoot(const uint256& hash) +{ + root = fetchNodeExternal(SHAMapNode(), hash); + root->makeInner(); + mTNByID[*root] = root; +} + void SHAMap::armDirty() { // begin saving dirty nodes ++mSeq; diff --git a/src/SHAMap.h b/src/SHAMap.h index 72a4f23c3e..f144b5ca9f 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -273,6 +273,8 @@ public: bool hasTargetIndex() const { return !mTargetIndex.isZero(); } }; +extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&); + class SHAMap { public: @@ -330,6 +332,7 @@ public: ScopedLock Lock() const { return ScopedLock(mLock); } bool hasNode(const SHAMapNode& id); + void fetchRoot(const uint256& hash); // normal hash access functions bool hasItem(const uint256& id); From cf6206f19b2a52e5b69d2f2c2403b6c500c22ab4 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:01:23 -0700 Subject: [PATCH 06/20] Some nicer logging. --- src/SHAMapNodes.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index 746176aba3..ad1e27749f 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -18,6 +18,9 @@ std::string SHAMapNode::getString() const { + if ((mDepth == 0) && (mNodeID.isZero())) + return "NodeID(root)"; + return str(boost::format("NodeID(%s,%s)") % boost::lexical_cast(mDepth) % mNodeID.GetHex()); @@ -507,4 +510,16 @@ bool SHAMapTreeNode::setChildHash(int m, const uint256 &hash) return updateHash(); } +std::ostream& operator<<(std::ostream& out, const SHAMapMissingNode& mn) +{ + if (mn.getMapType() == smtTRANSACTION) + out << "Missing/TXN(" << mn.getNodeID() << ")"; + else if (mn.getMapType() == smtSTATE) + out << "Missing/STA(" << mn.getNodeID() << ")"; + else + out << "Missing/" << mn.getNodeID(); +} + + + // vim:ts=4 From e18f8c47f5ce7a94e37a71e0ddc34c1525c79305 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:01:40 -0700 Subject: [PATCH 07/20] Use the local hashed object store to jump start fetching a ledger. --- src/LedgerAcquire.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/LedgerAcquire.h | 1 + 2 files changed, 40 insertions(+) diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index 1eac7dc3f8..95cf414156 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -96,6 +96,45 @@ LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE #endif } +bool LedgerAcquire::tryLocal() +{ // return value: true = no more work to do + HashedObject::pointer node = theApp->getHashedObjectStore().retrieve(mHash); + if (!node) + return false; + + mLedger = boost::make_shared(strCopy(node->getData())); + assert(mLedger->getHash() == mHash); + mHaveBase = true; + + if (!mLedger->getTransHash()) + mHaveTransactions = true; + else + { + try + { + mLedger->peekTransactionMap()->fetchRoot(mLedger->getTransHash()); + } + catch (SHAMapMissingNode&) + { + } + } + + if (!mLedger->getAccountHash()) + mHaveState = true; + else + { + try + { + mLedger->peekAccountStateMap()->fetchRoot(mLedger->getAccountHash()); + } + catch (SHAMapMissingNode&) + { + } + } + + return mHaveTransactions && mHaveState; +} + void LedgerAcquire::onTimer() { if (getTimeouts() > 6) diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index b589618299..7e2da168d8 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -94,6 +94,7 @@ public: bool takeAsNode(const std::list& IDs, const std::list >& data); bool takeAsRootNode(const std::vector& data); void trigger(Peer::ref, bool timer); + bool tryLocal(); }; class LedgerAcquireMaster From 4a8668557447485bf972609da140eb971ca3d253 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:02:02 -0700 Subject: [PATCH 08/20] A ledger fetched from SQL is always immutable. --- src/Ledger.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ledger.h b/src/Ledger.h index 41e30d2dbf..ffb9dbd81e 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); // used for database ledgers Ledger(const std::vector& rawLedger); From 37f246396fc4bf4861104c23787c86de5cab3b01 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:02:25 -0700 Subject: [PATCH 09/20] Try to populate the ledger root. --- src/Ledger.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 92f5621543..53491b9915 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -39,23 +39,21 @@ Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(s } Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, - uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq,bool isMutable) + uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, uint32 ledgerSeq) : mParentHash(parentHash), mTransHash(transHash), mAccountHash(accountHash), mTotCoins(totCoins), mLedgerSeq(ledgerSeq), mCloseTime(closeTime), mParentCloseTime(parentCloseTime), mCloseResolution(closeResolution), mCloseFlags(closeFlags), - mClosed(false), mValidHash(false), mAccepted(false), mImmutable(isMutable), - mTransactionMap(boost::make_shared(smtTRANSACTION)), - mAccountStateMap(boost::make_shared(smtSTATE)) + mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true), + mTransactionMap(boost::make_shared(smtTRANSACTION, transHash)), + mAccountStateMap(boost::make_shared(smtSTATE, accountHash)) { updateHash(); if (mTransHash.isNonZero()) - { - // WRITEME - } + mTransactionMap->fetchRoot(mTransHash); if (mAccountHash.isNonZero()) - { - // WRITEME - } + mAccountStateMap->fetchRoot(mAccountHash); + mTransactionMap->setImmutable(); + mAccountStateMap->setImmutable(); } Ledger::Ledger(Ledger& ledger, bool isMutable) : mTotCoins(ledger.mTotCoins), mLedgerSeq(ledger.mLedgerSeq), @@ -438,7 +436,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) } Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, - closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq, true)); + closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq)); if (ret->getHash() != ledgerHash) { if (sLog(lsERROR)) From ac8e2292cf7bed6e7b48193932aa751248bcafcf Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:03:14 -0700 Subject: [PATCH 10/20] Add a comment. --- src/Ledger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ledger.cpp b/src/Ledger.cpp index 53491b9915..5be6eb02b2 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -46,7 +46,7 @@ Ledger::Ledger(const uint256 &parentHash, const uint256 &transHash, const uint25 mClosed(false), mValidHash(false), mAccepted(false), mImmutable(true), mTransactionMap(boost::make_shared(smtTRANSACTION, transHash)), mAccountStateMap(boost::make_shared(smtSTATE, accountHash)) -{ +{ // This will throw if the root nodes are not available locally updateHash(); if (mTransHash.isNonZero()) mTransactionMap->fetchRoot(mTransHash); From 2a13b9b7db8b45950373dd6bf5bf8b91dfd8edd8 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 07:03:21 -0700 Subject: [PATCH 11/20] Fix the '--load' logic. --- src/Application.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/Application.cpp b/src/Application.cpp index fa8861fa13..11b19895b3 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -208,17 +208,25 @@ void Application::startNewLedger() void Application::loadOldLedger() { - Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); - - if (!lastLedger) + try { - std::cout << "No Ledger found?" << std::endl; + Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); + + if (!lastLedger) + { + std::cout << "No Ledger found?" << std::endl; + exit(-1); + } + lastLedger->setClosed(); + + Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); + mMasterLedger.switchLedgers(lastLedger, openLedger); + mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); + } + catch (SHAMapMissingNode& mn) + { + Log(lsFATAL) << "Cannot load ledger. " << mn; exit(-1); } - lastLedger->setClosed(); - - Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); - mMasterLedger.switchLedgers(lastLedger, openLedger); - mNetOps.setLastCloseTime(lastLedger->getCloseTimeNC()); } // vim:ts=4 From d270b6b074742d85f747742ed1f56e5b0b54d17e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 12:14:14 -0700 Subject: [PATCH 12/20] Fix ripple calc looping on errors. --- src/RippleCalc.cpp | 50 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index 2de815e3d3..c660e4dd86 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -1816,21 +1816,25 @@ void RippleCalc::pathNext(const PathState::pointer& pspCur, const int iPaths, co lesCurrent.bumpSeq(); // Begin ledger varance. pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality); + } - tLog(tesSUCCESS == pspCur->terStatus, lsDEBUG) + if (tesSUCCESS == pspCur->terStatus) + { + tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) << boost::str(boost::format("saOutPass=%s saInPass=%s") % pspCur->saOutPass.getFullText() % pspCur->saInPass.getFullText()); - // Make sure we have a quality. - assert(tesSUCCESS != pspCur->terStatus || (!!pspCur->saOutPass && !!pspCur->saInPass)); + assert(!!pspCur->saOutPass && !!pspCur->saInPass); - pspCur->uQuality = tesSUCCESS == pspCur->terStatus - ? STAmount::getRate(pspCur->saOutPass, pspCur->saInPass) // Calculate relative quality. - : 0; // Mark path as inactive. + pspCur->uQuality = STAmount::getRate(pspCur->saOutPass, pspCur->saInPass); // Calculate relative quality. cLog(lsINFO) << "Path after forward: " << pspCur->getJson(); } + else + { + pspCur->uQuality = 0; + } } // XXX Stand alone calculation not implemented, does not calculate required input. @@ -1968,12 +1972,27 @@ TER RippleCalc::rippleCalc( nothing(); } - else if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. - || !pspBest // Best is not yet set. - || PathState::lessPriority(pspBest, pspCur)) // Current is better than set. - { - lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state. - pspBest = pspCur; + else { + tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG) + << boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s") + % pspCur->uQuality + % pspCur->saInPass.getFullText() + % pspCur->saOutPass.getFullText()); + + assert(!!pspCur->saInPass && !!pspCur->saOutPass); + + if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. + || !pspBest // Best is not yet set. + || PathState::lessPriority(pspBest, pspCur)) // Current is better than set. + { + cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: better: uQuality=%016lX saInPass=%s saOutPass=%s") + % pspCur->uQuality + % pspCur->saInPass.getFullText() + % pspCur->saOutPass.getFullText()); + + lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state. + pspBest = pspCur; + } } } } @@ -1982,6 +2001,11 @@ TER RippleCalc::rippleCalc( { // Apply best path. + cLog(lsDEBUG) << boost::str(boost::format("calcOfferFirst: best: uQuality=%016lX saInPass=%s saOutPass=%s") + % pspBest->uQuality + % pspBest->saInPass.getFullText() + % pspBest->saOutPass.getFullText()); + // Record best pass' offers that became unfunded for deletion on success. vuUnfundedBecame.insert(vuUnfundedBecame.end(), pspBest->vUnfundedBecame.begin(), pspBest->vUnfundedBecame.end()); @@ -1991,7 +2015,7 @@ TER RippleCalc::rippleCalc( saInAct += pspBest->saInPass; saOutAct += pspBest->saOutPass; - if (temUNCERTAIN == terResult && saOutAct == saDstAmountReq) + if (saOutAct == saDstAmountReq) { // Done. Delivered requested amount. From 0509d5af373c6f542f569979d11fc3ef034294e6 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 16:19:24 -0700 Subject: [PATCH 13/20] JS: Split node utils into their own file. --- js/nodeutils.js | 88 ++++++++++++++++++ js/utils.js | 87 ------------------ test/server.js | 196 ++++++++++++++++++++-------------------- test/standalone-test.js | 3 +- 4 files changed, 187 insertions(+), 187 deletions(-) create mode 100644 js/nodeutils.js diff --git a/js/nodeutils.js b/js/nodeutils.js new file mode 100644 index 0000000000..befdb4abca --- /dev/null +++ b/js/nodeutils.js @@ -0,0 +1,88 @@ +var fs = require("fs"); +var path = require("path"); + +var utils = require("./utils.js"); + +// Empty a directory. +var emptyPath = function(dirPath, done) { + fs.readdir(dirPath, function(err, files) { + if (err) { + done(err); + } + else { + utils.mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done); + } + }); +}; + +// Make a directory and sub-directories. +var mkPath = function(dirPath, mode, done) { + fs.mkdir(dirPath, typeof mode === "string" ? parseInt(mode, 8) : mode, function(e) { + if (!e || e.code === "EEXIST") { + // Created or already exists, done. + done(); + } + else if (e.code === "ENOENT") { + // Missing sub dir. + + mkPath(path.dirname(dirPath), mode, function(e) { + if (e) { + throw e; + } + else { + mkPath(dirPath, mode, done); + } + }); + } + else { + throw e; + } + }); +}; + +// Create directory if needed and empty if needed. +var resetPath = function(dirPath, mode, done) { + mkPath(dirPath, mode, function(e) { + if (e) { + done(e); + } + else { + emptyPath(dirPath, done); + } + }); +}; + +// Remove path recursively. +var rmPath = function(dirPath, done) { +// console.log("rmPath: %s", dirPath); + + fs.lstat(dirPath, function(err, stats) { + if (err && err.code == "ENOENT") { + done(); + } + if (err) { + done(err); + } + else if (stats.isDirectory()) { + emptyPath(dirPath, function(e) { + if (e) { + done(e); + } + else { +// console.log("rmdir: %s", dirPath); done(); + fs.rmdir(dirPath, done); + } + }); + } + else { +// console.log("unlink: %s", dirPath); done(); + fs.unlink(dirPath, done); + } + }); +}; + +exports.mkPath = mkPath; +exports.resetPath = resetPath; +exports.rmPath = rmPath; + +// vim:sw=2:sts=2:ts=8 diff --git a/js/utils.js b/js/utils.js index 950da3e416..37f78736b9 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1,8 +1,3 @@ -// YYY Should probably have two versions: node vs browser - -var fs = require("fs"); -var path = require("path"); - Function.prototype.method = function(name,func) { this.prototype[name] = func; @@ -42,84 +37,6 @@ var mapOr = function(func, array, done) { } }; -// Make a directory and sub-directories. -var mkPath = function(dirPath, mode, done) { - fs.mkdir(dirPath, typeof mode === "string" ? parseInt(mode, 8) : mode, function(e) { - if (!e || e.code === "EEXIST") { - // Created or already exists, done. - done(); - } - else if (e.code === "ENOENT") { - // Missing sub dir. - - mkPath(path.dirname(dirPath), mode, function(e) { - if (e) { - throw e; - } - else { - mkPath(dirPath, mode, done); - } - }); - } - else { - throw e; - } - }); -}; - -// Empty a directory. -var emptyPath = function(dirPath, done) { - fs.readdir(dirPath, function(err, files) { - if (err) { - done(err); - } - else { - mapOr(rmPath, files.map(function(f) { return path.join(dirPath, f); }), done); - } - }); -}; - -// Remove path recursively. -var rmPath = function(dirPath, done) { -// console.log("rmPath: %s", dirPath); - - fs.lstat(dirPath, function(err, stats) { - if (err && err.code == "ENOENT") { - done(); - } - if (err) { - done(err); - } - else if (stats.isDirectory()) { - emptyPath(dirPath, function(e) { - if (e) { - done(e); - } - else { -// console.log("rmdir: %s", dirPath); done(); - fs.rmdir(dirPath, done); - } - }); - } - else { -// console.log("unlink: %s", dirPath); done(); - fs.unlink(dirPath, done); - } - }); -}; - -// Create directory if needed and empty if needed. -var resetPath = function(dirPath, mode, done) { - mkPath(dirPath, mode, function(e) { - if (e) { - done(e); - } - else { - emptyPath(dirPath, done); - } - }); -}; - var trace = function(comment, func) { return function() { console.log("%s: %s", trace, arguments.toString); @@ -151,11 +68,7 @@ var stringToHex = function (s) { }).join(""); }; -exports.emptyPath = emptyPath; exports.mapOr = mapOr; -exports.mkPath = mkPath; -exports.resetPath = resetPath; -exports.rmPath = rmPath; exports.trace = trace; exports.hexToString = hexToString; exports.stringToHex = stringToHex; diff --git a/test/server.js b/test/server.js index 4095b2a356..96d8d89c3e 100644 --- a/test/server.js +++ b/test/server.js @@ -8,148 +8,148 @@ // Servers are created in tmp/server/$server // -var config = require("./config.js"); -var utils = require("../js/utils.js"); +var config = require("./config.js"); +var nodeutils = require("../js/nodeutils.js"); -var fs = require("fs"); -var path = require("path"); -var util = require("util"); -var child = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var util = require("util"); +var child = require("child_process"); var servers = {}; // Create a server object var Server = function(name) { - this.name = name; + this.name = name; }; // Return a server's newcoind.cfg as string. -Server.method('configContent', function() { - var cfg = config.servers[this.name]; +Server.prototype.configContent = function() { + var cfg = config.servers[this.name]; - return Object.keys(cfg).map(function(o) { - return util.format("[%s]\n%s\n", o, cfg[o]); - }).join(""); -}); + return Object.keys(cfg).map(function(o) { + return util.format("[%s]\n%s\n", o, cfg[o]); + }).join(""); +}; -Server.method('serverPath', function() { +Server.prototype.serverPath = function() { return "tmp/server/" + this.name; -}); +}; -Server.method('configPath', function() { - return path.join(this.serverPath(), "newcoind.cfg"); -}); +Server.prototype.configPath = function() { + return path.join(this.serverPath(), "newcoind.cfg"); +}; // Write a server's newcoind.cfg. -Server.method('writeConfig', function(done) { - fs.writeFile(this.configPath(), this.configContent(), 'utf8', done); -}); +Server.prototype.writeConfig = function(done) { + fs.writeFile(this.configPath(), this.configContent(), 'utf8', done); +}; // Spawn the server. -Server.method('serverSpawnSync', function() { - // Spawn in standalone mode for now. - this.child = child.spawn( - config.newcoind, - [ - "-a", - "-v", - "--conf=newcoind.cfg" - ], - { - cwd: this.serverPath(), - env: process.env, - stdio: 'inherit' - }); +Server.prototype.serverSpawnSync = function() { + // Spawn in standalone mode for now. + this.child = child.spawn( + config.newcoind, + [ + "-a", + "-v", + "--conf=newcoind.cfg" + ], + { + cwd: this.serverPath(), + env: process.env, + stdio: 'inherit' + }); - console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.newcoind, this.configPath()); + console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.newcoind, this.configPath()); - // By default, just log exits. - this.child.on('exit', function(code, signal) { - // If could not exec: code=127, signal=null - // If regular exit: code=0, signal=null - console.log("server: spawn: server exited code=%s: signal=%s", code, signal); - }); + // By default, just log exits. + this.child.on('exit', function(code, signal) { + // If could not exec: code=127, signal=null + // If regular exit: code=0, signal=null + console.log("server: spawn: server exited code=%s: signal=%s", code, signal); + }); -}); +}; // Prepare server's working directory. -Server.method('makeBase', function(done) { +Server.prototype.makeBase = function(done) { var path = this.serverPath(); - var self = this; + var self = this; // Reset the server directory, build it if needed. - utils.resetPath(path, '0777', function(e) { - if (e) { - throw e; - } - else { - self.writeConfig(done); - } - }); -}); + nodeutils.resetPath(path, '0777', function(e) { + if (e) { + throw e; + } + else { + self.writeConfig(done); + } + }); +}; // Create a standalone server. // Prepare the working directory and spawn the server. -Server.method('start', function(done) { - var self = this; +Server.prototype.start = function(done) { + var self = this; this.makeBase(function(e) { - if (e) { - throw e; - } - else { - self.serverSpawnSync(); - done(); - } - }); -}); + if (e) { + throw e; + } + else { + self.serverSpawnSync(); + done(); + } + }); +}; // Stop a standalone server. -Server.method('stop', function(done) { - if (this.child) { - // Update the on exit to invoke done. - this.child.on('exit', function(code, signal) { - console.log("server: stop: server exited"); - done(); - }); - this.child.kill(); - } - else - { - console.log("server: stop: no such server"); - done(); - } -}); +Server.prototype.stop = function(done) { + if (this.child) { + // Update the on exit to invoke done. + this.child.on('exit', function(code, signal) { + console.log("server: stop: server exited"); + done(); + }); + this.child.kill(); + } + else + { + console.log("server: stop: no such server"); + done('noSuchServer'); + } +}; // Start the named server. exports.start = function(name, done) { - if (servers[name]) - { - console.log("server: start: server already started."); - } - else - { - var server = new Server(name); + if (servers[name]) + { + console.log("server: start: server already started."); + } + else + { + var server = new Server(name); - servers[name] = server; + servers[name] = server; - console.log("server: start: %s", JSON.stringify(server)); + console.log("server: start: %s", JSON.stringify(server)); - server.start(done); - } + server.start(done); + } }; // Delete the named server. exports.stop = function(name, done) { - console.log("server: stop: %s of %s", name, Object.keys(servers).toString()); + console.log("server: stop: %s of %s", name, Object.keys(servers).toString()); - var server = servers[name]; - if (server) { - server.stop(done); - delete servers[name]; - } + var server = servers[name]; + if (server) { + server.stop(done); + delete servers[name]; + } }; exports.Server = Server; -// vim:ts=4 +// vim:sw=2:sts=2:ts=8 diff --git a/test/standalone-test.js b/test/standalone-test.js index 68d86b7843..f8b8985956 100644 --- a/test/standalone-test.js +++ b/test/standalone-test.js @@ -1,9 +1,8 @@ -var fs = require("fs"); var buster = require("buster"); +var config = require("./config.js"); var server = require("./server.js"); var remote = require("../js/remote.js"); -var config = require("./config.js"); // How long to wait for server to start. var serverDelay = 1500; From 37da7aa8bc2f9fdf551d37c52d5b9df90d90a9cd Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 16:20:37 -0700 Subject: [PATCH 14/20] Make jsbn a single node style file. --- js/{jsbn/jsbn2.js => jsbn.js} | 565 +++++++++++++++++++++++++++++++++- js/jsbn/jsbn.js | 559 --------------------------------- 2 files changed, 564 insertions(+), 560 deletions(-) rename js/{jsbn/jsbn2.js => jsbn.js} (54%) delete mode 100644 js/jsbn/jsbn.js diff --git a/js/jsbn/jsbn2.js b/js/jsbn.js similarity index 54% rename from js/jsbn/jsbn2.js rename to js/jsbn.js index 5b2b725c46..61325fcbd1 100644 --- a/js/jsbn/jsbn2.js +++ b/js/jsbn.js @@ -1,12 +1,541 @@ +// Derived from Tom Wu's jsbn code. +// +// Changes made for clean up and to package as a node.js module. + // Copyright (c) 2005-2009 Tom Wu // All Rights Reserved. // See "LICENSE" for details. +// Basic JavaScript BN library - subset useful for RSA encryption. // Extended JavaScript BN functions, required for RSA private ops. - // Version 1.1: new BigInteger("0", 10) returns "proper" zero // Version 1.2: square() API, isProbablePrime fix +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +var BigInteger = function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +}; + +// return new, unset BigInteger +var nbi = function nbi() { return new BigInteger(null); }; + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} + +if(j_lm && 'undefined' !== typeof navigator && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && 'undefined' !== typeof navigator && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return (this.s<0)?-r:r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + // (public) function bnClone() { var r = nbi(); this.copyTo(r); return r; } @@ -654,3 +1183,37 @@ BigInteger.prototype.square = bnSquare; // int hashCode() // long longValue() // static BigInteger valueOf(long val) +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + +exports.nbi = nbi; +exports.BigInteger = BigInteger; + +// vim:sw=2:sts=2:ts=8 diff --git a/js/jsbn/jsbn.js b/js/jsbn/jsbn.js deleted file mode 100644 index 40bb9e2b9c..0000000000 --- a/js/jsbn/jsbn.js +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright (c) 2005 Tom Wu -// All Rights Reserved. -// See "LICENSE" for details. - -// Basic JavaScript BN library - subset useful for RSA encryption. - -// Bits per digit -var dbits; - -// JavaScript engine analysis -var canary = 0xdeadbeefcafe; -var j_lm = ((canary&0xffffff)==0xefcafe); - -// (public) Constructor -function BigInteger(a,b,c) { - if(a != null) - if("number" == typeof a) this.fromNumber(a,b,c); - else if(b == null && "string" != typeof a) this.fromString(a,256); - else this.fromString(a,b); -} - -// return new, unset BigInteger -function nbi() { return new BigInteger(null); } - -// am: Compute w_j += (x*this_i), propagate carries, -// c is initial carry, returns final carry. -// c < 3*dvalue, x < 2*dvalue, this_i < dvalue -// We need to select the fastest one that works in this environment. - -// am1: use a single mult and divide to get the high bits, -// max digit bits should be 26 because -// max internal value = 2*dvalue^2-2*dvalue (< 2^53) -function am1(i,x,w,j,c,n) { - while(--n >= 0) { - var v = x*this[i++]+w[j]+c; - c = Math.floor(v/0x4000000); - w[j++] = v&0x3ffffff; - } - return c; -} -// am2 avoids a big mult-and-extract completely. -// Max digit bits should be <= 30 because we do bitwise ops -// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) -function am2(i,x,w,j,c,n) { - var xl = x&0x7fff, xh = x>>15; - while(--n >= 0) { - var l = this[i]&0x7fff; - var h = this[i++]>>15; - var m = xh*l+h*xl; - l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); - c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); - w[j++] = l&0x3fffffff; - } - return c; -} -// Alternately, set max digit bits to 28 since some -// browsers slow down when dealing with 32-bit numbers. -function am3(i,x,w,j,c,n) { - var xl = x&0x3fff, xh = x>>14; - while(--n >= 0) { - var l = this[i]&0x3fff; - var h = this[i++]>>14; - var m = xh*l+h*xl; - l = xl*l+((m&0x3fff)<<14)+w[j]+c; - c = (l>>28)+(m>>14)+xh*h; - w[j++] = l&0xfffffff; - } - return c; -} -if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { - BigInteger.prototype.am = am2; - dbits = 30; -} -else if(j_lm && (navigator.appName != "Netscape")) { - BigInteger.prototype.am = am1; - dbits = 26; -} -else { // Mozilla/Netscape seems to prefer am3 - BigInteger.prototype.am = am3; - dbits = 28; -} - -BigInteger.prototype.DB = dbits; -BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; - r.t = this.t; - r.s = this.s; -} - -// (protected) set from integer value x, -DV <= x < DV -function bnpFromInt(x) { - this.t = 1; - this.s = (x<0)?-1:0; - if(x > 0) this[0] = x; - else if(x < -1) this[0] = x+DV; - else this.t = 0; -} - -// return bigint initialized to value -function nbv(i) { var r = nbi(); r.fromInt(i); return r; } - -// (protected) set from string and radix -function bnpFromString(s,b) { - var k; - if(b == 16) k = 4; - else if(b == 8) k = 3; - else if(b == 256) k = 8; // byte array - else if(b == 2) k = 1; - else if(b == 32) k = 5; - else if(b == 4) k = 2; - else { this.fromRadix(s,b); return; } - this.t = 0; - this.s = 0; - var i = s.length, mi = false, sh = 0; - while(--i >= 0) { - var x = (k==8)?s[i]&0xff:intAt(s,i); - if(x < 0) { - if(s.charAt(i) == "-") mi = true; - continue; - } - mi = false; - if(sh == 0) - this[this.t++] = x; - else if(sh+k > this.DB) { - this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); - } - else - this[this.t-1] |= x<= this.DB) sh -= this.DB; - } - if(k == 8 && (s[0]&0x80) != 0) { - this.s = -1; - if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; -} - -// (public) return string representation in given radix -function bnToString(b) { - if(this.s < 0) return "-"+this.negate().toString(b); - var k; - if(b == 16) k = 4; - else if(b == 8) k = 3; - else if(b == 2) k = 1; - else if(b == 32) k = 5; - else if(b == 4) k = 2; - else return this.toRadix(b); - var km = (1< 0) { - if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } - while(i >= 0) { - if(p < k) { - d = (this[i]&((1<>(p+=this.DB-k); - } - else { - d = (this[i]>>(p-=k))&km; - if(p <= 0) { p += this.DB; --i; } - } - if(d > 0) m = true; - if(m) r += int2char(d); - } - } - return m?r:"0"; -} - -// (public) -this -function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } - -// (public) |this| -function bnAbs() { return (this.s<0)?this.negate():this; } - -// (public) return + if this > a, - if this < a, 0 if equal -function bnCompareTo(a) { - var r = this.s-a.s; - if(r != 0) return r; - var i = this.t; - r = i-a.t; - if(r != 0) return (this.s<0)?-r:r; - while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; - return 0; -} - -// returns bit length of the integer x -function nbits(x) { - var r = 1, t; - if((t=x>>>16) != 0) { x = t; r += 16; } - if((t=x>>8) != 0) { x = t; r += 8; } - if((t=x>>4) != 0) { x = t; r += 4; } - if((t=x>>2) != 0) { x = t; r += 2; } - if((t=x>>1) != 0) { x = t; r += 1; } - return r; -} - -// (public) return the number of bits in "this" -function bnBitLength() { - if(this.t <= 0) return 0; - return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); -} - -// (protected) r = this << n*DB -function bnpDLShiftTo(n,r) { - var i; - for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; - for(i = n-1; i >= 0; --i) r[i] = 0; - r.t = this.t+n; - r.s = this.s; -} - -// (protected) r = this >> n*DB -function bnpDRShiftTo(n,r) { - for(var i = n; i < this.t; ++i) r[i-n] = this[i]; - r.t = Math.max(this.t-n,0); - r.s = this.s; -} - -// (protected) r = this << n -function bnpLShiftTo(n,r) { - var bs = n%this.DB; - var cbs = this.DB-bs; - var bm = (1<= 0; --i) { - r[i+ds+1] = (this[i]>>cbs)|c; - c = (this[i]&bm)<= 0; --i) r[i] = 0; - r[ds] = c; - r.t = this.t+ds+1; - r.s = this.s; - r.clamp(); -} - -// (protected) r = this >> n -function bnpRShiftTo(n,r) { - r.s = this.s; - var ds = Math.floor(n/this.DB); - if(ds >= this.t) { r.t = 0; return; } - var bs = n%this.DB; - var cbs = this.DB-bs; - var bm = (1<>bs; - for(var i = ds+1; i < this.t; ++i) { - r[i-ds-1] |= (this[i]&bm)<>bs; - } - if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; - } - if(a.t < this.t) { - c -= a.s; - while(i < this.t) { - c += this[i]; - r[i++] = c&this.DM; - c >>= this.DB; - } - c += this.s; - } - else { - c += this.s; - while(i < a.t) { - c -= a[i]; - r[i++] = c&this.DM; - c >>= this.DB; - } - c -= a.s; - } - r.s = (c<0)?-1:0; - if(c < -1) r[i++] = this.DV+c; - else if(c > 0) r[i++] = c; - r.t = i; - r.clamp(); -} - -// (protected) r = this * a, r != this,a (HAC 14.12) -// "this" should be the larger one if appropriate. -function bnpMultiplyTo(a,r) { - var x = this.abs(), y = a.abs(); - var i = x.t; - r.t = i+y.t; - while(--i >= 0) r[i] = 0; - for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); - r.s = 0; - r.clamp(); - if(this.s != a.s) BigInteger.ZERO.subTo(r,r); -} - -// (protected) r = this^2, r != this (HAC 14.16) -function bnpSquareTo(r) { - var x = this.abs(); - var i = r.t = 2*x.t; - while(--i >= 0) r[i] = 0; - for(i = 0; i < x.t-1; ++i) { - var c = x.am(i,x[i],r,2*i,0,1); - if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { - r[i+x.t] -= x.DV; - r[i+x.t+1] = 1; - } - } - if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); - r.s = 0; - r.clamp(); -} - -// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) -// r != q, this != m. q or r may be null. -function bnpDivRemTo(m,q,r) { - var pm = m.abs(); - if(pm.t <= 0) return; - var pt = this.abs(); - if(pt.t < pm.t) { - if(q != null) q.fromInt(0); - if(r != null) this.copyTo(r); - return; - } - if(r == null) r = nbi(); - var y = nbi(), ts = this.s, ms = m.s; - var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus - if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } - else { pm.copyTo(y); pt.copyTo(r); } - var ys = y.t; - var y0 = y[ys-1]; - if(y0 == 0) return; - var yt = y0*(1<1)?y[ys-2]>>this.F2:0); - var d1 = this.FV/yt, d2 = (1<= 0) { - r[r.t++] = 1; - r.subTo(t,r); - } - BigInteger.ONE.dlShiftTo(ys,t); - t.subTo(y,y); // "negative" y so we can replace sub with am later - while(y.t < ys) y[y.t++] = 0; - while(--j >= 0) { - // Estimate quotient digit - var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); - if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out - y.dlShiftTo(j,t); - r.subTo(t,r); - while(r[i] < --qd) r.subTo(t,r); - } - } - if(q != null) { - r.drShiftTo(ys,q); - if(ts != ms) BigInteger.ZERO.subTo(q,q); - } - r.t = ys; - r.clamp(); - if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder - if(ts < 0) BigInteger.ZERO.subTo(r,r); -} - -// (public) this mod a -function bnMod(a) { - var r = nbi(); - this.abs().divRemTo(a,null,r); - if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); - return r; -} - -// Modular reduction using "classic" algorithm -function Classic(m) { this.m = m; } -function cConvert(x) { - if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); - else return x; -} -function cRevert(x) { return x; } -function cReduce(x) { x.divRemTo(this.m,null,x); } -function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } -function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } - -Classic.prototype.convert = cConvert; -Classic.prototype.revert = cRevert; -Classic.prototype.reduce = cReduce; -Classic.prototype.mulTo = cMulTo; -Classic.prototype.sqrTo = cSqrTo; - -// (protected) return "-1/this % 2^DB"; useful for Mont. reduction -// justification: -// xy == 1 (mod m) -// xy = 1+km -// xy(2-xy) = (1+km)(1-km) -// x[y(2-xy)] = 1-k^2m^2 -// x[y(2-xy)] == 1 (mod m^2) -// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 -// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. -// JS multiply "overflows" differently from C/C++, so care is needed here. -function bnpInvDigit() { - if(this.t < 1) return 0; - var x = this[0]; - if((x&1) == 0) return 0; - var y = x&3; // y == 1/x mod 2^2 - y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 - y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 - y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 - // last step - calculate inverse mod DV directly; - // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints - y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits - // we really want the negative inverse, and -DV < y < DV - return (y>0)?this.DV-y:-y; -} - -// Montgomery reduction -function Montgomery(m) { - this.m = m; - this.mp = m.invDigit(); - this.mpl = this.mp&0x7fff; - this.mph = this.mp>>15; - this.um = (1<<(m.DB-15))-1; - this.mt2 = 2*m.t; -} - -// xR mod m -function montConvert(x) { - var r = nbi(); - x.abs().dlShiftTo(this.m.t,r); - r.divRemTo(this.m,null,r); - if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); - return r; -} - -// x/R mod m -function montRevert(x) { - var r = nbi(); - x.copyTo(r); - this.reduce(r); - return r; -} - -// x = x/R mod m (HAC 14.32) -function montReduce(x) { - while(x.t <= this.mt2) // pad x so am has enough room later - x[x.t++] = 0; - for(var i = 0; i < this.m.t; ++i) { - // faster way of calculating u0 = x[i]*mp mod DV - var j = x[i]&0x7fff; - var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; - // use am to combine the multiply-shift-add into one call - j = i+this.m.t; - x[j] += this.m.am(0,u0,x,i,0,this.m.t); - // propagate carry - while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } - } - x.clamp(); - x.drShiftTo(this.m.t,x); - if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); -} - -// r = "x^2/R mod m"; x != r -function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } - -// r = "xy/R mod m"; x,y != r -function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } - -Montgomery.prototype.convert = montConvert; -Montgomery.prototype.revert = montRevert; -Montgomery.prototype.reduce = montReduce; -Montgomery.prototype.mulTo = montMulTo; -Montgomery.prototype.sqrTo = montSqrTo; - -// (protected) true iff this is even -function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } - -// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) -function bnpExp(e,z) { - if(e > 0xffffffff || e < 1) return BigInteger.ONE; - var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; - g.copyTo(r); - while(--i >= 0) { - z.sqrTo(r,r2); - if((e&(1< 0) z.mulTo(r2,g,r); - else { var t = r; r = r2; r2 = t; } - } - return z.revert(r); -} - -// (public) this^e % m, 0 <= e < 2^32 -function bnModPowInt(e,m) { - var z; - if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); - return this.exp(e,z); -} - -// protected -BigInteger.prototype.copyTo = bnpCopyTo; -BigInteger.prototype.fromInt = bnpFromInt; -BigInteger.prototype.fromString = bnpFromString; -BigInteger.prototype.clamp = bnpClamp; -BigInteger.prototype.dlShiftTo = bnpDLShiftTo; -BigInteger.prototype.drShiftTo = bnpDRShiftTo; -BigInteger.prototype.lShiftTo = bnpLShiftTo; -BigInteger.prototype.rShiftTo = bnpRShiftTo; -BigInteger.prototype.subTo = bnpSubTo; -BigInteger.prototype.multiplyTo = bnpMultiplyTo; -BigInteger.prototype.squareTo = bnpSquareTo; -BigInteger.prototype.divRemTo = bnpDivRemTo; -BigInteger.prototype.invDigit = bnpInvDigit; -BigInteger.prototype.isEven = bnpIsEven; -BigInteger.prototype.exp = bnpExp; - -// public -BigInteger.prototype.toString = bnToString; -BigInteger.prototype.negate = bnNegate; -BigInteger.prototype.abs = bnAbs; -BigInteger.prototype.compareTo = bnCompareTo; -BigInteger.prototype.bitLength = bnBitLength; -BigInteger.prototype.mod = bnMod; -BigInteger.prototype.modPowInt = bnModPowInt; - -// "constants" -BigInteger.ZERO = nbv(0); -BigInteger.ONE = nbv(1); From 773f9263dce8fd8e647253b18632b0045bbf2703 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 16:22:07 -0700 Subject: [PATCH 15/20] JS: Add BigInteger support to Amount. --- js/amount.js | 177 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 29 deletions(-) diff --git a/js/amount.js b/js/amount.js index ead5aa74db..07a5780476 100644 --- a/js/amount.js +++ b/js/amount.js @@ -1,7 +1,10 @@ // Represent Newcoin amounts and currencies. +// - Numbers in hex are big-endian. +var utils = require('./utils.js'); +var jsbn = require('./jsbn.js'); -var utils = require("./utils.js"); +var BigInteger = jsbn.BigInteger; var UInt160 = function () { // Internal form: @@ -9,7 +12,13 @@ var UInt160 = function () { // XXX Should standardize on 'i' format or 20 format. }; -// Returns NaN on error. +UInt160.prototype.from_json = function (j) { + var u = new UInt160(); + + return u.parse_json(j); +}; + +// value === NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate @@ -49,7 +58,7 @@ UInt160.prototype.parse_json = function (j) { } } - return this.value; + return this; }; // Convert from internal form. @@ -109,19 +118,84 @@ var Amount = function () { // integer : XNS // { 'value' : ..., 'currency' : ..., 'issuer' : ...} - this.value = 0; - this.offset = 0; - this.is_native = false; - this.is_negative = false; + this.value = new BigInteger(); // NaN for bad value. Always positive for non-XNS. + this.offset = undefined; // For non-XNS. + this.is_native = true; // Default to XNS. Only valid if value is not NaN. + this.is_negative = undefined; // Undefined for XNS. this.currency = new Currency(); this.issuer = new UInt160(); }; -// Convert only value to JSON text. -Amount.prototype.to_text = function() { - // XXX Needs to work for native and non-native. - return this.is_negative ? -this.value : this.value; // XXX Use biginteger. +// YYY Might also check range. +Amount.prototype.is_valid = function() { + return NaN !== this.value; +} + +// Convert only value to JSON wire format. +Amount.prototype.to_text = function(allow_nan) { + if (NaN === this.value) { + // Never should happen. + return allow_nan ? NaN : "0"; + } + else if (this.is_native) { + if (this.value.compareTo(exports.consts.xns_max) > 0 || this.value.compareTo(exports.consts.xns_min) < 0) + { + // Never should happen. + return allow_nan ? NaN : "0"; + } + else + { + return this.value.toString(); + } + } + else if (this.value.equals(BigInteger.ZERO)) + { + return "0"; + } + else if (this.offset < -25 || mOffset > -5) + { + // Use e notation. + // XXX Clamp output. + + return (this.is_negative ? "-" : "") + this.value.toString() + "e" + this.offset; + } + else + { + var val = "000000000000000000000000000" + this.value.toString() + "00000000000000000000000"; + var pre = val.substring(0, this.offset + 43); + var post = val.substring(this.offset + 43); + var s_pre = val.match(/[1-9].*$/); // Everything but leading zeros. + var s_post = val.match(/0+$/); // Trailing zeros. + + + return (this.is_negative ? "-" : "") + + (null == s_pre ? "0" : s_pre[0]) + + "." + + post.substring(post.length - s_post.length); + } +}; + +Amount.prototype.canonicalize = function() { + if (NaN === this.value || !this.currency) { + // nothing + } + else if (this.value.equals(BigInteger.ZERO)) { + this.offset = -100; + this.is_negative = false; + } + else + { + while (this.value.compareTo(exports.consts.bi_man_min_value)) { + this.value.multiply(exports.consts.bi_10); + this.offset -= 1; + } + + while (this.value.compareTo(exports.consts.bi_man_max_value)) { + this.value.divide(exports.consts.bi_10); + this.offset += 1; + } + } }; Amount.prototype.to_json = function() { @@ -138,21 +212,35 @@ Amount.prototype.to_json = function() { } }; -// Parse a native value. +// Parse a XNS value from untrusted input. +// XXX Improvements: disallow leading zeros. Amount.prototype.parse_native = function(j) { - if ('integer' === typeof j) { - // XNS - this.value = j >= 0 ? j : -j; // XXX Use biginteger. - this.offset = 0; + var m; + + if ('string' === typeof j) + m = j.match(/^(\d+)(\.\d{1,6})?$/); + + if ('integer' === typeof j || null !== m) { + if ('integer' === typeof j || ("" === e[2])) { + this.value = new BigInteger(j); + } + else + { + // Decimal notation + var int_part = (new BigInteger(e[1])).multiply(exports.consts.xns_unit); + var fraction_part = (new BigInteger(e[2])).multiply(new BigInteger(Math.pow(10, exports.consts.xns_unit-e[2].length))); + + this.value = int_part.add(fraction_part); + } this.is_native = true; - this.is_negative = j < 0; + this.offset = undefined; + this.is_negative = undefined; + + if (this.value.compareTo(exports.consts.xns_max) > 0 || this.value.compareTo(exports.consts.xns_min) < 0) + { + this.value = NaN; + } } - else if ('string' === typeof j) { - this.value = j >= 0 ? j : -j; // XXX Use biginteger. - this.offset = 0; - this.is_native = true; - this.is_negative = j < 0; - } else { this.value = NaN; } @@ -161,16 +249,41 @@ Amount.prototype.parse_native = function(j) { // Parse a non-native value. Amount.prototype.parse_value = function(j) { if ('integer' === typeof j) { - this.value = j >= 0 ? j : -j; // XXX Use biginteger. + this.value = new BigInteger(j); this.offset = 0; this.is_native = false; this.is_negative = j < 0; + + this.canonicalize(); } else if ('string' === typeof j) { - this.value = j >= 0 ? j : -j; // XXX Use biginteger. - this.offset = 0; + var e = j.match(/^(-?\d+)e(\d+)/); + var d = j.match(/^(-?\d+)\.(\d+)/); + + if (null !== e) { + // e notation + + this.value = new BigInteger(e[1]); + this.offset = parseInt(e[2]); + } + else if (null !== d) { + // float notation + + this.value = (new BigInteger(e[1])).multiply((new BigInteger(exports.consts.bi_10)).pow(e[2].length)).add(new BigInteger(e[2])); + this.offset = -e[2].length; + } + else + { + // integer notation + + this.value = new BigInteger(j); + this.offset = 0; + } + this.is_native = false; - this.is_negative = j < 0; + this.is_negative = undefined; + + this.canonicalize(); } else { this.value = NaN; @@ -201,10 +314,16 @@ exports.consts = { 'address_one' : "iiiiiiiiiiiiiiiiiiiiBZbvjr", 'currency_xns' : 0, 'currency_one' : 1, - 'uint160_xns' : hexToString("0000000000000000000000000000000000000000"), - 'uint160_one' : hexToString("0000000000000000000000000000000000000001"), + 'uint160_xns' : utils.hexToString("0000000000000000000000000000000000000000"), + 'uint160_one' : utils.hexToString("0000000000000000000000000000000000000001"), 'hex_xns' : "0000000000000000000000000000000000000000", 'hex_one' : "0000000000000000000000000000000000000001", + 'xns_max' : new BigInteger("9000000000000000000"), // Json wire limit. + 'xns_min' : new BigInteger("-9000000000000000000"), // Json wire limit. + 'xns_unit' : new BigInteger('1000000'), + 'bi_man_min_value' : new BigInteger('1000000000000000'), + 'bi_man_max_value' : new BigInteger('9999999999999999'), + 'bi_10' : new BigInteger('10'), }; // vim:sw=2:sts=2:ts=8 From 1a5de6bd3d04a9f007f5ff5041f91290a82fff8a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Oct 2012 16:22:29 -0700 Subject: [PATCH 16/20] operator<< must return a value --- src/SHAMapNodes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index ad1e27749f..859b47382e 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -518,6 +518,7 @@ std::ostream& operator<<(std::ostream& out, const SHAMapMissingNode& mn) out << "Missing/STA(" << mn.getNodeID() << ")"; else out << "Missing/" << mn.getNodeID(); + return out; } From 8ebceaa53e089eeefc4696ad99d30f5d16b4d1b6 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 16:25:01 -0700 Subject: [PATCH 17/20] UT: Initial check in unit tests for amount.js. --- js/amount.js | 2 +- test/amount-test.js | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/amount-test.js diff --git a/js/amount.js b/js/amount.js index 07a5780476..49a60c0739 100644 --- a/js/amount.js +++ b/js/amount.js @@ -12,7 +12,7 @@ var UInt160 = function () { // XXX Should standardize on 'i' format or 20 format. }; -UInt160.prototype.from_json = function (j) { +UInt160.from_json = function (j) { var u = new UInt160(); return u.parse_json(j); diff --git a/test/amount-test.js b/test/amount-test.js new file mode 100644 index 0000000000..3a7e656a74 --- /dev/null +++ b/test/amount-test.js @@ -0,0 +1,13 @@ +var buster = require("buster"); + +var amount = require("../js/amount.js"); + +buster.testCase("Amount", { + "UInt160" : { + "Parse 0" : function () { + buster.assert.equals(0, amount.UInt160.from_json("0").value); + }, + } +}); + +// vim:sw=2:sts=2:ts=8 From 7fc13e35288eab9ad20a989a0f74ee6214bc1125 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 20:07:41 -0700 Subject: [PATCH 18/20] Clean up getJson() for STAmount. --- src/Amount.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Amount.cpp b/src/Amount.cpp index 78cde08d5f..672d67505f 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -1131,15 +1131,13 @@ Json::Value STAmount::getJson(int) const { Json::Value elem(Json::objectValue); - // This is a hack, many places don't specify a currency. STAmount is used just as a value. if (!mIsNative) { + // It is an error for currency or issuer not to be specified for valid json. + elem["value"] = getText(); elem["currency"] = getHumanCurrency(); - - if (!mIssuer.isZero()) - elem["issuer"] = NewcoinAddress::createHumanAccountID(mIssuer); - + elem["issuer"] = NewcoinAddress::createHumanAccountID(mIssuer); } else { From f419dcc77b4e55b0383bb3d57c0eb8916d2ba6ad Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 20:08:08 -0700 Subject: [PATCH 19/20] JS: fixes and tests from amounts. --- js/amount.js | 118 +++++++++++++++++++++++++++++--------------- test/amount-test.js | 21 ++++++++ 2 files changed, 100 insertions(+), 39 deletions(-) diff --git a/js/amount.js b/js/amount.js index 49a60c0739..d450d0fe35 100644 --- a/js/amount.js +++ b/js/amount.js @@ -13,9 +13,7 @@ var UInt160 = function () { }; UInt160.from_json = function (j) { - var u = new UInt160(); - - return u.parse_json(j); + return (new UInt160()).parse_json(j); }; // value === NaN on error. @@ -71,7 +69,7 @@ UInt160.prototype.to_json = function () { { return exports.consts.hex_one; } - else if (20 === this.value.length) { + else if ('string' === typeof this.value && 20 === this.value.length) { return utils.stringToHex(this.value); } else @@ -90,7 +88,11 @@ var Currency = function () { // XXX Should support hex, C++ doesn't currently allow it. } -// Returns NaN on error. +Currency.from_json = function (j) { + return (new Currency()).parse_json(j); +}; + +// this.value === NaN on error. Currency.prototype.parse_json = function (j) { if ("" === j || "0" === j || "XNS" === j) { this.value = 0; @@ -102,15 +104,15 @@ Currency.prototype.parse_json = function (j) { this.value = j; } - return this.value; + return this; }; Currency.prototype.to_json = function () { - return this.value ? this.value : 'XNS'; + return this.value ? this.value : "XNS"; }; Currency.prototype.to_human = function() { - return this.value ? this.value : 'XNS'; + return this.value ? this.value : "XNS"; }; var Amount = function () { @@ -127,6 +129,10 @@ var Amount = function () { this.issuer = new UInt160(); }; +Amount.from_json = function (j) { + return (new Amount()).parse_json(j); +}; + // YYY Might also check range. Amount.prototype.is_valid = function() { return NaN !== this.value; @@ -153,7 +159,7 @@ Amount.prototype.to_text = function(allow_nan) { { return "0"; } - else if (this.offset < -25 || mOffset > -5) + else if (this.offset < -25 || this.offset > -5) { // Use e notation. // XXX Clamp output. @@ -165,14 +171,12 @@ Amount.prototype.to_text = function(allow_nan) { var val = "000000000000000000000000000" + this.value.toString() + "00000000000000000000000"; var pre = val.substring(0, this.offset + 43); var post = val.substring(this.offset + 43); - var s_pre = val.match(/[1-9].*$/); // Everything but leading zeros. - var s_post = val.match(/0+$/); // Trailing zeros. - + var s_pre = pre.match(/[1-9].*$/); // Everything but leading zeros. + var s_post = post.match(/[1-9]0*$/); // Last non-zero plus trailing zeros. return (this.is_negative ? "-" : "") - + (null == s_pre ? "0" : s_pre[0]) - + "." - + post.substring(post.length - s_post.length); + + (s_pre ? s_pre[0] : "0") + + (s_post ? "." + post.substring(0, 1+post.length-s_post[0].length) : ""); } }; @@ -186,13 +190,13 @@ Amount.prototype.canonicalize = function() { } else { - while (this.value.compareTo(exports.consts.bi_man_min_value)) { - this.value.multiply(exports.consts.bi_10); + while (this.value.compareTo(exports.consts.bi_man_min_value) < 0) { + this.value = this.value.multiply(exports.consts.bi_10); this.offset -= 1; } - while (this.value.compareTo(exports.consts.bi_man_max_value)) { - this.value.divide(exports.consts.bi_10); + while (this.value.compareTo(exports.consts.bi_man_max_value) > 0) { + this.value = this.value.divide(exports.consts.bi_10); this.offset += 1; } } @@ -212,7 +216,17 @@ Amount.prototype.to_json = function() { } }; +Amount.prototype.to_text_full = function() { + return this.value === NaN + ? NaN + : this.is_native + ? this.to_text() + "/XNS" + : this.to_text() + "/" + this.currency.to_json() + "/" + this.issuer.to_json(); +}; + // Parse a XNS value from untrusted input. +// - integer = raw units +// - float = with precision 6 // XXX Improvements: disallow leading zeros. Amount.prototype.parse_native = function(j) { var m; @@ -220,18 +234,21 @@ Amount.prototype.parse_native = function(j) { if ('string' === typeof j) m = j.match(/^(\d+)(\.\d{1,6})?$/); - if ('integer' === typeof j || null !== m) { - if ('integer' === typeof j || ("" === e[2])) { - this.value = new BigInteger(j); - } - else - { - // Decimal notation - var int_part = (new BigInteger(e[1])).multiply(exports.consts.xns_unit); - var fraction_part = (new BigInteger(e[2])).multiply(new BigInteger(Math.pow(10, exports.consts.xns_unit-e[2].length))); + if (null !== m) { + if (undefined === m[2]) { + // Integer notation - this.value = int_part.add(fraction_part); + this.value = new BigInteger(m[1]); } + else { + // Decimal notation + + var int_part = (new BigInteger(m[1])).multiply(exports.consts.xns_unit); + var fraction_part = (new BigInteger(m[2])).multiply(new BigInteger(String(Math.pow(10, 1+exports.consts.xns_precision-m[2].length)))); + + this.value = int_part.add(fraction_part); + } + this.is_native = true; this.offset = undefined; this.is_negative = undefined; @@ -244,11 +261,13 @@ Amount.prototype.parse_native = function(j) { else { this.value = NaN; } + + return this; }; // Parse a non-native value. Amount.prototype.parse_value = function(j) { - if ('integer' === typeof j) { + if ('number' === typeof j) { this.value = new BigInteger(j); this.offset = 0; this.is_native = false; @@ -260,17 +279,19 @@ Amount.prototype.parse_value = function(j) { var e = j.match(/^(-?\d+)e(\d+)/); var d = j.match(/^(-?\d+)\.(\d+)/); - if (null !== e) { + if (e) { // e notation this.value = new BigInteger(e[1]); this.offset = parseInt(e[2]); } - else if (null !== d) { + else if (d) { // float notation - this.value = (new BigInteger(e[1])).multiply((new BigInteger(exports.consts.bi_10)).pow(e[2].length)).add(new BigInteger(e[2])); - this.offset = -e[2].length; + var integer = new BigInteger(d[1]); + var fraction = new BigInteger(d[2]); + this.value = integer.multiply(exports.consts.bi_10.clone().pow(d[2].length)).add(fraction); + this.offset = -d[2].length; } else { @@ -288,21 +309,39 @@ Amount.prototype.parse_value = function(j) { else { this.value = NaN; } + + return this; }; // <-> j Amount.prototype.parse_json = function(j) { - if ('object' === typeof j && j.currency) { + if ('string' === typeof j) { + // .../.../... notation is not a wire format. But allowed for easier testing. + var m = j.match(/^(.+)\/(...)\/(.+)$/); - this.parse_value(j); + if (m) { + this.parse_value(m[1]); + this.currency = Currency.from_json(m[2]); + this.issuer = UInt160.from_json(m[3]); + } + else { + this.parse_native(j); + this.currency = new Currency(); + this.issuer = new UInt160(); + } + } + else if ('object' === typeof j && j.currency) { + // Never XNS. + + this.parse_value(j.value); this.currency.parse_json(j.currency); this.issuer.parse_json(j.issuer); } else { - this.parse_native(j); - this.currency = 0; - this.issuer = 0; + this.value = NaN; } + + return this; }; exports.Amount = Amount; @@ -321,6 +360,7 @@ exports.consts = { 'xns_max' : new BigInteger("9000000000000000000"), // Json wire limit. 'xns_min' : new BigInteger("-9000000000000000000"), // Json wire limit. 'xns_unit' : new BigInteger('1000000'), + 'xns_precision' : 6, 'bi_man_min_value' : new BigInteger('1000000000000000'), 'bi_man_max_value' : new BigInteger('9999999999999999'), 'bi_10' : new BigInteger('10'), diff --git a/test/amount-test.js b/test/amount-test.js index 3a7e656a74..b9addaac10 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -7,6 +7,27 @@ buster.testCase("Amount", { "Parse 0" : function () { buster.assert.equals(0, amount.UInt160.from_json("0").value); }, + "Parse 0 export" : function () { + buster.assert.equals(amount.consts.hex_xns, amount.UInt160.from_json("0").to_json()); + }, + "Parse native 123" : function () { + buster.assert.equals("123/XNS", amount.Amount.from_json("123").to_text_full()); + }, + "Parse native 12.3" : function () { + buster.assert.equals("12300000/XNS", amount.Amount.from_json("12.3").to_text_full()); + }, + "Parse 123./USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("123/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("123./USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + "Parse 12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + "Parse 12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12.3/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + "Parse 1.2300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("1.23/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("1.2300/USD/iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh").to_text_full()); + }, } }); From bb83bcf0d12bcb2441d2ba42fa67014558475a15 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 11 Oct 2012 21:41:07 -0700 Subject: [PATCH 20/20] Revise remote.js and unit tests to use new amount.js. --- js/remote.js | 90 ++++++++++++++++++++++++++++++----------- test/standalone-test.js | 21 +++++----- 2 files changed, 79 insertions(+), 32 deletions(-) diff --git a/js/remote.js b/js/remote.js index 3bf421d697..719bc73470 100644 --- a/js/remote.js +++ b/js/remote.js @@ -7,10 +7,15 @@ // YYY A better model might be to allow requesting a target state: keep connected or not. // -var util = require('util'); +// Node +var util = require('util'); +// npm var WebSocket = require('ws'); +var amount = require('./amount.js'); +var Amount = amount.Amount; + // --> trusted: truthy, if remote is trusted var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { this.trusted = trusted; @@ -57,10 +62,10 @@ var flags = { // XXX This needs to be determined from the network. var fees = { - 'default' : 100, - 'account_create' : 1000, - 'nickname_create' : 1000, - 'offer' : 100, + 'default' : Amount.from_json("100"), + 'account_create' : Amount.from_json("1000"), + 'nickname_create' : Amount.from_json("1000"), + 'offer' : Amount.from_json("100"), }; Remote.prototype.connect_helper = function () { @@ -383,40 +388,79 @@ Remote.prototype.dirty_account_root = function (account) { // Transactions // -Remote.prototype.ripple_line_set = function (secret, src, dst, amount, onDone) { +Remote.prototype.offer_create = function (secret, src, taker_pays, taker_gets, expiration, onDone) { var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret; var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src; - var dst_account = this.config.accounts[dst] ? this.config.accounts[dst].account : dst; + + var transaction = { + 'TransactionType' : 'OfferCreate', + 'Account' : src_account, + 'Fee' : fees.offer.to_json(), + 'TakerPays' : taker_pays.to_json(), + 'TakerGets' : taker_gets.to_json(), + }; + + if (expiration) + transaction.Expiration = expiration; this.submit_seq( { - 'transaction' : { - 'TransactionType' : 'CreditSet', - 'Account' : src_account, - 'Destination' : dst_account, - 'Fee' : create ? fees.account_create : fees['default'], - 'Amount' : amount, - }, + 'transaction' : transaction, 'secret' : secret, }, function () { }, onDone); }; -Remote.prototype.send_xns = function (secret, src, dst, amount, create, onDone) { +Remote.prototype.ripple_line_set = function (secret, src, limit, quaility_in, quality_out, onDone) { + var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret; + var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src; + + var transaction = { + 'TransactionType' : 'CreditSet', + 'Account' : src_account, + 'Fee' : fees['default'].to_json(), + }; + + if (limit) + transaction.LimitAmount = limit.to_json(); + + if (quaility_in) + transaction.QualityIn = quaility_in; + + if (quaility_out) + transaction.QualityOut = quaility_out; + + this.submit_seq( + { + 'transaction' : transaction, + 'secret' : secret, + }, function () { + }, onDone); +}; + +// --> create: is only valid if destination gets XNS. +Remote.prototype.send = function (secret, src, dst, deliver_amount, send_max, create, onDone) { var secret = this.config.accounts[src] ? this.config.accounts[src].secret : secret; var src_account = this.config.accounts[src] ? this.config.accounts[src].account : src; var dst_account = this.config.accounts[dst] ? this.config.accounts[dst].account : dst; + var transaction = { + 'TransactionType' : 'Payment', + 'Account' : src_account, + 'Fee' : (create ? fees.account_create : fees['default']).to_json(), + 'Destination' : dst_account, + 'Amount' : deliver_amount.to_json(), + }; + + if (create) + transaction.Flags = flags.tfCreateAccount; + + if (send_max) + transaction.SendMax = send_max.to_json(); + this.submit_seq( { - 'transaction' : { - 'TransactionType' : 'Payment', - 'Account' : src_account, - 'Destination' : dst_account, - 'Fee' : create ? fees.account_create : fees['default'], - 'Flags' : create ? flags.tfCreateAccount : 0, - 'Amount' : amount, - }, + 'transaction' : transaction, 'secret' : secret, }, function () { }, onDone); diff --git a/test/standalone-test.js b/test/standalone-test.js index f8b8985956..1cf0d2a6da 100644 --- a/test/standalone-test.js +++ b/test/standalone-test.js @@ -2,8 +2,11 @@ var buster = require("buster"); var config = require("./config.js"); var server = require("./server.js"); +var amount = require("../js/amount.js"); var remote = require("../js/remote.js"); +var Amount = amount.Amount; + // How long to wait for server to start. var serverDelay = 1500; @@ -113,8 +116,8 @@ buster.testCase("Websocket commands", { alpha.request_ledger_entry({ 'ledger_closed' : r.ledger_closed, - 'type' : 'account_root', - 'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh' + 'type' : 'account_root', + 'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh' } , function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -133,8 +136,8 @@ buster.testCase("Websocket commands", { alpha.request_ledger_entry({ 'ledger_closed' : r.ledger_closed, - 'type' : 'account_root', - 'account_root' : 'foobar' + 'type' : 'account_root', + 'account_root' : 'foobar' } , function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -153,8 +156,8 @@ buster.testCase("Websocket commands", { alpha.request_ledger_entry({ 'ledger_closed' : r.ledger_closed, - 'type' : 'account_root', - 'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn' + 'type' : 'account_root', + 'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn' }, function (r) { console.log("account_root: %s", JSON.stringify(r)); @@ -173,8 +176,8 @@ buster.testCase("Websocket commands", { alpha.request_ledger_entry({ 'ledger_closed' : r.ledger_closed, - 'type' : 'account_root', - 'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8", + 'type' : 'account_root', + 'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8", } , function (r) { console.log("node: %s", JSON.stringify(r)); @@ -186,7 +189,7 @@ buster.testCase("Websocket commands", { 'create account' : function (done) { - alpha.send_xns(undefined, 'root', 'alice', 10000, true, function (r) { + alpha.send(undefined, 'root', 'alice', Amount.from_json("10000"), undefined, 'CREATE', function (r) { console.log(r); buster.refute(r.error);