diff --git a/.gitignore b/.gitignore index 894f12a7e7..793d90cba5 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ node_modules # Ignore tmp directory. tmp + +# Ignore database directory. +db/*.db \ No newline at end of file diff --git a/database/SqliteDatabase.cpp b/database/SqliteDatabase.cpp index 60d785d0c8..cbefe8759c 100644 --- a/database/SqliteDatabase.cpp +++ b/database/SqliteDatabase.cpp @@ -181,15 +181,21 @@ X'53514C697465' */ void SqliteDatabase::escape(const unsigned char* start, int size, std::string& retStr) { - retStr="X'"; + static const char toHex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' }; - char buf[3]; - for(int n=0; n> 4]; + retStr[pos++] = toHex[start[n] & 0x0f]; } - retStr.push_back('\''); + retStr[pos] = '\''; } + // vim:ts=4 diff --git a/js/amount.js b/js/amount.js index 10c9ffb392..6423ad595a 100644 --- a/js/amount.js +++ b/js/amount.js @@ -408,6 +408,10 @@ Amount.prototype.canonicalize = function() { return this; }; +Amount.prototype.is_native = function () { + return this.is_native; +}; + // Return a new value. Amount.prototype.negate = function () { return this.clone('NEGATE'); diff --git a/js/remote.js b/js/remote.js index 0c555388ed..73c1997055 100644 --- a/js/remote.js +++ b/js/remote.js @@ -65,7 +65,7 @@ Request.prototype.ledger_choose = function (current) { this.message.ledger_index = this.remote.ledger_current_index; } else { - this.message.ledger = this.remote.ledger_closed; + this.message.ledger_hash = this.remote.ledger_hash; } return this; @@ -74,8 +74,8 @@ Request.prototype.ledger_choose = function (current) { // Set the ledger for a request. // - ledger_entry // - transaction_entry -Request.prototype.ledger_closed = function (ledger) { - this.message.ledger_closed = ledger; +Request.prototype.ledger_hash = function (h) { + this.message.ledger_hash = h; return this; }; @@ -107,8 +107,14 @@ Request.prototype.secret = function (s) { return this; }; -Request.prototype.transaction = function (t) { - this.message.transaction = t; +Request.prototype.tx_hash = function (h) { + this.message.tx_hash = h; + + return this; +}; + +Request.prototype.tx_json = function (j) { + this.message.tx_json = j; return this; }; @@ -145,7 +151,7 @@ var Remote = function (trusted, websocket_ip, websocket_port, trace) { this.websocket_port = websocket_port; this.id = 0; this.trace = trace; - this.ledger_closed = undefined; + this.ledger_hash = undefined; this.ledger_current_index = undefined; this.stand_alone = undefined; this.online_target = false; @@ -410,10 +416,11 @@ Remote.prototype._connect_message = function (ws, json, flags) { // XXX Be more defensive fields could be missing or of wrong type. // YYY Might want to do some cache management. - this.ledger_closed = message.ledger_closed; - this.ledger_current_index = message.ledger_closed_index + 1; + this.ledger_time = message.ledger_time; + this.ledger_hash = message.ledger_hash; + this.ledger_current_index = message.ledger_index + 1; - this.emit('ledger_closed', message.ledger_closed, message.ledger_closed_index); + this.emit('ledger_closed', message.ledger_hash, message.ledger_index); break; default: @@ -466,16 +473,25 @@ Remote.prototype.request = function (request) { } }; -Remote.prototype.request_ledger_closed = function () { +// Only for unit testing. +Remote.prototype.request_ledger_hash = function () { assert(this.trusted); // If not trusted, need to check proof. - return new Request(this, 'ledger_closed'); + var request = new Request(this, 'rpc'); + + request.message.rpc_command = 'ledger_closed'; + + return request; }; // Get the current proposed ledger entry. May be closed (and revised) at any time (even before returning). -// Only for use by unit tests. +// Only for unit testing. Remote.prototype.request_ledger_current = function () { - return new Request(this, 'ledger_current'); + var request = new Request(this, 'rpc'); + + request.message.rpc_command = 'ledger_current'; + + return request; }; // --> ledger : optional @@ -484,14 +500,16 @@ Remote.prototype.request_ledger_entry = function (type) { assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. var self = this; - var request = new Request(this, 'ledger_entry'); + var request = new Request(this, 'rpc'); + + request.message.rpc_command = 'ledger_entry'; if (type) this.type = type; // Transparent caching: request.on('request', function (remote) { // Intercept default request. - if (this.ledger_closed) { + if (this.ledger_hash) { // XXX Add caching. } // else if (req.ledger_index) @@ -537,18 +555,30 @@ Remote.prototype.request_ledger_entry = function (type) { return request; }; +Remote.prototype.request_subscribe = function () { + var request = new Request(this, 'subscribe'); + + request.message.streams = [ 'ledger', 'server' ]; + + return request; +}; + Remote.prototype.request_transaction_entry = function (hash) { assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - return (new Request(this, 'transaction_entry')) - .transaction(hash); + var request = new Request(this, 'rpc'); + + request.message.rpc_command = 'transaction_entry'; + + return request + .tx_hash(hash); }; // Submit a transaction. Remote.prototype.submit = function (transaction) { var self = this; - if (this.trace) console.log("remote: submit: %s", JSON.stringify(transaction.transaction)); + if (this.trace) console.log("remote: submit: %s", JSON.stringify(transaction.tx_json)); if (transaction.secret && !this.trusted) { @@ -558,14 +588,14 @@ Remote.prototype.submit = function (transaction) { }); } else { - if (!transaction.transaction.Sequence) { - transaction.transaction.Sequence = this.account_seq(transaction.transaction.Account, 'ADVANCE'); - // console.log("Sequence: %s", transaction.transaction.Sequence); + if (!transaction.tx_json.Sequence) { + transaction.tx_json.Sequence = this.account_seq(transaction.tx_json.Account, 'ADVANCE'); + // console.log("Sequence: %s", transaction.tx_json.Sequence); } - if (!transaction.transaction.Sequence) { + if (!transaction.tx_json.Sequence) { // Look in the last closed ledger. - this.account_seq_cache(transaction.transaction.Account, false) + this.account_seq_cache(transaction.tx_json.Account, false) .on('success_account_seq_cache', function () { // Try again. self.submit(transaction); @@ -574,7 +604,7 @@ Remote.prototype.submit = function (transaction) { // XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number. // Look in the current ledger. - self.account_seq_cache(transaction.transaction.Account, 'CURRENT') + self.account_seq_cache(transaction.tx_json.Account, 'CURRENT') .on('success_account_seq_cache', function () { // Try again. self.submit(transaction); @@ -588,11 +618,16 @@ Remote.prototype.submit = function (transaction) { .request(); } else { - var submit_request = new Request(this, 'submit'); + var submit_request = new Request(this, 'rpc'); - submit_request.transaction(transaction.transaction); + submit_request.message.rpc_command = 'submit_json'; + + submit_request.tx_json(transaction.tx_json); submit_request.secret(transaction.secret); + if (transaction.build_path) + submit_request.build_path = true; + // Forward successes and errors. submit_request.on('success', function (message) { transaction.emit('success', message); }); submit_request.on('error', function (message) { transaction.emit('error', message); }); @@ -612,15 +647,16 @@ Remote.prototype.submit = function (transaction) { Remote.prototype._server_subscribe = function () { var self = this; - (new Request(this, 'server_subscribe')) + this.request_subscribe() .on('success', function (message) { self.stand_alone = !!message.stand_alone; - if (message.ledger_closed && message.ledger_current_index) { - self.ledger_closed = message.ledger_closed; - self.ledger_current_index = message.ledger_current_index; + if (message.ledger_hash && message.ledger_index) { + self.ledger_time = message.ledger_time; + self.ledger_hash = message.ledger_hash; + self.ledger_current_index = message.ledger_index+1; - self.emit('ledger_closed', self.ledger_closed, self.ledger_current_index-1); + self.emit('ledger_closed', self.ledger_hash, self.ledger_current_index-1); } self.emit('subscribed'); @@ -633,13 +669,17 @@ Remote.prototype._server_subscribe = function () { }; // Ask the remote to accept the current ledger. -// - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_closed' events. +// - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_hash' events. // A good way to be notified of the result of this is: -// remote.once('ledger_closed', function (ledger_closed, ledger_closed_index) { ... } ); +// remote.once('ledger_closed', function (ledger_closed, ledger_index) { ... } ); Remote.prototype.ledger_accept = function () { if (this.stand_alone || undefined === this.stand_alone) { - (new Request(this, 'ledger_accept')) + var request = new Request(this, 'rpc'); + + request.message.rpc_command = 'ledger_accept'; + + request .request(); } else { @@ -837,7 +877,8 @@ var Transaction = function (remote) { this.remote = remote; this.secret = undefined; - this.transaction = { // Transaction data. + this.build_path = true; + this.tx_json = { // Transaction data. 'Flags' : 0, // XXX Would be nice if server did not require this. }; this.hash = undefined; @@ -846,12 +887,12 @@ var Transaction = function (remote) { this.on('success', function (message) { if (message.engine_result) { - self.hash = message.transaction.hash; + self.hash = message.tx_json.hash; self.set_state('client_proposed'); self.emit('proposed', { - 'transaction' : message.transaction, + 'tx_json' : message.tx_json, 'result' : message.engine_result, 'result_code' : message.engine_result_code, 'result_message' : message.engine_result_message, @@ -913,14 +954,14 @@ Transaction.prototype.set_state = function (state) { }; // Submit a transaction to the network. -// XXX Don't allow a submit without knowing ledger_closed_index. +// XXX Don't allow a submit without knowing ledger_index. // XXX Have a network canSubmit(), post events for following. // XXX Also give broader status for tracking through network disconnects. Transaction.prototype.submit = function () { - var self = this; - var transaction = this.transaction; + var self = this; + var tx_json = this.tx_json; - if ('string' !== typeof transaction.Account) + if ('string' !== typeof tx_json.Account) { this.emit('error', { 'error' : 'invalidAccount', @@ -931,14 +972,14 @@ Transaction.prototype.submit = function () { // YYY Might check paths for invalid accounts. - if (undefined === transaction.Fee) { - if ('Payment' === transaction.TransactionType - && transaction.Flags & Remote.flags.Payment.CreateAccount) { + if (undefined === tx_json.Fee) { + if ('Payment' === tx_json.TransactionType + && tx_json.Flags & Remote.flags.Payment.CreateAccount) { - transaction.Fee = Remote.fees.account_create.to_json(); + tx_json.Fee = Remote.fees.account_create.to_json(); } else { - transaction.Fee = Remote.fees['default'].to_json(); + tx_json.Fee = Remote.fees['default'].to_json(); } } @@ -947,12 +988,12 @@ Transaction.prototype.submit = function () { this.submit_index = this.remote.ledger_current_index; - var on_ledger_closed = function (ledger_closed, ledger_closed_index) { + var on_ledger_closed = function (ledger_hash, ledger_index) { var stop = false; // XXX make sure self.hash is available. self.remote.request_transaction_entry(self.hash) - .ledger_closed(ledger_closed) + .ledger_hash(ledger_hash) .on('success', function (message) { self.set_state(message.metadata.TransactionResult); self.emit('final', message); @@ -960,12 +1001,12 @@ Transaction.prototype.submit = function () { .on('error', function (message) { if ('remoteError' === message.error && 'transactionNotFound' === message.remote.error) { - if (self.submit_index + SUBMIT_LOST < ledger_closed_index) { + if (self.submit_index + SUBMIT_LOST < ledger_index) { self.set_state('client_lost'); // Gave up. self.emit('lost'); stop = true; } - else if (self.submit_index + SUBMIT_MISSING < ledger_closed_index) { + else if (self.submit_index + SUBMIT_MISSING < ledger_index) { self.set_state('client_missing'); // We don't know what happened to transaction, still might find. self.emit('pending'); } @@ -997,6 +1038,12 @@ Transaction.prototype.submit = function () { // Set options for Transactions // +Transaction.prototype.build_path = function (build) { + this.build_path = build; + + return this; +} + Transaction._path_rewrite = function (path) { var path_new = []; @@ -1020,8 +1067,8 @@ Transaction._path_rewrite = function (path) { } Transaction.prototype.path_add = function (path) { - this.transaction.Paths = this.transaction.Paths || [] - this.transaction.Paths.push(Transaction._path_rewrite(path)); + this.tx_json.Paths = this.tx_json.Paths || [] + this.tx_json.Paths.push(Transaction._path_rewrite(path)); return this; } @@ -1043,16 +1090,16 @@ Transaction.prototype.secret = function (secret) { Transaction.prototype.send_max = function (send_max) { if (send_max) - this.transaction.SendMax = Amount.json_rewrite(send_max); + this.tx_json.SendMax = Amount.json_rewrite(send_max); return this; } // --> rate: In billionths. Transaction.prototype.transfer_rate = function (rate) { - this.transaction.TransferRate = Number(rate); + this.tx_json.TransferRate = Number(rate); - if (this.transaction.TransferRate < 1e9) + if (this.tx_json.TransferRate < 1e9) throw 'invalidTransferRate'; return this; @@ -1062,10 +1109,10 @@ Transaction.prototype.transfer_rate = function (rate) { // --> flags: undefined, _flag_, or [ _flags_ ] Transaction.prototype.set_flags = function (flags) { if (flags) { - var transaction_flags = Remote.flags[this.transaction.TransactionType]; + var transaction_flags = Remote.flags[this.tx_json.TransactionType]; - if (undefined == this.transaction.Flags) // We plan to not define this field on new Transaction. - this.transaction.Flags = 0; + if (undefined == this.tx_json.Flags) // We plan to not define this field on new Transaction. + this.tx_json.Flags = 0; var flag_set = 'object' === typeof flags ? flags : [ flags ]; @@ -1074,15 +1121,15 @@ Transaction.prototype.set_flags = function (flags) { if (flag in transaction_flags) { - this.transaction.Flags += transaction_flags[flag]; + this.tx_json.Flags += transaction_flags[flag]; } else { // XXX Immediately report an error or mark it. } } - if (this.transaction.Flags & Remote.flags.Payment.CreateAccount) - this.transaction.Fee = Remote.fees.account_create.to_json(); + if (this.tx_json.Flags & Remote.flags.Payment.CreateAccount) + this.tx_json.Fee = Remote.fees.account_create.to_json(); } return this; @@ -1103,43 +1150,43 @@ Transaction.prototype._account_secret = function (account) { // .transfer_rate() // .wallet_locator() NYI Transaction.prototype.account_set = function (src) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'AccountSet'; - this.transaction.Account = UInt160.json_rewrite(src); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'AccountSet'; + this.tx_json.Account = UInt160.json_rewrite(src); return this; }; Transaction.prototype.claim = function (src, generator, public_key, signature) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'Claim'; - this.transaction.Generator = generator; - this.transaction.PublicKey = public_key; - this.transaction.Signature = signature; + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'Claim'; + this.tx_json.Generator = generator; + this.tx_json.PublicKey = public_key; + this.tx_json.Signature = signature; return this; }; Transaction.prototype.offer_cancel = function (src, sequence) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'OfferCancel'; - this.transaction.Account = UInt160.json_rewrite(src); - this.transaction.OfferSequence = Number(sequence); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'OfferCancel'; + this.tx_json.Account = UInt160.json_rewrite(src); + this.tx_json.OfferSequence = Number(sequence); return this; }; // --> expiration : Date or Number Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'OfferCreate'; - this.transaction.Account = UInt160.json_rewrite(src); - this.transaction.Fee = Remote.fees.offer.to_json(); - this.transaction.TakerPays = Amount.json_rewrite(taker_pays); - this.transaction.TakerGets = Amount.json_rewrite(taker_gets); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'OfferCreate'; + this.tx_json.Account = UInt160.json_rewrite(src); + this.tx_json.Fee = Remote.fees.offer.to_json(); + this.tx_json.TakerPays = Amount.json_rewrite(taker_pays); + this.tx_json.TakerGets = Amount.json_rewrite(taker_gets); if (expiration) - this.transaction.Expiration = Date === expiration.constructor + this.tx_json.Expiration = Date === expiration.constructor ? expiration.getTime() : Number(expiration); @@ -1147,20 +1194,20 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi }; Transaction.prototype.password_fund = function (src, dst) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'PasswordFund'; - this.transaction.Destination = UInt160.json_rewrite(dst); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'PasswordFund'; + this.tx_json.Destination = UInt160.json_rewrite(dst); return this; } Transaction.prototype.password_set = function (src, authorized_key, generator, public_key, signature) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'PasswordSet'; - this.transaction.AuthorizedKey = authorized_key; - this.transaction.Generator = generator; - this.transaction.PublicKey = public_key; - this.transaction.Signature = signature; + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'PasswordSet'; + this.tx_json.AuthorizedKey = authorized_key; + this.tx_json.Generator = generator; + this.tx_json.PublicKey = public_key; + this.tx_json.Signature = signature; return this; } @@ -1175,34 +1222,35 @@ Transaction.prototype.password_set = function (src, authorized_key, generator, p // // Options: // .paths() +// .build_path() // .path_add() // .secret() // .send_max() // .set_flags() Transaction.prototype.payment = function (src, dst, deliver_amount) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'Payment'; - this.transaction.Account = UInt160.json_rewrite(src); - this.transaction.Amount = Amount.json_rewrite(deliver_amount); - this.transaction.Destination = UInt160.json_rewrite(dst); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'Payment'; + this.tx_json.Account = UInt160.json_rewrite(src); + this.tx_json.Amount = Amount.json_rewrite(deliver_amount); + this.tx_json.Destination = UInt160.json_rewrite(dst); return this; } Transaction.prototype.ripple_line_set = function (src, limit, quality_in, quality_out) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'CreditSet'; - this.transaction.Account = UInt160.json_rewrite(src); + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'CreditSet'; + this.tx_json.Account = UInt160.json_rewrite(src); // Allow limit of 0 through. if (undefined !== limit) - this.transaction.LimitAmount = Amount.json_rewrite(limit); + this.tx_json.LimitAmount = Amount.json_rewrite(limit); if (quality_in) - this.transaction.QualityIn = quality_in; + this.tx_json.QualityIn = quality_in; if (quality_out) - this.transaction.QualityOut = quality_out; + this.tx_json.QualityOut = quality_out; // XXX Throw an error if nothing is set. @@ -1210,12 +1258,12 @@ Transaction.prototype.ripple_line_set = function (src, limit, quality_in, qualit }; Transaction.prototype.wallet_add = function (src, amount, authorized_key, public_key, signature) { - this.secret = this._account_secret(src); - this.transaction.TransactionType = 'WalletAdd'; - this.transaction.Amount = Amount.json_rewrite(amount); - this.transaction.AuthorizedKey = authorized_key; - this.transaction.PublicKey = public_key; - this.transaction.Signature = signature; + this.secret = this._account_secret(src); + this.tx_json.TransactionType = 'WalletAdd'; + this.tx_json.Amount = Amount.json_rewrite(amount); + this.tx_json.AuthorizedKey = authorized_key; + this.tx_json.PublicKey = public_key; + this.tx_json.Signature = signature; return this; }; diff --git a/src/Application.cpp b/src/Application.cpp index aa15bc6539..ebbf9dae04 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -38,11 +38,12 @@ DatabaseCon::~DatabaseCon() } Application::Application() : - mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), - mNetOps(mIOService, &mMasterLedger), mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), - mSNTPClient(mAuxService), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), - mHashNodeDB(NULL), mNetNodeDB(NULL), - mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService), mRPCHandler(&mNetOps) + mIOWork(mIOService), mAuxWork(mAuxService), mUNL(mIOService), mNetOps(mIOService, &mMasterLedger), + mTempNodeCache("NodeCache", 16384, 90), mHashedObjectStore(16384, 300), + mSNTPClient(mAuxService), mRPCHandler(&mNetOps), + mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL), + mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL), + mSweepTimer(mAuxService) { RAND_bytes(mNonce256.begin(), mNonce256.size()); RAND_bytes(reinterpret_cast(&mNonceST), sizeof(mNonceST)); @@ -146,7 +147,7 @@ void Application::run() } else { - std::cerr << "Peer interface: disabled" << std::endl; + cLog(lsINFO) << "Peer interface: disabled"; } // @@ -158,10 +159,32 @@ void Application::run() } else { - std::cerr << "RPC interface: disabled" << std::endl; + cLog(lsINFO) << "RPC interface: disabled"; } - mWSDoor = WSDoor::createWSDoor(); + // + // Allow private WS connections. + // + if (!theConfig.WEBSOCKET_IP.empty() && theConfig.WEBSOCKET_PORT) + { + mWSPrivateDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_IP, theConfig.WEBSOCKET_PORT, false); + } + else + { + cLog(lsINFO) << "WS private interface: disabled"; + } + + // + // Allow public WS connections. + // + if (!theConfig.WEBSOCKET_PUBLIC_IP.empty() && theConfig.WEBSOCKET_PUBLIC_PORT) + { + mWSPublicDoor = WSDoor::createWSDoor(theConfig.WEBSOCKET_PUBLIC_IP, theConfig.WEBSOCKET_PUBLIC_PORT, true); + } + else + { + cLog(lsINFO) << "WS public interface: disabled"; + } // // Begin connecting to network. @@ -180,9 +203,13 @@ void Application::run() mIOService.run(); // This blocks - mWSDoor->stop(); + if (mWSPublicDoor) + mWSPublicDoor->stop(); - std::cout << "Done." << std::endl; + if (mWSPrivateDoor) + mWSPrivateDoor->stop(); + + cLog(lsINFO) << "Done."; } void Application::sweep() diff --git a/src/Application.h b/src/Application.h index da956fae28..a100190966 100644 --- a/src/Application.h +++ b/src/Application.h @@ -62,7 +62,8 @@ class Application ConnectionPool mConnectionPool; PeerDoor* mPeerDoor; RPCDoor* mRPCDoor; - WSDoor* mWSDoor; + WSDoor* mWSPublicDoor; + WSDoor* mWSPrivateDoor; uint256 mNonce256; std::size_t mNonceST; diff --git a/src/Config.cpp b/src/Config.cpp index f69e58446b..8a665ff134 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -30,6 +30,8 @@ #define SECTION_UNL_DEFAULT "unl_default" #define SECTION_VALIDATION_QUORUM "validation_quorum" #define SECTION_VALIDATION_SEED "validation_seed" +#define SECTION_WEBSOCKET_PUBLIC_IP "websocket_public_ip" +#define SECTION_WEBSOCKET_PUBLIC_PORT "websocket_public_port" #define SECTION_WEBSOCKET_IP "websocket_ip" #define SECTION_WEBSOCKET_PORT "websocket_port" #define SECTION_VALIDATORS "validators" @@ -124,6 +126,7 @@ void Config::setup(const std::string& strConf) PEER_PORT = SYSTEM_PEER_PORT; RPC_PORT = 5001; WEBSOCKET_PORT = SYSTEM_WEBSOCKET_PORT; + WEBSOCKET_PUBLIC_PORT = SYSTEM_WEBSOCKET_PUBLIC_PORT; NUMBER_CONNECTIONS = 30; // a new ledger every minute @@ -235,6 +238,11 @@ void Config::load() if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PORT, strTemp)) WEBSOCKET_PORT = boost::lexical_cast(strTemp); + (void) sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_IP, WEBSOCKET_PUBLIC_IP); + + if (sectionSingleB(secConfig, SECTION_WEBSOCKET_PUBLIC_PORT, strTemp)) + WEBSOCKET_PUBLIC_PORT = boost::lexical_cast(strTemp); + if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp)) { VALIDATION_SEED.setSeedGeneric(strTemp); diff --git a/src/Config.h b/src/Config.h index cb17b46ed6..3807b51dff 100644 --- a/src/Config.h +++ b/src/Config.h @@ -24,8 +24,9 @@ #define DEFAULT_VALIDATORS_SITE "redstem.com" #define VALIDATORS_FILE_NAME "validators.txt" -const int SYSTEM_PEER_PORT = 6561; -const int SYSTEM_WEBSOCKET_PORT = 6562; +const int SYSTEM_PEER_PORT = 6561; +const int SYSTEM_WEBSOCKET_PORT = 6562; +const int SYSTEM_WEBSOCKET_PUBLIC_PORT = 6563; // XXX Going away. // Allow anonymous DH. #define DEFAULT_PEER_SSL_CIPHER_LIST "ALL:!LOW:!EXP:!MD5:@STRENGTH" @@ -82,6 +83,9 @@ public: unsigned int PEER_CONNECT_LOW_WATER; // Websocket networking parameters + std::string WEBSOCKET_PUBLIC_IP; // XXX Going away. Merge with the inbound peer connction. + int WEBSOCKET_PUBLIC_PORT; + std::string WEBSOCKET_IP; int WEBSOCKET_PORT; diff --git a/src/ConnectionPool.cpp b/src/ConnectionPool.cpp index 93fce5842c..121eea8e63 100644 --- a/src/ConnectionPool.cpp +++ b/src/ConnectionPool.cpp @@ -110,6 +110,21 @@ bool ConnectionPool::savePeer(const std::string& strIp, int iPort, char code) return bNew; } +Peer::pointer ConnectionPool::getPeerById(const uint64& id) +{ + boost::mutex::scoped_lock sl(mPeerLock); + const boost::unordered_map::iterator& it = mPeerIdMap.find(id); + if (it == mPeerIdMap.end()) + return Peer::pointer(); + return it->second; +} + +bool ConnectionPool::hasPeer(const uint64& id) +{ + boost::mutex::scoped_lock sl(mPeerLock); + return mPeerIdMap.find(id) != mPeerIdMap.end(); +} + // An available peer is one we had no trouble connect to last time and that we are not currently knowingly connected or connecting // too. // @@ -268,11 +283,11 @@ void ConnectionPool::relayMessageTo(const std::set& fromPeers, const Pac { // Relay message to the specified peers boost::mutex::scoped_lock sl(mPeerLock); - BOOST_FOREACH(naPeer pair, mConnectedMap) + BOOST_FOREACH(const uint64& peerID, fromPeers) { - Peer::ref peer = pair.second; - if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) > 0)) - peer->sendPacket(msg); + const boost::unordered_map::iterator& it = mPeerIdMap.find(peerID); + if ((it != mPeerIdMap.end()) && it->second->isConnected()) + it->second->sendPacket(msg); } } @@ -403,7 +418,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, else { boost::mutex::scoped_lock sl(mPeerLock); - boost::unordered_map::iterator itCm = mConnectedMap.find(naPeer); + const boost::unordered_map::iterator& itCm = mConnectedMap.find(naPeer); if (itCm == mConnectedMap.end()) { @@ -412,6 +427,9 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, mConnectedMap[naPeer] = peer; bNew = true; + + assert(peer->getPeerId() != 0); + mPeerIdMap.insert(std::make_pair(peer->getPeerId(), peer)); } // Found in map, already connected. else if (!strIP.empty()) @@ -450,13 +468,11 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, // We maintain a map of public key to peer for connected and verified peers. Maintain it. void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer) { + boost::mutex::scoped_lock sl(mPeerLock); + if (naPeer.isValid()) { - boost::unordered_map::iterator itCm; - - boost::mutex::scoped_lock sl(mPeerLock); - - itCm = mConnectedMap.find(naPeer); + const boost::unordered_map::iterator& itCm = mConnectedMap.find(naPeer); if (itCm == mConnectedMap.end()) { @@ -482,6 +498,9 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPee { //cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort(); } + + assert(peer->getPeerId() != 0); + mPeerIdMap.erase(peer->getPeerId()); } // Schedule for immediate scanning, if not already scheduled. @@ -553,9 +572,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP bool bRedundant = true; { boost::mutex::scoped_lock sl(mPeerLock); - boost::unordered_map::iterator itIp; - - itIp = mIpMap.find(ipPeer); + const boost::unordered_map::iterator& itIp = mIpMap.find(ipPeer); if (itIp == mIpMap.end()) { diff --git a/src/ConnectionPool.h b/src/ConnectionPool.h index e3801ef734..dbc6cd8b27 100644 --- a/src/ConnectionPool.h +++ b/src/ConnectionPool.h @@ -19,7 +19,7 @@ private: boost::mutex mPeerLock; uint64 mLastPeer; - typedef std::pair naPeer; + typedef std::pair naPeer; typedef std::pair pipPeer; // Peers we are connecting with and non-thin peers we are connected to. @@ -33,6 +33,9 @@ private: // Peers we have the public key for. boost::unordered_map mConnectedMap; + // Connections with have a 64-bit identifier + boost::unordered_map mPeerIdMap; + boost::asio::ssl::context mCtx; Peer::pointer mScanning; @@ -94,6 +97,8 @@ public: // Peer 64-bit ID function uint64 assignPeerId(); + Peer::pointer getPeerById(const uint64& id); + bool hasPeer(const uint64& id); // // Scanning diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index ee992abdb2..aa5dc56618 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -76,7 +76,7 @@ void HashedObjectStore::bulkWrite() return; } } - cLog(lsTRACE) << "HOS: writing " << set.size(); +// cLog(lsTRACE) << "HOS: writing " << set.size(); static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); static boost::format diff --git a/src/HashedObject.h b/src/HashedObject.h index 7f31912542..20a187fb72 100644 --- a/src/HashedObject.h +++ b/src/HashedObject.h @@ -36,10 +36,10 @@ public: HashedObject(HashedObjectType type, uint32 index, const std::vector& data, const uint256& hash) : mType(type), mHash(hash), mLedgerIndex(index), mData(data) { ; } - const std::vector& getData() { return mData; } - const uint256& getHash() { return mHash; } - HashedObjectType getType() { return mType; } - uint32 getIndex() { return mLedgerIndex; } + const std::vector& getData() const { return mData; } + const uint256& getHash() const { return mHash; } + HashedObjectType getType() const { return mType; } + uint32 getIndex() const { return mLedgerIndex; } }; class HashedObjectStore diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index 50497b3cda..46b48cf74b 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -26,42 +26,15 @@ PeerSet::PeerSet(const uint256& hash, int interval) : mHash(hash), mTimerInterva void PeerSet::peerHas(Peer::ref ptr) { boost::recursive_mutex::scoped_lock sl(mLock); - std::vector< boost::weak_ptr >::iterator it = mPeers.begin(); - while (it != mPeers.end()) - { - Peer::pointer pr = it->lock(); - if (!pr) // we have a dead entry, remove it - it = mPeers.erase(it); - else - { - if (pr->samePeer(ptr)) - return; // we already have this peer - ++it; - } - } - mPeers.push_back(ptr); + if (!mPeers.insert(std::make_pair(ptr->getPeerId(), 0)).second) + return; newPeer(ptr); } void PeerSet::badPeer(Peer::ref ptr) { boost::recursive_mutex::scoped_lock sl(mLock); - std::vector< boost::weak_ptr >::iterator it = mPeers.begin(); - while (it != mPeers.end()) - { - Peer::pointer pr = it->lock(); - if (!pr) // we have a dead entry, remove it - it = mPeers.erase(it); - else - { - if (ptr->samePeer(pr)) - { // We found a pointer to the bad peer - mPeers.erase(it); - return; - } - ++it; - } - } + mPeers.erase(ptr->getPeerId()); } void PeerSet::resetTimer() @@ -76,10 +49,13 @@ void PeerSet::invokeOnTimer() { ++mTimeouts; cLog(lsWARNING) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size() << " acquiring " << mHash; + onTimer(false); } else + { mProgress = false; - onTimer(); + onTimer(true); + } } void PeerSet::TimerEntry(boost::weak_ptr wptr, const boost::system::error_code& result) @@ -138,18 +114,19 @@ bool LedgerAcquire::tryLocal() return mHaveTransactions && mHaveState; } -void LedgerAcquire::onTimer() +void LedgerAcquire::onTimer(bool progress) { if (getTimeouts() > 6) { setFailed(); done(); } - else + else if (!progress) { if (!getPeerCount()) addPeers(); - trigger(Peer::pointer(), true); + else + trigger(Peer::pointer(), true); } } @@ -225,13 +202,13 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) if (sLog(lsTRACE)) { if (peer) - cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); + cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); else - cLog(lsTRACE) << "Trigger acquiring ledger " << mHash; + cLog(lsTRACE) << "Trigger acquiring ledger " << mHash; if (mComplete || mFailed) - cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; + cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; else - cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; + cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; } if (!mHaveBase) @@ -281,7 +258,7 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) BOOST_FOREACH(SHAMapNode& it, nodeIDs) *(tmGL.add_nodeids()) = it.getRawString(); cLog(lsTRACE) << "Sending TX node " << nodeIDs.size() - << "request to " << (peer ? "selected peer" : "all peers"); + << " request to " << (peer ? "selected peer" : "all peers"); sendRequest(tmGL, peer); } } @@ -325,7 +302,8 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) BOOST_FOREACH(SHAMapNode& it, nodeIDs) *(tmGL.add_nodeids()) = it.getRawString(); cLog(lsTRACE) << "Sending AS node " << nodeIDs.size() - << "request to " << (peer ? "selected peer" : "all peers"); + << " request to " << (peer ? "selected peer" : "all peers"); + tLog(nodeIDs.size() == 1, lsTRACE) << "AS node: " << nodeIDs[0]; sendRequest(tmGL, peer); } } @@ -355,42 +333,32 @@ void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL) return; PackedMessage::pointer packet = boost::make_shared(tmGL, ripple::mtGET_LEDGER); - - std::vector< boost::weak_ptr >::iterator it = mPeers.begin(); - while (it != mPeers.end()) + for (boost::unordered_map::iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it) { - if (it->expired()) - it = mPeers.erase(it); - else - { - // FIXME: Track last peer sent to and time sent - Peer::pointer peer = it->lock(); - if (peer) - peer->sendPacket(packet); - return; - } + Peer::pointer peer = theApp->getConnectionPool().getPeerById(it->first); + if (peer) + peer->sendPacket(packet); } } -int PeerSet::takePeerSetFrom(const PeerSet& s) +int PeerSet::takePeerSetFrom(const PeerSet& s) { int ret = 0; mPeers.clear(); - mPeers.reserve(s.mPeers.size()); - BOOST_FOREACH(const boost::weak_ptr& p, s.mPeers) - if (p.lock()) - { - mPeers.push_back(p); - ++ret; - } + for (boost::unordered_map::const_iterator it = s.mPeers.begin(), end = s.mPeers.end(); + it != end; ++it) + { + mPeers.insert(std::make_pair(it->first, 0)); + ++ret; + } return ret; } int PeerSet::getPeerCount() const { int ret = 0; - BOOST_FOREACH(const boost::weak_ptr& p, mPeers) - if (p.lock()) + for (boost::unordered_map::const_iterator it = mPeers.begin(), end = mPeers.end(); it != end; ++it) + if (theApp->getConnectionPool().hasPeer(it->first)) ++ret; return ret; } @@ -464,10 +432,15 @@ bool LedgerAcquire::takeTxNode(const std::list& nodeIDs, bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, const std::list< std::vector >& data) { -#ifdef LA_DEBUG - cLog(lsTRACE) << "got ASdata acquiring ledger " << mHash; -#endif - if (!mHaveBase) return false; + cLog(lsTRACE) << "got ASdata (" << nodeIDs.size() <<") acquiring ledger " << mHash; + tLog(nodeIDs.size() == 1, lsTRACE) << "got AS node: " << nodeIDs.front(); + + if (!mHaveBase) + { + cLog(lsWARNING) << "Don't have ledger base"; + return false; + } + std::list::const_iterator nodeIDit = nodeIDs.begin(); std::list< std::vector >::const_iterator nodeDatait = data.begin(); AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq()); @@ -477,10 +450,16 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, { if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), *nodeDatait, snfWIRE, &tFilter)) + { + cLog(lsWARNING) << "Bad ledger base"; return false; + } } else if (!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter)) + { + cLog(lsWARNING) << "Unable to add AS node"; return false; + } ++nodeIDit; ++nodeDatait; } @@ -564,7 +543,7 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref return false; } memcpy(hash.begin(), packet.ledgerhash().data(), 32); - cLog(lsTRACE) << "Got data for acquiring ledger: " << hash; + cLog(lsTRACE) << "Got data ( " << packet.nodes().size() << ") for acquiring ledger: " << hash; LedgerAcquire::pointer ledger = find(hash); if (!ledger) @@ -582,7 +561,7 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref } if (!ledger->takeBase(packet.nodes(0).nodedata())) { - cLog(lsWARNING) << "Got unwanted base data"; + cLog(lsWARNING) << "Got invalid base data"; return false; } if ((packet.nodes().size() > 1) && !ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata()))) @@ -611,7 +590,10 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref { const ripple::TMLedgerNode& node = packet.nodes(i); if (!node.has_nodeid() || !node.has_nodedata()) + { + cLog(lsWARNING) << "Got bad node"; return false; + } nodeIDs.push_back(SHAMapNode(node.nodeid().data(), node.nodeid().size())); nodeData.push_back(std::vector(node.nodedata().begin(), node.nodedata().end())); diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index 2dcf018854..2ce741529a 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "Ledger.h" @@ -28,7 +29,7 @@ protected: boost::recursive_mutex mLock; boost::asio::deadline_timer mTimer; - std::vector< boost::weak_ptr > mPeers; + boost::unordered_map mPeers; PeerSet(const uint256& hash, int interval); virtual ~PeerSet() { ; } @@ -53,7 +54,7 @@ public: protected: virtual void newPeer(Peer::ref) = 0; - virtual void onTimer(void) = 0; + virtual void onTimer(bool progress) = 0; virtual boost::weak_ptr pmDowncast() = 0; void setComplete() { mComplete = true; } @@ -76,7 +77,7 @@ protected: std::vector< boost::function > mOnComplete; void done(); - void onTimer(); + void onTimer(bool progress); void newPeer(Peer::ref peer) { trigger(peer, false); } diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 48939b5cde..bcdf318a8e 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -46,7 +46,7 @@ void TransactionAcquire::done() } } -void TransactionAcquire::onTimer() +void TransactionAcquire::onTimer(bool progress) { if (!getPeerCount()) { // out of peers @@ -68,7 +68,7 @@ void TransactionAcquire::onTimer() peerHas(peer); } } - else + else if (!progress) trigger(Peer::pointer(), true); } diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index 8ac72634a1..064d5800c9 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -30,7 +30,7 @@ protected: SHAMap::pointer mMap; bool mHaveRoot; - void onTimer(); + void onTimer(bool progress); void newPeer(Peer::ref peer) { trigger(peer, false); } void done(); diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index fa3872ea97..3b3017b744 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -860,8 +860,6 @@ uint32 LedgerEntrySet::rippleTransferRate(const uint160& uIssuerID) % !!sleAccount % (uQuality/1000000000.0)); - assert(sleAccount); - return uQuality; } diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 21ce906666..e00b086df7 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -917,12 +917,12 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri { Json::Value jvObj(Json::objectValue); - jvObj["type"] = "accountInfoBootstrap"; - jvObj["account"] = naAccountID.humanAccountID(); - jvObj["owner"] = getOwnerInfo(lpAccepted, naAccountID); - jvObj["ledger_closed_index"] = lpAccepted->getLedgerSeq(); - jvObj["ledger_closed"] = lpAccepted->getHash().ToString(); - jvObj["ledger_closed_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC())); + jvObj["type"] = "accountInfoBootstrap"; + jvObj["account"] = naAccountID.humanAccountID(); + jvObj["owner"] = getOwnerInfo(lpAccepted, naAccountID); + jvObj["ledger_index"] = lpAccepted->getLedgerSeq(); + jvObj["ledger_hash"] = lpAccepted->getHash().ToString(); + jvObj["ledger_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC())); return jvObj; } @@ -930,7 +930,7 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult) { Json::Value jvObj = transJson(stTxn, terResult, false, lpCurrent, "transaction"); - + { boost::interprocess::sharable_lock sl(mMonitorLock); BOOST_FOREACH(InfoSub* ispListener, mSubRTTransactions) @@ -956,10 +956,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) { Json::Value jvObj(Json::objectValue); - jvObj["type"] = "ledgerClosed"; - jvObj["ledger_closed_index"] = lpAccepted->getLedgerSeq(); - jvObj["ledger_closed"] = lpAccepted->getHash().ToString(); - jvObj["ledger_closed_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC())); + jvObj["type"] = "ledgerClosed"; + jvObj["ledger_index"] = lpAccepted->getLedgerSeq(); + jvObj["ledger_hash"] = lpAccepted->getHash().ToString(); + jvObj["ledger_time"] = Json::Value::UInt(utFromSeconds(lpAccepted->getCloseTimeNC())); BOOST_FOREACH(InfoSub* ispListener, mSubLedger) { @@ -967,7 +967,7 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) } } } - + { // we don't lock since pubAcceptedTransaction is locking if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty() || !mSubmitMap.empty() ) @@ -986,8 +986,6 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) // TODO: remove old entries from the submit map } } - - } Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType) @@ -1001,8 +999,8 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes jvObj["type"] = strType; jvObj["transaction"] = stTxn.getJson(0); if (bAccepted) { - jvObj["ledger_closed_index"] = lpCurrent->getLedgerSeq(); - jvObj["ledger_closed"] = lpCurrent->getHash().ToString(); + jvObj["ledger_index"] = lpCurrent->getLedgerSeq(); + jvObj["ledger_hash"] = lpCurrent->getHash().ToString(); } else { @@ -1032,7 +1030,7 @@ void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedT ispListener->send(jvObj); } } - + pubAccountTransaction(lpCurrent,stTxn,terResult,true); } @@ -1181,8 +1179,12 @@ void NetworkOPs::unsubAccountChanges(InfoSub* ispListener) #endif // <-- bool: true=added, false=already there -bool NetworkOPs::subLedger(InfoSub* ispListener) +bool NetworkOPs::subLedger(InfoSub* ispListener, Json::Value& jvResult) { + jvResult["ledger_index"] = getClosedLedger()->getLedgerSeq(); + jvResult["ledger_hash"] = getClosedLedger()->getHash().ToString(); + jvResult["ledger_time"] = Json::Value::UInt(utFromSeconds(getClosedLedger()->getCloseTimeNC())); + return mSubLedger.insert(ispListener).second; } @@ -1193,8 +1195,10 @@ bool NetworkOPs::unsubLedger(InfoSub* ispListener) } // <-- bool: true=added, false=already there -bool NetworkOPs::subServer(InfoSub* ispListener) +bool NetworkOPs::subServer(InfoSub* ispListener, Json::Value& jvResult) { + jvResult["stand_alone"] = theConfig.RUN_STANDALONE; + return mSubServer.insert(ispListener).second; } diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index b14b06e09a..b7cc79f936 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -74,8 +74,8 @@ protected: // XXX Split into more locks. boost::interprocess::interprocess_upgradable_mutex mMonitorLock; - subInfoMapType mSubAccount; - subInfoMapType mSubRTAccount; + subInfoMapType mSubAccount; + subInfoMapType mSubRTAccount; subSubmitMapType mSubmitMap; boost::unordered_set mSubLedger; // accepted ledgers @@ -111,11 +111,12 @@ public: return mMode >= omTRACKING; } + Ledger::pointer getClosedLedger() { return mLedgerMaster->getClosedLedger(); } Ledger::pointer getCurrentLedger() { return mLedgerMaster->getCurrentLedger(); } Ledger::pointer getLedgerByHash(const uint256& hash) { return mLedgerMaster->getLedgerByHash(hash); } Ledger::pointer getLedgerBySeq(const uint32 seq) { return mLedgerMaster->getLedgerBySeq(seq); } - uint256 getClosedLedger() + uint256 getClosedLedgerHash() { return mLedgerMaster->getClosedLedger()->getHash(); } SLE::pointer getSLE(Ledger::pointer lpLedger, const uint256& uHash) { return lpLedger->getSLE(uHash); } @@ -224,10 +225,10 @@ public: void subAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt); void unsubAccount(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs,bool rt); - bool subLedger(InfoSub* ispListener); + bool subLedger(InfoSub* ispListener, Json::Value& jvResult); bool unsubLedger(InfoSub* ispListener); - bool subServer(InfoSub* ispListener); + bool subServer(InfoSub* ispListener, Json::Value& jvResult); bool unsubServer(InfoSub* ispListener); bool subTransactions(InfoSub* ispListener); diff --git a/src/Peer.cpp b/src/Peer.cpp index 5e4b121288..3a64ef21e5 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -1172,6 +1172,10 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) map = theApp->getOPs().getTXMap(txHash); if (!map) { + if (packet.has_querytype() && !packet.has_requestcookie()) + { + // WRITEME: try to route + } cLog(lsERROR) << "We do not have the map our peer wants"; punishPeer(PP_INVALID_REQUEST); return; @@ -1326,6 +1330,11 @@ void Peer::recvLedger(ripple::TMLedgerData& packet) return; } + if (packet.has_requestcookie()) + { + // WRITEME: Route to original requester + } + if (packet.type() == ripple::liTS_CANDIDATE) { // got data for a candidate transaction set uint256 hash; diff --git a/src/RPCHandler.cpp b/src/RPCHandler.cpp index fe98e650cd..e1decfb4a2 100644 --- a/src/RPCHandler.cpp +++ b/src/RPCHandler.cpp @@ -1,7 +1,7 @@ +#include "Log.h" #include "NetworkOPs.h" #include "RPCHandler.h" #include "Application.h" -#include "Log.h" #include "RippleLines.h" #include "Wallet.h" #include "RippleAddress.h" @@ -13,7 +13,7 @@ #include #include /* -carries out the RPC +carries out the RPC */ @@ -326,8 +326,6 @@ Json::Value RPCHandler::doAcceptLedger(const Json::Value ¶ms) return obj; } - - // account_info || // account_info || [] Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) @@ -341,7 +339,7 @@ Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) // Get info on account. - uint256 uAccepted = mNetOps->getClosedLedger(); + uint256 uAccepted = mNetOps->getClosedLedgerHash(); Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); if (jAccepted.empty()) @@ -507,7 +505,7 @@ Json::Value RPCHandler::doOwnerInfo(const Json::Value& params) // Get info on account. - uint256 uAccepted = mNetOps->getClosedLedger(); + uint256 uAccepted = mNetOps->getClosedLedgerHash(); Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); ret["accepted"] = jAccepted.empty() ? mNetOps->getOwnerInfo(uAccepted, naAccount) : jAccepted; @@ -621,7 +619,7 @@ Json::Value RPCHandler::doProfile(const Json::Value ¶ms) // ripple_lines_get || [] Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) { - // uint256 uAccepted = mNetOps->getClosedLedger(); + // uint256 uAccepted = mNetOps->getClosedLedgerHash(); std::string strIdent = params[0u].asString(); bool bIndex; @@ -690,24 +688,36 @@ Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) // submit private_key json Json::Value RPCHandler::doSubmit(const Json::Value& params) { - Json::Value txJSON; - Json::Reader reader; - if(reader.parse(params[1u].asString(),txJSON)) + Json::Value txJSON; + Json::Reader reader; + + if (reader.parse(params[1u].asString(), txJSON)) { - return handleJSONSubmit(params[0u].asString(), txJSON ); + Json::Value jvRequest; + + jvRequest["secret"] = params[0u].asString(); + jvRequest["tx_json"] = txJSON; + + return handleJSONSubmit(jvRequest); } - return rpcError(rpcSRC_ACT_MALFORMED); + return rpcError(rpcINVALID_PARAMS); +} + +Json::Value RPCHandler::doSubmitJson(const Json::Value& jvRequest) +{ + return handleJSONSubmit(jvRequest); } -Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) +Json::Value RPCHandler::handleJSONSubmit(const Json::Value& jvRequest) { - Json::Value jvResult; + Json::Value jvResult; RippleAddress naSeed; RippleAddress srcAddress; + Json::Value txJSON = jvRequest["tx_json"]; - if (!naSeed.setSeedGeneric(key)) + if (!naSeed.setSeedGeneric(jvRequest["secret"].asString())) { return rpcError(rpcBAD_SEED); } @@ -723,22 +733,28 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) AccountState::pointer asSrc = mNetOps->getAccountState(uint256(0), srcAddress); if( txJSON["type"]=="Payment") - { + { txJSON["TransactionType"]=0; RippleAddress dstAccountID; + + if (!txJSON.isMember("Destination")) + { + return rpcError(rpcDST_ACT_MISSING); + } if (!dstAccountID.setAccountID(txJSON["Destination"].asString())) { return rpcError(rpcDST_ACT_MALFORMED); } - + if(!txJSON.isMember("Fee")) { if(mNetOps->getAccountState(uint256(0), dstAccountID)) txJSON["Fee"]=(int)theConfig.FEE_DEFAULT; else txJSON["Fee"]=(int)theConfig.FEE_ACCOUNT_CREATE; } - if(!txJSON.isMember("Paths")) + + if(!txJSON.isMember("Paths") && (!jvRequest.isMember("build_path") || jvRequest["build_path"].asBool())) { if(txJSON["Amount"].isObject() || txJSON.isMember("SendMax") ) { // we need a ripple path @@ -746,11 +762,15 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) uint160 srcCurrencyID; if(txJSON.isMember("SendMax") && txJSON["SendMax"].isMember("currency")) { - STAmount::currencyFromString(srcCurrencyID, "XRP"); - }else STAmount::currencyFromString(srcCurrencyID, txJSON["SendMax"]["currency"].asString()); + STAmount::currencyFromString(srcCurrencyID, txJSON["SendMax"]["currency"].asString()); + } + else + { + srcCurrencyID = CURRENCY_XRP; + } STAmount dstAmount; - if(txJSON["Amount"].isObject()) + if(txJSON["Amount"].isObject()) { std::string issuerStr; if( txJSON["Amount"].isMember("issuer")) issuerStr=txJSON["Amount"]["issuer"].asString(); @@ -771,7 +791,7 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) else txJSON["Flags"]=2; } } - + }else if( txJSON["type"]=="OfferCreate" ) { txJSON["TransactionType"]=7; @@ -789,11 +809,11 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) txJSON.removeMember("type"); if(!txJSON.isMember("Sequence")) txJSON["Sequence"]=asSrc->getSeq(); - if(!txJSON.isMember("Flags")) txJSON["Flags"]=0; - + if(!txJSON.isMember("Flags")) txJSON["Flags"]=0; + Ledger::pointer lpCurrent = mNetOps->getCurrentLedger(); SLE::pointer sleAccountRoot = mNetOps->getSLE(lpCurrent, Ledger::getAccountRootIndex(srcAddress.getAccountID())); - + if (!sleAccountRoot) { // XXX Ignore transactions for accounts not created. @@ -804,7 +824,7 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) RippleAddress naAuthorizedPublic; - RippleAddress naSecret = RippleAddress::createSeedGeneric(key); + RippleAddress naSecret = RippleAddress::createSeedGeneric(jvRequest["secret"].asString()); RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSecret); // Find the index of Account from the master generator, so we can generate the public and private keys. @@ -858,7 +878,7 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) { jvResult["error"] = "malformedTransaction"; jvResult["error_exception"] = e.what(); - return(jvResult); + return jvResult; } sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic()); @@ -888,7 +908,7 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) { jvResult["error"] = "internalTransaction"; jvResult["error_exception"] = e.what(); - return(jvResult); + return jvResult; } try @@ -898,19 +918,19 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) if (!tpTrans) { jvResult["error"] = "invalidTransaction"; jvResult["error_exception"] = "Unable to sterilize transaction."; - return(jvResult); + return jvResult; } } catch (std::exception& e) { jvResult["error"] = "internalSubmit"; jvResult["error_exception"] = e.what(); - return(jvResult); + return jvResult; } try { - jvResult["transaction"] = tpTrans->getJson(0); + jvResult["tx_json"] = tpTrans->getJson(0); if (temUNCERTAIN != tpTrans->getResult()) { @@ -923,15 +943,14 @@ Json::Value RPCHandler::handleJSONSubmit(std::string& key, Json::Value& txJSON) jvResult["engine_result_code"] = tpTrans->getResult(); jvResult["engine_result_message"] = sHuman; } - return(jvResult); + return jvResult; } catch (std::exception& e) { jvResult["error"] = "internalJson"; jvResult["error_exception"] = e.what(); - return(jvResult); + return jvResult; } - } Json::Value RPCHandler::doServerInfo(const Json::Value& params) @@ -1005,11 +1024,11 @@ Json::Value RPCHandler::doTx(const Json::Value& params) Json::Value RPCHandler::doLedgerClosed(const Json::Value& params) { Json::Value jvResult; - uint256 uLedger = mNetOps->getClosedLedger(); + uint256 uLedger = mNetOps->getClosedLedgerHash(); - jvResult["ledger_closed_index"] = mNetOps->getLedgerID(uLedger); - jvResult["ledger_closed"] = uLedger.ToString(); - //jvResult["ledger_closed_time"] = uLedger. + jvResult["ledger_index"] = mNetOps->getLedgerID(uLedger); + jvResult["ledger_hash"] = uLedger.ToString(); + //jvResult["ledger_time"] = uLedger. return jvResult; } @@ -1273,22 +1292,22 @@ Json::Value RPCHandler::doWalletAccounts(const Json::Value& params) } } - -Json::Value RPCHandler::doLogRotate(const Json::Value& params) +Json::Value RPCHandler::doLogRotate(const Json::Value& params) { return Log::rotateLog(); } -Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params,int role) +Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params, int role) { cLog(lsTRACE) << "RPC:" << command; + cLog(lsTRACE) << "RPC params:" << params; static struct { - const char* pCommand; - doFuncPtr dfpFunc; - int iMinParams; - int iMaxParams; - bool mAdminRequired; + const char* pCommand; + doFuncPtr dfpFunc; + int iMinParams; + int iMaxParams; + bool mAdminRequired; unsigned int iOptions; } commandsA[] = { { "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true }, @@ -1300,6 +1319,10 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { "data_store", &RPCHandler::doDataStore, 2, 2, true }, { "get_counts", &RPCHandler::doGetCounts, 0, 1, true }, { "ledger", &RPCHandler::doLedger, 0, 2, false, optNetwork }, + { "ledger_accept", &RPCHandler::doLedgerAccept, 0, 0, true, optCurrent }, + { "ledger_closed", &RPCHandler::doLedgerClosed, 0, 0, false, optClosed }, + { "ledger_current", &RPCHandler::doLedgerCurrent, 0, 0, false, optCurrent }, + { "ledger_entry", &RPCHandler::doLedgerEntry, -1, -1, false, optCurrent }, { "log_level", &RPCHandler::doLogLevel, 0, 2, true }, { "logrotate", &RPCHandler::doLogRotate, 0, 0, true }, { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent }, @@ -1308,8 +1331,10 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { "profile", &RPCHandler::doProfile, 1, 9, false, optCurrent }, { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, optCurrent }, { "submit", &RPCHandler::doSubmit, 2, 2, false, optCurrent }, + { "submit_json", &RPCHandler::doSubmitJson, -1, -1, false, optCurrent }, { "server_info", &RPCHandler::doServerInfo, 0, 0, true }, { "stop", &RPCHandler::doStop, 0, 0, true }, + { "transaction_entry", &RPCHandler::doTransactionEntry, -1, -1, false, optCurrent }, { "tx", &RPCHandler::doTx, 1, 1, true }, { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, }, @@ -1344,8 +1369,12 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { return rpcError(rpcNO_PERMISSION); } - else if (params.size() < commandsA[i].iMinParams - || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) + else if (commandsA[i].iMinParams >= 0 + ? commandsA[i].iMaxParams + ? (params.size() < commandsA[i].iMinParams + || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) + : false + : params.isArray()) { return rpcError(rpcINVALID_PARAMS); } @@ -1358,7 +1387,7 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param { return rpcError(rpcNO_CURRENT); } - else if ((commandsA[i].iOptions & optClosed) && mNetOps->getClosedLedger().isZero()) + else if ((commandsA[i].iOptions & optClosed) && !mNetOps->getClosedLedger()) { return rpcError(rpcNO_CLOSED); } @@ -1590,6 +1619,7 @@ Json::Value RPCHandler::doUnlLoad(const Json::Value& params) Json::Value RPCHandler::doLedgerAccept(const Json::Value& ) { Json::Value jvResult; + if (!theConfig.RUN_STANDALONE) { jvResult["error"] = "notStandAlone"; @@ -1600,17 +1630,13 @@ Json::Value RPCHandler::doLedgerAccept(const Json::Value& ) jvResult["ledger_current_index"] = mNetOps->getCurrentLedgerID(); } - return(jvResult); + + return jvResult; } - -Json::Value RPCHandler::doTransactionEntry(const Json::Value& params) +Json::Value RPCHandler::doTransactionEntry(const Json::Value& jvRequest) { Json::Value jvResult; - Json::Value jvRequest; - Json::Reader reader; - if(!reader.parse(params[0u].asString(),jvRequest)) - return rpcError(rpcINVALID_PARAMS); if (!jvRequest.isMember("tx_hash")) { @@ -1640,36 +1666,29 @@ Json::Value RPCHandler::doTransactionEntry(const Json::Value& params) Transaction::pointer tpTrans; TransactionMetaSet::pointer tmTrans; - if (!lpLedger-> getTransaction(uTransID, tpTrans, tmTrans)) + if (!lpLedger->getTransaction(uTransID, tpTrans, tmTrans)) { jvResult["error"] = "transactionNotFound"; } else { - jvResult["transaction"] = tpTrans->getJson(0); - jvResult["metadata"] = tmTrans->getJson(0); + jvResult["tx_json"] = tpTrans->getJson(0); + jvResult["metadata"] = tmTrans->getJson(0); // 'accounts' // 'engine_...' // 'ledger_...' } } } - + return jvResult; } - - -Json::Value RPCHandler::doLedgerEntry(const Json::Value& params) +Json::Value RPCHandler::doLedgerEntry(const Json::Value& jvRequest) { Json::Value jvResult; - Json::Value jvRequest; - Json::Reader reader; - if(!reader.parse(params[0u].asString(),jvRequest)) - return rpcError(rpcINVALID_PARAMS); - - uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0; + uint256 uLedger = jvRequest.isMember("ledger_hash") ? uint256(jvRequest["ledger_hash"].asString()) : 0; uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0; Ledger::pointer lpLedger; @@ -1707,9 +1726,9 @@ Json::Value RPCHandler::doLedgerEntry(const Json::Value& params) if (lpLedger->isClosed()) { if (!!uLedger) - jvResult["ledger_closed"] = uLedger.ToString(); + jvResult["ledger_hash"] = uLedger.ToString(); - jvResult["ledger_closed_index"] = uLedgerIndex; + jvResult["ledger_index"] = uLedgerIndex; } else { @@ -1907,7 +1926,7 @@ Json::Value RPCHandler::doLedgerEntry(const Json::Value& params) jvResult["index"] = uNodeIndex.ToString(); } } - + return jvResult; } diff --git a/src/RPCHandler.h b/src/RPCHandler.h index 6dc6275467..ca71d7baca 100644 --- a/src/RPCHandler.h +++ b/src/RPCHandler.h @@ -28,7 +28,7 @@ class RPCHandler Json::Value accountFromString(const uint256& uLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex); Json::Value doAcceptLedger(const Json::Value ¶ms); - + Json::Value doAccountInfo(const Json::Value& params); Json::Value doAccountTransactions(const Json::Value& params); Json::Value doConnect(const Json::Value& params); @@ -39,21 +39,22 @@ class RPCHandler Json::Value doLedger(const Json::Value& params); Json::Value doLogRotate(const Json::Value& params); Json::Value doNicknameInfo(const Json::Value& params); - + Json::Value doOwnerInfo(const Json::Value& params); - + Json::Value doProfile(const Json::Value& params); Json::Value doPeers(const Json::Value& params); - + Json::Value doRippleLinesGet(const Json::Value ¶ms); Json::Value doServerInfo(const Json::Value& params); Json::Value doSessionClose(const Json::Value& params); Json::Value doSessionOpen(const Json::Value& params); Json::Value doLogLevel(const Json::Value& params); Json::Value doStop(const Json::Value& params); + Json::Value doSubmit(const Json::Value& params); + Json::Value doSubmitJson(const Json::Value& jvRequest); Json::Value doTx(const Json::Value& params); Json::Value doTxHistory(const Json::Value& params); - Json::Value doSubmit(const Json::Value& params); Json::Value doUnlAdd(const Json::Value& params); @@ -154,8 +155,9 @@ public: Json::Value doCommand(const std::string& command, Json::Value& params,int role); Json::Value rpcError(int iError); - Json::Value handleJSONSubmit(std::string& key, Json::Value& txJSON); + Json::Value handleJSONSubmit(const Json::Value& jvRequest); }; #endif +// vim:ts=4 diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index b154141ef5..efd7cf0202 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -286,7 +286,7 @@ TER RippleCalc::calcNodeDeliverRev( STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be adjusted. - saOutAct = 0; + saOutAct.zero(saOutReq); while (saOutAct != saOutReq) // Did not deliver limit. { @@ -1650,6 +1650,11 @@ PathState::PathState( uInCurrencyID, uSenderID); +cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uSenderID) + % STAmount::createHumanCurrency(uInCurrencyID) + % RippleAddress::createHumanAccountID(uSenderID)); + if (tesSUCCESS == terStatus && !!uInCurrencyID // First was not XRC && uInIssuerID != uSenderID) { // Issuer was not same as sender @@ -1667,11 +1672,19 @@ PathState::PathState( : uOutIssuerID : ACCOUNT_XRP; +cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s") + % RippleAddress::createHumanAccountID(uNxtCurrencyID) + % RippleAddress::createHumanAccountID(uNxtAccountID)); + // Can't just use push implied, because it can't compensate for next account. if (!uNxtCurrencyID // Next is XRC - will have offer next || uInCurrencyID != uNxtCurrencyID // Next is different current - will have offer next || uInIssuerID != uNxtAccountID) // Next is not implied issuer { +cLog(lsDEBUG) << boost::str(boost::format("PathState: implied: account=%s currency=%s issuer=%s") + % RippleAddress::createHumanAccountID(uInIssuerID) + % RippleAddress::createHumanAccountID(uInCurrencyID) + % RippleAddress::createHumanAccountID(uInIssuerID)); // Add implied account. terStatus = pushNode( STPathElement::typeAccount diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index a1e661cd02..5c419f1062 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -215,7 +215,10 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vector stack = getStack(node.getNodeID(), true, true); if (stack.empty()) + { + cLog(lsWARNING) << "AddKnownNode with empty stack"; return false; + } SHAMapTreeNode::pointer iNode = stack.top(); if (!iNode) @@ -241,7 +247,7 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorgetDepth() != (node.getDepth() - 1)) { // Either this node is broken or we didn't request it (yet) - cLog(lsINFO) << "unable to hook node " << node; + cLog(lsWARNING) << "unable to hook node " << node; cLog(lsINFO) << " stuck at " << *iNode; cLog(lsINFO) << "got depth=" << node.getDepth() << ", walked to= " << iNode->getDepth(); return false; @@ -254,7 +260,11 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorgetChildHash(branch); - if (!hash) return false; + if (!hash) + { + cLog(lsWARNING) << "AddKnownNode for empty branch"; + return false; + } SHAMapTreeNode::pointer newNode = boost::make_shared(node, rawNode, mSeq, snfWIRE); if (hash != newNode->getNodeHash()) // these aren't the droids we're looking for @@ -293,6 +303,7 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorsetFullBelow(); } while (!stack.empty()); + if (root->isFullBelow()) clearSynching(); return true; diff --git a/src/WSConnection.cpp b/src/WSConnection.cpp index 8e8328784a..7037836b16 100644 --- a/src/WSConnection.cpp +++ b/src/WSConnection.cpp @@ -1,13 +1,16 @@ +// +// WSConnection +// + +#include "Log.h" + +SETUP_LOG(); + #include "WSConnection.h" #include "WSHandler.h" #include "../json/reader.h" #include "../json/writer.h" -// -// WSConnection -// - -SETUP_LOG(); WSConnection::~WSConnection() { @@ -62,8 +65,6 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) Json::Value jvResult(Json::objectValue); - jvResult["type"] = "response"; - if (i < 0) { jvResult["error"] = "unknownCommand"; // Unknown command. @@ -88,6 +89,8 @@ Json::Value WSConnection::invokeCommand(Json::Value& jvRequest) jvResult["result"] = "success"; } + jvResult["type"] = "response"; + return jvResult; } @@ -131,22 +134,22 @@ void WSConnection::doSubscribe(Json::Value& jvResult, Json::Value& jvRequest) { for (Json::Value::iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) { - if ((*it).isString() ) + if ((*it).isString()) { std::string streamName=(*it).asString(); if(streamName=="server") { - mNetwork.subServer(this); + mNetwork.subServer(this, jvResult); }else if(streamName=="ledger") { - mNetwork.subLedger(this); + mNetwork.subLedger(this, jvResult); }else if(streamName=="transactions") { mNetwork.subTransactions(this); }else if(streamName=="rt_transactions") { - mNetwork.subRTTransactions(this); + mNetwork.subRTTransactions(this); }else { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); @@ -220,7 +223,7 @@ void WSConnection::doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest) mNetwork.unsubTransactions(this); }else if(streamName=="rt_transactions") { - mNetwork.unsubRTTransactions(this); + mNetwork.unsubRTTransactions(this); }else { jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); @@ -273,16 +276,23 @@ void WSConnection::doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest) } } - - void WSConnection::doRPC(Json::Value& jvResult, Json::Value& jvRequest) { if (jvRequest.isMember("rpc_command") ) { - jvResult=theApp->getRPCHandler().doCommand(jvRequest["rpc_command"].asString(),jvRequest["params"],RPCHandler::GUEST); - - }else jvResult["error"] = "fieldNotCommand"; + jvResult = theApp->getRPCHandler().doCommand( + jvRequest["rpc_command"].asString(), + jvRequest.isMember("params") + ? jvRequest["params"] + : jvRequest, + mHandler->getPublic() ? RPCHandler::GUEST : RPCHandler::ADMIN); + jvResult["type"] = "response"; + } + else + { + jvResult["error"] = "fieldNotCommand"; + } } // XXX Currently requires secret. Allow signed transaction as an alternative. @@ -290,15 +300,16 @@ void WSConnection::doSubmit(Json::Value& jvResult, Json::Value& jvRequest) { if (!jvRequest.isMember("tx_json")) { - jvResult["error"] = "fieldNotFoundTransaction"; - }else if (!jvRequest.isMember("key")) + jvResult["error"] = "fieldNotFoundTxJson"; + }else if (!jvRequest.isMember("secret")) { - jvResult["error"] = "fieldNotFoundKey"; - }else + jvResult["error"] = "fieldNotFoundSecret"; + }else { - jvResult=theApp->getRPCHandler().handleJSONSubmit(jvRequest["key"].asString(),jvRequest["tx_json"]); + jvResult=theApp->getRPCHandler().handleJSONSubmit(jvRequest); // TODO: track the transaction mNetwork.subSubmit(this, jvResult["tx hash"] ); } } +// vim:ts=4 diff --git a/src/WSConnection.h b/src/WSConnection.h index e93609c1e2..47cdf898b8 100644 --- a/src/WSConnection.h +++ b/src/WSConnection.h @@ -52,4 +52,6 @@ public: void doSubscribe(Json::Value& jvResult, Json::Value& jvRequest); void doUnsubscribe(Json::Value& jvResult, Json::Value& jvRequest); -}; \ No newline at end of file +}; + +// vim:ts=4 diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 9629841590..55e46b58a1 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -1,14 +1,17 @@ -#include "WSDoor.h" +#include "Log.h" +SETUP_LOG(); #include "Application.h" #include "Config.h" -#include "Log.h" #include "NetworkOPs.h" #include "utils.h" #include "WSConnection.h" #include "WSHandler.h" + +#include "WSDoor.h" + #include #include @@ -17,8 +20,6 @@ #include -SETUP_LOG(); - // // This is a light weight, untrusted interface for web clients. // For now we don't provide proof. Later we will. @@ -39,10 +40,6 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength) return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024(); } - - - - void WSDoor::startListening() { // Generate a single SSL context for use by all connections. @@ -57,7 +54,7 @@ void WSDoor::startListening() SSL_CTX_set_tmp_dh_callback(mCtx->native_handle(), handleTmpDh); // Construct a single handler for all requests. - websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler(mCtx)); + websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler(mCtx, mPublic)); // Construct a websocket server. mEndpoint = new websocketpp::WSDOOR_SERVER(handler); @@ -68,25 +65,22 @@ void WSDoor::startListening() // Call the main-event-loop of the websocket server. mEndpoint->listen( boost::asio::ip::tcp::endpoint( - boost::asio::ip::address().from_string(theConfig.WEBSOCKET_IP), theConfig.WEBSOCKET_PORT)); + boost::asio::ip::address().from_string(mIp), mPort)); delete mEndpoint; } -WSDoor* WSDoor::createWSDoor() +WSDoor* WSDoor::createWSDoor(const std::string& strIp, const int iPort, bool bPublic) { - WSDoor* wdpResult = new WSDoor(); + WSDoor* wdpResult = new WSDoor(strIp, iPort, bPublic); - if (!theConfig.WEBSOCKET_IP.empty() && theConfig.WEBSOCKET_PORT) - { - Log(lsINFO) << "Websocket: Listening: " << theConfig.WEBSOCKET_IP << " " << theConfig.WEBSOCKET_PORT; + cLog(lsINFO) << + boost::str(boost::format("Websocket: %s: Listening: %s %d ") + % (bPublic ? "Public" : "Private") + % strIp + % iPort); - wdpResult->mThread = new boost::thread(boost::bind(&WSDoor::startListening, wdpResult)); - } - else - { - Log(lsINFO) << "Websocket: Disabled"; - } + wdpResult->mThread = new boost::thread(boost::bind(&WSDoor::startListening, wdpResult)); return wdpResult; } diff --git a/src/WSDoor.h b/src/WSDoor.h index af90ddaf88..fe41d1bc5a 100644 --- a/src/WSDoor.h +++ b/src/WSDoor.h @@ -21,16 +21,19 @@ class WSDoor private: websocketpp::WSDOOR_SERVER* mEndpoint; boost::thread* mThread; + bool mPublic; + std::string mIp; + int mPort; void startListening(); public: - WSDoor() : mEndpoint(0), mThread(0) { ; } + WSDoor(const std::string& strIp, int iPort, bool bPublic) : mEndpoint(0), mThread(0), mPublic(bPublic), mIp(strIp), mPort(iPort) { ; } void stop(); - static WSDoor* createWSDoor(); + static WSDoor* createWSDoor(const std::string& strIp, const int iPort, bool bPublic); }; #endif diff --git a/src/WSHandler.h b/src/WSHandler.h index e400da35f7..43c9a9ce66 100644 --- a/src/WSHandler.h +++ b/src/WSHandler.h @@ -18,15 +18,18 @@ public: }; private: - boost::shared_ptr mCtx; + boost::shared_ptr mCtx; protected: - boost::mutex mMapLock; + boost::mutex mMapLock; // For each connection maintain an associated object to track subscriptions. boost::unordered_map > mMap; + bool mPublic; public: - WSServerHandler(boost::shared_ptr spCtx) : mCtx(spCtx) {} + WSServerHandler(boost::shared_ptr spCtx, bool bPublic) : mCtx(spCtx), mPublic(bPublic) {} + + bool getPublic() { return mPublic; }; boost::shared_ptr on_tls_init() { @@ -87,6 +90,8 @@ public: Json::Value jvRequest; Json::Reader jrReader; + cLog(lsDEBUG) << "Ws:: Receiving '" << mpMessage->get_payload() << "'"; + if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT) { Json::Value jvResult(Json::objectValue); @@ -122,3 +127,5 @@ public: }; #endif + +// vim:ts=4 diff --git a/src/ripple.proto b/src/ripple.proto index 64cd1b71f9..6d2292436c 100644 --- a/src/ripple.proto +++ b/src/ripple.proto @@ -236,13 +236,18 @@ enum TMLedgerType { ltCLOSED = 2; } +enum TMQueryType { + qtINDIRECT = 0; +} + message TMGetLedger { required TMLedgerInfoType itype = 1; optional TMLedgerType ltype = 2; optional bytes ledgerHash = 3; // Can also be the transaction set hash if liTS_CANDIDATE optional uint32 ledgerSeq = 4; repeated bytes nodeIDs = 5; - optional uint32 requestCookie = 6; + optional uint64 requestCookie = 6; + optional TMQueryType queryType = 7; } enum TMReplyError { diff --git a/test/remote-test.js b/test/remote-test.js index eec201357f..a085676c5b 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -17,7 +17,7 @@ buster.testCase("Remote functions", { 'setUp' : testutils.build_setup(), 'tearDown' : testutils.build_teardown(), - 'request_ledger_current' : + "request_ledger_current" : function (done) { this.remote.request_ledger_current().on('success', function (m) { // console.log(m); @@ -30,15 +30,16 @@ buster.testCase("Remote functions", { buster.assert(false); }) + .request(); }, - 'request_ledger_closed' : + "request_ledger_hash" : function (done) { - this.remote.request_ledger_closed().on('success', function (m) { + this.remote.request_ledger_hash().on('success', function (m) { // console.log("result: %s", JSON.stringify(m)); - buster.assert.equals(m.ledger_closed_index, 2); + buster.assert.equals(m.ledger_index, 2); done(); }) .on('error', function(m) { @@ -49,16 +50,16 @@ buster.testCase("Remote functions", { .request(); }, - 'manual account_root success' : + "manual account_root success" : function (done) { var self = this; - this.remote.request_ledger_closed().on('success', function (r) { + this.remote.request_ledger_hash().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); self.remote .request_ledger_entry('account_root') - .ledger_closed(r.ledger_closed) + .ledger_hash(r.ledger_hash) .account_root("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -82,16 +83,16 @@ buster.testCase("Remote functions", { }, // XXX This should be detected locally. - 'account_root remote malformedAddress' : + "account_root remote malformedAddress" : function (done) { var self = this; - this.remote.request_ledger_closed().on('success', function (r) { + this.remote.request_ledger_hash().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); self.remote .request_ledger_entry('account_root') - .ledger_closed(r.ledger_closed) + .ledger_hash(r.ledger_hash) .account_root("zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -115,16 +116,16 @@ buster.testCase("Remote functions", { .request(); }, - 'account_root entryNotFound' : + "account_root entryNotFound" : function (done) { var self = this; - this.remote.request_ledger_closed().on('success', function (r) { + this.remote.request_ledger_hash().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); self.remote .request_ledger_entry('account_root') - .ledger_closed(r.ledger_closed) + .ledger_hash(r.ledger_hash) .account_root("alice") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -147,16 +148,16 @@ buster.testCase("Remote functions", { }).request(); }, - 'ledger_entry index' : + "ledger_entry index" : function (done) { var self = this; - this.remote.request_ledger_closed().on('success', function (r) { + this.remote.request_ledger_hash().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); self.remote .request_ledger_entry('index') - .ledger_closed(r.ledger_closed) + .ledger_hash(r.ledger_hash) .account_root("alice") .index("2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8") .on('success', function (r) { @@ -180,7 +181,7 @@ buster.testCase("Remote functions", { .request(); }, - 'create account' : + "create account" : function (done) { this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000"))