diff --git a/js/remote.js b/js/remote.js index ffab43eaad..de94590e37 100644 --- a/js/remote.js +++ b/js/remote.js @@ -12,194 +12,318 @@ var util = require('util'); var WebSocket = require('ws'); // --> trusted: truthy, if remote is trusted -var Remote = function(trusted, websocket_ip, websocket_port, trace) { - this.trusted = trusted; - this.websocket_ip = websocket_ip; - this.websocket_port = websocket_port; - this.id = 0; - this.trace = trace; +var Remote = function (trusted, websocket_ip, websocket_port, trace) { + this.trusted = trusted; + this.websocket_ip = websocket_ip; + this.websocket_port = websocket_port; + this.id = 0; + this.trace = trace; + this.ledger_closed = undefined; + this.ledger_current_index = undefined; + this.stand_alone = undefined; + + // Cache information for accounts. + this.account = {}; + + // Cache for various ledgers. + // XXX Clear when ledger advances. + this.ledgers = { + 'current' : {} + }; }; -var remoteConfig = function(config, server, trace) { - var serverConfig = config.servers[server]; - - return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, trace); +var remoteConfig = function (config, server, trace) { + var serverConfig = config.servers[server]; + return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, trace); }; -Remote.method('connect_helper', function() { - var self = this; +// XXX This needs to be determined from the network. +var fees = { + 'default' : 100, + 'account_create' : 1000, + 'nickname_create' : 1000, + 'offer' : 100, +}; - if (this.trace) - console.log("remote: connect: %s", this.url); +// For accounts we cache things like sequence numbers. +var accounts = { + // Consider sequence numbers stable if you know you're not generating bad transactions. + // Otherwise, clear it to have it automatically refreshed from the network. + + // acount : { seq : __ } +}; - this.ws = new WebSocket(this.url); - - var ws = this.ws; - - ws.response = {}; - - ws.onopen = function() { - if (this.trace) - console.log("remote: onopen: %s", ws.readyState); - - ws.onclose = undefined; - ws.onerror = undefined; - - self.done(ws.readyState); - }; - - ws.onerror = function() { - if (this.trace) - console.log("remote: onerror: %s", ws.readyState); - - ws.onclose = undefined; - - if (self.expire) { - if (this.trace) - console.log("remote: was expired"); - - self.done(ws.readyState); - } - else - { - // Delay and retry. - setTimeout(function() { - if (this.trace) - console.log("remote: retry"); - - self.connect_helper(); - }, 50); // Retry rate 50ms. - } - }; - - // Covers failure to open. - ws.onclose = function() { - if (this.trace) - console.log("remote: onclose: %s", ws.readyState); - - ws.onerror = undefined; - - self.done(ws.readyState); - }; - - // Node's ws module doesn't pass arguments to onmessage. - ws.on('message', function(json, flags) { - var message = JSON.parse(json); - // console.log("message: %s", json); - - if (message.type !== 'response') { - console.log("unexpected message: %s", json); - - } else { - var done = ws.response[message.id]; - - if (done) { - done(message); - - } else { - console.log("unexpected message id: %s", json); - } - } - }); +Remote.method('connect_helper', function () { + var self = this; + + if (this.trace) console.log("remote: connect: %s", this.url); + + var ws = this.ws = new WebSocket(this.url);; + + ws.response = {}; + + ws.onopen = function () { + if (this.trace) console.log("remote: onopen: %s", ws.readyState); + + ws.onclose = undefined; + ws.onerror = undefined; + + self.done(ws.readyState); + }; + + ws.onerror = function () { + if (this.trace) console.log("remote: onerror: %s", ws.readyState); + + ws.onclose = undefined; + + if (self.expire) { + if (this.trace) console.log("remote: was expired"); + self.done(ws.readyState); + } else { + // Delay and retry. + setTimeout(function () { + if (this.trace) console.log("remote: retry"); + self.connect_helper(); + }, 50); // Retry rate 50ms. + } + }; + + // Covers failure to open. + ws.onclose = function () { + if (this.trace) console.log("remote: onclose: %s", ws.readyState); + ws.onerror = undefined; + self.done(ws.readyState); + }; + + // Node's ws module doesn't pass arguments to onmessage. + ws.on('message', function (json, flags) { + var message = JSON.parse(json); + // console.log("message: %s", json); + + if (message.type !== 'response') { + console.log("unexpected message: %s", json); + } else { + var done = ws.response[message.id]; + if (done) { + done(message); + } else { + console.log("unexpected message id: %s", json); + } + } + }); }); // Target state is connectted. // done(readyState): // --> readyState: OPEN, CLOSED -Remote.method('connect', function(done, timeout) { - var self = this; - - this.url = util.format("ws://%s:%s", this.websocket_ip, this.websocket_port); - this.done = done; - - if (timeout) { - if (this.trace) - console.log("remote: expire: false"); - - this.expire = false; - - setTimeout(function () { - if (this.trace) - console.log("remote: expire: timeout"); - - self.expire = true; - }, timeout); - } - else { - if (this.trace) - console.log("remote: expire: false"); - - this.expire = true; - } - - this.connect_helper(); - +Remote.method('connect', function (done, timeout) { + var self = this; + + this.url = util.format("ws://%s:%s", this.websocket_ip, this.websocket_port); + this.done = done; + + if (timeout) { + if (this.trace) console.log("remote: expire: false"); + + this.expire = false; + + setTimeout(function () { + if (this.trace) console.log("remote: expire: timeout"); + self.expire = true; + }, timeout); + } else { + if (this.trace) console.log("remote: expire: false"); + this.expire = true; + } + + this.connect_helper(); }); // Target stated is disconnected. -Remote.method('disconnect', function(done) { - var ws = this.ws; - - ws.onclose = function() { - if (this.trace) - console.log("remote: onclose: %s", ws.readyState); - - done(ws.readyState); - }; - - ws.close(); +Remote.method('disconnect', function (done) { + var ws = this.ws; + + ws.onclose = function () { + if (this.trace) console.log("remote: onclose: %s", ws.readyState); + done(ws.readyState); + }; + + ws.close(); }); // Send a command. The comman should lack the id. // <-> command: what to send, consumed. -Remote.method('request', function(command, done) { - this.id += 1; // Advance id. - - var ws = this.ws; - - command.id = this.id; - - ws.response[command.id] = done; - - if (this.trace) - console.log("remote: send: %s", JSON.stringify(command)); - - ws.send(JSON.stringify(command)); +Remote.method('request', function (request, onDone, onFailure) { + this.id += 1; // Advance id. + + var ws = this.ws; + + request.id = this.id; + + ws.response[request.id] = function (response) { + if (this.trace) console.log("remote: response: %s", JSON.stringify(response)); + + if (onFailure && response.error) + { + onFailure(response); + } + else + { + onDone(response); + } + }; + + if (this.trace) console.log("remote: request: %s", JSON.stringify(request)); + + ws.send(JSON.stringify(request)); }); -Remote.method('ledger_closed', function(done) { - assert(this.trusted); // If not trusted, need to check proof. - - this.request({ 'command' : 'ledger_closed' }, done); +Remote.method('request_ledger_closed', function (onDone, onFailure) { + assert(this.trusted); // If not trusted, need to check proof. + this.request({ 'command' : 'ledger_closed' }, onDone, onFailure); }); // Get the current proposed ledger entry. May be closed (and revised) at any time (even before returning). // Only for use by unit tests. -Remote.method('ledger_current', function(done) { - this.request({ 'command' : 'ledger_current' }, done); +Remote.method('request_ledger_current', function (onDone, onFailure) { + this.request({ 'command' : 'ledger_current' }, onDone, onFailure); }); -// <-> params: -// --> ledger : optional -// --> ledger_index : optional -Remote.method('ledger_entry', function(params, done) { - assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - - params.command = 'ledger_entry'; - - this.request(params, done); +// <-> request: +// --> ledger : optional +// --> ledger_index : optional +// --> type +Remote.method('request_ledger_entry', function (req, onDone, onFailure) { + assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. + + req.command = 'ledger_entry'; + + if (req.ledger_closed) + { + // XXX Initial implementation no caching. + this.request(req, onDone, onFailure); + } + else if (req.ledger_index) + { + // Current + // XXX Only allow with standalone mode. Must sync response with advance. + var entry; + + switch (req.type) { + case 'account_root': + var cache = this.ledgers.current.account_root; + + if (!cache) + { + cache = this.ledgers.current.account_root = {}; + } + + var entry = this.ledgers.current.account_root[req.account]; + break; + + default: + // This type not cached. + } + + if (entry) + { + onDone(entry); + } + else + { + // Not cached. + + // Submit request + this.request(req, function (r) { + // Got result. + switch (req.type) { + case 'account_root': + this.ledgers.current.account_root.account = r; + break; + + default: + // This type not cached. + } + onDone(r); + }, onFailure); + } + } }); // Submit a json transaction. // done(value) // <-> value: { 'status', status, 'result' : result, ... } // done may be called up to 3 times. -Remote.method('submit', function(json, done) { -// this.request(..., function() { -// }); +Remote.method('submit', function (json, private_key, onDone, onFailure) { + var req = {}; + + req.command = 'submit'; + req.json = json; + + if (private_key && !this.trusted) + { + onFailure({ 'error' : 'untrustedSever', 'request' : req }); + } + else + { + this.request(req, onDone, onFailure); + } }); -exports.Remote = Remote; -exports.remoteConfig = remoteConfig; +// +// Higher level functions. +// -// vim:ts=4 +// Subscribe to a server to get the current and closed ledger. +// XXX Set up routine to update on notification. +Remote.method('server_subscribe', function (onDone, onFailure) { + this.request( + { 'command' : 'server_subscribe' }, + function (r) { + this.ledger_current_index = r.ledger_current_index; + this.ledger_closed = r.ledger_closed; + this.stand_alone = r.stand_alone; + onDone(); + }, + onFailure + ); +}); + +// Refresh accounts[account].seq +// done(result); +Remote.method('account_seq', function (account, onDone, onFailure) { + var account_root_entry = this.accounts[account]; + + if (account_root_entry && account_root_entry.seq) + { + onDone(account_root_entry.seq); + } + else + { + // Need to get the ledger entry. + this.request_ledger_entry( + { + 'ledger' : this.ledger_closed, + 'account_root' : account + }, + function (r) { + // Extract the seqence number from the account root entry. + this.accounts[account].seq = r.seq; + onDone(r.seq); + }, + onFailure + ); + } +}); + +// A submit that fills in the sequence number. +Remote.method('submit_seq', function (onDone, onFailure) { + +}); + +exports.Remote = Remote; +exports.remoteConfig = remoteConfig; +exports.fees = fees; +exports.accounts = accounts; + +// vim:sw=2:sts=2:ts=8 diff --git a/src/Amount.cpp b/src/Amount.cpp index 9c5d0df513..9012cf69f3 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -83,9 +83,9 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) throw std::runtime_error("invalid amount string"); value = elements[0]; - if (elements.size() > 0) - currency = elements[1]; if (elements.size() > 1) + currency = elements[1]; + if (elements.size() > 2) issuer = elements[2]; } else diff --git a/src/DeterministicKeys.cpp b/src/DeterministicKeys.cpp index dc050067f1..08a14bb430 100644 --- a/src/DeterministicKeys.cpp +++ b/src/DeterministicKeys.cpp @@ -5,6 +5,8 @@ #include #include +// #define EC_DEBUG + // Functions to add CKey support for deterministic EC keys #include "Serializer.h" @@ -107,7 +109,9 @@ EC_KEY* CKey::GenerateRootDeterministicKey(const uint128& seed) BN_CTX_free(ctx); - assert(EC_KEY_check_key(pkey)==1); +#ifdef EC_DEBUG + assert(EC_KEY_check_key(pkey)==1); // CAUTION: This check is *very* expensive +#endif return pkey; } diff --git a/src/FieldNames.cpp b/src/FieldNames.cpp index 612d250c97..2946ca2552 100644 --- a/src/FieldNames.cpp +++ b/src/FieldNames.cpp @@ -7,15 +7,17 @@ #include #include +#include "utils.h" // These must stay at the top of this file std::map SField::codeToField; boost::mutex SField::mapMutex; SField sfInvalid(-1), sfGeneric(0); -SField sfLedgerEntry(FIELD_CODE(STI_LEDGERENTRY, 1), STI_LEDGERENTRY, 1, "LedgerEntry"); -SField sfTransaction(FIELD_CODE(STI_TRANSACTION, 1), STI_TRANSACTION, 1, "Transaction"); -SField sfValidation(FIELD_CODE(STI_VALIDATION, 1), STI_VALIDATION, 1, "Validation"); +SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry"); +SField sfTransaction(STI_TRANSACTION, 1, "Transaction"); +SField sfValidation(STI_VALIDATION, 1, "Validation"); +SField sfID(STI_HASH256, 257, "id"); #define FIELD(name, type, index) SField sf##name(FIELD_CODE(STI_##type, index), STI_##type, index, #name); #define TYPE(name, type, index) @@ -29,7 +31,7 @@ SField::ref SField::getField(int code) int type = code >> 16; int field = code % 0xffff; - if ((type <= 0) || (type >= 256) || (field <= 0) || (field >= 256)) + if ((type <= 0) || (field <= 0)) return sfInvalid; boost::mutex::scoped_lock sl(mapMutex); @@ -52,7 +54,8 @@ SField::ref SField::getField(int code) return sfInvalid; } - return *(new SField(code, static_cast(type), field, NULL)); + std::string dynName = lexical_cast_i(type) + "/" + lexical_cast_i(field); + return *(new SField(code, static_cast(type), field, dynName.c_str())); } int SField::compare(SField::ref f1, SField::ref f2) @@ -69,11 +72,6 @@ int SField::compare(SField::ref f1, SField::ref f2) return 0; } -SField::ref SField::getField(int type, int value) -{ - return getField(FIELD_CODE(type, value)); -} - std::string SField::getName() const { if (!fieldName.empty()) @@ -95,3 +93,13 @@ SField::ref SField::getField(const std::string& fieldName) } return sfInvalid; } + +SField::~SField() +{ + boost::mutex::scoped_lock sl(mapMutex); + std::map::iterator it = codeToField.find(fieldCode); + if ((it != codeToField.end()) && (it->second == this)) + codeToField.erase(it); +} + +// vim:ts=4 diff --git a/src/FieldNames.h b/src/FieldNames.h index 784900c148..cea532fb63 100644 --- a/src/FieldNames.h +++ b/src/FieldNames.h @@ -52,14 +52,26 @@ public: SField(int fc, SerializedTypeID tid, int fv, const char* fn) : fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn) - { codeToField[fc] = this; } + { + boost::mutex::scoped_lock sl(mapMutex); + codeToField[fieldCode] = this; + } + + SField(SerializedTypeID tid, int fv, const char *fn) : + fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn) + { + boost::mutex::scoped_lock sl(mapMutex); + codeToField[fieldCode] = this; + } SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0) { ; } + ~SField(); + static SField::ref getField(int fieldCode); - static SField::ref getField(int fieldType, int fieldValue); static SField::ref getField(const std::string& fieldName); - static SField::ref getField(SerializedTypeID type, int value) { return getField(FIELD_CODE(type, value)); } + static SField::ref getField(int type, int value) { return getField(FIELD_CODE(type, value)); } + static SField::ref getField(SerializedTypeID type, int value) { return getField(FIELD_CODE(type, value)); } std::string getName() const; bool hasName() const { return !fieldName.empty(); } @@ -67,6 +79,7 @@ public: bool isGeneric() const { return fieldCode == 0; } bool isInvalid() const { return fieldCode == -1; } bool isKnown() const { return fieldType != STI_UNKNOWN; } + bool isBinary() const { return fieldValue < 256; } bool operator==(const SField& f) const { return fieldCode == f.fieldCode; } bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; } diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index b96589212a..fc18926b48 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -794,8 +794,8 @@ void LedgerConsensus::startAcquiring(const TransactionAcquire::pointer& acquire) std::vector peerList = theApp->getConnectionPool().getPeerVector(); BOOST_FOREACH(Peer::ref peer, peerList) { - if (peer->hasTxSet(acquire->getHash()) - acquire->peerHash(peer); + if (peer->hasTxSet(acquire->getHash())) + acquire->peerHas(peer); } acquire->resetTimer(); @@ -1114,12 +1114,13 @@ void LedgerConsensus::accept(SHAMap::ref set) SerializedValidation::pointer v = boost::make_shared (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing); v->setTrusted(); - Log(lsINFO) << "CNF Val " << newLCLHash; 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)); + int j = theApp->getConnectionPool().relayMessage(NULL, + boost::make_shared(val, newcoin::mtVALIDATION)); + Log(lsINFO) << "CNF Val " << newLCLHash << " to " << j << " peers"; } else Log(lsINFO) << "CNF newLCL " << newLCLHash; diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index 478290bb33..a3d4ef2ba5 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -5,6 +5,8 @@ #include "Log.h" +// #define META_DEBUG + // Small for testing, should likely be 32 or 64. #define DIR_NODE_MAX 2 @@ -294,7 +296,9 @@ SLE::pointer LedgerEntrySet::getForMod(const uint256& node, Ledger::ref ledger, bool LedgerEntrySet::threadTx(const NewcoinAddress& threadTo, Ledger::ref ledger, boost::unordered_map& newMods) { +#ifdef META_DEBUG Log(lsTRACE) << "Thread to " << threadTo.getAccountID(); +#endif SLE::pointer sle = getForMod(Ledger::getAccountRootIndex(threadTo.getAccountID()), ledger, newMods); if (!sle) { @@ -321,12 +325,16 @@ bool LedgerEntrySet::threadOwners(SLE::ref node, Ledger::ref ledger, boost::unor { // thread new or modified node to owner or owners if (node->hasOneOwner()) // thread to owner's account { +#ifdef META_DEBUG Log(lsTRACE) << "Thread to single owner"; +#endif return threadTx(node->getOwner(), ledger, newMods); } else if (node->hasTwoOwners()) // thread to owner's accounts] { +#ifdef META_DEBUG Log(lsTRACE) << "Thread to two owners"; +#endif return threadTx(node->getFirstOwner(), ledger, newMods) && threadTx(node->getSecondOwner(), ledger, newMods); @@ -349,17 +357,23 @@ void LedgerEntrySet::calcRawMeta(Serializer& s) switch (it->second.mAction) { case taaMODIFY: +#ifdef META_DEBUG Log(lsTRACE) << "Modified Node " << it->first; +#endif nType = TMNModifiedNode; break; case taaDELETE: +#ifdef META_DEBUG Log(lsTRACE) << "Deleted Node " << it->first; +#endif nType = TMNDeletedNode; break; case taaCREATE: +#ifdef META_DEBUG Log(lsTRACE) << "Created Node " << it->first; +#endif nType = TMNCreatedNode; break; @@ -420,10 +434,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s) if ((nType == TMNCreatedNode) || (nType == TMNModifiedNode)) { if (curNode->isThreadedType()) // always thread to self - { - Log(lsTRACE) << "Thread to self"; threadTx(curNode, mLedger, newMod); - } } if (nType == TMNModifiedNode) @@ -454,7 +465,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s) it != end; ++it) entryModify(it->second); -#ifdef DEBUG +#ifdef META_DEBUG Log(lsINFO) << "Metadata:" << mSet.getJson(0); #endif diff --git a/src/Pathfinder.cpp b/src/Pathfinder.cpp index 4f0276a451..9290f921ae 100644 --- a/src/Pathfinder.cpp +++ b/src/Pathfinder.cpp @@ -84,24 +84,65 @@ Pathfinder::Pathfinder(NewcoinAddress& srcAccountID, NewcoinAddress& dstAccountI bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet) { - if(mLedger) - { - PathOption::pointer head(new PathOption(mSrcAccountID,mSrcCurrencyID,mDstAmount.getCurrency())); - addOptions(head); + if(mLedger) { + std::queue pqueue; + STPathElement ele(mSrcAccountID, + mSrcCurrencyID, + uint160()); + STPath path; + path.addElement(ele); + pqueue.push(path); + while(pqueue.size()) { - for(int n=0; n tempPaths=mBuildingPaths; - mBuildingPaths.clear(); - BOOST_FOREACH(PathOption::pointer path,tempPaths) - { - addOptions(path); - } - if(checkComplete(retPathSet)) return(true); - } - } + STPath path = pqueue.front(); + pqueue.pop(); + // get the first path from the queue - return(false); + ele = path.mPath.back(); + // get the last node from the path + + if (ele.mAccountID == mDstAccountID) { + path.mPath.erase(path.mPath.begin()); + path.mPath.erase(path.mPath.begin() + path.mPath.size()-1); + retPathSet.addPath(path); + return true; + } + // found the destination + + if (!ele.mCurrencyID) { + BOOST_FOREACH(OrderBook::pointer book,mOrderBook.getXNSInBooks()) + { + //if (!path.hasSeen(line->getAccountIDPeer().getAccountID())) + { + + STPath new_path(path); + STPathElement new_ele(uint160(), book->getCurrencyOut(), book->getIssuerOut()); + new_path.mPath.push_back(new_ele); + pqueue.push(new_path); + } + } + + } else { + RippleLines rippleLines(ele.mAccountID); + BOOST_FOREACH(RippleState::pointer line,rippleLines.getLines()) + { + if (!path.hasSeen(line->getAccountIDPeer().getAccountID())) + { + STPath new_path(path); + STPathElement new_ele(line->getAccountIDPeer().getAccountID(), + ele.mCurrencyID, + uint160()); + + new_path.mPath.push_back(new_ele); + pqueue.push(new_path); + } + } + } + // enumerate all adjacent nodes, construct a new path and push it into the queue + } // While + } // if there is a ledger + + return false; } bool Pathfinder::checkComplete(STPathSet& retPathSet) diff --git a/src/Peer.cpp b/src/Peer.cpp index 2397071ae1..2ea4f85f69 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -756,11 +756,16 @@ void Peer::recvValidation(newcoin::TMValidation& packet) { if (packet.validation().size() < 50) { + Log(lsWARNING) << "Too small validation from peer"; punishPeer(PP_UNKNOWN_REQUEST); return; } +// The four #ifndef/#endif's are commented out temporarily to avoid +// an update hassle. They can be removed once all nodes are running this code +//#ifndef TRUST_NETWORK try +//#endif { Serializer s(packet.validation()); SerializerIterator sit(s); @@ -768,10 +773,14 @@ void Peer::recvValidation(newcoin::TMValidation& packet) uint256 signingHash = val->getSigningHash(); if (!theApp->isNew(signingHash)) + { + Log(lsTRACE) << "Validation is duplicate"; return; + } if (!val->isValid(signingHash)) { + Log(lsWARNING) << "Validation is invalid"; punishPeer(PP_UNKNOWN_REQUEST); return; } @@ -782,10 +791,13 @@ void Peer::recvValidation(newcoin::TMValidation& packet) theApp->getConnectionPool().relayMessage(this, message); } } +//#ifndef TRUST_NETWORK catch (...) { + Log(lsWARNING) << "Exception processing validation"; punishPeer(PP_UNKNOWN_REQUEST); } +//#endif } void Peer::recvGetValidation(newcoin::TMGetValidations& packet) diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 3790691058..4b5de9ef8d 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1890,20 +1890,16 @@ Json::Value RPCServer::doSend(const Json::Value& params) // XXX Don't allow send to self of same currency. Transaction::pointer trans; - if (asDst) { // Destination exists, ordinary send. - STPathSet spsPaths; - uint160 srcCurrencyID; -// bool ret_b; -// ret_b = false; + STPathSet spsPaths; + uint160 srcCurrencyID; if (!saSrcAmountMax.isNative() || !saDstAmount.isNative()) { STAmount::currencyFromString(srcCurrencyID, sSrcCurrency); Pathfinder pf(naSrcAccountID, naDstAccountID, srcCurrencyID, saDstAmount); -// ret_b = pf.findPaths(5, 1, spsPaths); pf.findPaths(5, 1, spsPaths); } diff --git a/src/RippleLines.cpp b/src/RippleLines.cpp index bae60414b1..e9a8ad25fa 100644 --- a/src/RippleLines.cpp +++ b/src/RippleLines.cpp @@ -8,6 +8,13 @@ RippleLines::RippleLines(const uint160& accountID, Ledger::pointer ledger) fillLines(accountID,ledger); } +void RippleLines::printRippleLines() { + for (int i =0; i < mLines.size(); i++) { + std::cout << i << ": " << mLines[i]->getAccountID().humanAccountID() << std::endl; + } + std::cout << std::endl; +} + RippleLines::RippleLines(const uint160& accountID ) { fillLines(accountID,theApp->getMasterLedger().getCurrentLedger()); diff --git a/src/RippleLines.h b/src/RippleLines.h index 13ea7f5dc4..00373838dd 100644 --- a/src/RippleLines.h +++ b/src/RippleLines.h @@ -16,4 +16,5 @@ public: RippleLines(const uint160& accountID ); // looks in the current ledger std::vector& getLines(){ return(mLines); } -}; \ No newline at end of file + void printRippleLines(); +}; diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index baf9ba1cbc..4aea81b52b 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "../json/writer.h" @@ -205,7 +206,10 @@ bool STObject::set(SerializerIterator& sit, int depth) return true; SField::ref fn = SField::getField(type, field); if (fn.isInvalid()) + { + Log(lsWARNING) << "Unknown field: field_type=" << type << ", field_name=" << field; throw std::runtime_error("Unknown field"); + } giveObject(makeDeserializedObject(fn.fieldType, fn, sit, depth + 1)); } return false; @@ -248,10 +252,11 @@ void STObject::add(Serializer& s, bool withSigningFields) const BOOST_FOREACH(const SerializedType& it, mData) { // pick out the fields and sort them - if (it.getSType() != STI_NOTPRESENT) + if ((it.getSType() != STI_NOTPRESENT) && it.getFName().isBinary()) { SField::ref fName = it.getFName(); - if (withSigningFields || ((fName != sfTxnSignature) && (fName != sfTxnSignatures))) + if (withSigningFields || + ((fName != sfTxnSignature) && (fName != sfTxnSignatures) && (fName != sfSignature))) fields.insert(std::make_pair(it.getFName().fieldCode, &it)); } } @@ -427,7 +432,7 @@ void STObject::makeFieldAbsent(SField::ref field) if (f.getSType() == STI_NOTPRESENT) return; - mData.replace(index, makeDefaultObject(f.getFName())); + mData.replace(index, makeNonPresentObject(f.getFName())); } bool STObject::delField(SField::ref field) @@ -761,17 +766,44 @@ Json::Value STVector256::getJson(int options) const std::string STArray::getFullText() const { - return "WRITEME"; + std::string r = "["; + + bool first = true; + BOOST_FOREACH(const STObject& o, value) + { + if (!first) + r += ","; + r += o.getFullText(); + first = false; + } + + r += "]"; + return r; } std::string STArray::getText() const { - return "WRITEME"; + std::string r = "["; + + bool first = true; + BOOST_FOREACH(const STObject& o, value) + { + if (!first) + r += ","; + r += o.getText(); + first = false; + } + + r += "]"; + return r; } -Json::Value STArray::getJson(int) const +Json::Value STArray::getJson(int p) const { - return Json::Value("WRITEME"); + Json::Value v = Json::arrayValue; + BOOST_FOREACH(const STObject& o, value) + v.append(o.getJson(p)); + return v; } void STArray::add(Serializer& s) const @@ -805,7 +837,10 @@ STArray* STArray::construct(SerializerIterator& sit, SField::ref field) SField::ref fn = SField::getField(type, field); if (fn.isInvalid()) + { + Log(lsTRACE) << "Unknown field: " << type << "/" << field; throw std::runtime_error("Unknown field"); + } value.push_back(STObject(fn)); value.rbegin()->set(sit, 1); @@ -938,6 +973,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r case STI_VL: if (!value.isString()) + throw std::runtime_error("Incorrect type"); data.push_back(new STVariableLength(field, strUnHex(value.asString()))); break; @@ -1004,13 +1040,24 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r if (!currency.isString()) throw std::runtime_error("path element currencies must be strings"); hasCurrency = true; - // WRITEME + if (currency.asString().size() == 40) + uCurrency.SetHex(currency.asString()); + else if (!STAmount::currencyFromString(uCurrency, currency.asString())) + throw std::runtime_error("invalid currency"); } if (!issuer.isNull()) { // human account id if (!issuer.isString()) throw std::runtime_error("path element issuers must be strings"); - // WRITEME + if (issuer.asString().size() == 40) + uIssuer.SetHex(issuer.asString()); + else + { + NewcoinAddress a; + if (!a.setAccountPublic(issuer.asString())) + throw std::runtime_error("path element issuer invalid"); + uIssuer = a.getAccountID(); + } } p.addElement(STPathElement(uAccount, uCurrency, uIssuer, hasCurrency)); } @@ -1033,8 +1080,11 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r else { // newcoin addres NewcoinAddress a; - if (!a.setAccountPublic(strValue)) + if (!a.setAccountID(strValue)) + { + Log(lsINFO) << "Invalid acccount JSON: " << fieldName << ": " << strValue; throw std::runtime_error("Account invalid"); + } data.push_back(new STAccount(field, a.getAccountID())); } } @@ -1069,67 +1119,68 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r return std::auto_ptr(new STObject(*name, data)); } -#if 0 +BOOST_AUTO_TEST_SUITE(SerializedObject) -static SOElement testSOElements[2][16] = -{ // field, name, id, type, flags - { - { sfFlags, "Flags", STI_UINT32, SOE_FLAGS, 0 }, - { sfTest1, "Test1", STI_VL, SOE_REQUIRED, 0 }, - { sfTest2, "Test2", STI_HASH256, SOE_IFFLAG, 1 }, - { sfTest3, "Test3", STI_UINT32, SOE_REQUIRED, 0 }, - { sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } - } -}; - -void STObject::unitTest() +BOOST_AUTO_TEST_CASE( FieldManipulation_test ) { - STObject object1(testSOElements[0], "TestElement1"); + SField sfTestVL(STI_VL, 255, "TestVL"); + SField sfTestH256(STI_HASH256, 255, "TestH256"); + SField sfTestU32(STI_UINT32, 255, "TestU32"); + SField sfTestObject(STI_OBJECT, 255, "TestObject"); + + std::vector elements; + elements.push_back(new SOElement(sfFlags, SOE_REQUIRED)); + elements.push_back(new SOElement(sfTestVL, SOE_REQUIRED)); + elements.push_back(new SOElement(sfTestH256, SOE_OPTIONAL)); + elements.push_back(new SOElement(sfTestU32, SOE_REQUIRED)); + + STObject object1(elements, sfTestObject); STObject object2(object1); - if (object1.getSerializer() != object2.getSerializer()) throw std::runtime_error("STObject error"); + if (object1.getSerializer() != object2.getSerializer()) BOOST_FAIL("STObject error 1"); - if (object1.isFieldPresent(sfTest2) || !object1.isFieldPresent(sfTest1)) - throw std::runtime_error("STObject error"); + if (object1.isFieldPresent(sfTestH256) || !object1.isFieldPresent(sfTestVL)) + BOOST_FAIL("STObject error"); - object1.makeFieldPresent(sfTest2); - if (!object1.isFieldPresent(sfTest2)) throw std::runtime_error("STObject Error"); + object1.makeFieldPresent(sfTestH256); + if (!object1.isFieldPresent(sfTestH256)) BOOST_FAIL("STObject Error 2"); + if (object1.getFieldH256(sfTestH256) != uint256()) BOOST_FAIL("STObject error 3"); - if ((object1.getFlags() != 1) || (object2.getFlags() != 0)) throw std::runtime_error("STObject error"); - if (object1.getFieldH256(sfTest2) != uint256()) throw std::runtime_error("STObject error"); - - if (object1.getSerializer() == object2.getSerializer()) throw std::runtime_error("STObject error"); - object1.makeFieldAbsent(sfTest2); - if (object1.isFieldPresent(sfTest2)) throw std::runtime_error("STObject error"); - if (object1.getFlags() != 0) throw std::runtime_error("STObject error"); - if (object1.getSerializer() != object2.getSerializer()) throw std::runtime_error("STObject error"); + if (object1.getSerializer() == object2.getSerializer()) + { + Log(lsINFO) << "O1: " << object1.getJson(0); + Log(lsINFO) << "O2: " << object2.getJson(0); + BOOST_FAIL("STObject error 4"); + } + object1.makeFieldAbsent(sfTestH256); + if (object1.isFieldPresent(sfTestH256)) BOOST_FAIL("STObject error 5"); + if (object1.getFlags() != 0) BOOST_FAIL("STObject error 6"); + if (object1.getSerializer() != object2.getSerializer()) BOOST_FAIL("STObject error 7"); STObject copy(object1); - if (object1.isFieldPresent(sfTest2)) throw std::runtime_error("STObject error"); - if (copy.isFieldPresent(sfTest2)) throw std::runtime_error("STObject error"); - if (object1.getSerializer() != copy.getSerializer()) throw std::runtime_error("STObject error"); - copy.setFieldU32(sfTest3, 1); - if (object1.getSerializer() == copy.getSerializer()) throw std::runtime_error("STObject error"); -#ifdef DEBUG - Log(lsDEBUG) << copy.getJson(0); -#endif + if (object1.isFieldPresent(sfTestH256)) BOOST_FAIL("STObject error 8"); + if (copy.isFieldPresent(sfTestH256)) BOOST_FAIL("STObject error 9"); + if (object1.getSerializer() != copy.getSerializer()) BOOST_FAIL("STObject error 10"); + copy.setFieldU32(sfTestU32, 1); + if (object1.getSerializer() == copy.getSerializer()) BOOST_FAIL("STObject error 11"); for (int i = 0; i < 1000; i++) { - std::cerr << "tol: i=" << i << std::endl; std::vector j(i, 2); - object1.setFieldVL(sfTest1, j); + + object1.setFieldVL(sfTestVL, j); Serializer s; object1.add(s); SerializerIterator it(s); - STObject object3(testSOElements[0], it, "TestElement3"); - if (object1.getFieldVL(sfTest1) != j) throw std::runtime_error("STObject error"); - if (object3.getFieldVL(sfTest1) != j) throw std::runtime_error("STObject error"); + STObject object3(elements, it, sfTestObject); + + if (object1.getFieldVL(sfTestVL) != j) BOOST_FAIL("STObject error"); + if (object3.getFieldVL(sfTestVL) != j) BOOST_FAIL("STObject error"); } } -#endif +BOOST_AUTO_TEST_SUITE_END(); // vim:ts=4 diff --git a/src/SerializedObject.h b/src/SerializedObject.h index ed8d06560e..2e28fc958b 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -145,8 +145,6 @@ public: { return makeDefaultObject(STI_NOTPRESENT, name); } static std::auto_ptr makeDefaultObject(SField::ref name) { return makeDefaultObject(name.fieldType, name); } - - static void unitTest(); }; class STArray : public SerializedType diff --git a/src/SerializedTransaction.cpp b/src/SerializedTransaction.cpp index 617125c02e..869a9b6066 100644 --- a/src/SerializedTransaction.cpp +++ b/src/SerializedTransaction.cpp @@ -2,6 +2,7 @@ #include "SerializedTransaction.h" #include +#include #include "Application.h" #include "Log.h" @@ -109,6 +110,20 @@ void SerializedTransaction::sign(const NewcoinAddress& naAccountPrivate) setFieldVL(sfTxnSignature, signature); } +bool SerializedTransaction::checkSign() const +{ + try + { + NewcoinAddress n; + n.setAccountPublic(getFieldVL(sfSigningPubKey)); + return checkSign(n); + } + catch (...) + { + return false; + } +} + bool SerializedTransaction::checkSign(const NewcoinAddress& naAccountPublic) const { try @@ -166,4 +181,40 @@ std::string SerializedTransaction::getSQL(Serializer rawTxn, uint32 inLedger, ch } +BOOST_AUTO_TEST_SUITE(SerializedTransactionTS) + +BOOST_AUTO_TEST_CASE( STrans_test ) +{ + NewcoinAddress seed; + seed.setSeedRandom(); + NewcoinAddress generator = NewcoinAddress::createGeneratorPublic(seed); + NewcoinAddress publicAcct = NewcoinAddress::createAccountPublic(generator, 1); + NewcoinAddress privateAcct = NewcoinAddress::createAccountPrivate(generator, seed, 1); + + SerializedTransaction j(ttCLAIM); + j.setSourceAccount(publicAcct); + j.setSigningPubKey(publicAcct); + j.setFieldVL(sfPublicKey, publicAcct.getAccountPublic()); + j.sign(privateAcct); + + if (!j.checkSign()) BOOST_FAIL("Transaction fails signature test"); + + Serializer rawTxn; + j.add(rawTxn); + SerializerIterator sit(rawTxn); + SerializedTransaction copy(sit); + if (copy != j) + { + Log(lsFATAL) << j.getJson(0); + Log(lsFATAL) << copy.getJson(0); + BOOST_FAIL("Transaction fails serialize/deserialize test"); + } + std::auto_ptr new_obj = STObject::parseJson(j.getJson(0), sfGeneric); + if (new_obj.get() == NULL) BOOST_FAIL("Unable to build object from json"); + Log(lsINFO) << "ORIG: " << j.getJson(0); + Log(lsINFO) << "BUILT " << new_obj->getJson(0); +} + +BOOST_AUTO_TEST_SUITE_END(); + // vim:ts=4 diff --git a/src/SerializedTransaction.h b/src/SerializedTransaction.h index 0d9f59e529..a2089f2240 100644 --- a/src/SerializedTransaction.h +++ b/src/SerializedTransaction.h @@ -63,6 +63,7 @@ public: void sign(const NewcoinAddress& naAccountPrivate); bool checkSign(const NewcoinAddress& naAccountPublic) const; + bool checkSign() const; // SQL Functions static std::string getSQLValueHeader(); diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index 92316d8b96..32fab92f21 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -10,10 +10,35 @@ #include "Log.h" #include "NewcoinAddress.h" #include "utils.h" +#include "NewcoinAddress.h" STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0); STAmount saOne(CURRENCY_ONE, ACCOUNT_ONE, 1); +void STPathSet::printDebug() { + for (int i = 0; i < value.size(); i++) { + std::cout << i << ": "; + for (int j = 0; j < value[i].mPath.size(); j++) { + //STPathElement pe = value[i].mPath[j]; + NewcoinAddress nad; + nad.setAccountID(value[i].mPath[j].mAccountID); + std::cout << " " << nad.humanAccountID(); + //std::cout << " " << pe.mAccountID.GetHex(); + } + std::cout << std::endl; + } + +} + +void STPath::printDebug() { + std::cout << "STPath:" << std::endl; + for(int i =0; i < mPath.size(); i++) { + NewcoinAddress nad; + nad.setAccountID(mPath[i].mAccountID); + std::cout << " " << i << ": " << nad.humanAccountID() << std::endl; + } +} + std::string SerializedType::getFullText() const { std::string ret; @@ -331,6 +356,16 @@ bool STPathSet::isEquivalent(const SerializedType& t) const return v && (value == v->value); } +bool STPath::hasSeen(const uint160 &acct) { + + for (int i = 0; i < mPath.size();i++) { + STPathElement ele = getElement(i); + if (ele.getAccountID() == acct) + return true; + } + + return false; +} int STPath::getSerializeSize() const { int iBytes = 0; diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 0c1cd3bac2..cee687a7fd 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -518,6 +518,9 @@ public: class STPathElement { + friend class STPathSet; + friend class STPath; + friend class Pathfinder; public: enum { typeEnd = 0x00, @@ -567,6 +570,8 @@ public: class STPath { + friend class STPathSet; + friend class Pathfinder; protected: std::vector mPath; @@ -574,12 +579,14 @@ public: STPath() { ; } STPath(const std::vector& p) : mPath(p) { ; } + void printDebug(); int getElementCount() const { return mPath.size(); } bool isEmpty() const { return mPath.empty(); } const STPathElement& getElement(int offset) const { return mPath[offset]; } const STPathElement& getElemet(int offset) { return mPath[offset]; } - void addElement(const STPathElement& e) { mPath.push_back(e); } + void addElement(const STPathElement &e) { mPath.push_back(e); } void clear() { mPath.clear(); } + bool hasSeen(const uint160 &acct); int getSerializeSize() const; // std::string getText() const; Json::Value getJson(int) const; @@ -636,7 +643,6 @@ protected: static STPathSet* construct(SerializerIterator&, SField::ref); public: - STPathSet() { ; } STPathSet(SField::ref n) : SerializedType(n) { ; } STPathSet(const std::vector& v) : value(v) { ; } @@ -658,6 +664,8 @@ public: virtual bool isEquivalent(const SerializedType& t) const; + void printDebug(); + 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/SerializedValidation.cpp b/src/SerializedValidation.cpp index 84b40eb8ac..646fc3d9ff 100644 --- a/src/SerializedValidation.cpp +++ b/src/SerializedValidation.cpp @@ -2,6 +2,7 @@ #include "SerializedValidation.h" #include "HashPrefixes.h" +#include "Log.h" std::vector sValidationFormat; @@ -15,30 +16,38 @@ static bool SVFInit() sValidationFormat.push_back(new SOElement(sfBaseFee, SOE_OPTIONAL)); sValidationFormat.push_back(new SOElement(sfSigningTime, SOE_REQUIRED)); sValidationFormat.push_back(new SOElement(sfSigningPubKey, SOE_REQUIRED)); + sValidationFormat.push_back(new SOElement(sfSignature, SOE_OPTIONAL)); return true; }; bool SVFinitComplete = SVFInit(); -const uint32 SerializedValidation::sFullFlag = 0x00010000; +const uint32 SerializedValidation::sFullFlag = 0x1; SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature) - : STObject(sValidationFormat, sit, sfValidation), mSignature(sit, sfSignature), mTrusted(false) + : STObject(sValidationFormat, sit, sfValidation), mTrusted(false) { - if (checkSignature && !isValid()) throw std::runtime_error("Invalid validation"); + if (checkSignature && !isValid()) + { + Log(lsTRACE) << "Invalid validation " << getJson(0); + throw std::runtime_error("Invalid validation"); + } } SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull) - : STObject(sValidationFormat, sfValidation), mSignature(sfSignature), mTrusted(false) + : STObject(sValidationFormat, sfValidation), mTrusted(false) { setFieldH256(sfLedgerHash, ledgerHash); setFieldU32(sfSigningTime, signTime); if (naSeed.isValid()) setFieldVL(sfSigningPubKey, NewcoinAddress::createNodePublic(naSeed).getNodePublic()); - if (!isFull) setFlag(sFullFlag); + if (!isFull) + setFlag(sFullFlag); - NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue()); + std::vector signature; + NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), signature); + setFieldVL(sfSignature, signature); // XXX Check if this can fail. // if (!NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue())) // throw std::runtime_error("Unable to sign validation"); @@ -46,12 +55,7 @@ SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 sig uint256 SerializedValidation::getSigningHash() const { - Serializer s; - - s.add32(sHP_Validation); - add(s); - - return s.getSHA512Half(); + return STObject::getSigningHash(sHP_Validation); } uint256 SerializedValidation::getLedgerHash() const @@ -79,10 +83,11 @@ bool SerializedValidation::isValid(const uint256& signingHash) const try { NewcoinAddress naPublicKey = NewcoinAddress::createNodePublic(getFieldVL(sfSigningPubKey)); - return naPublicKey.isValid() && naPublicKey.verifyNodePublic(signingHash, mSignature.peekValue()); + return naPublicKey.isValid() && naPublicKey.verifyNodePublic(signingHash, getFieldVL(sfSignature)); } catch (...) { + Log(lsINFO) << "exception validating validation"; return false; } } @@ -99,26 +104,16 @@ bool SerializedValidation::isFull() const return (getFlags() & sFullFlag) != 0; } -void SerializedValidation::addSigned(Serializer& s) const +std::vector SerializedValidation::getSignature() const { - add(s); - mSignature.add(s); -} - -void SerializedValidation::addSignature(Serializer& s) const -{ - mSignature.add(s); + return getFieldVL(sfSignature); } std::vector SerializedValidation::getSigned() const { Serializer s; - addSigned(s); + add(s); return s.peekData(); } -std::vector SerializedValidation::getSignature() const -{ - return mSignature.peekValue(); -} // vim:ts=4 diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index c76b233c54..1aab631041 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -7,7 +7,6 @@ class SerializedValidation : public STObject { protected: - STVariableLength mSignature; uint256 mPreviousHash; bool mTrusted; @@ -21,8 +20,6 @@ public: // These throw if the object is not valid SerializedValidation(SerializerIterator& sit, bool checkSignature = true); - SerializedValidation(const Serializer& s, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull); uint256 getLedgerHash() const; @@ -36,8 +33,6 @@ public: bool isValid(const uint256&) const; void setTrusted() { mTrusted = true; } - void addSigned(Serializer&) const; - void addSignature(Serializer&) const; std::vector getSigned() const; std::vector getSignature() const; diff --git a/src/Serializer.cpp b/src/Serializer.cpp index cd7e0d6a8e..78067feeb4 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -182,7 +182,7 @@ int Serializer::addFieldID(int type, int name) return ret; } -bool Serializer::getFieldID(int& type, int & name, int offset) const +bool Serializer::getFieldID(int& type, int& name, int offset) const { if (!get8(type, offset)) return false; diff --git a/src/TaggedCache.h b/src/TaggedCache.h index fb31a157b5..62a9e699d4 100644 --- a/src/TaggedCache.h +++ b/src/TaggedCache.h @@ -96,10 +96,7 @@ template void TaggedCache::sweep while (cit != mCache.end()) { if (cit->second->second.first < target) - { - typename boost::unordered_map::iterator tmp = cit++; - mCache.erase(tmp); - } + mCache.erase(cit++); else ++cit; } @@ -109,10 +106,7 @@ template void TaggedCache::sweep while (mit != mMap.end()) { if (mit->second->expired()) - { - typename boost::unordered_map::iterator tmp = mit++; mMap.erase(mit++); - } else ++mit; } diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 208c5c71c3..f45299ec32 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -89,8 +89,8 @@ public: void doAccountTransactionSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); void doAccountTransactionUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); - void doLedgerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doServerSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doServerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); void doLedgerAccountsSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); void doLedgerAccountsUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); void doTransactionSubcribe(Json::Value& jvResult, const Json::Value& jvRequest); @@ -311,10 +311,10 @@ Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) { "account_info_unsubscribe", &WSConnection::doAccountInfoUnsubscribe }, { "account_transaction_subscribe", &WSConnection::doAccountTransactionSubscribe }, { "account_transaction_unsubscribe", &WSConnection::doAccountTransactionUnsubscribe }, - { "ledger_subscribe", &WSConnection::doLedgerSubcribe }, - { "ledger_unsubscribe", &WSConnection::doLedgerUnsubscribe }, { "ledger_accounts_subscribe", &WSConnection::doLedgerAccountsSubcribe }, { "ledger_accounts_unsubscribe", &WSConnection::doLedgerAccountsUnsubscribe }, + { "server_subscribe", &WSConnection::doServerSubscribe }, + { "server_unsubscribe", &WSConnection::doServerUnsubscribe }, { "transaction_subscribe", &WSConnection::doTransactionSubcribe }, { "transaction_unsubscribe", &WSConnection::doTransactionUnsubscribe }, }; @@ -523,22 +523,6 @@ void WSConnection::doAccountTransactionUnsubscribe(Json::Value& jvResult, const } } -void WSConnection::doLedgerSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!theApp->getOPs().subLedger(this)) - { - jvResult["error"] = "ledgerSubscribed"; - } -} - -void WSConnection::doLedgerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) -{ - if (!theApp->getOPs().unsubLedger(this)) - { - jvResult["error"] = "ledgerNotSubscribed"; - } -} - void WSConnection::doLedgerAccountsSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) { if (!theApp->getOPs().subLedgerAccounts(this)) @@ -559,13 +543,13 @@ void WSConnection::doLedgerClosed(Json::Value& jvResult, const Json::Value& jvRe { uint256 uLedger = theApp->getOPs().getClosedLedger(); - jvResult["ledger_index"] = theApp->getOPs().getLedgerID(uLedger); - jvResult["ledger"] = uLedger.ToString(); + jvResult["ledger_closed_index"] = theApp->getOPs().getLedgerID(uLedger); + jvResult["ledger_closed"] = uLedger.ToString(); } void WSConnection::doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvRequest) { - jvResult["ledger_index"] = theApp->getOPs().getCurrentLedgerID(); + jvResult["ledger_current_index"] = theApp->getOPs().getCurrentLedgerID(); } void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest) @@ -606,10 +590,17 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq uLedgerIndex = lpLedger->getLedgerSeq(); // Set the current index. } - if (!!uLedger) - jvResult["ledger"] = uLedger.ToString(); + if (lpLedger->isClosed()) + { + if (!!uLedger) + jvResult["ledger_closed"] = uLedger.ToString(); - jvResult["ledger_index"] = uLedgerIndex; + jvResult["ledger_closed_index"] = uLedgerIndex; + } + else + { + jvResult["ledger_current_index"] = uLedgerIndex; + } uint256 uNodeIndex; bool bNodeBinary = false; @@ -788,6 +779,32 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } } +void WSConnection::doServerSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!theApp->getOPs().subLedger(this)) + { + jvResult["error"] = "serverSubscribed"; + } + else + { + if (theConfig.RUN_STANDALONE) + jvResult["stand_alone"] = 1; + + // XXX Make sure these values are available before returning them. + // XXX return connected status. + jvResult["ledger_closed"] = theApp->getOPs().getClosedLedger().ToString(); + jvResult["ledger_current_index"] = theApp->getOPs().getCurrentLedgerID(); + } +} + +void WSConnection::doServerUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (!theApp->getOPs().unsubLedger(this)) + { + jvResult["error"] = "serverNotSubscribed"; + } +} + void WSConnection::doTransactionSubcribe(Json::Value& jvResult, const Json::Value& jvRequest) { if (!theApp->getOPs().subTransaction(this)) diff --git a/test/config.js b/test/config.js index 0610373db9..10d084a532 100644 --- a/test/config.js +++ b/test/config.js @@ -9,17 +9,57 @@ exports.newcoind = path.join(process.cwd(), "newcoind"); // Configuration for servers. exports.servers = { - // A local test server. - alpha : { - 'trusted' : true, - // "peer_ip" : "0.0.0.0", - // "peer_port" : 51235, - 'rpc_ip' : "0.0.0.0", - 'rpc_port' : 5005, - 'websocket_ip' : "127.0.0.1", - 'websocket_port' : 6005, - 'validation_seed' : "shhDFVsmS2GSu5vUyZSPXYfj1r79h", - 'validators' : "n9L8LZZCwsdXzKUN9zoVxs4YznYXZ9hEhsQZY7aVpxtFaSceiyDZ beta" - } + // A local test server. + 'alpha' : { + 'trusted' : true, + // "peer_ip" : "0.0.0.0", + // "peer_port" : 51235, + 'rpc_ip' : "0.0.0.0", + 'rpc_port' : 5005, + 'websocket_ip' : "127.0.0.1", + 'websocket_port' : 6005, + 'validation_seed' : "shhDFVsmS2GSu5vUyZSPXYfj1r79h", + 'validators' : "n9L8LZZCwsdXzKUN9zoVxs4YznYXZ9hEhsQZY7aVpxtFaSceiyDZ beta" + } }; -// vim:ts=4 + +// Configuration for test accounts. +exports.accounts = { + // Users + 'alice' : { + 'account' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn', + 'passphrase' : 'alice', + }, + 'bob' : { + 'account' : 'iPMh7Pr9ct699rZUTWaytJUoHcJ7cgyzrK', + 'passphrase' : 'bob', + }, + 'carol' : { + 'account' : 'iH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW', + 'passphrase' : 'carol', + }, + + // Nexuses + 'bitstamp' : { + 'account' : 'i4jKmc2nQb5yEU6eycefrNKGHTU5NQJASx', + 'passphrase' : 'bitstamp', + }, + 'mtgox' : { + 'account' : 'iGrhwhaqU8g7ahwAvTq6rX5ivsfcbgZw6v', + 'passphrase' : 'mtgox', + }, + + // Merchants + 'amazon' : { + 'account' : 'ihheXqX7bDnXePJeMHhubDDvw2uUTtenPd', + 'passphrase' : 'amazon', + }, + + // Master account + 'root' : { + 'account' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh', + 'passphrase' : 'masterpassphrase', + }, +}; + +// vim:sw=2:sts=2:ts=8 diff --git a/test/standalone-test.js b/test/standalone-test.js index 6a18592a42..600e0a9df6 100644 --- a/test/standalone-test.js +++ b/test/standalone-test.js @@ -11,178 +11,179 @@ var serverDelay = 1500; buster.testRunner.timeout = 5000; buster.testCase("Standalone server startup", { - "server start and stop": function(done) { - server.start("alpha", - function(e) { - buster.refute(e); - server.stop("alpha", function(e) { - buster.refute(e); - done(); - }); - }); - } + "server start and stop": function (done) { + server.start("alpha", + function (e) { + buster.refute(e); + server.stop("alpha", function (e) { + buster.refute(e); + done(); + }); + }); + } }); buster.testCase("WebSocket connection", { - 'setUp' : - function(done) { - server.start("alpha", - function(e) { - buster.refute(e); - done(); - } - ); - }, + 'setUp' : + function (done) { + server.start("alpha", + function (e) { + buster.refute(e); + done(); + } + ); + }, - 'tearDown' : - function(done) { - server.stop("alpha", function(e) { - buster.refute(e); - done(); - }); - }, + 'tearDown' : + function (done) { + server.stop("alpha", function (e) { + buster.refute(e); + done(); + }); + }, - "websocket connect and disconnect" : - function(done) { - var alpha = remote.remoteConfig(config, "alpha"); + "websocket connect and disconnect" : + function (done) { + var alpha = remote.remoteConfig(config, "alpha"); - alpha.connect(function(stat) { - buster.assert(1 == stat); // OPEN + alpha.connect(function (stat) { + buster.assert(1 == stat); // OPEN - alpha.disconnect(function(stat) { - buster.assert(3 == stat); // CLOSED - done(); - }); - }, serverDelay); - }, + alpha.disconnect(function (stat) { + buster.assert(3 == stat); // CLOSED + done(); + }); + }, serverDelay); + }, }); buster.testCase("Websocket commands", { - 'setUp' : - function(done) { - server.start("alpha", - function(e) { - buster.refute(e); + 'setUp' : + function (done) { + server.start("alpha", + function (e) { + buster.refute(e); - alpha = remote.remoteConfig(config, "alpha"); + alpha = remote.remoteConfig(config, "alpha"); - alpha.connect(function(stat) { - buster.assert(1 == stat); // OPEN + alpha.connect(function (stat) { + buster.assert(1 == stat); // OPEN + done(); + }, serverDelay); + }); + }, - done(); - }, serverDelay); - }); - }, + 'tearDown' : + function (done) { + alpha.disconnect(function (stat) { + buster.assert(3 == stat); // CLOSED - 'tearDown' : - function(done) { - alpha.disconnect(function(stat) { - buster.assert(3 == stat); // CLOSED + server.stop("alpha", function (e) { + buster.refute(e); + done(); + }); + }); + }, - server.stop("alpha", function(e) { - buster.refute(e); + 'ledger_current' : + function (done) { + alpha.request_ledger_current(function (r) { + console.log(r); - done(); - }); - }); - }, + buster.assert.equals(r.ledger_current_index, 3); + done(); + }); + }, - 'ledger_current' : - function(done) { - alpha.ledger_current(function (r) { - console.log(r); + '// ledger_closed' : + function (done) { + alpha.request_ledger_closed(function (r) { + console.log("result: %s", JSON.stringify(r)); - buster.assert.equals(r.ledger_index, 3); - done(); - }); - }, + buster.assert.equals(r.ledger_closed_index, 2); + done(); + }); + }, - '// ledger_closed' : - function(done) { - alpha.ledger_closed(function (r) { - console.log("result: %s", JSON.stringify(r)); + 'account_root success' : + function (done) { + alpha.request_ledger_closed(function (r) { + // console.log("result: %s", JSON.stringify(r)); - buster.assert.equals(r.ledger_index, 2); - done(); - }); - }, + buster.refute(r.error); - 'account_root success' : - function(done) { - alpha.ledger_closed(function (r) { - // console.log("result: %s", JSON.stringify(r)); + alpha.request_ledger_entry({ + 'ledger_closed' : r.ledger_closed, + 'type' : 'account_root', + 'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh' + } , function (r) { + // console.log("account_root: %s", JSON.stringify(r)); - buster.refute('error' in r); + buster.assert('node' in r); + done(); + }); + }); + }, - alpha.ledger_entry({ - 'ledger_index' : r.ledger_index, - 'account_root' : 'iHb9CJAWyB4ij91VRWn96DkukG4bwdtyTh' - } , function (r) { - // console.log("account_root: %s", JSON.stringify(r)); + 'account_root malformedAddress' : + function (done) { + alpha.request_ledger_closed(function (r) { + // console.log("result: %s", JSON.stringify(r)); - buster.assert('node' in r); - done(); - }); - }); - }, + buster.refute(r.error); - 'account_root malformedAddress' : - function(done) { - alpha.ledger_closed(function (r) { - // console.log("result: %s", JSON.stringify(r)); + alpha.request_ledger_entry({ + 'ledger_closed' : r.ledger_closed, + 'type' : 'account_root', + 'account_root' : 'foobar' + } , function (r) { + // console.log("account_root: %s", JSON.stringify(r)); - buster.refute('error' in r); + buster.assert.equals(r.error, 'malformedAddress'); + done(); + }); + }); + }, - alpha.ledger_entry({ - 'ledger_index' : r.ledger_index, - 'account_root' : 'foobar' - } , function (r) { - // console.log("account_root: %s", JSON.stringify(r)); + 'account_root entryNotFound' : + function (done) { + alpha.request_ledger_closed(function (r) { + console.log("result: %s", JSON.stringify(r)); - buster.assert.equals(r.error, 'malformedAddress'); - done(); - }); - }); - }, + buster.refute(r.error); - 'account_root entryNotFound' : - function(done) { - alpha.ledger_closed(function (r) { - // console.log("result: %s", JSON.stringify(r)); + alpha.request_ledger_entry({ + 'ledger_closed' : r.ledger_closed, + 'type' : 'account_root', + 'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn' + }, function (r) { + console.log("account_root: %s", JSON.stringify(r)); - buster.refute('error' in r); + buster.assert.equals(r.error, 'entryNotFound'); + done(); + }); + }); + }, - alpha.ledger_entry({ - 'ledger_index' : r.ledger_index, - 'account_root' : 'iG1QQv2nh2gi7RCZ1P8YYcBUKCCN633jCn' - } , function (r) { - // console.log("account_root: %s", JSON.stringify(r)); + 'ledger_entry index' : + function (done) { + alpha.request_ledger_closed(function (r) { + // console.log("result: %s", JSON.stringify(r)); - buster.assert.equals(r.error, 'entryNotFound'); - done(); - }); - }); - }, + buster.refute(r.error); - 'ledger_entry index' : - function(done) { - alpha.ledger_closed(function (r) { - // console.log("result: %s", JSON.stringify(r)); - - buster.refute('error' in r); - - alpha.ledger_entry({ - 'ledger_index' : r.ledger_index, - 'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8", - } , function (r) { - console.log("node: %s", JSON.stringify(r)); - - buster.assert('node_binary' in r); - done(); - }); - }); - }, + alpha.request_ledger_entry({ + 'ledger_closed' : r.ledger_closed, + 'type' : 'account_root', + 'index' : "2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8", + } , function (r) { + console.log("node: %s", JSON.stringify(r)); + buster.assert('node_binary' in r); + done(); + }); + }); + }, }); -// vim:ts=4 +// vim:sw=2:sts=2:ts=8