diff --git a/SConstruct b/SConstruct index 6371da57e..32e614c5a 100644 --- a/SConstruct +++ b/SConstruct @@ -88,7 +88,7 @@ PROTO_SRCS = env.Protoc([], 'src/ripple.proto', PROTOCOUTDIR='obj', PROTOCPYTHON env.Clean(PROTO_SRCS, 'site_scons/site_tools/protoc.pyc') # Remove unused source files. -UNUSED_SRCS = ['src/HttpReply.cpp'] +UNUSED_SRCS = [] for file in UNUSED_SRCS: NEWCOIN_SRCS.remove(file) diff --git a/js/amount.js b/js/amount.js index 51e18a563..d2da47a05 100644 --- a/js/amount.js +++ b/js/amount.js @@ -1,20 +1,143 @@ -// Represent Newcoin amounts and currencies. +// Represent Ripple amounts and currencies. // - Numbers in hex are big-endian. +var sjcl = require('./sjcl/core.js'); +var bn = require('./sjcl/core.js').bn; var utils = require('./utils.js'); var jsbn = require('./jsbn.js'); +// Don't include in browser context. +var config = require('../test/config.js'); + var BigInteger = jsbn.BigInteger; +var nbi = jsbn.nbi; + +var alphabets = { + 'ripple' : "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", + 'bitcoin' : "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" +}; + +// --> input: big-endian array of bytes. +// <-- string at least as long as input. +var encode_base = function (input, alphabet) { + var alphabet = alphabets[alphabet || 'ripple']; + var bi_base = new BigInteger(String(alphabet.length)); + var bi_q = nbi(); + var bi_r = nbi(); + var bi_value = new BigInteger(input); + var buffer = []; + + while (bi_value.compareTo(BigInteger.ZERO) > 0) + { + bi_value.divRemTo(bi_base, bi_q, bi_r); + bi_q.copyTo(bi_value); + + buffer.push(alphabet[bi_r.intValue()]); + } + + var i; + + for (i = 0; i != input.length && !input[i]; i += 1) { + buffer.push(alphabet[0]); + } + + return buffer.reverse().join(""); +}; + +// --> input: String +// <-- array of bytes or undefined. +var decode_base = function (input, alphabet) { + var alphabet = alphabets[alphabet || 'ripple']; + var bi_base = new BigInteger(String(alphabet.length)); + var bi_value = nbi(); + var i; + + for (i = 0; i != input.length && input[i] === alphabet[0]; i += 1) + ; + + for (; i != input.length; i += 1) { + var v = alphabet.indexOf(input[i]); + + if (v < 0) + return undefined; + + var r = nbi(); + + r.fromInt(v); + + bi_value = bi_value.multiply(bi_base).add(r); + } + + // toByteArray: + // - Returns leading zeros! + // - Returns signed bytes! + var bytes = bi_value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0}); + var extra = 0; + + while (extra != bytes.length && !bytes[extra]) + extra += 1; + + if (extra) + bytes = bytes.slice(extra); + + var zeros = 0; + + while (zeros !== input.length && input[zeros] === alphabet[0]) + zeros += 1; + + return [].concat(utils.arraySet(zeros, 0), bytes); +}; + +var sha256 = function (bytes) { + return sjcl.codec.bytes.fromBits(sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes))); +}; + +var sha256hash = function (bytes) { + return sha256(sha256(bytes)); +}; + +// --> input: Array +// <-- String +var encode_base_check = function (version, input, alphabet) { + var buffer = [].concat(version, input); + var check = sha256(sha256(buffer)).slice(0, 4); + + return encode_base([].concat(buffer, check), alphabet); +} + +// --> input : String +// <-- NaN || BigInteger +var decode_base_check = function (version, input, alphabet) { + var buffer = decode_base(input, alphabet); + + if (!buffer || buffer[0] !== version || buffer.length < 5) + return NaN; + + var computed = sha256hash(buffer.slice(0, -4)).slice(0, 4); + var checksum = buffer.slice(-4); + var i; + + for (i = 0; i != 4; i += 1) + if (computed[i] !== checksum[i]) + return NaN; + + return new BigInteger(buffer.slice(1, -4)); +} var UInt160 = function () { - // Internal form: - // 0, 1, 'iXXXXX', 20 byte string, or NaN. - // XXX Should standardize on 'i' format or 20 format. + // Internal form: NaN or BigInteger this.value = NaN; }; +UInt160.json_rewrite = function (j) { + return UInt160.from_json(j).to_json(); +}; + +// Return a new UInt160 from j. UInt160.from_json = function (j) { - return (new UInt160()).parse_json(j); + return 'string' === typeof j + ? (new UInt160()).parse_json(j) + : j.clone(); }; UInt160.prototype.clone = function() { @@ -28,9 +151,15 @@ UInt160.prototype.copyTo = function(d) { return d; }; -// value === NaN on error. +UInt160.prototype.equals = function(d) { + return isNaN(this.value) || isNaN(d.value) ? false : this.value.equals(d.value); +}; + +// value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate + if (config.accounts && j in config.accounts) + j = config.accounts[j].account; switch (j) { case undefined: @@ -38,14 +167,15 @@ UInt160.prototype.parse_json = function (j) { case exports.consts.address_xns: case exports.consts.uint160_xns: case exports.consts.hex_xns: - this.value = 0; + this.value = nbi(); break; case "1": case exports.consts.address_one: case exports.consts.uint160_one: case exports.consts.hex_one: - this.value = 1; + this.value = new BigInteger([1]); + break; default: @@ -53,15 +183,14 @@ UInt160.prototype.parse_json = function (j) { this.value = NaN; } else if (20 === j.length) { - this.value = j; + this.value = new BigInteger(utils.stringToArray(j), 256); } else if (40 === j.length) { - this.value = utils.hexToString(j); + // XXX Check char set! + this.value = new BigInteger(j, 16); } else if (j[0] === "r") { - // XXX Do more checking convert to string. - - this.value = j; + this.value = decode_base_check(0, j); } else { this.value = NaN; @@ -74,25 +203,26 @@ UInt160.prototype.parse_json = function (j) { // Convert from internal form. // XXX Json form should allow 0 and 1, C++ doesn't currently allow it. UInt160.prototype.to_json = function () { - if ("0" === this.value) { - return exports.consts.hex_xns; - } - else if ("1" === this.value) - { - return exports.consts.hex_one; - } - else if ('string' === typeof this.value && 20 === this.value.length) { - return utils.stringToHex(this.value); - } - else - { - return this.value; - } + if (isNaN(this.value)) + return NaN; + + var bytes = this.value.toByteArray().map(function (b) { return b ? b < 0 ? 256+b : b : 0}); + var target = 20; + + // XXX Make sure only trim off leading zeros. + var array = bytes.length < target + ? bytes.length + ? [].concat(utils.arraySet(target - bytes.length, 0), bytes) + : utils.arraySet(target, 0) + : bytes.slice(target - bytes.length); + var output = encode_base_check(0, array); + + return output; }; var Currency = function () { // Internal form: 0 = XNS. 3 letter-code. - // XXX Internal should be 0 or hex. + // XXX Internal should be 0 or hex with three letter annotation when valid. // Json form: // '', 'XNS', '0': 0 @@ -117,7 +247,7 @@ Currency.prototype.copyTo = function(d) { return d; }; -// this.value === NaN on error. +// this.value = NaN on error. Currency.prototype.parse_json = function(j) { if ("" === j || "0" === j || "XNS" === j) { this.value = 0; @@ -154,19 +284,32 @@ var Amount = function () { this.issuer = new UInt160(); }; +// Given "100/USD/mtgox" return the a string with mtgox remapped. +Amount.text_full_rewrite = function (j) { + return Amount.from_json(j).to_text_full(); +} + +// Given "100/USD/mtgox" return the json. +Amount.json_rewrite = function(j) { + return Amount.from_json(j).to_json(); +}; + Amount.from_json = function(j) { return (new Amount()).parse_json(j); }; -Amount.prototype.clone = function() { - return this.copyTo(new Amount()); +Amount.prototype.clone = function(negate) { + return this.copyTo(new Amount(), negate); }; // Returns copy. -Amount.prototype.copyTo = function(d) { +Amount.prototype.copyTo = function(d, negate) { if ('object' === typeof this.value) { - this.value.copyTo(d.value); + if (this.is_native && negate) + this.value.negate().copyTo(d.value); + else + this.value.copyTo(d.value); } else { @@ -175,7 +318,11 @@ Amount.prototype.copyTo = function(d) { d.offset = this.offset; d.is_native = this.is_native; - d.is_negative = this.is_negative; + d.is_negative = this.is_native + ? undefined // Native sign in BigInteger. + : negate + ? !this.is_negative // Negating. + : this.is_negative; // Just copying. this.currency.copyTo(d.currency); this.issuer.copyTo(d.issuer); @@ -185,12 +332,12 @@ Amount.prototype.copyTo = function(d) { // YYY Might also provide is_valid_json. Amount.prototype.is_valid = function() { - return NaN !== this.value; + return !isNaN(this.value); }; // Convert only value to JSON wire format. Amount.prototype.to_text = function(allow_nan) { - if (NaN === this.value) { + if (isNaN(this.value)) { // Never should happen. return allow_nan ? NaN : "0"; } @@ -231,7 +378,7 @@ Amount.prototype.to_text = function(allow_nan) { }; Amount.prototype.canonicalize = function() { - if (NaN === this.value || !this.currency) { + if (isNaN(this.value) || !this.currency) { // nothing } else if (this.value.equals(BigInteger.ZERO)) { @@ -250,6 +397,13 @@ Amount.prototype.canonicalize = function() { this.offset += 1; } } + + return this; +}; + +// Return a new value. +Amount.prototype.negate = function () { + return this.clone('NEGATE'); }; Amount.prototype.to_json = function() { @@ -267,7 +421,7 @@ Amount.prototype.to_json = function() { }; Amount.prototype.to_text_full = function() { - return this.value === NaN + return isNaN(this.value) ? NaN : this.is_native ? this.to_text() + "/XNS" @@ -282,23 +436,26 @@ Amount.prototype.parse_native = function(j) { var m; if ('string' === typeof j) - m = j.match(/^(\d+)(\.\d{1,6})?$/); + m = j.match(/^(-?)(\d+)(\.\d{1,6})?$/); if (m) { - if (undefined === m[2]) { + if (undefined === m[3]) { // Integer notation - this.value = new BigInteger(m[1]); + this.value = new BigInteger(m[2]); } else { // Decimal notation - var int_part = (new BigInteger(m[1])).multiply(exports.consts.bi_xns_unit); - var fraction_part = (new BigInteger(m[2])).multiply(new BigInteger(String(Math.pow(10, 1+exports.consts.xns_precision-m[2].length)))); + var int_part = (new BigInteger(m[2])).multiply(exports.consts.bi_xns_unit); + var fraction_part = (new BigInteger(m[3])).multiply(new BigInteger(String(Math.pow(10, 1+exports.consts.xns_precision-m[3].length)))); this.value = int_part.add(fraction_part); } + if (m[1]) + this.value = this.value.negate(); + this.is_native = true; this.offset = undefined; this.is_negative = undefined; @@ -317,44 +474,58 @@ Amount.prototype.parse_native = function(j) { // Parse a non-native value. Amount.prototype.parse_value = function(j) { + this.is_native = false; + if ('number' === typeof j) { + this.is_negative = j < 0; + if (this.is_negative) j = -j; this.value = new BigInteger(j); this.offset = 0; - this.is_native = false; - this.is_negative = j < 0; this.canonicalize(); } else if ('string' === typeof j) { - var e = j.match(/^(-?\d+)e(\d+)/); - var d = j.match(/^(-?\d+)\.(\d+)/); + var i = j.match(/^(-?)(\d+)$/); + var d = !i && j.match(/^(-?)(\d+)\.(\d*)$/); + var e = !e && j.match(/^(-?)(\d+)e(\d+)$/); if (e) { // e notation - this.value = new BigInteger(e[1]); - this.offset = parseInt(e[2]); + this.value = new BigInteger(e[2]); + this.offset = parseInt(e[3]); + this.is_negative = !!e[1]; + + this.canonicalize(); } else if (d) { // float notation - var integer = new BigInteger(d[1]); - var fraction = new BigInteger(d[2]); - this.value = integer.multiply(exports.consts.bi_10.clone().pow(d[2].length)).add(fraction); - this.offset = -d[2].length; + var integer = new BigInteger(d[2]); + var fraction = new BigInteger(d[3]); + var precision = d[3].length; + + this.value = integer.multiply(exports.consts.bi_10.clone().pow(precision)).add(fraction); + this.offset = -precision; + this.is_negative = !!d[1]; + + this.canonicalize(); } - else - { + else if (i) { // integer notation - this.value = new BigInteger(j); - this.offset = 0; + this.value = new BigInteger(i[2]); + this.offset = 0; + this.is_negative = !!i[1]; + + this.canonicalize(); } - - this.is_native = false; - this.is_negative = undefined; - - this.canonicalize(); + else { + this.value = NaN; + } + } + else if (j.constructor == BigInteger) { + this.value = j.clone(); } else { this.value = NaN; @@ -380,23 +551,46 @@ Amount.prototype.parse_json = function(j) { this.issuer = new UInt160(); } } - else if ('object' === typeof j && j.currency) { - // Never XNS. + else if ('object' === typeof j && j.constructor == Amount) { + j.copyTo(this); + } + else if ('object' === typeof j && 'value' in j) { + // Parse the passed value to sanitize and copy it. this.parse_value(j.value); - this.currency.parse_json(j.currency); + this.currency.parse_json(j.currency); // Never XNS. this.issuer.parse_json(j.issuer); } else { - this.value = NaN; + this.value = NaN; } return this; }; -exports.Amount = Amount; -exports.Currency = Currency; -exports.UInt160 = UInt160; +Amount.prototype.parse_issuer = function (issuer) { + this.issuer.parse_json(issuer); + + return this; +}; + +// Check BigInteger NaN +Amount.prototype.equals = function (d) { + return 'string' === typeof (d) + ? this.equals(Amount.from_json(d)) + : this === d + || (d.constructor === Amount + && this.is_native === d.is_native + && (this.is_native + ? this.value.equals(d.value) + : this.is_negative === d.is_negative + ? this.value.equals(d.value) + : this.value.equals(BigInteger.ZERO) && d.value.equals(BigInteger.ZERO))); +}; + +exports.Amount = Amount; +exports.Currency = Currency; +exports.UInt160 = UInt160; exports.consts = { 'address_xns' : "rrrrrrrrrrrrrrrrrrrrrhoLvTp", diff --git a/js/remote.js b/js/remote.js index bed251dd7..9fa97054a 100644 --- a/js/remote.js +++ b/js/remote.js @@ -3,10 +3,15 @@ // - We use the W3C interface for node and browser compatibility: // http://www.w3.org/TR/websockets/#the-websocket-interface // -// YYY Will later provide a network access which use multiple instances of this. -// YYY A better model might be to allow requesting a target state: keep connected or not. -// XXX Make subscribe target state. -// XXX Auto subscribe on connect. +// This class is intended for both browser and node.js use. +// +// This class is designed to work via peer protocol via either the public or +// private websocket interfaces. The JavaScript class for the peer protocol +// has not yet been implemented. However, this class has been designed for it +// to be a very simple drop option. +// +// YYY Will later provide js/network.js which will transparently use multiple +// instances of this class for network access. // // Node @@ -16,8 +21,11 @@ var EventEmitter = require('events').EventEmitter; // npm var WebSocket = require('ws'); -var amount = require('./amount.js'); -var Amount = amount.Amount; +var Amount = require('./amount.js').Amount; +var UInt160 = require('./amount.js').UInt160; + +// Don't include in browser context. +var config = require('../test/config.js'); // Request events emmitted: // 'success' : Request successful. @@ -26,13 +34,17 @@ var Amount = amount.Amount; // 'remoteUnexpected' // 'remoteDisconnected' var Request = function (remote, command) { + var self = this; + this.message = { 'command' : command, 'id' : undefined, }; this.remote = remote; - this.on('request', this.request_default); + this.on('request', function () { + self.request_default(); + }); }; Request.prototype = new EventEmitter; @@ -44,6 +56,12 @@ Request.prototype.on = function (e, c) { return this; }; +Request.prototype.once = function (e, c) { + EventEmitter.prototype.once.call(this, e, c); + + return this; +}; + // Send the request to a remote. Request.prototype.request = function (remote) { this.emit('request', remote); @@ -53,10 +71,23 @@ Request.prototype.request_default = function () { this.remote.request(this); }; +Request.prototype.ledger_choose = function (current) { + if (current) + { + this.message.ledger_index = this.remote.ledger_current_index; + } + else { + this.message.ledger = this.remote.ledger_closed; + } + + return this; +}; + // Set the ledger for a request. // - ledger_entry -Request.prototype.ledger = function (ledger) { - this.message.ledger = ledger; +// - transaction_entry +Request.prototype.ledger_closed = function (ledger) { + this.message.ledger_closed = ledger; return this; }; @@ -70,7 +101,7 @@ Request.prototype.ledger_index = function (ledger_index) { }; Request.prototype.account_root = function (account) { - this.message.account_root = account; + this.message.account_root = UInt160.json_rewrite(account); return this; }; @@ -94,17 +125,47 @@ Request.prototype.transaction = function (t) { return this; }; +Request.prototype.ripple_state = function (account, issuer, currency) { + this.message.ripple_state = { + 'accounts' : [ + UInt160.json_rewrite(account), + UInt160.json_rewrite(issuer) + ], + 'currency' : currency + }; + + return this; +}; + +// +// Remote - access to a remote Ripple server via websocket. +// +// Events: +// 'connectted' +// 'disconnected' +// 'state': +// - 'online' : connectted and subscribed +// - 'offline' : not subscribed or not connectted. +// 'ledger_closed': A good indicate of ready to serve. +// 'subscribed' : This indicates stand-alone is available. +// + // --> trusted: truthy, if remote is trusted -var Remote = function (trusted, websocket_ip, websocket_port, config, 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.config = config; this.trace = trace; this.ledger_closed = undefined; this.ledger_current_index = undefined; this.stand_alone = undefined; + this.online_target = false; + this.online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing' + this.state = 'offline'; // 'online', 'offline' + this.retry_timer = undefined; + this.retry = undefined; + this.config = config || { 'accounts' : {}}; // Cache information for accounts. this.accounts = { @@ -118,16 +179,18 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { // Cache for various ledgers. // XXX Clear when ledger advances. this.ledgers = { - 'current' : {} + 'current' : { + 'account_root' : {} + } }; }; Remote.prototype = new EventEmitter; -var remoteConfig = function (config, server, trace) { - var serverConfig = config.servers[server]; +Remote.from_config = function (name, trace) { + var serverConfig = config.servers[name]; - return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, config, trace); + return new Remote(serverConfig.trusted, serverConfig.websocket_ip, serverConfig.websocket_port, trace); }; var isTemMalformed = function (engine_result_code) { @@ -138,7 +201,7 @@ var isTefFailure = function (engine_result_code) { return (engine_result_code >= -299 && engine_result_code < 199); }; -var flags = { +Remote.flags = { 'OfferCreate' : { 'Passive' : 0x00010000, }, @@ -152,205 +215,267 @@ var flags = { }; // XXX This needs to be determined from the network. -var fees = { +Remote.fees = { 'default' : Amount.from_json("100"), 'account_create' : Amount.from_json("1000"), 'nickname_create' : Amount.from_json("1000"), 'offer' : Amount.from_json("100"), }; -Remote.prototype.connect_helper = function () { +// Set the emited state: 'online' or 'offline' +Remote.prototype._set_state = function (state) { + if (this.trace) console.log("remote: set_state: %s", state); + + if (this.state !== state) { + this.state = state; + + this.emit('state', state); + switch (state) { + case 'online': + this.online_state = 'open'; + this.emit('connected'); + break; + + case 'offline': + this.online_state = 'closed'; + this.emit('disconnected'); + break; + } + } +}; + +Remote.prototype.set_trace = function (trace) { + this.trace = undefined === trace || trace; + + return this; +}; + +// Set the target online state. Defaults to false. +Remote.prototype.connect = function (online) { + var target = undefined === online || online; + + if (this.online_target != target) { + this.online_target = target; + + // If we were in a stable state, go dynamic. + switch (this.online_state) { + case 'open': + if (!target) this._connect_stop(); + break; + + case 'closed': + if (target) this._connect_retry(); + break; + } + } + + return this; +}; + +// Stop from open state. +Remote.prototype._connect_stop = function () { + delete this.ws.onerror; + delete this.ws.onclose; + + this.ws.terminate(); + delete this.ws; + + this._set_state('offline'); +}; + +// Implictly we are not connected. +Remote.prototype._connect_retry = function () { + var self = this; + + if (!self.online_target) { + // Do not continue trying to connect. + this._set_state('offline'); + } + else if ('connecting' !== this.online_state) { + // New to connecting state. + this.online_state = 'connecting'; + this.retry = 0; + + this._connect_start(); + } + else + { + // Delay and retry. + this.retry += 1; + this.retry_timer = setTimeout(function () { + if (self.trace) console.log("remote: retry"); + + if (self.online_target) { + self._connect_start(); + } + else { + self._connect_retry(); + } + }, this.retry < 40 ? 1000/20 : 1000); // 20 times per second for 2 seconds then once per second. + } +}; + +Remote.prototype._connect_start = function () { + // Note: as a browser client can't make encrypted connections to random ips + // with self-signed certs as the user must have pre-approved the self-signed certs. + var self = this; + var url = util.format("ws://%s:%s", this.websocket_ip, this.websocket_port); - if (this.trace) console.log("remote: connect: %s", this.url); + if (this.trace) console.log("remote: connect: %s", url); - var ws = this.ws = new WebSocket(this.url);; + var ws = this.ws = new WebSocket(url); ws.response = {}; ws.onopen = function () { - if (self.trace) console.log("remote: onopen: %s", ws.readyState); + if (self.trace) console.log("remote: onopen: %s: online_target=%s", ws.readyState, self.online_target); - ws.onclose = undefined; - ws.onerror = undefined; - - clearTimeout(self.connect_timer); delete self.connect_timer; - clearTimeout(self.retry_timer); delete self.retry_timer; + ws.onerror = function () { + if (self.trace) console.log("remote: onerror: %s", ws.readyState); - self.done(ws.readyState); + delete ws.onclose; + + self._connect_retry(); + }; + + ws.onclose = function () { + if (self.trace) console.log("remote: onclose: %s", ws.readyState); + + delete ws.onerror; + + self._connect_retry(); + }; + + if (self.online_target) { + self._set_state('online'); + + // Note, we could get disconnected before tis go through. + self._server_subscribe(); // Automatically subscribe. + } + else { + self._connect_stop(); + } }; ws.onerror = function () { if (self.trace) console.log("remote: onerror: %s", ws.readyState); + + delete ws.onclose; - ws.onclose = undefined; - - if (self.expire) { - if (self.trace) console.log("remote: was expired"); - - ws.onerror = undefined; - self.done(ws.readyState); - - } else { - // Delay and retry. - - clearTimeout(self.retry_timer); - self.retry_timer = setTimeout(function () { - if (self.trace) console.log("remote: retry"); - - self.connect_helper(); - }, 50); // Retry rate 50ms. - } + self._connect_retry(); }; - - // Covers failure to open. + + // Failure to open. ws.onclose = function () { if (self.trace) console.log("remote: onclose: %s", ws.readyState); - ws.onerror = undefined; + delete ws.onerror; - clearTimeout(self.retry_timer); - delete self.retry_timer; - - self.done(ws.readyState); + self._connect_retry(); }; // Node's ws module doesn't pass arguments to onmessage. ws.on('message', function (json, flags) { - var message = JSON.parse(json); - var unexpected = false; - var request; - - if ('object' !== typeof message) { - unexpected = true; - } - else { - switch (message.type) { - case 'response': - { - request = ws.response[message.id]; - - if (!request) { - unexpected = true; - } - else if ('success' === message.result) { - if (self.trace) console.log("message: %s", json); - - request.emit('success', message); - } - else if (message.error) { - if (self.trace) console.log("message: %s", json); - - request.emit('error', { - 'error' : 'remoteError', - 'error_message' : 'Remote reported an error.', - 'remote' : message, - }); - } - } - break; - - case 'ledgerClosed': - // XXX If not trusted, need to verify we consider ledger closed. - // XXX Also need to consider a slow server or out of order response. - // XXX Be more defensive fields could be missing or of wrong type. - // YYY Might want to do some cache management. - - self.ledger_closed = message.ledger_closed; - self.ledger_current_index = message.ledger_closed_index + 1; - - self.emit('ledger_closed', self.ledger_closed, self.ledger_closed_index); - break; - - default: - unexpected = true; - break; - } - } - - if (!unexpected) { - } - // Unexpected response from remote. - // XXX This isn't so robust. Hard fails should probably only happen in a debugging scenairo. - else if (self.trusted) { - // Remote is trusted, report an error. - console.log("unexpected message from trusted remote: %s", json); - - (request || self).emit('error', { - 'error' : 'remoteUnexpected', - 'error_message' : 'Unexpected response from remote.' - }); - } - else { - // Treat as a disconnect. - if (self.trace) console.log("unexpected message from untrusted remote: %s", json); - - // XXX All pending request need this treatment and need to actionally disconnect. - (request || self).emit('error', { - 'error' : 'remoteDisconnected', - 'error_message' : 'Remote disconnected.' - }); - } - }); + self._connect_message(ws, json, flags); + }); }; -// Target state is connectted. -// XXX Get rid of 'done' use event model. -// done(readyState): -// --> readyState: OPEN, CLOSED -Remote.prototype.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; +// It is possible for messages to be dispatched after the connection is closed. +Remote.prototype._connect_message = function (ws, json, flags) { + var message = JSON.parse(json); + var unexpected = false; + var request; - this.connect_timer = setTimeout(function () { - if (self.trace) console.log("remote: expire: timeout"); - - delete self.connect_timer; - self.expire = true; - }, timeout); - - } else { - if (this.trace) console.log("remote: expire: false"); - this.expire = true; + if ('object' !== typeof message) { + unexpected = true; } - - this.connect_helper(); -}; + else { + switch (message.type) { + case 'response': + { + request = ws.response[message.id]; -// Target stated is disconnected. -// Note: if exiting or other side is going away, don't need to disconnect. -Remote.prototype.disconnect = function (done) { - var self = this; - var ws = this.ws; + if (!request) { + unexpected = true; + } + else if ('success' === message.result) { + if (this.trace) console.log("message: %s", json); - if (this.trace) console.log("remote: disconnect"); - - ws.onclose = function () { - if (self.trace) console.log("remote: onclose: %s", ws.readyState); - done(ws.readyState); - }; + request.emit('success', message); + } + else if (message.error) { + if (this.trace) console.log("message: %s", json); - // ws package has a hard coded 30 second timeout. - ws.close(); + request.emit('error', { + 'error' : 'remoteError', + 'error_message' : 'Remote reported an error.', + 'remote' : message, + }); + } + } + break; + + case 'ledgerClosed': + // XXX If not trusted, need to verify we consider ledger closed. + // XXX Also need to consider a slow server or out of order response. + // 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.emit('ledger_closed', message.ledger_closed, message.ledger_closed_index); + break; + + default: + unexpected = true; + break; + } + } + + if (!unexpected) { + } + // Unexpected response from remote. + // XXX This isn't so robust. Hard fails should probably only happen in a debugging scenairo. + else if (this.trusted) { + // Remote is trusted, report an error. + console.log("unexpected message from trusted remote: %s", json); + + (request || this).emit('error', { + 'error' : 'remoteUnexpected', + 'error_message' : 'Unexpected response from remote.' + }); + } + else { + // Treat as a disconnect. + if (this.trace) console.log("unexpected message from untrusted remote: %s", json); + + // XXX All pending request need this treatment and need to actionally disconnect. + (request || this).emit('error', { + 'error' : 'remoteDisconnected', + 'error_message' : 'Remote disconnected.' + }); + } }; // Send a request. // <-> request: what to send, consumed. Remote.prototype.request = function (request) { - this.ws.response[request.message.id = this.id] = request; - - this.id += 1; // Advance id. - - if (this.trace) console.log("remote: request: %s", JSON.stringify(request.message)); - - this.ws.send(JSON.stringify(request.message)); + if (this.ws) { + // Only bother if we are still connected. + + this.ws.response[request.message.id = this.id] = request; + + this.id += 1; // Advance id. + + if (this.trace) console.log("remote: request: %s", JSON.stringify(request.message)); + + this.ws.send(JSON.stringify(request.message)); + } + else { + if (this.trace) console.log("remote: request: DROPPING: %s", JSON.stringify(request.message)); + } }; Remote.prototype.request_ledger_closed = function () { @@ -379,9 +504,10 @@ Remote.prototype.request_ledger_entry = function (type) { // Transparent caching: request.on('request', function (remote) { // Intercept default request. if (this.ledger_closed) { - // XXX Initial implementation no caching. + // XXX Add caching. } // else if (req.ledger_index) + // else if ('ripple_state' === this.type) // YYY Could be cached per ledger. else if ('account_root' === this.type) { var cache = self.ledgers.current.account_root; @@ -415,7 +541,7 @@ Remote.prototype.request_ledger_entry = function (type) { // This type not cached. } - this.request_default(remote); + this.request_default(); } } }); @@ -423,17 +549,11 @@ Remote.prototype.request_ledger_entry = function (type) { return request; }; -// --> ledger_closed : optional -Remote.prototype.request_transaction_entry = function (hash, ledger_closed) { +Remote.prototype.request_transaction_entry = function (hash) { assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol. - var request = new Request(this, 'transaction_entry'); - - request.message.transaction = hash; - if (ledger_closed) - request.message.ledger_closed = ledger_closed; - - return request; + return (new Request(this, 'transaction_entry')) + .transaction(hash); }; // Submit a transaction. @@ -455,19 +575,28 @@ Remote.prototype.submit = function (transaction) { } if (!transaction.transaction.Sequence) { - var cache_request = this.account_cache(transaction.transaction.Account); - - cache_request.on('success_account_cache', function () { + // Look in the last closed ledger. + this.account_seq_cache(transaction.transaction.Account, false) + .on('success_account_seq_cache', function () { // Try again. self.submit(transaction); - }); + }) + .on('error', function (message) { + // XXX Maybe be smarter about this. Don't want to trust an untrusted server for this seq number. - cache_request.on('error', function (message) { - // Forward errors. - transaction.emit('error', message); - }); - - cache_request.request(); + // Look in the current ledger. + self.account_seq_cache(transaction.transaction.Account, 'CURRENT') + .on('success_account_seq_cache', function () { + // Try again. + self.submit(transaction); + }) + .on('error', function (message) { + // Forward errors. + transaction.emit('error', message); + }) + .request(); + }) + .request(); } else { var submit_request = new Request(this, 'submit'); @@ -479,10 +608,6 @@ Remote.prototype.submit = function (transaction) { submit_request.on('success', function (message) { transaction.emit('success', message); }); submit_request.on('error', function (message) { transaction.emit('error', message); }); - // XXX If transaction has a 'final' event listeners, register transaction to listen to final results. - // XXX Final messages only happen if a transaction makes it into a ledger. - // XXX A transaction may be "lost" or even resubmitted in this case. - // XXX For when ledger closes, can look up transaction meta data. submit_request.request(); } } @@ -495,20 +620,21 @@ Remote.prototype.submit = function (transaction) { // Subscribe to a server to get 'ledger_closed' events. // 'subscribed' : This command was successful. // 'ledger_closed : ledger_closed and ledger_current_index are updated. -Remote.prototype.server_subscribe = function () { +Remote.prototype._server_subscribe = function () { var self = this; - var request = new Request(this, 'server_subscribe'); - - request. - on('success', function (message) { - self.ledger_closed = message.ledger_closed; - self.ledger_current_index = message.ledger_current_index; + (new Request(this, 'server_subscribe')) + .on('success', function (message) { self.stand_alone = !!message.stand_alone; - self.emit('subscribed'); + if (message.ledger_closed && message.ledger_current_index) { + self.ledger_closed = message.ledger_closed; + self.ledger_current_index = message.ledger_current_index; - self.emit('ledger_closed', self.ledger_closed, self.ledger_current_index-1); + self.emit('ledger_closed', self.ledger_closed, self.ledger_current_index-1); + } + + self.emit('subscribed'); }) .request(); @@ -519,12 +645,13 @@ 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. +// A good way to be notified of the result of this is: +// remote.once('ledger_closed', function (ledger_closed, ledger_closed_index) { ... } ); Remote.prototype.ledger_accept = function () { if (this.stand_alone || undefined === this.stand_alone) { - var request = new Request(this, 'ledger_accept'); - - request.request(); + (new Request(this, 'ledger_accept')) + .request(); } else { this.emit('error', { @@ -535,15 +662,27 @@ Remote.prototype.ledger_accept = function () { return this; }; +// Return a request to refresh the account balance. +Remote.prototype.request_account_balance = function (account, current) { + return (this.request_ledger_entry('account_root')) + .account_root(account) + .ledger_choose(current) + .on('success', function (message) { + // If the caller also waits for 'success', they might run before this. + request.emit('account_balance', message.node.Balance); + }); +}; + // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { - var account_info = this.accounts[account]; + var account = UInt160.json_rewrite(account); + var account_info = this.accounts[account]; var seq; if (account_info && account_info.seq) { - var seq = account_info.seq; + seq = account_info.seq; if (advance) account_info.seq += 1; } @@ -551,36 +690,80 @@ Remote.prototype.account_seq = function (account, advance) { return seq; } +Remote.prototype.set_account_seq = function (account, seq) { + var account = UInt160.json_rewrite(account); + + if (!this.accounts[account]) this.accounts[account] = {}; + + this.accounts[account].seq = seq; +} + // Return a request to refresh accounts[account].seq. -Remote.prototype.account_cache = function (account) { +Remote.prototype.account_seq_cache = function (account, current) { var self = this; - var request = this.request_ledger_entry('account_root') + var request = this.request_ledger_entry('account_root'); - // Only care about a closed ledger. - // YYY Might be more advanced and work with a changing current ledger. - request.ledger(this.ledger_closed); // XXX Requires active server_subscribe - request.account_root(account); + return request + .account_root(account) + .ledger_choose(current) + .on('success', function (message) { + var seq = message.node.Sequence; + + if (!self.accounts[account]) self.accounts[account] = {}; - request.on('success', function (message) { - var seq = message.node.Sequence; - - if (!self.accounts[account]) - self.accounts[account] = {}; + self.accounts[account].seq = seq; - self.accounts[account].seq = seq; - - // If the caller also waits for 'success', they might run before this. - request.emit('success_account_cache'); - }); - - return request; + // If the caller also waits for 'success', they might run before this. + request.emit('success_account_seq_cache'); + }); }; // Mark an account's root node as dirty. Remote.prototype.dirty_account_root = function (account) { + var account = UInt160.json_rewrite(account); + delete this.ledgers.current.account_root[account]; }; +// Return a request to get a ripple balance. +// +// --> account: String +// --> issuer: String +// --> currency: String +// --> current: bool : true = current ledger +// +// If does not exist: emit('error', 'error' : 'remoteError', 'remote' : { 'error' : 'entryNotFound' }) +Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { + var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. + + return request + .ripple_state(account, issuer, currency) + .ledger_choose(current) + .on('success', function (message) { + var node = message.node; + + var lowLimit = Amount.from_json(node.LowLimit); + var highLimit = Amount.from_json(node.HighLimit); + // The amount the low account holds of issuer. + var balance = Amount.from_json(node.Balance); + // accountHigh implies: for account: balance is negated, highLimit is the limit set by account. + var accountHigh = UInt160.from_json(account).equals(highLimit.issuer); + // The limit set by account. + var accountLimit = (accountHigh ? highLimit : lowLimit).parse_issuer(account); + // The limit set by issuer. + var issuerLimit = (accountHigh ? lowLimit : highLimit).parse_issuer(issuer); + var accountBalance = (accountHigh ? balance.negate() : balance).parse_issuer(account); + var issuerBalance = (accountHigh ? balance : balance.negate()).parse_issuer(issuer); + + request.emit('ripple_state', { + 'issuer_balance' : issuerBalance, // Balance with dst as issuer. + 'account_balance' : accountBalance, // Balance with account as issuer. + 'issuer_limit' : issuerLimit, // Limit set by issuer with src as issuer. + 'account_limit' : accountLimit // Limit set by account with dst as issuer. + }); + }); +} + Remote.prototype.transaction = function () { return new Transaction(this); }; @@ -588,10 +771,17 @@ Remote.prototype.transaction = function () { // // Transactions // -// Transaction events: +// Construction: +// remote.transaction() // Build a transaction object. +// .offer_create(...) // Set major parameters. +// .set_flags() // Set optional parameters. +// .on() // Register for events. +// .submit(); // Send to network. +// +// Events: // 'success' : Transaction submitted without error. // 'error' : Error submitting transaction. -// 'proposed: Advisory proposed status transaction. +// 'proposed' : Advisory proposed status transaction. // - A client should expect 0 to multiple results. // - Might not get back. The remote might just forward the transaction. // - A success could be reverted in final. @@ -599,21 +789,23 @@ Remote.prototype.transaction = function () { // - malformed error: local server thought it was malformed. // - The client should only trust this when talking to a trusted server. // 'final' : Final status of transaction. -// - Only expect a final from honest clients after a tesSUCCESS or ter*. +// - Only expect a final from dishonest servers after a tesSUCCESS or ter*. +// 'lost' : Gave up looking for on ledger_closed. +// 'pending' : Transaction was not found on ledger_closed. // 'state' : Follow the state of a transaction. -// 'clientSubmitted' - Sent to remote -// |- 'remoteError' - Remote rejected transaction. -// \- 'clientProposed' - Remote provisionally accepted transaction. -// |- 'clientMissing' - Transaction has not appeared in ledger as expected. -// | |- 'clientLost' - No longer monitoring missing transaction. +// 'client_submitted' - Sent to remote +// |- 'remoteError' - Remote rejected transaction. +// \- 'client_proposed' - Remote provisionally accepted transaction. +// |- 'client_missing' - Transaction has not appeared in ledger as expected. +// | |\- 'client_lost' - No longer monitoring missing transaction. // |/ -// |- 'tesSUCCESS' - Transaction in ledger as expected. -// |- 'ter...' - Transaction failed. -// |- 'tep...' - Transaction partially succeeded. +// |- 'tesSUCCESS' - Transaction in ledger as expected. +// |- 'ter...' - Transaction failed. +// \- 'tep...' - Transaction partially succeeded. // // Notes: -// - All transactions including locally errors and malformed errors may be -// forwarded. +// - All transactions including those with local and malformed errors may be +// forwarded anyway. // - A malicous server can: // - give any proposed result. // - it may declare something correct as incorrect or something correct as incorrect. @@ -630,8 +822,6 @@ var SUBMIT_LOST = 8; // Give up tracking. var Transaction = function (remote) { var self = this; - this.prototype = EventEmitter; // XXX Node specific. - this.remote = remote; this.secret = undefined; this.transaction = { // Transaction data. @@ -645,9 +835,10 @@ var Transaction = function (remote) { if (message.engine_result) { self.hash = message.transaction.hash; - self.set_state('clientProposed'); + self.set_state('client_proposed'); self.emit('proposed', { + 'transaction' : message.transaction, 'result' : message.engine_result, 'result_code' : message.engine_result_code, 'result_message' : message.engine_result_message, @@ -725,17 +916,17 @@ Transaction.prototype.submit = function () { if (undefined === transaction.Fee) { if ('Payment' === transaction.TransactionType - && transaction.Flags & exports.flags.Payment.CreateAccount) { + && transaction.Flags & Remote.flags.Payment.CreateAccount) { - transaction.Fee = fees.account_create.to_json(); + transaction.Fee = Remote.fees.account_create.to_json(); } else { - transaction.Fee = fees['default'].to_json(); + transaction.Fee = Remote.fees['default'].to_json(); } } - if (this.listeners('final').length) { - // There are listeners for 'final' arrange to emit it. + if (this.listeners('final').length || this.listeners('lost').length || this.listeners('pending').length) { + // There are listeners for 'final', 'lost', or 'pending' arrange to emit them. this.submit_index = this.remote.ledger_current_index; @@ -743,24 +934,26 @@ Transaction.prototype.submit = function () { var stop = false; // XXX make sure self.hash is available. - self.remote.request_transaction_entry(self.hash, ledger_closed) + self.remote.request_transaction_entry(self.hash) + .ledger_closed(ledger_closed) .on('success', function (message) { - // XXX Fake results for now. - if (!message.metadata.result) - message.metadata.result = 'tesSUCCESS'; - - self.set_state(message.metadata.result); // XXX Untested. + self.set_state(message.metadata.TransactionResult); self.emit('final', message); }) .on('error', function (message) { if ('remoteError' === message.error && 'transactionNotFound' === message.remote.error) { if (self.submit_index + SUBMIT_LOST < ledger_closed_index) { - self.set_state('clientLost'); // Gave up. + self.set_state('client_lost'); // Gave up. + self.emit('lost'); stop = true; } else if (self.submit_index + SUBMIT_MISSING < ledger_closed_index) { - self.set_state('clientMissing'); // We don't know what happened to transaction, still might find. + self.set_state('client_missing'); // We don't know what happened to transaction, still might find. + self.emit('pending'); + } + else { + self.emit('pending'); } } // XXX Could log other unexpectedness. @@ -776,7 +969,7 @@ Transaction.prototype.submit = function () { this.remote.on('ledger_closed', on_ledger_closed); } - this.set_state('clientSubmitted'); + this.set_state('client_submitted'); this.remote.submit(this); @@ -801,9 +994,9 @@ Transaction.prototype.send_max = function (send_max) { // Add flags to a transaction. // --> flags: undefined, _flag_, or [ _flags_ ] -Transaction.prototype.flags = function (flags) { +Transaction.prototype.set_flags = function (flags) { if (flags) { - var transaction_flags = exports.flags[this.transaction.TransactionType]; + var transaction_flags = Remote.flags[this.transaction.TransactionType]; if (undefined == this.transaction.Flags) // We plan to not define this field on new Transaction. this.transaction.Flags = 0; @@ -822,8 +1015,8 @@ Transaction.prototype.flags = function (flags) { } } - if (this.transaction.Flags & exports.flags.Payment.CreateAccount) - this.transaction.Fee = fees.account_create.to_json(); + if (this.transaction.Flags & Remote.flags.Payment.CreateAccount) + this.transaction.Fee = Remote.fees.account_create.to_json(); } return this; @@ -832,80 +1025,127 @@ Transaction.prototype.flags = function (flags) { // // Transactions // -// Construction: -// remote.transaction() // Build a transaction object. -// .offer_create(...) // Set major parameters. -// .flags() // Set optional parameters. -// .on() // Register for events. -// .submit(); // Send to network. -// -// Events: -// 'success' // Transaction was successfully submitted: hash, proposed TER -// 'error' // Error submitting transaction. -// 'closed' // Result from closed ledger: TER -// -// Allow config account defaults to be used. -Transaction.prototype.account_default = function (account) { - return this.remote.config.accounts[account] ? this.remote.config.accounts[account].account : account; -}; - -Transaction.prototype.account_secret = function (account) { +Transaction.prototype._account_secret = function (account) { // Fill in secret from config, if needed. return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined; }; -Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { - this.secret = this.account_secret(src); - this.transaction.TransactionType = 'OfferCreate'; - this.transaction.Account = this.account_default(src); - this.transaction.Fee = fees.offer.to_json(); - this.transaction.TakerPays = taker_pays.to_json(); - this.transaction.TakerGets = taker_gets.to_json(); - - if (expiration) - this.transaction.Expiration = expiration; +// .wallet_locator() +// .message_key() +// .domain() +// .transfer_rate() +// .publish() +Transaction.prototype.account_set = function (src) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'AccountSet'; 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; + + 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); + + 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); + + if (expiration) + this.transaction.Expiration = Date === expiration.constructor + ? expiration.getTime() + : Number(expiration); + + return this; +}; + +Transaction.prototype.password_fund = function (src, dst) { + this.secret = this._account_secret(src); + this.transaction.TransactionType = 'PasswordFund'; + this.transaction.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; + + return this; +} + // Construct a 'payment' transaction. // // When a transaction is submitted: // - If the connection is reliable and the server is not merely forwarding and is not malicious, +// --> src : UInt160 or String +// --> dst : UInt160 or String +// --> deliver_amount : Amount or String. Transaction.prototype.payment = function (src, dst, deliver_amount) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'Payment'; - this.transaction.Account = this.account_default(src); - this.transaction.Amount = deliver_amount.to_json(); - this.transaction.Destination = this.account_default(dst); + this.transaction.Account = UInt160.json_rewrite(src); + this.transaction.Amount = Amount.json_rewrite(deliver_amount); + this.transaction.Destination = UInt160.json_rewrite(dst); return this; } -Remote.prototype.ripple_line_set = function (src, limit, quaility_in, quality_out) { - this.secret = this.account_secret(src); +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 = this.account_default(src); + this.transaction.Account = UInt160.json_rewrite(src); // Allow limit of 0 through. if (undefined !== limit) - this.transaction.LimitAmount = limit.to_json(); + this.transaction.LimitAmount = Amount.json_rewrite(limit); - if (quaility_in) - this.transaction.QualityIn = quaility_in; + if (quality_in) + this.transaction.QualityIn = quality_in; - if (quaility_out) - this.transaction.QualityOut = quaility_out; + if (quality_out) + this.transaction.QualityOut = quality_out; // XXX Throw an error if nothing is set. return this; }; +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; + + return this; +}; + exports.Remote = Remote; -exports.remoteConfig = remoteConfig; -exports.fees = fees; -exports.flags = flags; // vim:sw=2:sts=2:ts=8 diff --git a/js/utils.js b/js/utils.js index 37f78736b..288445993 100644 --- a/js/utils.js +++ b/js/utils.js @@ -44,6 +44,16 @@ var trace = function(comment, func) { }; }; +var arraySet = function (count, value) { + var a = new Array(count); + var i; + + for (i = 0; i != count; i += 1) + a[i] = value; + + return a; +}; + var hexToString = function (h) { var a = []; var i = 0; @@ -68,9 +78,21 @@ var stringToHex = function (s) { }).join(""); }; +var stringToArray = function (s) { + var a = new Array(s.length); + var i; + + for (i = 0; i != a.length; i += 1) + a[i] = s.charCodeAt(i); + + return a; +}; + exports.mapOr = mapOr; exports.trace = trace; +exports.arraySet = arraySet; exports.hexToString = hexToString; +exports.stringToArray = stringToArray; exports.stringToHex = stringToHex; // vim:sw=2:sts=2:ts=8 diff --git a/json/json_value.cpp b/json/json_value.cpp index 41d98787f..b1b1a5221 100644 --- a/json/json_value.cpp +++ b/json/json_value.cpp @@ -734,6 +734,7 @@ Value::asInt() const case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: + return boost::lexical_cast(value_.string_); case arrayValue: case objectValue: JSON_ASSERT_MESSAGE( false, "Type is not convertible to int" ); @@ -761,6 +762,7 @@ Value::asUInt() const case booleanValue: return value_.bool_ ? 1 : 0; case stringValue: + return boost::lexical_cast(value_.string_); case arrayValue: case objectValue: JSON_ASSERT_MESSAGE( false, "Type is not convertible to uint" ); diff --git a/newcoin.vcxproj b/newcoin.vcxproj index 67586127c..273a5a419 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -108,6 +108,7 @@ + @@ -134,7 +135,6 @@ - @@ -142,6 +142,7 @@ + @@ -236,6 +237,7 @@ + diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 7941c68ec..a404ec7ba 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -120,9 +120,6 @@ Source Files - - Source Files - Source Files @@ -303,6 +300,12 @@ Source Files + + Source Files + + + Source Files + @@ -557,6 +560,9 @@ Header Files + + Header Files + diff --git a/src/AccountState.cpp b/src/AccountState.cpp index 9aee8f869..beaa5cbcc 100644 --- a/src/AccountState.cpp +++ b/src/AccountState.cpp @@ -11,7 +11,7 @@ #include "Serializer.h" #include "Log.h" -AccountState::AccountState(const NewcoinAddress& naAccountID) : mAccountID(naAccountID), mValid(false) +AccountState::AccountState(const RippleAddress& naAccountID) : mAccountID(naAccountID), mValid(false) { if (!naAccountID.isValid()) return; @@ -22,7 +22,7 @@ AccountState::AccountState(const NewcoinAddress& naAccountID) : mAccountID(naAcc mValid = true; } -AccountState::AccountState(SLE::ref ledgerEntry, const NewcoinAddress& naAccountID) : +AccountState::AccountState(SLE::ref ledgerEntry, const RippleAddress& naAccountID) : mAccountID(naAccountID), mLedgerEntry(ledgerEntry), mValid(false) { if (!mLedgerEntry) diff --git a/src/AccountState.h b/src/AccountState.h index 728d5ae96..f2587ac50 100644 --- a/src/AccountState.h +++ b/src/AccountState.h @@ -12,7 +12,7 @@ #include "../json/value.h" #include "types.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "SerializedLedger.h" class AccountState @@ -21,22 +21,22 @@ public: typedef boost::shared_ptr pointer; private: - NewcoinAddress mAccountID; - NewcoinAddress mAuthorizedKey; + RippleAddress mAccountID; + RippleAddress mAuthorizedKey; SerializedLedgerEntry::pointer mLedgerEntry; bool mValid; public: - AccountState(const NewcoinAddress& naAccountID); // For new accounts - AccountState(SLE::ref ledgerEntry,const NewcoinAddress& naAccountI); // For accounts in a ledger + AccountState(const RippleAddress& naAccountID); // For new accounts + AccountState(SLE::ref ledgerEntry,const RippleAddress& naAccountI); // For accounts in a ledger bool bHaveAuthorizedKey() { return mLedgerEntry->isFieldPresent(sfAuthorizedKey); } - NewcoinAddress getAuthorizedKey() + RippleAddress getAuthorizedKey() { return mLedgerEntry->getFieldAccount(sfAuthorizedKey); } diff --git a/src/Amount.cpp b/src/Amount.cpp index 7b9252e8f..bfcd5e27a 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -12,6 +12,8 @@ #include "SerializedTypes.h" #include "utils.h" +SETUP_LOG(); + uint64 STAmount::uRateOne = STAmount::getRate(STAmount(1), STAmount(1)); // --> sCurrency: "", "XNS", or three letter ISO code. @@ -63,15 +65,21 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) if (v.isObject()) { - value = v["value"]; - currency = v["currency"]; - issuer = v["issuer"]; + cLog(lsTRACE) + << boost::str(boost::format("value='%s', currency='%s', issuer='%s'") + % v["value"].asString() + % v["currency"].asString() + % v["issuer"].asString()); + + value = v["value"]; + currency = v["currency"]; + issuer = v["issuer"]; } else if (v.isArray()) { - value = v.get(Json::UInt(0), 0); - currency = v.get(Json::UInt(1), Json::nullValue); - issuer = v.get(Json::UInt(2), Json::nullValue); + value = v.get(Json::UInt(0), 0); + currency = v.get(Json::UInt(1), Json::nullValue); + issuer = v.get(Json::UInt(2), Json::nullValue); } else if (v.isString()) { @@ -93,6 +101,31 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) mIsNative = !currency.isString() || currency.asString().empty() || (currency.asString() == SYSTEM_CURRENCY_CODE); + if (!mIsNative) { + if (!currencyFromString(mCurrency, currency.asString())) + throw std::runtime_error("invalid currency"); + + if (!issuer.isString()) + throw std::runtime_error("invalid issuer"); + + if (issuer.size() == (160/4)) + { + mIssuer.SetHex(issuer.asString()); + } + else + { + RippleAddress is; + + if(!is.setAccountID(issuer.asString())) + throw std::runtime_error("invalid issuer"); + + mIssuer = is.getAccountID(); + } + + if (mIssuer.isZero()) + throw std::runtime_error("invalid issuer"); + } + if (value.isInt()) { if (value.asInt() >= 0) @@ -102,9 +135,15 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) mValue = -value.asInt(); mIsNegative = true; } + + canonicalize(); } else if (value.isUInt()) + { mValue = v.asUInt(); + + canonicalize(); + } else if (value.isString()) { if (mIsNative) @@ -117,35 +156,16 @@ STAmount::STAmount(SField::ref n, const Json::Value& v) mValue = -val; mIsNegative = true; } + + canonicalize(); } else + { setValue(value.asString()); + } } else throw std::runtime_error("invalid amount type"); - - if (mIsNative) - return; - - if (!currencyFromString(mCurrency, currency.asString())) - throw std::runtime_error("invalid currency"); - - if (!issuer.isString()) - throw std::runtime_error("invalid issuer"); - - if (issuer.size() == (160/4)) - mIssuer.SetHex(issuer.asString()); - else - { - NewcoinAddress is; - if(!is.setAccountID(issuer.asString())) - throw std::runtime_error("invalid issuer"); - mIssuer = is.getAccountID(); - } - if (mIssuer.isZero()) - throw std::runtime_error("invalid issuer"); - - canonicalize(); } std::string STAmount::createHumanCurrency(const uint160& uCurrency) @@ -196,7 +216,7 @@ std::string STAmount::createHumanCurrency(const uint160& uCurrency) // Assumes trusted input. bool STAmount::setValue(const std::string& sAmount) -{ // Note: mIsNative must be set already! +{ // Note: mIsNative and mCurrency must be set already! uint64 uValue; int iOffset; size_t uDecimal = sAmount.find_first_of(mIsNative ? "^" : "."); @@ -348,7 +368,7 @@ bool STAmount::setFullValue(const std::string& sAmount, const std::string& sCurr // // Figure out the issuer. // - NewcoinAddress naIssuerID; + RippleAddress naIssuerID; // Issuer must be "" or a valid account string. if (!naIssuerID.setAccountID(sIssuer)) @@ -1119,7 +1139,7 @@ std::string STAmount::getFullText() const return str(boost::format("%s/%s/%s") % getText() % getHumanCurrency() - % NewcoinAddress::createHumanAccountID(mIssuer)); + % RippleAddress::createHumanAccountID(mIssuer)); } } @@ -1135,7 +1155,7 @@ std::string STAmount::getExtendedText() const return str(boost::format("%s/%s/%s %dE%d" ) % getText() % getHumanCurrency() - % NewcoinAddress::createHumanAccountID(mIssuer) + % RippleAddress::createHumanAccountID(mIssuer) % getMantissa() % getExponent()); } @@ -1152,7 +1172,7 @@ Json::Value STAmount::getJson(int) const elem["value"] = getText(); elem["currency"] = getHumanCurrency(); - elem["issuer"] = NewcoinAddress::createHumanAccountID(mIssuer); + elem["issuer"] = RippleAddress::createHumanAccountID(mIssuer); } else { diff --git a/src/Application.cpp b/src/Application.cpp index ec56e6b91..b898cbdcc 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -70,7 +70,11 @@ void Application::run() { assert(mTxnDB == NULL); if (!theConfig.DEBUG_LOGFILE.empty()) + { // Let DEBUG messages go to the file but only WARNING or higher to regular output (unless verbose) Log::setLogFile(theConfig.DEBUG_LOGFILE); + if (Log::getMinSeverity() > lsDEBUG) + LogPartition::setSeverity(lsDEBUG); + } boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService)); auxThread.detach(); @@ -109,6 +113,13 @@ void Application::run() else startNewLedger(); + if (theConfig.FULL_HISTORY && (theConfig.START_UP != Config::LOAD)) + { + Ledger::pointer ledger = Ledger::getLastFullLedger(); + if (ledger) + mMasterLedger.setLedgerRangePresent(0, ledger->getLedgerSeq()); + } + // // Begin validation and ip maintenance. // - Wallet maintains local information: including identity and network connection persistence information. @@ -182,9 +193,9 @@ Application::~Application() void Application::startNewLedger() { // New stuff. - NewcoinAddress rootSeedMaster = NewcoinAddress::createSeedGeneric("masterpassphrase"); - NewcoinAddress rootGeneratorMaster = NewcoinAddress::createGeneratorPublic(rootSeedMaster); - NewcoinAddress rootAddress = NewcoinAddress::createAccountPublic(rootGeneratorMaster, 0); + RippleAddress rootSeedMaster = RippleAddress::createSeedGeneric("masterpassphrase"); + RippleAddress rootGeneratorMaster = RippleAddress::createGeneratorPublic(rootSeedMaster); + RippleAddress rootAddress = RippleAddress::createAccountPublic(rootGeneratorMaster, 0); // Print enough information to be able to claim root account. cLog(lsINFO) << "Root master seed: " << rootSeedMaster.humanSeed(); @@ -201,7 +212,7 @@ void Application::startNewLedger() Ledger::pointer secondLedger = boost::make_shared(true, boost::ref(*firstLedger)); secondLedger->setClosed(); secondLedger->setAccepted(); - mMasterLedger.pushLedger(secondLedger, boost::make_shared(true, boost::ref(*secondLedger))); + mMasterLedger.pushLedger(secondLedger, boost::make_shared(true, boost::ref(*secondLedger)), false); assert(!!secondLedger->getAccountState(rootAddress)); mNetOps.setLastCloseTime(secondLedger->getCloseTimeNC()); } @@ -211,7 +222,7 @@ void Application::loadOldLedger() { try { - Ledger::pointer lastLedger = Ledger::getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); + Ledger::pointer lastLedger = Ledger::getLastFullLedger(); if (!lastLedger) { @@ -240,6 +251,7 @@ void Application::loadOldLedger() cLog(lsFATAL) << "Ledger is not sane."; exit(-1); } + mMasterLedger.setLedgerRangePresent(0, lastLedger->getLedgerSeq()); Ledger::pointer openLedger = boost::make_shared(false, boost::ref(*lastLedger)); mMasterLedger.switchLedgers(lastLedger, openLedger); diff --git a/src/CanonicalTXSet.cpp b/src/CanonicalTXSet.cpp index e3af1e487..8b536d354 100644 --- a/src/CanonicalTXSet.cpp +++ b/src/CanonicalTXSet.cpp @@ -37,7 +37,7 @@ bool CanonicalTXKey::operator>=(const CanonicalTXKey& key)const return mTXid >= key.mTXid; } -void CanonicalTXSet::push_back(const SerializedTransaction::pointer& txn) +void CanonicalTXSet::push_back(SerializedTransaction::ref txn) { uint256 effectiveAccount = mSetHash; effectiveAccount ^= txn->getSourceAccount().getAccountID().to256(); diff --git a/src/CanonicalTXSet.h b/src/CanonicalTXSet.h index 3315625a3..c75fb6b1b 100644 --- a/src/CanonicalTXSet.h +++ b/src/CanonicalTXSet.h @@ -38,7 +38,7 @@ protected: public: CanonicalTXSet(const uint256& lclHash) : mSetHash(lclHash) { ; } - void push_back(const SerializedTransaction::pointer& txn); + void push_back(SerializedTransaction::ref txn); iterator erase(const iterator& it); iterator begin() { return mMap.begin(); } diff --git a/src/Config.cpp b/src/Config.cpp index 1f75b1e5b..c0ac11fa3 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -14,6 +14,7 @@ #define SECTION_FEE_NICKNAME_CREATE "fee_nickname_create" #define SECTION_FEE_OFFER "fee_offer" #define SECTION_FEE_OPERATION "fee_operation" +#define SECTION_FULL_HISTORY "full_history" #define SECTION_IPS "ips" #define SECTION_NETWORK_QUORUM "network_quorum" #define SECTION_PEER_CONNECT_LOW_WATER "peer_connect_low_water" @@ -150,6 +151,8 @@ void Config::setup(const std::string& strConf) FEE_DEFAULT = DEFAULT_FEE_DEFAULT; FEE_CONTRACT_OPERATION = DEFAULT_FEE_OPERATION; + FULL_HISTORY = false; + ACCOUNT_PROBE_MAX = 10; VALIDATORS_SITE = DEFAULT_VALIDATORS_SITE; @@ -231,7 +234,14 @@ void Config::load() WEBSOCKET_PORT = boost::lexical_cast(strTemp); if (sectionSingleB(secConfig, SECTION_VALIDATION_SEED, strTemp)) + { VALIDATION_SEED.setSeedGeneric(strTemp); + if (VALIDATION_SEED.isValid()) + { + VALIDATION_PUB = RippleAddress::createNodePublic(VALIDATION_SEED); + VALIDATION_PRIV = RippleAddress::createNodePrivate(VALIDATION_SEED); + } + } (void) sectionSingleB(secConfig, SECTION_PEER_SSL_CIPHER_LIST, PEER_SSL_CIPHER_LIST); @@ -266,6 +276,9 @@ void Config::load() if (sectionSingleB(secConfig, SECTION_FEE_OPERATION, strTemp)) FEE_CONTRACT_OPERATION = boost::lexical_cast(strTemp); + if (sectionSingleB(secConfig, SECTION_FULL_HISTORY, strTemp)) + FULL_HISTORY = boost::lexical_cast(strTemp); + if (sectionSingleB(secConfig, SECTION_ACCOUNT_PROBE_MAX, strTemp)) ACCOUNT_PROBE_MAX = boost::lexical_cast(strTemp); diff --git a/src/Config.h b/src/Config.h index c7270e1d7..2bc0662e5 100644 --- a/src/Config.h +++ b/src/Config.h @@ -2,7 +2,7 @@ #define __CONFIG__ #include "types.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "ParseSection.h" #include "SerializedTypes.h" @@ -93,7 +93,7 @@ public: bool RPC_ALLOW_REMOTE; // Validation - NewcoinAddress VALIDATION_SEED; + RippleAddress VALIDATION_SEED, VALIDATION_PUB, VALIDATION_PRIV; // Fees uint64 FEE_DEFAULT; // Default fee. @@ -102,6 +102,9 @@ public: uint64 FEE_OFFER; // Rate per day. int FEE_CONTRACT_OPERATION; // fee for each contract operation + // Node storage configuration + bool FULL_HISTORY; + // Client behavior int ACCOUNT_PROBE_MAX; // How far to scan for accounts. diff --git a/src/ConnectionPool.cpp b/src/ConnectionPool.cpp index 6285d4555..c9e93f3f7 100644 --- a/src/ConnectionPool.cpp +++ b/src/ConnectionPool.cpp @@ -14,6 +14,7 @@ #include "utils.h" #include "Log.h" +SETUP_LOG(); // How often to enforce policies. #define POLICY_INTERVAL_SECONDS 5 @@ -164,7 +165,7 @@ void ConnectionPool::policyLowWater() if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER) { // Above low water mark, don't need more connections. - Log(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER; + cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER; nothing(); } @@ -178,7 +179,7 @@ void ConnectionPool::policyLowWater() else if (!peerAvailable(strIp, iPort)) { // No more connections available to start. - Log(lsTRACE) << "Pool: Low water: no peers available."; + cLog(lsTRACE) << "Pool: Low water: no peers available."; // XXX Might ask peers for more ips. nothing(); @@ -186,10 +187,12 @@ void ConnectionPool::policyLowWater() else { // Try to start connection. - Log(lsTRACE) << "Pool: Low water: start connection."; + cLog(lsTRACE) << "Pool: Low water: start connection."; if (!peerConnect(strIp, iPort)) - Log(lsINFO) << "Pool: Low water: already connected."; + { + cLog(lsINFO) << "Pool: Low water: already connected."; + } // Check if we need more. policyLowWater(); @@ -303,11 +306,11 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort) if (ppResult) { - //Log(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; } else { - //Log(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort; } return ppResult; @@ -346,7 +349,7 @@ std::vector ConnectionPool::getPeerVector() // Now know peer's node public key. Determine if we want to stay connected. // <-- bNew: false = redundant -bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, +bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort) { bool bNew = false; @@ -355,17 +358,17 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, if (naPeer == theApp->getWallet().getNodePublic()) { - Log(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; + cLog(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; } else { boost::mutex::scoped_lock sl(mPeerLock); - boost::unordered_map::iterator itCm = mConnectedMap.find(naPeer); + boost::unordered_map::iterator itCm = mConnectedMap.find(naPeer); if (itCm == mConnectedMap.end()) { // New connection. - //Log(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; mConnectedMap[naPeer] = peer; bNew = true; @@ -378,7 +381,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, if (itCm->second->getIP().empty()) { // Old peer did not know it's IP. - //Log(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort; itCm->second->setIpPort(strIP, iPort); @@ -388,14 +391,14 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, else { // Old peer knew its IP. Do nothing. - //Log(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; nothing(); } } else { - //Log(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; nothing(); } @@ -405,11 +408,11 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, } // We maintain a map of public key to peer for connected and verified peers. Maintain it. -void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPeer) +void ConnectionPool::peerDisconnected(Peer::ref peer, const RippleAddress& naPeer) { if (naPeer.isValid()) { - boost::unordered_map::iterator itCm; + boost::unordered_map::iterator itCm; boost::mutex::scoped_lock sl(mPeerLock); @@ -418,12 +421,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe if (itCm == mConnectedMap.end()) { // Did not find it. Not already connecting or connected. - Log(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent."; + cLog(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent."; // XXX Maybe bad error, considering we have racing connections, may not so bad. } else if (itCm->second != peer) { - Log(lsWARNING) << "Pool: disconected: non canonical entry"; + cLog(lsWARNING) << "Pool: disconected: non canonical entry"; nothing(); } @@ -432,12 +435,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe // Found it. Delete it. mConnectedMap.erase(itCm); - //Log(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort(); + //cLog(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort(); } } else { - //Log(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort(); + //cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort(); } } @@ -463,7 +466,7 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort) boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time(); boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds(iInterval); - //Log(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;") @@ -479,13 +482,13 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort) // boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time(); // boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext")); - //Log(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); } } else { - //Log(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort; + //cLog(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort; } return bScanDirty; @@ -500,7 +503,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP // If the connection was our scan, we are no longer scanning. if (mScanning && mScanning == peer) { - //Log(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort; mScanning = Peer::pointer(); // No longer scanning. bScanRefresh = true; // Look for more to scan. @@ -517,13 +520,13 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP if (itIp == mIpMap.end()) { // Did not find it. Not already connecting or connected. - Log(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + cLog(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; // XXX Internal error. } else if (mIpMap[ipPeer] == peer) { // We were the identified connection. - //Log(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; // Delete our entry. mIpMap.erase(itIp); @@ -533,7 +536,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP else { // Found it. But, we were redundant. - //Log(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; } } @@ -557,7 +560,7 @@ void ConnectionPool::peerVerified(Peer::ref peer) std::string strIpPort = str(boost::format("%s %d") % strIp % iPort); - //Log(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort); + //cLog(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort); if (peer->getNodePublic() == theApp->getWallet().getNodePublic()) { @@ -604,7 +607,7 @@ void ConnectionPool::scanRefresh() if (mScanning) { // Currently scanning, will scan again after completion. - Log(lsTRACE) << "Pool: Scan: already scanning"; + cLog(lsTRACE) << "Pool: Scan: already scanning"; nothing(); } @@ -641,7 +644,7 @@ void ConnectionPool::scanRefresh() if (tpNow.is_not_a_date_time()) { - //Log(lsINFO) << "Pool: Scan: stop."; + //cLog(lsINFO) << "Pool: Scan: stop."; (void) mScanTimer.cancel(); } @@ -656,7 +659,7 @@ void ConnectionPool::scanRefresh() tpNext = tpNow + boost::posix_time::seconds(iInterval); - //Log(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); iInterval *= 2; @@ -681,7 +684,7 @@ void ConnectionPool::scanRefresh() } else { - //Log(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)") // % strIpPort % tpNext % (tpNext-tpNow).total_seconds()); mScanTimer.expires_at(tpNext); diff --git a/src/ConnectionPool.h b/src/ConnectionPool.h index 4db023985..569a1e458 100644 --- a/src/ConnectionPool.h +++ b/src/ConnectionPool.h @@ -9,14 +9,14 @@ #include "types.h" // -// Access to the Newcoin network. +// Access to the Ripple network. // class ConnectionPool { private: boost::mutex mPeerLock; - 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. @@ -28,7 +28,7 @@ private: // Non-thin peers which we are connected to. // Peers we have the public key for. - boost::unordered_map mConnectedMap; + boost::unordered_map mConnectedMap; boost::asio::ssl::context mCtx; @@ -72,10 +72,10 @@ public: // We know peers node public key. // <-- bool: false=reject - bool peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, const std::string& strIP, int iPort); + bool peerConnected(Peer::ref peer, const RippleAddress& naPeer, const std::string& strIP, int iPort); // No longer connected. - void peerDisconnected(Peer::ref peer, const NewcoinAddress& naPeer); + void peerDisconnected(Peer::ref peer, const RippleAddress& naPeer); // As client accepted. void peerVerified(Peer::ref peer); diff --git a/src/DeterministicKeys.cpp b/src/DeterministicKeys.cpp index 2c5241a58..1f1e1a0c9 100644 --- a/src/DeterministicKeys.cpp +++ b/src/DeterministicKeys.cpp @@ -9,7 +9,10 @@ // Functions to add CKey support for deterministic EC keys +#include + #include "Serializer.h" +#include "Log.h" // <-- seed uint128 CKey::PassPhraseToKey(const std::string& passPhrase) @@ -155,7 +158,7 @@ EC_KEY* CKey::GenerateRootPubKey(BIGNUM* pubGenerator) } // --> public generator -static BIGNUM* makeHash(const NewcoinAddress& pubGen, int seq, BIGNUM* order) +static BIGNUM* makeHash(const RippleAddress& pubGen, int seq, BIGNUM* order) { int subSeq=0; BIGNUM* ret=NULL; @@ -175,7 +178,7 @@ static BIGNUM* makeHash(const NewcoinAddress& pubGen, int seq, BIGNUM* order) } // --> public generator -EC_KEY* CKey::GeneratePublicDeterministicKey(const NewcoinAddress& pubGen, int seq) +EC_KEY* CKey::GeneratePublicDeterministicKey(const RippleAddress& pubGen, int seq) { // publicKey(n) = rootPublicKey EC_POINT_+ Hash(pubHash|seq)*point EC_KEY* rootKey = CKey::GenerateRootPubKey(pubGen.getGeneratorBN()); const EC_POINT* rootPubKey = EC_KEY_get0_public_key(rootKey); @@ -228,14 +231,14 @@ EC_KEY* CKey::GeneratePublicDeterministicKey(const NewcoinAddress& pubGen, int s return success ? pkey : NULL; } -EC_KEY* CKey::GeneratePrivateDeterministicKey(const NewcoinAddress& pubGen, const uint256& u, int seq) +EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const uint256& u, int seq) { CBigNum bn(u); return GeneratePrivateDeterministicKey(pubGen, static_cast(&bn), seq); } // --> root private key -EC_KEY* CKey::GeneratePrivateDeterministicKey(const NewcoinAddress& pubGen, const BIGNUM* rootPrivKey, int seq) +EC_KEY* CKey::GeneratePrivateDeterministicKey(const RippleAddress& pubGen, const BIGNUM* rootPrivKey, int seq) { // privateKey(n) = (rootPrivateKey + Hash(pubHash|seq)) % order BN_CTX* ctx=BN_CTX_new(); if(ctx==NULL) return NULL; @@ -304,4 +307,34 @@ EC_KEY* CKey::GeneratePrivateDeterministicKey(const NewcoinAddress& pubGen, cons return pkey; } +BOOST_AUTO_TEST_SUITE(DeterministicKeys_test) + +BOOST_AUTO_TEST_CASE(DeterminsticKeys_test1) +{ + Log(lsDEBUG) << "Beginning deterministic key test"; + + uint128 seed1, seed2; + seed1.SetHex("71ED064155FFADFA38782C5E0158CB26"); + seed2.SetHex("CF0C3BE4485961858C4198515AE5B965"); + CKey root1(seed1), root2(seed2); + + uint256 priv1, priv2; + root1.GetPrivateKeyU(priv1); + root2.GetPrivateKeyU(priv2); + + if (priv1.GetHex() != "7CFBA64F771E93E817E15039215430B53F7401C34931D111EAB3510B22DBB0D8") + BOOST_FAIL("Incorrect private key for generator"); + if (priv2.GetHex() != "98BC2EACB26EB021D1A6293C044D88BA2F0B6729A2772DEEBF2E21A263C1740B") + BOOST_FAIL("Incorrect private key for generator"); + + RippleAddress nSeed; + nSeed.setSeed(seed1); + if (nSeed.humanSeed() != "shHM53KPZ87Gwdqarm1bAmPeXg8Tn") + BOOST_FAIL("Incorrect human seed"); + if (nSeed.humanSeed1751() != "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH") + BOOST_FAIL("Incorrect 1751 seed"); +} + +BOOST_AUTO_TEST_SUITE_END(); + // vim:ts=4 diff --git a/src/FieldNames.cpp b/src/FieldNames.cpp index 2f84ec7ab..67695731d 100644 --- a/src/FieldNames.cpp +++ b/src/FieldNames.cpp @@ -26,6 +26,31 @@ SField sfIndex(STI_HASH256, 258, "index"); #undef FIELD #undef TYPE +static int initFields() +{ + sfHighQualityIn.setMeta(SFM_CHANGE); sfHighQualityOut.setMeta(SFM_CHANGE); + sfLowQualityIn.setMeta(SFM_CHANGE); sfLowQualityOut.setMeta(SFM_CHANGE); + + sfLowLimit.setMeta(SFM_ALWAYS); sfHighLimit.setMeta(SFM_ALWAYS); + sfTakerPays.setMeta(SFM_ALWAYS); sfTakerGets.setMeta(SFM_ALWAYS); + sfQualityIn.setMeta(SFM_ALWAYS); sfQualityOut.setMeta(SFM_ALWAYS); + + sfBalance.setMeta(SFM_ALWAYS); + + sfPublicKey.setMeta(SFM_CHANGE); sfMessageKey.setMeta(SFM_CHANGE); + sfSigningPubKey.setMeta(SFM_CHANGE); sfAuthorizedKey.setMeta(SFM_CHANGE); + sfSigningAccounts.setMeta(SFM_CHANGE); + + sfWalletLocator.setMeta(SFM_CHANGE); + sfNickname.setMeta(SFM_CHANGE); + sfAmount.setMeta(SFM_ALWAYS); + sfDomain.setMeta(SFM_CHANGE); + sfOwner.setMeta(SFM_ALWAYS); + + return 0; +} +static const int f = initFields(); + SField::SField(SerializedTypeID tid, int fv) : fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv) { // call with the map mutex diff --git a/src/FieldNames.h b/src/FieldNames.h index 3a09f67a4..cdb11ad6b 100644 --- a/src/FieldNames.h +++ b/src/FieldNames.h @@ -33,6 +33,14 @@ enum SOE_Flags SOE_OPTIONAL = 1, // optional }; +enum SF_Meta +{ + SFM_NEVER = 0, + SFM_CHANGE = 1, + SFM_DELETE = 2, + SFM_ALWAYS = 3 +}; + class SField { public: @@ -51,16 +59,17 @@ public: const SerializedTypeID fieldType; // STI_* const int fieldValue; // Code number for protocol std::string fieldName; + SF_Meta fieldMeta; SField(int fc, SerializedTypeID tid, int fv, const char* fn) : - fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn) + fieldCode(fc), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) { 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) + fieldCode(FIELD_CODE(tid, fv)), fieldType(tid), fieldValue(fv), fieldName(fn), fieldMeta(SFM_NEVER) { boost::mutex::scoped_lock sl(mapMutex); codeToField[fieldCode] = this; @@ -84,6 +93,11 @@ public: bool isBinary() const { return fieldValue < 256; } bool isDiscardable() const { return fieldValue > 256; } + SF_Meta getMeta() const { return fieldMeta; } + bool shouldMetaDel() const { return (fieldMeta == SFM_DELETE) || (fieldMeta == SFM_ALWAYS); } + bool shouldMetaMod() const { return (fieldMeta == SFM_CHANGE) || (fieldMeta == SFM_ALWAYS); } + void setMeta(SF_Meta m) { fieldMeta = m; } + bool operator==(const SField& f) const { return fieldCode == f.fieldCode; } bool operator!=(const SField& f) const { return fieldCode != f.fieldCode; } diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp new file mode 100644 index 000000000..c3f365e0e --- /dev/null +++ b/src/HTTPRequest.cpp @@ -0,0 +1,102 @@ +#include "HTTPRequest.h" + +#include +#include +#include + +#include "Log.h" +SETUP_LOG(); + +void HTTPRequest::reset() +{ + vHeaders.clear(); + sRequestBody.clear(); + sAuthorization.clear(); + iDataSize = 0; + bShouldClose = true; + eState = await_request; +} + +HTTPRequestAction HTTPRequest::requestDone(bool forceClose) +{ + if (forceClose || bShouldClose) + return haCLOSE_CONN; + reset(); + return haREAD_LINE; +} + +std::string HTTPRequest::getReplyHeaders(bool forceClose) +{ + if (forceClose || bShouldClose) + return "Connection: close\r\n"; + else + return "Connection: Keep-Alive\r\n"; +} + +HTTPRequestAction HTTPRequest::consume(boost::asio::streambuf& buf) +{ + std::string line; + std::istream is(&buf); + std::getline(is, line); + boost::trim(line); + +// cLog(lsTRACE) << "HTTPRequest line: " << line; + + if (eState == await_request) + { // VERB URL PROTO + if (line.empty()) + return haREAD_LINE; + sRequest = line; + bShouldClose = sRequest.find("HTTP/1.1") == std::string::npos; + + eState = await_header; + return haREAD_LINE; + } + + if (eState == await_header) + { // HEADER_NAME: HEADER_BODY + if (line.empty()) // empty line or bare \r + { + if (iDataSize == 0) + { // no body + eState = do_request; + return haDO_REQUEST; + } + eState = getting_body; + return haREAD_RAW; + } + vHeaders.push_back(line); + + size_t colon = line.find(':'); + if (colon != std::string::npos) + { + std::string headerName = line.substr(0, colon); + boost::trim(headerName); + boost::to_lower(headerName); + + std::string headerValue = line.substr(colon+1); + boost::trim(headerValue); + + if (headerName == "connection") + { + boost::to_lower(headerValue); + if ((headerValue == "keep-alive") || (headerValue == "keepalive")) + bShouldClose = false; + if (headerValue == "close") + bShouldClose = true; + } + + if (headerName == "content-length") + iDataSize = boost::lexical_cast(headerValue); + + if (headerName == "authorization") + sAuthorization = headerValue; + + } + + return haREAD_LINE; + } + + assert(false); + return haERROR; +} diff --git a/src/HashedObject.cpp b/src/HashedObject.cpp index c1df0b3fa..537270336 100644 --- a/src/HashedObject.cpp +++ b/src/HashedObject.cpp @@ -59,15 +59,15 @@ void HashedObjectStore::waitWrite() void HashedObjectStore::bulkWrite() { - std::vector< boost::shared_ptr > set; while (1) { - set.clear(); + std::vector< boost::shared_ptr > set; set.reserve(128); { boost::unique_lock sl(mWriteMutex); mWriteSet.swap(set); + assert(mWriteSet.empty()); if (set.empty()) { mWritePending = false; @@ -75,7 +75,7 @@ void HashedObjectStore::bulkWrite() return; } } -// cLog(lsINFO) << "HOS: writing " << set.size(); + cLog(lsTRACE) << "HOS: writing " << set.size(); static boost::format fExists("SELECT ObjType FROM CommittedObjects WHERE Hash = '%s';"); static boost::format @@ -122,7 +122,8 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash) } } - if (!theApp || !theApp->getHashNodeDB()) return HashedObject::pointer(); + if (!theApp || !theApp->getHashNodeDB()) + return HashedObject::pointer(); std::string sql = "SELECT * FROM CommittedObjects WHERE Hash='"; sql.append(hash.GetHex()); sql.append("';"); diff --git a/src/HttpReply.cpp b/src/HttpReply.cpp deleted file mode 100644 index 4dbb9182d..000000000 --- a/src/HttpReply.cpp +++ /dev/null @@ -1,241 +0,0 @@ -#include "HttpReply.h" -#include -#include -#include - - -namespace status_strings -{ - const std::string ok = - "HTTP/1.0 200 OK\r\n"; - const std::string created = - "HTTP/1.0 201 Created\r\n"; - const std::string accepted = - "HTTP/1.0 202 Accepted\r\n"; - const std::string no_content = - "HTTP/1.0 204 No Content\r\n"; - const std::string multiple_choices = - "HTTP/1.0 300 Multiple Choices\r\n"; - const std::string moved_permanently = - "HTTP/1.0 301 Moved Permanently\r\n"; - const std::string moved_temporarily = - "HTTP/1.0 302 Moved Temporarily\r\n"; - const std::string not_modified = - "HTTP/1.0 304 Not Modified\r\n"; - const std::string bad_request = - "HTTP/1.0 400 Bad Request\r\n"; - const std::string unauthorized = - "HTTP/1.0 401 Unauthorized\r\n"; - const std::string forbidden = - "HTTP/1.0 403 Forbidden\r\n"; - const std::string not_found = - "HTTP/1.0 404 Not Found\r\n"; - const std::string internal_server_error = - "HTTP/1.0 500 Internal Server Error\r\n"; - const std::string not_implemented = - "HTTP/1.0 501 Not Implemented\r\n"; - const std::string bad_gateway = - "HTTP/1.0 502 Bad Gateway\r\n"; - const std::string service_unavailable = - "HTTP/1.0 503 Service Unavailable\r\n"; - - boost::asio::const_buffer to_buffer(HttpReply::status_type status) - { - switch (status) - { - case HttpReply::ok: - return boost::asio::buffer(ok); - case HttpReply::created: - return boost::asio::buffer(created); - case HttpReply::accepted: - return boost::asio::buffer(accepted); - case HttpReply::no_content: - return boost::asio::buffer(no_content); - case HttpReply::multiple_choices: - return boost::asio::buffer(multiple_choices); - case HttpReply::moved_permanently: - return boost::asio::buffer(moved_permanently); - case HttpReply::moved_temporarily: - return boost::asio::buffer(moved_temporarily); - case HttpReply::not_modified: - return boost::asio::buffer(not_modified); - case HttpReply::bad_request: - return boost::asio::buffer(bad_request); - case HttpReply::unauthorized: - return boost::asio::buffer(unauthorized); - case HttpReply::forbidden: - return boost::asio::buffer(forbidden); - case HttpReply::not_found: - return boost::asio::buffer(not_found); - case HttpReply::internal_server_error: - return boost::asio::buffer(internal_server_error); - case HttpReply::not_implemented: - return boost::asio::buffer(not_implemented); - case HttpReply::bad_gateway: - return boost::asio::buffer(bad_gateway); - case HttpReply::service_unavailable: - return boost::asio::buffer(service_unavailable); - default: - return boost::asio::buffer(internal_server_error); - } - } - -} // namespace status_strings - -namespace misc_strings -{ - const char name_value_separator[] = { ':', ' ' }; - const char crlf[] = { '\r', '\n' }; -} // namespace misc_strings - -std::vector HttpReply::to_buffers() -{ - std::vector buffers; - buffers.push_back(status_strings::to_buffer(status)); - for (std::size_t i = 0; i < headers.size(); ++i) - { - HttpHeader& h = headers[i]; - buffers.push_back(boost::asio::buffer(h.name)); - buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator)); - buffers.push_back(boost::asio::buffer(h.value)); - buffers.push_back(boost::asio::buffer(misc_strings::crlf)); - } - buffers.push_back(boost::asio::buffer(misc_strings::crlf)); - buffers.push_back(boost::asio::buffer(content)); - return buffers; -} - -namespace stock_replies { - -const char ok[] = ""; -const char created[] = - "" - "Created" - "

201 Created

" - ""; -const char accepted[] = - "" - "Accepted" - "

202 Accepted

" - ""; -const char no_content[] = - "" - "No Content" - "

204 Content

" - ""; -const char multiple_choices[] = - "" - "Multiple Choices" - "

300 Multiple Choices

" - ""; -const char moved_permanently[] = - "" - "Moved Permanently" - "

301 Moved Permanently

" - ""; -const char moved_temporarily[] = - "" - "Moved Temporarily" - "

302 Moved Temporarily

" - ""; -const char not_modified[] = - "" - "Not Modified" - "

304 Not Modified

" - ""; -const char bad_request[] = - "" - "Bad Request" - "

400 Bad Request

" - ""; -const char unauthorized[] = - "" - "Unauthorized" - "

401 Unauthorized

" - ""; -const char forbidden[] = - "" - "Forbidden" - "

403 Forbidden

" - ""; -const char not_found[] = - "" - "Not Found" - "

404 Not Found

" - ""; -const char internal_server_error[] = - "" - "Internal Server Error" - "

500 Internal Server Error

" - ""; -const char not_implemented[] = - "" - "Not Implemented" - "

501 Not Implemented

" - ""; -const char bad_gateway[] = - "" - "Bad Gateway" - "

502 Bad Gateway

" - ""; -const char service_unavailable[] = - "" - "Service Unavailable" - "

503 Service Unavailable

" - ""; - -std::string to_string(HttpReply::status_type status) -{ - switch (status) - { - case HttpReply::ok: - return ok; - case HttpReply::created: - return created; - case HttpReply::accepted: - return accepted; - case HttpReply::no_content: - return no_content; - case HttpReply::multiple_choices: - return multiple_choices; - case HttpReply::moved_permanently: - return moved_permanently; - case HttpReply::moved_temporarily: - return moved_temporarily; - case HttpReply::not_modified: - return not_modified; - case HttpReply::bad_request: - return bad_request; - case HttpReply::unauthorized: - return unauthorized; - case HttpReply::forbidden: - return forbidden; - case HttpReply::not_found: - return not_found; - case HttpReply::internal_server_error: - return internal_server_error; - case HttpReply::not_implemented: - return not_implemented; - case HttpReply::bad_gateway: - return bad_gateway; - case HttpReply::service_unavailable: - return service_unavailable; - default: - return internal_server_error; - } -} - -} // namespace stock_replies - -HttpReply HttpReply::stock_reply(HttpReply::status_type status) -{ - HttpReply rep; - rep.status = status; - rep.content = stock_replies::to_string(status); - rep.headers.resize(2); - rep.headers[0].name = "Content-Length"; - rep.headers[0].value = boost::lexical_cast(rep.content.size()); - rep.headers[1].name = "Content-Type"; - rep.headers[1].value = "text/html"; - return rep; -} diff --git a/src/HttpReply.h b/src/HttpReply.h deleted file mode 100644 index d80232739..000000000 --- a/src/HttpReply.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef HTTP_REPLY_HPP -#define HTTP_REPLY_HPP - -#include -#include -#include -#include "HttpRequest.h" - -/// A reply to be sent to a client. -class HttpReply -{ -public: - /// The status of the reply. - enum status_type - { - ok = 200, - created = 201, - accepted = 202, - no_content = 204, - multiple_choices = 300, - moved_permanently = 301, - moved_temporarily = 302, - not_modified = 304, - bad_request = 400, - unauthorized = 401, - forbidden = 403, - not_found = 404, - internal_server_error = 500, - not_implemented = 501, - bad_gateway = 502, - service_unavailable = 503 - } status; - - /// The headers to be included in the reply. - std::vector headers; - - /// The content to be sent in the reply. - std::string content; - - /// Convert the reply into a vector of buffers. The buffers do not own the - /// underlying memory blocks, therefore the reply object must remain valid and - /// not be changed until the write operation has completed. - std::vector to_buffers(); - - /// Get a stock reply. - static HttpReply stock_reply(status_type status); -}; - - -#endif // HTTP_REPLY_HPP \ No newline at end of file diff --git a/src/HttpRequest.h b/src/HttpRequest.h deleted file mode 100644 index 2f6b54522..000000000 --- a/src/HttpRequest.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef HTTP_REQUEST_HPP -#define HTTP_REQUEST_HPP - -#include -#include - -struct HttpHeader -{ - std::string name; - std::string value; -}; - -/// A request received from a client. -struct HttpRequest -{ - std::string method; - std::string uri; - int http_version_major; - int http_version_minor; - std::vector headers; - std::string mBody; -}; - -#endif // HTTP_REQUEST_HPP diff --git a/src/Ledger.cpp b/src/Ledger.cpp index dd67647b5..af61e7993 100644 --- a/src/Ledger.cpp +++ b/src/Ledger.cpp @@ -21,7 +21,7 @@ SETUP_LOG(); -Ledger::Ledger(const NewcoinAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1), +Ledger::Ledger(const RippleAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1), mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0), mClosed(false), mValidHash(false), mAccepted(false), mImmutable(false), mTransactionMap(boost::make_shared(smtTRANSACTION)), @@ -173,7 +173,7 @@ void Ledger::setAccepted() mImmutable = true; } -AccountState::pointer Ledger::getAccountState(const NewcoinAddress& accountID) +AccountState::pointer Ledger::getAccountState(const RippleAddress& accountID) { #ifdef DEBUG // std::cerr << "Ledger:getAccountState(" << accountID.humanAccountID() << ")" << std::endl; @@ -337,8 +337,9 @@ uint256 Ledger::getHash() return mHash; } -void Ledger::saveAcceptedLedger() +void Ledger::saveAcceptedLedger(bool fromConsensus) { // can be called in a different thread + cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq(); static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;"); static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;"); static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';"); @@ -378,11 +379,11 @@ void Ledger::saveAcceptedLedger() if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex()))) { // Transaction not in AccountTransactions - std::vector accts = txn->getAffectedAccounts(); + std::vector accts = txn->getAffectedAccounts(); std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES "; bool first = true; - for (std::vector::iterator it = accts.begin(), end = accts.end(); it != end; ++it) + for (std::vector::iterator it = accts.begin(), end = accts.end(); it != end; ++it) { if (!first) sql += ", ('"; @@ -428,7 +429,13 @@ void Ledger::saveAcceptedLedger() mAccountHash.GetHex() % mTransHash.GetHex())); } + if (!fromConsensus) + return; + + theApp->getMasterLedger().setFullLedger(shared_from_this()); + theApp->getOPs().pubLedger(shared_from_this()); + } Ledger::pointer Ledger::getSQL(const std::string& sql) @@ -464,6 +471,7 @@ Ledger::pointer Ledger::getSQL(const std::string& sql) db->endIterRows(); } + Log(lsTRACE) << "Constructing ledger " << ledgerSeq << " from SQL"; Ledger::pointer ret = Ledger::pointer(new Ledger(prevHash, transHash, accountHash, totCoins, closingTime, prevClosingTime, closeFlags, closeResolution, ledgerSeq)); if (ret->getHash() != ledgerHash) @@ -498,6 +506,19 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash) return getSQL(sql); } +Ledger::pointer Ledger::getLastFullLedger() +{ + try + { + return getSQL("SELECT * from Ledgers order by LedgerSeq desc limit 1;"); + } + catch (SHAMapMissingNode& sn) + { + cLog(lsWARNING) << "Database contains ledger with missing nodes: " << sn; + return Ledger::pointer(); + } +} + void Ledger::addJson(Json::Value& ret, int options) { ret["ledger"] = getJson(options); @@ -753,7 +774,7 @@ SLE::pointer Ledger::getAccountRoot(const uint160& accountID) return getASNode(qry, getAccountRootIndex(accountID), ltACCOUNT_ROOT); } -SLE::pointer Ledger::getAccountRoot(const NewcoinAddress& naAccountID) +SLE::pointer Ledger::getAccountRoot(const RippleAddress& naAccountID) { LedgerStateParms qry = lepNONE; @@ -880,9 +901,9 @@ uint256 Ledger::getBookBase(const uint160& uTakerPaysCurrency, const uint160& uT Log(lsINFO) << str(boost::format("getBookBase(%s,%s,%s,%s) = %s") % STAmount::createHumanCurrency(uTakerPaysCurrency) - % NewcoinAddress::createHumanAccountID(uTakerPaysIssuerID) + % RippleAddress::createHumanAccountID(uTakerPaysIssuerID) % STAmount::createHumanCurrency(uTakerGetsCurrency) - % NewcoinAddress::createHumanAccountID(uTakerGetsIssuerID) + % RippleAddress::createHumanAccountID(uTakerGetsIssuerID) % uBaseIndex.ToString()); return uBaseIndex; @@ -950,7 +971,7 @@ uint256 Ledger::getOwnerDirIndex(const uint160& uAccountID) return s.getSHA512Half(); } -uint256 Ledger::getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddress& naB, const uint160& uCurrency) +uint256 Ledger::getRippleStateIndex(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency) { uint160 uAID = naA.getAccountID(); uint160 uBID = naB.getAccountID(); @@ -967,15 +988,20 @@ uint256 Ledger::getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddr bool Ledger::walkLedger() { - std::vector missingNodes; - mAccountStateMap->walkMap(missingNodes, 32); - if (sLog(lsINFO) && !missingNodes.empty()) + std::vector missingNodes1, missingNodes2; + mAccountStateMap->walkMap(missingNodes1, 32); + if (sLog(lsINFO) && !missingNodes1.empty()) { - Log(lsINFO) << missingNodes.size() << " missing account node(s)"; - Log(lsINFO) << "First: " << missingNodes[0]; + Log(lsINFO) << missingNodes1.size() << " missing account node(s)"; + Log(lsINFO) << "First: " << missingNodes1[0]; } - mTransactionMap->walkMap(missingNodes, 32); - return missingNodes.empty(); + mTransactionMap->walkMap(missingNodes2, 32); + if (sLog(lsINFO) && !missingNodes2.empty()) + { + Log(lsINFO) << missingNodes2.size() << " missing transaction node(s)"; + Log(lsINFO) << "First: " << missingNodes2[0]; + } + return missingNodes1.empty() && missingNodes2.empty(); } bool Ledger::assertSane() diff --git a/src/Ledger.h b/src/Ledger.h index 2b03b88ea..01ea5cd83 100644 --- a/src/Ledger.h +++ b/src/Ledger.h @@ -85,7 +85,7 @@ protected: SLE::pointer getASNode(LedgerStateParms& parms, const uint256& nodeID, LedgerEntryType let); public: - Ledger(const NewcoinAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger + Ledger(const RippleAddress& masterID, uint64 startAmount); // used for the starting bootstrap ledger Ledger(const uint256 &parentHash, const uint256 &transHash, const uint256 &accountHash, uint64 totCoins, uint32 closeTime, uint32 parentCloseTime, int closeFlags, int closeResolution, @@ -100,6 +100,7 @@ public: Ledger(Ledger& target, bool isMutable); // snapshot static Ledger::pointer getSQL(const std::string& sqlStatement); + static Ledger::pointer getLastFullLedger(); void updateHash(); void setClosed() { mClosed = true; } @@ -150,13 +151,13 @@ public: static SerializedTransaction::pointer getSTransaction(SHAMapItem::ref, SHAMapTreeNode::TNType); // high-level functions - AccountState::pointer getAccountState(const NewcoinAddress& acctID); + AccountState::pointer getAccountState(const RippleAddress& acctID); LedgerStateParms writeBack(LedgerStateParms parms, SLE::ref); SLE::pointer getAccountRoot(const uint160& accountID); - SLE::pointer getAccountRoot(const NewcoinAddress& naAccountID); + SLE::pointer getAccountRoot(const RippleAddress& naAccountID); // database functions - void saveAcceptedLedger(); + void saveAcceptedLedger(bool fromConsensus); static Ledger::pointer loadByIndex(uint32 ledgerIndex); static Ledger::pointer loadByHash(const uint256& ledgerHash); @@ -172,7 +173,7 @@ public: // index calculation functions static uint256 getAccountRootIndex(const uint160& uAccountID); - static uint256 getAccountRootIndex(const NewcoinAddress& account) + static uint256 getAccountRootIndex(const RippleAddress& account) { return getAccountRootIndex(account.getAccountID()); } // @@ -256,9 +257,9 @@ public: // // Index of node which is the ripple state between two accounts for a currency. - static uint256 getRippleStateIndex(const NewcoinAddress& naA, const NewcoinAddress& naB, const uint160& uCurrency); + static uint256 getRippleStateIndex(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency); static uint256 getRippleStateIndex(const uint160& uiA, const uint160& uiB, const uint160& uCurrency) - { return getRippleStateIndex(NewcoinAddress::createAccountID(uiA), NewcoinAddress::createAccountID(uiB), uCurrency); } + { return getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency); } RippleState::pointer accessRippleState(const uint256& uNode); @@ -270,11 +271,11 @@ public: return getRippleState(qry, uNode); } - SLE::pointer getRippleState(const NewcoinAddress& naA, const NewcoinAddress& naB, const uint160& uCurrency) + SLE::pointer getRippleState(const RippleAddress& naA, const RippleAddress& naB, const uint160& uCurrency) { return getRippleState(getRippleStateIndex(naA, naB, uCurrency)); } SLE::pointer getRippleState(const uint160& uiA, const uint160& uiB, const uint160& uCurrency) - { return getRippleState(getRippleStateIndex(NewcoinAddress::createAccountID(uiA), NewcoinAddress::createAccountID(uiB), uCurrency)); } + { return getRippleState(getRippleStateIndex(RippleAddress::createAccountID(uiA), RippleAddress::createAccountID(uiB), uCurrency)); } Json::Value getJson(int options); void addJson(Json::Value&, int options); diff --git a/src/LedgerAcquire.cpp b/src/LedgerAcquire.cpp index f5579a9c3..6559ce715 100644 --- a/src/LedgerAcquire.cpp +++ b/src/LedgerAcquire.cpp @@ -10,7 +10,9 @@ #include "SHAMapSync.h" #include "HashPrefixes.h" -// #define LA_DEBUG +SETUP_LOG(); + +#define LA_DEBUG #define LEDGER_ACQUIRE_TIMEOUT 750 #define TRUST_NETWORK @@ -72,7 +74,7 @@ void PeerSet::invokeOnTimer() if (!mProgress) { ++mTimeouts; - Log(lsWARNING) << "Timeout " << mTimeouts << " acquiring " << mHash; + cLog(lsWARNING) << "Timeout(" << mTimeouts << ") pc=" << mPeers.size() << " acquiring " << mHash; } else mProgress = false; @@ -89,10 +91,10 @@ void PeerSet::TimerEntry(boost::weak_ptr wptr, const boost::system::err } LedgerAcquire::LedgerAcquire(const uint256& hash) : PeerSet(hash, LEDGER_ACQUIRE_TIMEOUT), - mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false) + mHaveBase(false), mHaveState(false), mHaveTransactions(false), mAborted(false), mSignaled(false), mAccept(false) { #ifdef LA_DEBUG - Log(lsTRACE) << "Acquiring ledger " << mHash; + cLog(lsTRACE) << "Acquiring ledger " << mHash; #endif } @@ -142,7 +144,8 @@ void LedgerAcquire::onTimer() setFailed(); done(); } - else trigger(Peer::pointer(), true); + else + trigger(Peer::pointer(), true); } boost::weak_ptr LedgerAcquire::pmDowncast() @@ -156,7 +159,7 @@ void LedgerAcquire::done() return; mSignaled = true; #ifdef LA_DEBUG - Log(lsTRACE) << "Done acquiring ledger " << mHash; + cLog(lsTRACE) << "Done acquiring ledger " << mHash; #endif std::vector< boost::function > triggers; @@ -167,7 +170,11 @@ void LedgerAcquire::done() mLock.unlock(); if (mLedger) + { + if (mAccept) + mLedger->setAccepted(); theApp->getMasterLedger().storeLedger(mLedger); + } for (unsigned int i = 0; i < triggers.size(); ++i) triggers[i](shared_from_this()); @@ -185,12 +192,12 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) if (mAborted || mComplete || mFailed) return; #ifdef LA_DEBUG - if (peer) Log(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); - else Log(lsTRACE) << "Trigger acquiring ledger " << mHash; + if (peer) cLog(lsTRACE) << "Trigger acquiring ledger " << mHash << " from " << peer->getIP(); + else cLog(lsTRACE) << "Trigger acquiring ledger " << mHash; if (mComplete || mFailed) - Log(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; + cLog(lsTRACE) << "complete=" << mComplete << " failed=" << mFailed; else - Log(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; + cLog(lsTRACE) << "base=" << mHaveBase << " tx=" << mHaveTransactions << " as=" << mHaveState; #endif if (!mHaveBase) { @@ -204,7 +211,7 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) if (mHaveBase && !mHaveTransactions) { #ifdef LA_DEBUG - Log(lsTRACE) << "need tx"; + cLog(lsTRACE) << "need tx"; #endif assert(mLedger); if (mLedger->peekTransactionMap()->getHash().isZero()) @@ -238,8 +245,8 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_ledgerseq(mLedger->getLedgerSeq()); tmGL.set_itype(ripple::liTX_NODE); - for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it) - *(tmGL.add_nodeids()) = it->getRawString(); + BOOST_FOREACH(SHAMapNode& it, nodeIDs) + *(tmGL.add_nodeids()) = it.getRawString(); sendRequest(tmGL, peer); } } @@ -248,7 +255,7 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) if (mHaveBase && !mHaveState) { #ifdef LA_DEBUG - Log(lsTRACE) << "need as"; + cLog(lsTRACE) << "need as"; #endif assert(mLedger); if (mLedger->peekAccountStateMap()->getHash().isZero()) @@ -282,8 +289,8 @@ void LedgerAcquire::trigger(Peer::ref peer, bool timer) tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_ledgerseq(mLedger->getLedgerSeq()); tmGL.set_itype(ripple::liAS_NODE); - for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it) - *(tmGL.add_nodeids()) = it->getRawString(); + BOOST_FOREACH(SHAMapNode& it, nodeIDs) + *(tmGL.add_nodeids()) = it.getRawString(); sendRequest(tmGL, peer); } } @@ -326,18 +333,41 @@ void PeerSet::sendRequest(const ripple::TMGetLedger& tmGL) } } +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; + } + return ret; +} + +int PeerSet::getPeerCount() const +{ + int ret = 0; + BOOST_FOREACH(const boost::weak_ptr& p, mPeers) + if (p.lock()) + ++ret; + return ret; +} + bool LedgerAcquire::takeBase(const std::string& data) { // Return value: true=normal, false=bad data #ifdef LA_DEBUG - Log(lsTRACE) << "got base acquiring ledger " << mHash; + cLog(lsTRACE) << "got base acquiring ledger " << mHash; #endif boost::recursive_mutex::scoped_lock sl(mLock); if (mHaveBase) return true; mLedger = boost::make_shared(data); if (mLedger->getHash() != mHash) { - Log(lsWARNING) << "Acquire hash mismatch"; - Log(lsWARNING) << mLedger->getHash() << "!=" << mHash; + cLog(lsWARNING) << "Acquire hash mismatch"; + cLog(lsWARNING) << mLedger->getHash() << "!=" << mHash; mLedger = Ledger::pointer(); #ifdef TRUST_NETWORK assert(false); @@ -371,7 +401,7 @@ bool LedgerAcquire::takeTxNode(const std::list& nodeIDs, { if (nodeIDit->isRoot()) { - if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, snfWIRE)) + if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), *nodeDatait, snfWIRE, &tFilter)) return false; } else if (!mLedger->peekTransactionMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter)) @@ -396,7 +426,7 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, const std::list< std::vector >& data) { #ifdef LA_DEBUG - Log(lsTRACE) << "got ASdata acquiring ledger " << mHash; + cLog(lsTRACE) << "got ASdata acquiring ledger " << mHash; #endif if (!mHaveBase) return false; std::list::const_iterator nodeIDit = nodeIDs.begin(); @@ -406,7 +436,8 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, { if (nodeIDit->isRoot()) { - if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), *nodeDatait, snfWIRE)) + if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), + *nodeDatait, snfWIRE, &tFilter)) return false; } else if (!mLedger->peekAccountStateMap()->addKnownNode(*nodeIDit, *nodeDatait, &tFilter)) @@ -429,14 +460,22 @@ bool LedgerAcquire::takeAsNode(const std::list& nodeIDs, bool LedgerAcquire::takeAsRootNode(const std::vector& data) { - if (!mHaveBase) return false; - return mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE); + if (!mHaveBase) + return false; + AccountStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq()); + if (!mLedger->peekAccountStateMap()->addRootNode(mLedger->getAccountHash(), data, snfWIRE, &tFilter)) + return false; + return true; } bool LedgerAcquire::takeTxRootNode(const std::vector& data) { - if (!mHaveBase) return false; - return mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE); + if (!mHaveBase) + return false; + TransactionStateSF tFilter(mLedger->getHash(), mLedger->getLedgerSeq()); + if (!mLedger->peekTransactionMap()->addRootNode(mLedger->getTransHash(), data, snfWIRE, &tFilter)) + return false; + return true; } LedgerAcquire::pointer LedgerAcquireMaster::findCreate(const uint256& hash) @@ -479,7 +518,7 @@ void LedgerAcquireMaster::dropLedger(const uint256& hash) bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref peer) { #ifdef LA_DEBUG - Log(lsTRACE) << "got data for acquiring ledger "; + cLog(lsTRACE) << "got data for acquiring ledger "; #endif uint256 hash; if (packet.ledgerhash().size() != 32) @@ -489,7 +528,7 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref } memcpy(hash.begin(), packet.ledgerhash().data(), 32); #ifdef LA_DEBUG - Log(lsTRACE) << hash; + cLog(lsTRACE) << hash; #endif LedgerAcquire::pointer ledger = find(hash); @@ -509,7 +548,7 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref } if (!ledger->takeAsRootNode(strCopy(packet.nodes(1).nodedata()))) { - Log(lsWARNING) << "Included ASbase invalid"; + cLog(lsWARNING) << "Included ASbase invalid"; } if (packet.nodes().size() == 2) { @@ -517,7 +556,9 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref return true; } if (!ledger->takeTxRootNode(strCopy(packet.nodes(2).nodedata()))) - Log(lsWARNING) << "Invcluded TXbase invalid"; + { + cLog(lsWARNING) << "Invcluded TXbase invalid"; + } ledger->trigger(peer, false); return true; } @@ -548,4 +589,5 @@ bool LedgerAcquireMaster::gotLedgerData(ripple::TMLedgerData& packet, Peer::ref return false; } + // vim:ts=4 diff --git a/src/LedgerAcquire.h b/src/LedgerAcquire.h index 196a325c1..cdf9cda56 100644 --- a/src/LedgerAcquire.h +++ b/src/LedgerAcquire.h @@ -45,6 +45,9 @@ public: void badPeer(Peer::ref); void resetTimer(); + int takePeerSetFrom(const PeerSet& s); + int getPeerCount() const; + protected: virtual void newPeer(Peer::ref) = 0; virtual void onTimer(void) = 0; @@ -65,7 +68,7 @@ public: protected: Ledger::pointer mLedger; - bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled; + bool mHaveBase, mHaveState, mHaveTransactions, mAborted, mSignaled, mAccept; std::vector< boost::function > mOnComplete; @@ -85,6 +88,7 @@ public: bool isTransComplete() const { return mHaveTransactions; } Ledger::pointer getLedger() { return mLedger; } void abort() { mAborted = true; } + bool setAccept() { if (mAccept) return false; mAccept = true; return true; } void addOnComplete(boost::function); diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index f241934ed..e0917da0e 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -84,8 +84,8 @@ void TransactionAcquire::trigger(Peer::ref peer, bool timer) ripple::TMGetLedger tmGL; tmGL.set_ledgerhash(mHash.begin(), mHash.size()); tmGL.set_itype(ripple::liTS_CANDIDATE); - for (std::vector::iterator it = nodeIDs.begin(); it != nodeIDs.end(); ++it) - *(tmGL.add_nodeids()) = it->getRawString(); + BOOST_FOREACH(SHAMapNode& it, nodeIDs) + *(tmGL.add_nodeids()) = it.getRawString(); sendRequest(tmGL, peer); } } @@ -114,7 +114,7 @@ bool TransactionAcquire::takeNodes(const std::list& nodeIDs, cLog(lsWARNING) << "Got root TXS node, already have it"; return false; } - if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE)) + if (!mMap->addRootNode(getHash(), *nodeDatait, snfWIRE, NULL)) return false; else mHaveRoot = true; @@ -155,14 +155,14 @@ void LCTransaction::setVote(const uint160& peer, bool votesYes) } else if (votesYes && !res.first->second) { // changes vote to yes - cLog(lsTRACE) << "Peer " << peer << " now votes YES on " << mTransactionID; + cLog(lsDEBUG) << "Peer " << peer << " now votes YES on " << mTransactionID; --mNays; ++mYays; res.first->second = true; } else if (!votesYes && res.first->second) { // changes vote to no - cLog(lsTRACE) << "Peer " << peer << " now votes NO on " << mTransactionID; + cLog(lsDEBUG) << "Peer " << peer << " now votes NO on " << mTransactionID; ++mNays; --mYays; res.first->second = false; @@ -216,13 +216,14 @@ bool LCTransaction::updateVote(int percentTime, bool proposing) return false; } mOurVote = newPosition; - cLog(lsTRACE) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; + cLog(lsDEBUG) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; return true; } LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previousLedger, uint32 closeTime) : mState(lcsPRE_CLOSE), mCloseTime(closeTime), mPrevLedgerHash(prevLCLHash), mPreviousLedger(previousLedger), - mValSeed(theConfig.VALIDATION_SEED), mCurrentMSeconds(0), mClosePercent(0), mHaveCloseTimeConsensus(false), + mValPublic(theConfig.VALIDATION_PUB), mValPrivate(theConfig.VALIDATION_PRIV), + mCurrentMSeconds(0), mClosePercent(0), mHaveCloseTimeConsensus(false), mConsensusStartTime(boost::posix_time::microsec_clock::universal_time()) { cLog(lsDEBUG) << "Creating consensus object"; @@ -234,7 +235,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou mCloseResolution = ContinuousLedgerTiming::getNextLedgerTimeResolution( mPreviousLedger->getCloseResolution(), mPreviousLedger->getCloseAgree(), previousLedger->getLedgerSeq() + 1); - if (mValSeed.isValid()) + if (mValPublic.isValid() && mValPrivate.isValid()) { cLog(lsINFO) << "Entering consensus process, validating"; mValidating = true; @@ -246,11 +247,16 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou mProposing = mValidating = false; } - handleLCL(prevLCLHash); + mHaveCorrectLCL = (mPreviousLedger->getHash() == mPrevLedgerHash); if (!mHaveCorrectLCL) { - cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash(); - cLog(lsINFO) << "Correct LCL is: " << prevLCLHash; + handleLCL(mPrevLedgerHash); + if (!mHaveCorrectLCL) + { + mProposing = mValidating = false; + cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash(); + cLog(lsINFO) << "Correct LCL is: " << prevLCLHash; + } } } @@ -356,7 +362,7 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) } } - cLog(lsINFO) << "Acquired the consensus ledger " << mPrevLedgerHash; + cLog(lsINFO) << "Have the consensus ledger " << mPrevLedgerHash; mHaveCorrectLCL = true; mAcquiringLedger = LedgerAcquire::pointer(); theApp->getOPs().clearNeedNetworkLedger(); @@ -374,7 +380,7 @@ void LedgerConsensus::takeInitialPosition(Ledger& initialLedger) if (mValidating) mOurPosition = boost::make_shared - (mValSeed, initialLedger.getParentHash(), txSet, mCloseTime); + (mValPublic, mValPrivate, initialLedger.getParentHash(), txSet, mCloseTime); else mOurPosition = boost::make_shared(initialLedger.getParentHash(), txSet, mCloseTime); @@ -404,17 +410,19 @@ void LedgerConsensus::createDisputes(SHAMap::ref m1, SHAMap::ref m2) { SHAMap::SHAMapDiff differences; m1->compare(m2, differences, 16384); - for (SHAMap::SHAMapDiff::iterator pos = differences.begin(), end = differences.end(); pos != end; ++pos) + + typedef std::pair u256_diff_pair; + BOOST_FOREACH (u256_diff_pair& pos, differences) { // create disputed transactions (from the ledger that has them) - if (pos->second.first) + if (pos.second.first) { // transaction is in first map - assert(!pos->second.second); - addDisputedTransaction(pos->first, pos->second.first->peekData()); + assert(!pos.second.second); + addDisputedTransaction(pos.first, pos.second.first->peekData()); } - else if (pos->second.second) + else if (pos.second.second) { // transaction is in second map - assert(!pos->second.first); - addDisputedTransaction(pos->first, pos->second.second->peekData()); + assert(!pos.second.first); + addDisputedTransaction(pos.first, pos.second.second->peekData()); } else // No other disagreement over a transaction should be possible assert(false); @@ -556,9 +564,9 @@ void LedgerConsensus::stateEstablish() updateOurPositions(); if (!mHaveCloseTimeConsensus) { - tLog(haveConsensus(), lsINFO) << "We have TX consensus but not CT consensus"; + tLog(haveConsensus(false), lsINFO) << "We have TX consensus but not CT consensus"; } - else if (haveConsensus()) + else if (haveConsensus(true)) { cLog(lsINFO) << "Converge cutoff (" << mPeerPositions.size() << " participants)"; mState = lcsFINISHED; @@ -715,7 +723,7 @@ void LedgerConsensus::updateOurPositions() } } -bool LedgerConsensus::haveConsensus() +bool LedgerConsensus::haveConsensus(bool forReal) { // FIXME: Should check for a supermajority on each disputed transaction // counting unacquired TX sets as disagreeing int agree = 0, disagree = 0; @@ -732,12 +740,10 @@ bool LedgerConsensus::haveConsensus() } int currentValidations = theApp->getValidations().getNodesAfter(mPrevLedgerHash); -#ifdef LC_DEBUG - cLog(lsINFO) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree; -#endif + cLog(lsDEBUG) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree; return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations, - mPreviousMSeconds, mCurrentMSeconds); + mPreviousMSeconds, mCurrentMSeconds, forReal); } SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire) @@ -812,7 +818,8 @@ void LedgerConsensus::startAcquiring(const TransactionAcquire::pointer& acquire) void LedgerConsensus::propose() { - cLog(lsTRACE) << "We propose: " << mOurPosition->getCurrentHash(); + cLog(lsTRACE) << "We propose: " << + (mOurPosition->isBowOut() ? std::string("bowOut") : mOurPosition->getCurrentHash().GetHex()); ripple::TMProposeSet prop; prop.set_currenttxhash(mOurPosition->getCurrentHash().begin(), 256 / 8); @@ -830,7 +837,7 @@ void LedgerConsensus::propose() void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vector& tx) { - cLog(lsTRACE) << "Transaction " << txID << " is disputed"; + cLog(lsDEBUG) << "Transaction " << txID << " is disputed"; boost::unordered_map::iterator it = mDisputes.find(txID); if (it != mDisputes.end()) return; @@ -910,7 +917,7 @@ bool LedgerConsensus::peerPosition(const LedgerProposal::pointer& newPosition) it.second->setVote(peerID, set->hasItem(it.first)); } else - cLog(lsTRACE) << "Don't have that tx set"; + cLog(lsDEBUG) << "Don't have that tx set"; return true; } @@ -956,12 +963,7 @@ void LedgerConsensus::beginAccept(bool synchronous) if (synchronous) accept(consensusSet); else - theApp->getIOService().post(boost::bind(&LedgerConsensus::Saccept, shared_from_this(), consensusSet)); -} - -void LedgerConsensus::Saccept(boost::shared_ptr This, SHAMap::pointer txSet) -{ - This->accept(txSet); + theApp->getIOService().post(boost::bind(&LedgerConsensus::accept, shared_from_this(), consensusSet)); } void LedgerConsensus::playbackProposals() @@ -988,7 +990,7 @@ void LedgerConsensus::playbackProposals() } } -void LedgerConsensus::applyTransaction(TransactionEngine& engine, const SerializedTransaction::pointer& txn, +void LedgerConsensus::applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn, Ledger::ref ledger, CanonicalTXSet& failedTransactions, bool openLedger) { TransactionEngineParams parms = openLedger ? tapOPEN_LEDGER : tapNONE; @@ -1139,9 +1141,12 @@ void LedgerConsensus::accept(SHAMap::ref set) statusChange(ripple::neACCEPTED_LEDGER, *newLCL); if (mValidating) { + uint256 signingHash; SerializedValidation::pointer v = boost::make_shared - (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing); + (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValPublic, mValPrivate, + mProposing, boost::ref(signingHash)); v->setTrusted(); + theApp->isNew(signingHash); // suppress it if we receive it theApp->getValidations().addValidation(v); std::vector validation = v->getSigned(); ripple::TMValidation val; @@ -1179,7 +1184,7 @@ void LedgerConsensus::accept(SHAMap::ref set) cLog(lsINFO) << "Applying transactions from current ledger"; applyTransactions(theApp->getMasterLedger().getCurrentLedger()->peekTransactionMap(), newOL, newLCL, failedTransactions, true); - theApp->getMasterLedger().pushLedger(newLCL, newOL); + theApp->getMasterLedger().pushLedger(newLCL, newOL, true); mNewLedgerHash = newLCL->getHash(); mState = lcsACCEPTED; sl.unlock(); diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index a88aee079..38a2dde9f 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -87,7 +87,7 @@ protected: Ledger::pointer mPreviousLedger; LedgerAcquire::pointer mAcquiringLedger; LedgerProposal::pointer mOurPosition; - NewcoinAddress mValSeed; + RippleAddress mValPublic, mValPrivate; bool mProposing, mValidating, mHaveCorrectLCL; int mCurrentMSeconds, mClosePercent, mCloseResolution; @@ -117,7 +117,6 @@ protected: boost::unordered_set mDeadNodes; // final accept logic - static void Saccept(boost::shared_ptr This, SHAMap::pointer txSet); void accept(SHAMap::ref txSet); void weHave(const uint256& id, Peer::ref avoidPeer); @@ -134,7 +133,7 @@ protected: void sendHaveTxSet(const uint256& set, bool direct); void applyTransactions(SHAMap::ref transactionSet, Ledger::ref targetLedger, Ledger::ref checkLedger, CanonicalTXSet& failedTransactions, bool openLgr); - void applyTransaction(TransactionEngine& engine, const SerializedTransaction::pointer& txn, + void applyTransaction(TransactionEngine& engine, SerializedTransaction::ref txn, Ledger::ref targetLedger, CanonicalTXSet& failedTransactions, bool openLgr); uint32 roundCloseTime(uint32 closeTime); @@ -174,7 +173,7 @@ public: void stateFinished(); void stateAccepted(); - bool haveConsensus(); + bool haveConsensus(bool forReal); bool peerPosition(const LedgerProposal::pointer&); @@ -183,6 +182,8 @@ public: bool peerGaveNodes(Peer::ref peer, const uint256& setHash, const std::list& nodeIDs, const std::list< std::vector >& nodeData); + bool isOurPubKey(const RippleAddress &k) { return k == mValPublic; } + // test/debug void simulate(); }; diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp index d074f902d..2e1fd606a 100644 --- a/src/LedgerEntrySet.cpp +++ b/src/LedgerEntrySet.cpp @@ -2,6 +2,7 @@ #include "LedgerEntrySet.h" #include +#include #include "Log.h" @@ -295,7 +296,7 @@ SLE::pointer LedgerEntrySet::getForMod(const uint256& node, Ledger::ref ledger, } -bool LedgerEntrySet::threadTx(const NewcoinAddress& threadTo, Ledger::ref ledger, +bool LedgerEntrySet::threadTx(const RippleAddress& threadTo, Ledger::ref ledger, boost::unordered_map& newMods) { #ifdef META_DEBUG @@ -318,9 +319,11 @@ bool LedgerEntrySet::threadTx(SLE::ref threadTo, Ledger::ref ledger, uint32 prevLgrID; if (!threadTo->thread(mSet.getTxID(), mSet.getLgrSeq(), prevTxID, prevLgrID)) return false; - if (TransactionMetaSet::thread(mSet.getAffectedNode(threadTo->getIndex(), sfModifiedNode, false), + + if (prevTxID.isZero() || TransactionMetaSet::thread(mSet.getAffectedNode(threadTo->getIndex(), sfModifiedNode), prevTxID, prevLgrID)) return true; + assert(false); return false; } @@ -354,30 +357,30 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) // Entries modified only as a result of building the transaction metadata boost::unordered_map newMod; - for (std::map::const_iterator it = mEntries.begin(), - end = mEntries.end(); it != end; ++it) + typedef std::pair u256_LES_pair; + BOOST_FOREACH(u256_LES_pair& it, mEntries) { SField::ptr type = &sfGeneric; - switch (it->second.mAction) + switch (it.second.mAction) { case taaMODIFY: #ifdef META_DEBUG - cLog(lsTRACE) << "Modified Node " << it->first; + cLog(lsTRACE) << "Modified Node " << it.first; #endif type = &sfModifiedNode; break; case taaDELETE: #ifdef META_DEBUG - cLog(lsTRACE) << "Deleted Node " << it->first; + cLog(lsTRACE) << "Deleted Node " << it.first; #endif type = &sfDeletedNode; break; case taaCREATE: #ifdef META_DEBUG - cLog(lsTRACE) << "Created Node " << it->first; + cLog(lsTRACE) << "Created Node " << it.first; #endif type = &sfCreatedNode; break; @@ -389,47 +392,39 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) if (type == &sfGeneric) continue; - SLE::pointer origNode = mLedger->getSLE(it->first); + SLE::pointer origNode = mLedger->getSLE(it.first); if (origNode && (origNode->getType() == ltDIR_NODE)) // No metadata for dir nodes continue; - SLE::pointer curNode = it->second.mEntry; - STObject &metaNode = mSet.getAffectedNode(it->first, *type, true); + SLE::pointer curNode = it.second.mEntry; + mSet.setAffectedNode(it.first, *type); if (type == &sfDeletedNode) { assert(origNode); threadOwners(origNode, mLedger, newMod); - if (origNode->isFieldPresent(sfAmount)) - { // node has an amount, covers ripple state nodes - STAmount amount = origNode->getFieldAmount(sfAmount); - if (amount.isNonZero()) - metaNode.setFieldAmount(sfPreviousBalance, amount); - amount = curNode->getFieldAmount(sfAmount); - if (amount.isNonZero()) - metaNode.setFieldAmount(sfFinalBalance, amount); - - if (origNode->getType() == ltRIPPLE_STATE) - { - metaNode.setFieldAccount(sfLowID, - NewcoinAddress::createAccountID(origNode->getFieldAmount(sfLowLimit).getIssuer())); - metaNode.setFieldAccount(sfHighID, - NewcoinAddress::createAccountID(origNode->getFieldAmount(sfHighLimit).getIssuer())); - } + STObject finals(sfFinalFields); + BOOST_FOREACH(const SerializedType& obj, *curNode) + { // search the deleted node for values saved on delete + if (obj.getFName().shouldMetaDel() && !obj.isDefault()) + finals.addObject(obj); } + if (!finals.empty()) + mSet.getAffectedNode(it.first, *type).addObject(finals); + } - if (origNode->getType() == ltOFFER) - { // check for non-zero balances - STAmount amount = origNode->getFieldAmount(sfTakerPays); - if (amount.isNonZero()) - metaNode.setFieldAmount(sfFinalTakerPays, amount); - amount = origNode->getFieldAmount(sfTakerGets); - if (amount.isNonZero()) - metaNode.setFieldAmount(sfFinalTakerGets, amount); + if ((type == &sfDeletedNode || type == &sfModifiedNode)) + { + STObject mods(sfPreviousFields); + BOOST_FOREACH(const SerializedType& obj, *origNode) + { // search the original node for values saved on modify + if (obj.getFName().shouldMetaMod() && !obj.isDefault() && !curNode->hasMatchingEntry(obj)) + mods.addObject(obj); } - + if (!mods.empty()) + mSet.getAffectedNode(it.first, *type).addObject(mods); } if (type == &sfCreatedNode) // if created, thread to owner(s) @@ -443,28 +438,6 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) if (curNode->isThreadedType()) // always thread to self threadTx(curNode, mLedger, newMod); } - - if (type == &sfModifiedNode) - { - assert(origNode); - if (origNode->isFieldPresent(sfAmount)) - { // node has an amount, covers account root nodes and ripple nodes - STAmount amount = origNode->getFieldAmount(sfAmount); - if (amount != curNode->getFieldAmount(sfAmount)) - metaNode.setFieldAmount(sfPreviousBalance, amount); - } - - if (origNode->getType() == ltOFFER) - { - STAmount amount = origNode->getFieldAmount(sfTakerPays); - if (amount != curNode->getFieldAmount(sfTakerPays)) - metaNode.setFieldAmount(sfPreviousTakerPays, amount); - amount = origNode->getFieldAmount(sfTakerGets); - if (amount != curNode->getFieldAmount(sfTakerGets)) - metaNode.setFieldAmount(sfPreviousTakerGets, amount); - } - - } } // add any new modified nodes to the modification set @@ -472,9 +445,7 @@ void LedgerEntrySet::calcRawMeta(Serializer& s, TER result) it != end; ++it) entryModify(it->second); -#ifdef META_DEBUG - cLog(lsINFO) << "Metadata:" << mSet.getJson(0); -#endif + cLog(lsTRACE) << "Metadata:" << mSet.getJson(0); mSet.addRaw(s, result); } @@ -844,9 +815,9 @@ STAmount LedgerEntrySet::rippleOwed(const uint160& uToAccountID, const uint160& else { cLog(lsINFO) << "rippleOwed: No credit line between " - << NewcoinAddress::createHumanAccountID(uFromAccountID) + << RippleAddress::createHumanAccountID(uFromAccountID) << " and " - << NewcoinAddress::createHumanAccountID(uToAccountID) + << RippleAddress::createHumanAccountID(uToAccountID) << " for " << STAmount::createHumanCurrency(uCurrencyID) << "." ; @@ -884,7 +855,7 @@ uint32 LedgerEntrySet::rippleTransferRate(const uint160& uIssuerID) : QUALITY_ONE; cLog(lsINFO) << boost::str(boost::format("rippleTransferRate: uIssuerID=%s account_exists=%d transfer_rate=%f") - % NewcoinAddress::createHumanAccountID(uIssuerID) + % RippleAddress::createHumanAccountID(uIssuerID) % !!sleAccount % (uQuality/1000000000.0)); @@ -929,8 +900,8 @@ uint32 LedgerEntrySet::rippleQualityIn(const uint160& uToAccountID, const uint16 cLog(lsINFO) << boost::str(boost::format("rippleQuality: %s uToAccountID=%s uFromAccountID=%s uCurrencyID=%s bLine=%d uQuality=%f") % (sfLow == sfLowQualityIn ? "in" : "out") - % NewcoinAddress::createHumanAccountID(uToAccountID) - % NewcoinAddress::createHumanAccountID(uFromAccountID) + % RippleAddress::createHumanAccountID(uToAccountID) + % RippleAddress::createHumanAccountID(uFromAccountID) % STAmount::createHumanCurrency(uCurrencyID) % !!sleRippleState % (uQuality/1000000000.0)); @@ -975,7 +946,7 @@ STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& } cLog(lsINFO) << boost::str(boost::format("accountHolds: uAccountID=%s saAmount=%s") - % NewcoinAddress::createHumanAccountID(uAccountID) + % RippleAddress::createHumanAccountID(uAccountID) % saAmount.getFullText()); return saAmount; @@ -995,7 +966,7 @@ STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& saFunds = saDefault; cLog(lsINFO) << boost::str(boost::format("accountFunds: uAccountID=%s saDefault=%s SELF-FUNDED") - % NewcoinAddress::createHumanAccountID(uAccountID) + % RippleAddress::createHumanAccountID(uAccountID) % saDefault.getFullText()); } else @@ -1003,7 +974,7 @@ STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& saFunds = accountHolds(uAccountID, saDefault.getCurrency(), saDefault.getIssuer()); cLog(lsINFO) << boost::str(boost::format("accountFunds: uAccountID=%s saDefault=%s saFunds=%s") - % NewcoinAddress::createHumanAccountID(uAccountID) + % RippleAddress::createHumanAccountID(uAccountID) % saDefault.getFullText() % saFunds.getFullText()); } @@ -1130,9 +1101,9 @@ void LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uRecei : SLE::pointer(); cLog(lsINFO) << boost::str(boost::format("accountSend> %s (%s) -> %s (%s) : %s") - % NewcoinAddress::createHumanAccountID(uSenderID) + % RippleAddress::createHumanAccountID(uSenderID) % (sleSender ? (sleSender->getFieldAmount(sfBalance)).getFullText() : "-") - % NewcoinAddress::createHumanAccountID(uReceiverID) + % RippleAddress::createHumanAccountID(uReceiverID) % (sleReceiver ? (sleReceiver->getFieldAmount(sfBalance)).getFullText() : "-") % saAmount.getFullText()); @@ -1149,9 +1120,9 @@ void LedgerEntrySet::accountSend(const uint160& uSenderID, const uint160& uRecei } cLog(lsINFO) << boost::str(boost::format("accountSend< %s (%s) -> %s (%s) : %s") - % NewcoinAddress::createHumanAccountID(uSenderID) + % RippleAddress::createHumanAccountID(uSenderID) % (sleSender ? (sleSender->getFieldAmount(sfBalance)).getFullText() : "-") - % NewcoinAddress::createHumanAccountID(uReceiverID) + % RippleAddress::createHumanAccountID(uReceiverID) % (sleReceiver ? (sleReceiver->getFieldAmount(sfBalance)).getFullText() : "-") % saAmount.getFullText()); } diff --git a/src/LedgerEntrySet.h b/src/LedgerEntrySet.h index 2558f1288..1554ab89e 100644 --- a/src/LedgerEntrySet.h +++ b/src/LedgerEntrySet.h @@ -42,7 +42,7 @@ protected: SLE::pointer getForMod(const uint256& node, Ledger::ref ledger, boost::unordered_map& newMods); - bool threadTx(const NewcoinAddress& threadTo, Ledger::ref ledger, + bool threadTx(const RippleAddress& threadTo, Ledger::ref ledger, boost::unordered_map& newMods); bool threadTx(SLE::ref threadTo, Ledger::ref ledger, boost::unordered_map& newMods); @@ -65,8 +65,8 @@ public: void init(Ledger::ref ledger, const uint256& transactionID, uint32 ledgerID); void clear(); - Ledger::pointer& getLedger() { return mLedger; } - const Ledger::pointer& getLedgerRef() const { return mLedger; } + Ledger::pointer& getLedger() { return mLedger; } + Ledger::ref getLedgerRef() const { return mLedger; } // basic entry functions SLE::pointer getEntry(const uint256& index, LedgerEntryAction&); @@ -123,14 +123,24 @@ public: void calcRawMeta(Serializer&, TER result); // iterator functions + typedef std::map::iterator iterator; + typedef std::map::const_iterator const_iterator; bool isEmpty() const { return mEntries.empty(); } std::map::const_iterator begin() const { return mEntries.begin(); } std::map::const_iterator end() const { return mEntries.end(); } - std::map::iterator begin() { return mEntries.begin(); } + std::map::iterator begin() { return mEntries.begin(); } std::map::iterator end() { return mEntries.end(); } static bool intersect(const LedgerEntrySet& lesLeft, const LedgerEntrySet& lesRight); }; +inline LedgerEntrySet::iterator range_begin(LedgerEntrySet& x) { return x.begin(); } +inline LedgerEntrySet::iterator range_end(LedgerEntrySet &x) { return x.end(); } +namespace boost +{ + template<> struct range_mutable_iterator { typedef LedgerEntrySet::iterator type; }; + template<> struct range_const_iterator { typedef LedgerEntrySet::const_iterator type; }; +} + #endif // vim:ts=4 diff --git a/src/LedgerFormats.cpp b/src/LedgerFormats.cpp index c696d72ed..c187286cd 100644 --- a/src/LedgerFormats.cpp +++ b/src/LedgerFormats.cpp @@ -4,10 +4,10 @@ std::map LedgerEntryFormat::byType; std::map LedgerEntryFormat::byName; -#define LEF_BASE \ - << SOElement(sfLedgerIndex, SOE_OPTIONAL) \ - << SOElement(sfLedgerEntryType, SOE_REQUIRED) \ - << SOElement(sfFlags, SOE_REQUIRED) +#define LEF_BASE \ + << SOElement(sfLedgerIndex, SOE_OPTIONAL) \ + << SOElement(sfLedgerEntryType, SOE_REQUIRED) \ + << SOElement(sfFlags, SOE_REQUIRED) #define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE @@ -16,74 +16,74 @@ static bool LEFInit() LedgerEntryFormat* lef; DECLARE_LEF(AccountRoot, ltACCOUNT_ROOT) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfSequence, SOE_REQUIRED) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfAuthorizedKey, SOE_OPTIONAL) - << SOElement(sfEmailHash, SOE_OPTIONAL) - << SOElement(sfWalletLocator, SOE_OPTIONAL) - << SOElement(sfMessageKey, SOE_OPTIONAL) - << SOElement(sfTransferRate, SOE_OPTIONAL) - << SOElement(sfDomain, SOE_OPTIONAL) - << SOElement(sfPublishHash, SOE_OPTIONAL) - << SOElement(sfPublishSize, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfSequence, SOE_REQUIRED) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfAuthorizedKey, SOE_OPTIONAL) + << SOElement(sfEmailHash, SOE_OPTIONAL) + << SOElement(sfWalletLocator, SOE_OPTIONAL) + << SOElement(sfMessageKey, SOE_OPTIONAL) + << SOElement(sfTransferRate, SOE_OPTIONAL) + << SOElement(sfDomain, SOE_OPTIONAL) + << SOElement(sfPublishHash, SOE_OPTIONAL) + << SOElement(sfPublishSize, SOE_OPTIONAL) ; DECLARE_LEF(Contract, ltCONTRACT) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfIssuer, SOE_REQUIRED) - << SOElement(sfOwner, SOE_REQUIRED) - << SOElement(sfExpiration, SOE_REQUIRED) - << SOElement(sfBondAmount, SOE_REQUIRED) - << SOElement(sfCreateCode, SOE_REQUIRED) - << SOElement(sfFundCode, SOE_REQUIRED) - << SOElement(sfRemoveCode, SOE_REQUIRED) - << SOElement(sfExpireCode, SOE_REQUIRED) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfIssuer, SOE_REQUIRED) + << SOElement(sfOwner, SOE_REQUIRED) + << SOElement(sfExpiration, SOE_REQUIRED) + << SOElement(sfBondAmount, SOE_REQUIRED) + << SOElement(sfCreateCode, SOE_REQUIRED) + << SOElement(sfFundCode, SOE_REQUIRED) + << SOElement(sfRemoveCode, SOE_REQUIRED) + << SOElement(sfExpireCode, SOE_REQUIRED) ; DECLARE_LEF(DirectoryNode, ltDIR_NODE) - << SOElement(sfIndexes, SOE_REQUIRED) - << SOElement(sfIndexNext, SOE_OPTIONAL) - << SOElement(sfIndexPrevious, SOE_OPTIONAL) + << SOElement(sfIndexes, SOE_REQUIRED) + << SOElement(sfIndexNext, SOE_OPTIONAL) + << SOElement(sfIndexPrevious, SOE_OPTIONAL) ; DECLARE_LEF(GeneratorMap, ltGENERATOR_MAP) - << SOElement(sfGenerator, SOE_REQUIRED) + << SOElement(sfGenerator, SOE_REQUIRED) ; DECLARE_LEF(Nickname, ltNICKNAME) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfMinimumOffer, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfMinimumOffer, SOE_OPTIONAL) ; DECLARE_LEF(Offer, ltOFFER) - << SOElement(sfAccount, SOE_REQUIRED) - << SOElement(sfSequence, SOE_REQUIRED) - << SOElement(sfTakerPays, SOE_REQUIRED) - << SOElement(sfTakerGets, SOE_REQUIRED) - << SOElement(sfBookDirectory, SOE_REQUIRED) - << SOElement(sfBookNode, SOE_REQUIRED) - << SOElement(sfOwnerNode, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfExpiration, SOE_OPTIONAL) + << SOElement(sfAccount, SOE_REQUIRED) + << SOElement(sfSequence, SOE_REQUIRED) + << SOElement(sfTakerPays, SOE_REQUIRED) + << SOElement(sfTakerGets, SOE_REQUIRED) + << SOElement(sfBookDirectory, SOE_REQUIRED) + << SOElement(sfBookNode, SOE_REQUIRED) + << SOElement(sfOwnerNode, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfExpiration, SOE_OPTIONAL) ; DECLARE_LEF(RippleState, ltRIPPLE_STATE) - << SOElement(sfBalance, SOE_REQUIRED) - << SOElement(sfLowLimit, SOE_REQUIRED) - << SOElement(sfHighLimit, SOE_REQUIRED) - << SOElement(sfLastTxnID, SOE_REQUIRED) - << SOElement(sfLastTxnSeq, SOE_REQUIRED) - << SOElement(sfLowQualityIn, SOE_OPTIONAL) - << SOElement(sfLowQualityOut, SOE_OPTIONAL) - << SOElement(sfHighQualityIn, SOE_OPTIONAL) - << SOElement(sfHighQualityOut, SOE_OPTIONAL) + << SOElement(sfBalance, SOE_REQUIRED) + << SOElement(sfLowLimit, SOE_REQUIRED) + << SOElement(sfHighLimit, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_REQUIRED) + << SOElement(sfPreviousTxnLgrSeq, SOE_REQUIRED) + << SOElement(sfLowQualityIn, SOE_OPTIONAL) + << SOElement(sfLowQualityOut, SOE_OPTIONAL) + << SOElement(sfHighQualityIn, SOE_OPTIONAL) + << SOElement(sfHighQualityOut, SOE_OPTIONAL) ; return true; diff --git a/src/LedgerHistory.cpp b/src/LedgerHistory.cpp index 75519538a..45ccaad46 100644 --- a/src/LedgerHistory.cpp +++ b/src/LedgerHistory.cpp @@ -26,7 +26,7 @@ void LedgerHistory::addLedger(Ledger::pointer ledger) mLedgersByHash.canonicalize(ledger->getHash(), ledger, true); } -void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger) +void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus) { assert(ledger && ledger->isAccepted()); uint256 h(ledger->getHash()); @@ -37,7 +37,7 @@ void LedgerHistory::addAcceptedLedger(Ledger::pointer ledger) assert(ledger->isImmutable()); mLedgersByIndex.insert(std::make_pair(ledger->getLedgerSeq(), ledger)); - boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, ledger)); + boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, ledger, fromConsensus)); thread.detach(); } @@ -45,11 +45,13 @@ Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index) { boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex()); std::map::iterator it(mLedgersByIndex.find(index)); - if (it != mLedgersByIndex.end()) return it->second; + if (it != mLedgersByIndex.end()) + return it->second; sl.unlock(); Ledger::pointer ret(Ledger::loadByIndex(index)); - if (!ret) return ret; + if (!ret) + return ret; assert(ret->getLedgerSeq() == index); sl.lock(); @@ -61,25 +63,14 @@ Ledger::pointer LedgerHistory::getLedgerBySeq(uint32 index) Ledger::pointer LedgerHistory::getLedgerByHash(const uint256& hash) { Ledger::pointer ret = mLedgersByHash.fetch(hash); - if (ret) return ret; - -#if 0 -// FIXME: A ledger without SHA maps isn't very useful -// This code will need to build them - - // The fix is to audit all callers to this function and replace them with - // higher-level functions that ask more-specific questions that we can - // test against our database + if (ret) + return ret; ret = Ledger::loadByHash(hash); - if (!ret) return ret; + if (!ret) + return ret; assert(ret->getHash() == hash); - boost::recursive_mutex::scoped_lock sl(mLedgersByHash.peekMutex()); - mLedgersByHash.canonicalize(hash, ret); - if (ret->isAccepted()) mLedgersByIndex[ret->getLedgerSeq()] = ret; -#endif - return ret; } diff --git a/src/LedgerHistory.h b/src/LedgerHistory.h index e593c739b..ed58f4fc9 100644 --- a/src/LedgerHistory.h +++ b/src/LedgerHistory.h @@ -13,7 +13,7 @@ public: LedgerHistory(); void addLedger(Ledger::pointer ledger); - void addAcceptedLedger(Ledger::pointer ledger); + void addAcceptedLedger(Ledger::pointer ledger, bool fromConsensus); Ledger::pointer getLedgerBySeq(uint32 index); Ledger::pointer getLedgerByHash(const uint256& hash); diff --git a/src/LedgerMaster.cpp b/src/LedgerMaster.cpp index dd0f6ecca..872be8920 100644 --- a/src/LedgerMaster.cpp +++ b/src/LedgerMaster.cpp @@ -4,9 +4,11 @@ #include #include "Application.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "Log.h" +SETUP_LOG(); + uint32 LedgerMaster::getCurrentLedgerIndex() { return mCurrentLedger->getLedgerSeq(); @@ -22,19 +24,19 @@ void LedgerMaster::pushLedger(Ledger::ref newLedger) { // Caller should already have properly assembled this ledger into "ready-to-close" form -- // all candidate transactions must already be applied - Log(lsINFO) << "PushLedger: " << newLedger->getHash(); - ScopedLock sl(mLock); + cLog(lsINFO) << "PushLedger: " << newLedger->getHash(); + boost::recursive_mutex::scoped_lock ml(mLock); if (!!mFinalizedLedger) { mFinalizedLedger->setClosed(); - Log(lsTRACE) << "Finalizes: " << mFinalizedLedger->getHash(); + cLog(lsTRACE) << "Finalizes: " << mFinalizedLedger->getHash(); } mFinalizedLedger = mCurrentLedger; mCurrentLedger = newLedger; mEngine.setLedger(newLedger); } -void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL) +void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus) { assert(newLCL->isClosed() && newLCL->isAccepted()); assert(!newOL->isClosed() && !newOL->isAccepted()); @@ -43,12 +45,12 @@ void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL) { assert(newLCL->isClosed()); assert(newLCL->isImmutable()); - mLedgerHistory.addAcceptedLedger(newLCL); - Log(lsINFO) << "StashAccepted: " << newLCL->getHash(); + mLedgerHistory.addAcceptedLedger(newLCL, fromConsensus); + cLog(lsINFO) << "StashAccepted: " << newLCL->getHash(); } + boost::recursive_mutex::scoped_lock ml(mLock); mFinalizedLedger = newLCL; - ScopedLock sl(mLock); mCurrentLedger = newOL; mEngine.setLedger(newOL); } @@ -56,11 +58,15 @@ void LedgerMaster::pushLedger(Ledger::ref newLCL, Ledger::ref newOL) void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current) { assert(lastClosed && current); - mFinalizedLedger = lastClosed; - mFinalizedLedger->setClosed(); - mFinalizedLedger->setAccepted(); - mCurrentLedger = current; + { + boost::recursive_mutex::scoped_lock ml(mLock); + mFinalizedLedger = lastClosed; + mFinalizedLedger->setClosed(); + mFinalizedLedger->setAccepted(); + mCurrentLedger = current; + } + assert(!mCurrentLedger->isClosed()); mEngine.setLedger(mCurrentLedger); } @@ -68,10 +74,11 @@ void LedgerMaster::switchLedgers(Ledger::ref lastClosed, Ledger::ref current) void LedgerMaster::storeLedger(Ledger::ref ledger) { mLedgerHistory.addLedger(ledger); + if (ledger->isAccepted()) + mLedgerHistory.addAcceptedLedger(ledger, false); } - Ledger::pointer LedgerMaster::closeLedger() { boost::recursive_mutex::scoped_lock sl(mLock); @@ -88,5 +95,84 @@ TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEng return result; } +void LedgerMaster::acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq) +{ + mMissingLedger = theApp->getMasterLedgerAcquire().findCreate(ledgerHash); + if (mMissingLedger->isComplete()) + { + mMissingLedger = LedgerAcquire::pointer(); + return; + } + mMissingSeq = ledgerSeq; + if (mMissingLedger->setAccept()) + mMissingLedger->addOnComplete(boost::bind(&LedgerMaster::missingAcquireComplete, this, _1)); +} + +void LedgerMaster::missingAcquireComplete(LedgerAcquire::pointer acq) +{ + boost::recursive_mutex::scoped_lock ml(mLock); + + if (acq->isFailed() && (mMissingSeq != 0)) + { + cLog(lsWARNING) << "Acquire failed for " << mMissingSeq; + } + + mMissingLedger = LedgerAcquire::pointer(); + mMissingSeq = 0; + + if (!acq->isFailed()) + { + boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, acq->getLedger(), false)); + thread.detach(); + setFullLedger(acq->getLedger()); + } +} + +void LedgerMaster::setFullLedger(Ledger::ref ledger) +{ + boost::recursive_mutex::scoped_lock ml(mLock); + + mCompleteLedgers.setValue(ledger->getLedgerSeq()); + + if ((ledger->getLedgerSeq() != 0) && mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1)) + { // we think we have the previous ledger, double check + Ledger::pointer prevLedger = getLedgerBySeq(ledger->getLedgerSeq() - 1); + if (prevLedger && (prevLedger->getHash() != ledger->getParentHash())) + { + cLog(lsWARNING) << "Ledger " << ledger->getLedgerSeq() << " invalidates prior ledger"; + mCompleteLedgers.clearValue(prevLedger->getLedgerSeq()); + } + } + + if (mMissingLedger && mMissingLedger->isComplete()) + mMissingLedger = LedgerAcquire::pointer(); + + if (mMissingLedger || !theConfig.FULL_HISTORY) + return; + + // see if there's a ledger gap we need to fill + if (!mCompleteLedgers.hasValue(ledger->getLedgerSeq() - 1)) + { + cLog(lsINFO) << "We need the ledger before the ledger we just accepted"; + acquireMissingLedger(ledger->getParentHash(), ledger->getLedgerSeq() - 1); + } + else + { + uint32 prevMissing = mCompleteLedgers.prevMissing(ledger->getLedgerSeq()); + if (prevMissing != RangeSet::RangeSetAbsent) + { + cLog(lsINFO) << "Ledger " << prevMissing << " is missing"; + assert(!mCompleteLedgers.hasValue(prevMissing)); + Ledger::pointer nextLedger = getLedgerBySeq(prevMissing + 1); + if (nextLedger) + acquireMissingLedger(nextLedger->getParentHash(), nextLedger->getLedgerSeq() - 1); + else + { + mCompleteLedgers.clearValue(prevMissing); + cLog(lsWARNING) << "We have a gap we can't fix: " << prevMissing + 1; + } + } + } +} // vim:ts=4 diff --git a/src/LedgerMaster.h b/src/LedgerMaster.h index 2f198b884..278b25623 100644 --- a/src/LedgerMaster.h +++ b/src/LedgerMaster.h @@ -5,8 +5,10 @@ #include "LedgerHistory.h" #include "Peer.h" #include "types.h" +#include "LedgerAcquire.h" #include "Transaction.h" #include "TransactionEngine.h" +#include "RangeSet.h" // Tracks the current ledger and any ledgers in the process of closing // Tracks ledger history @@ -25,13 +27,20 @@ class LedgerMaster std::map mHeldTransactionsByID; + RangeSet mCompleteLedgers; + LedgerAcquire::pointer mMissingLedger; + uint32 mMissingSeq; + void applyFutureTransactions(uint32 ledgerIndex); bool isValidTransaction(const Transaction::pointer& trans); bool isTransactionOnFutureList(const Transaction::pointer& trans); + void acquireMissingLedger(const uint256& ledgerHash, uint32 ledgerSeq); + void missingAcquireComplete(LedgerAcquire::pointer); + public: - LedgerMaster() { ; } + LedgerMaster() : mMissingSeq(0) { ; } uint32 getCurrentLedgerIndex(); @@ -46,11 +55,15 @@ public: TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params); void pushLedger(Ledger::ref newLedger); - void pushLedger(Ledger::ref newLCL, Ledger::ref newOL); + void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus); void storeLedger(Ledger::ref); + void setFullLedger(Ledger::ref ledger); + void switchLedgers(Ledger::ref lastClosed, Ledger::ref newCurrent); + std::string getCompleteLedgers() { return mCompleteLedgers.toString(); } + Ledger::pointer closeLedger(); Ledger::pointer getLedgerBySeq(uint32 index) @@ -76,6 +89,8 @@ public: return mLedgerHistory.getLedgerByHash(hash); } + void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); } + bool addHeldTransaction(const Transaction::pointer& trans); }; diff --git a/src/LedgerProposal.cpp b/src/LedgerProposal.cpp index e7953abad..775f7b93d 100644 --- a/src/LedgerProposal.cpp +++ b/src/LedgerProposal.cpp @@ -8,7 +8,7 @@ #include "HashPrefixes.h" LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& tx, uint32 closeTime, - const NewcoinAddress& naPeerPublic) : + const RippleAddress& naPeerPublic) : mPreviousLedger(pLgr), mCurrentHash(tx), mCloseTime(closeTime), mProposeSeq(seq), mPublicKey(naPeerPublic) { // XXX Validate key. @@ -20,12 +20,11 @@ LedgerProposal::LedgerProposal(const uint256& pLgr, uint32 seq, const uint256& t } -LedgerProposal::LedgerProposal(const NewcoinAddress& naSeed, const uint256& prevLgr, - const uint256& position, uint32 closeTime) : +LedgerProposal::LedgerProposal(const RippleAddress& naPub, const RippleAddress& naPriv, + const uint256& prevLgr, const uint256& position, uint32 closeTime) : mPreviousLedger(prevLgr), mCurrentHash(position), mCloseTime(closeTime), mProposeSeq(0), - mPublicKey(NewcoinAddress::createNodePublic(naSeed)), - mPrivateKey(NewcoinAddress::createNodePrivate(naSeed)) -{ + mPublicKey(naPub), mPrivateKey(naPriv) +{ // OPTIMIZEME: This is expensive. We create both the public and private keys separately each time mPeerID = mPublicKey.getNodeID(); mTime = boost::posix_time::second_clock::universal_time(); } diff --git a/src/LedgerProposal.h b/src/LedgerProposal.h index fb7df3430..4119a444d 100644 --- a/src/LedgerProposal.h +++ b/src/LedgerProposal.h @@ -8,7 +8,7 @@ #include "../json/value.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "Serializer.h" class LedgerProposal @@ -19,8 +19,8 @@ protected: uint32 mCloseTime, mProposeSeq; uint160 mPeerID; - NewcoinAddress mPublicKey; - NewcoinAddress mPrivateKey; // If ours + RippleAddress mPublicKey; + RippleAddress mPrivateKey; // If ours std::string mSignature; // set only if needed boost::posix_time::ptime mTime; @@ -32,11 +32,11 @@ public: // proposal from peer LedgerProposal(const uint256& prevLgr, uint32 proposeSeq, const uint256& propose, - uint32 closeTime, const NewcoinAddress& naPeerPublic); + uint32 closeTime, const RippleAddress& naPeerPublic); // our first proposal - LedgerProposal(const NewcoinAddress& privKey, const uint256& prevLedger, const uint256& position, - uint32 closeTime); + LedgerProposal(const RippleAddress& pubKey, const RippleAddress& privKey, + const uint256& prevLedger, const uint256& position, uint32 closeTime); // an unsigned "dummy" proposal for nodes not validating LedgerProposal(const uint256& prevLedger, const uint256& position, uint32 closeTime); @@ -51,7 +51,7 @@ public: const uint256& getPrevLedger() const { return mPreviousLedger; } uint32 getProposeSeq() const { return mProposeSeq; } uint32 getCloseTime() const { return mCloseTime; } - const NewcoinAddress& peekPublic() const { return mPublicKey; } + const RippleAddress& peekPublic() const { return mPublicKey; } std::vector getPubKey() const { return mPublicKey.getNodePublic(); } std::vector sign(); diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp index 9ce760fc4..6119123a4 100644 --- a/src/LedgerTiming.cpp +++ b/src/LedgerTiming.cpp @@ -6,6 +6,7 @@ #include #include "Log.h" +SETUP_LOG(); // NOTE: First and last times must be repeated int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 }; @@ -23,7 +24,7 @@ bool ContinuousLedgerTiming::shouldClose( if ((previousMSeconds < -1000) || (previousMSeconds > 600000) || (currentMSeconds < -1000) || (currentMSeconds > 600000)) { - Log(lsWARNING) << + cLog(lsWARNING) << boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") % (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentMSeconds % previousMSeconds); @@ -34,14 +35,14 @@ bool ContinuousLedgerTiming::shouldClose( { // no transactions so far this interval if (proposersClosed > (previousProposers / 4)) // did we miss a transaction? { - Log(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, " + cLog(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, " << previousProposers << " before)"; return true; } #if 0 // This false triggers on the genesis ledger if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close { - Log(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")"; + cLog(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")"; if (previousMSeconds < 2000) return previousMSeconds; return previousMSeconds - 1000; @@ -59,12 +60,14 @@ bool ContinuousLedgerTiming::haveConsensus( int previousProposers, // proposers in the last closing (not including us) int currentProposers, // proposers in this closing so far (not including us) int currentAgree, // proposers who agree with us - int currentClosed, // proposers who have currently closed their ledgers + int currentFinished, // proposers who have validated a ledger after this one int previousAgreeTime, // how long it took to agree on the last ledger - int currentAgreeTime) // how long we've been trying to agree + int currentAgreeTime, // how long we've been trying to agree + bool forReal) // deciding whether to stop consensus process { - Log(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d") % - currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime); + cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") % + currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime % + (forReal ? "" : "X")); if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) return false; @@ -73,7 +76,7 @@ bool ContinuousLedgerTiming::haveConsensus( { // Less than 3/4 of the last ledger's proposers are present, we may need more time if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS)) { - Log(lsTRACE) << "too fast, not enough proposers"; + tLog(forReal, lsTRACE) << "too fast, not enough proposers"; return false; } } @@ -81,19 +84,19 @@ bool ContinuousLedgerTiming::haveConsensus( // If 80% of current proposers (plus us) agree on a set, we have consensus if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) { - Log(lsTRACE) << "normal consensus"; + tLog(forReal, lsINFO) << "normal consensus"; return true; } - // If 50% of the nodes on your UNL (minus us) have closed, you should close - if (((currentClosed * 100 - 100) / (currentProposers + 1)) > 50) + // If 50% of the nodes on your UNL have moved on, you should declare consensus + if (((currentFinished * 100) / (currentProposers + 1)) > 50) { - Log(lsTRACE) << "many closers"; + tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on"; return true; } // no consensus yet - Log(lsTRACE) << "no consensus"; + tLog(forReal, lsTRACE) << "no consensus"; return false; } diff --git a/src/LedgerTiming.h b/src/LedgerTiming.h index 551c9bfce..6cf535c40 100644 --- a/src/LedgerTiming.h +++ b/src/LedgerTiming.h @@ -65,7 +65,8 @@ public: static bool haveConsensus( int previousProposers, int currentProposers, int currentAgree, int currentClosed, - int previousAgreeTime, int currentAgreeTime); + int previousAgreeTime, int currentAgreeTime, + bool forReal); static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq); }; diff --git a/src/Log.cpp b/src/Log.cpp index 74904c6a3..c4cf54ea2 100644 --- a/src/Log.cpp +++ b/src/Log.cpp @@ -4,6 +4,7 @@ #include #include +#include boost::recursive_mutex Log::sLock; @@ -15,6 +16,28 @@ uint32 Log::logRotateCounter = 0; LogPartition* LogPartition::headLog = NULL; +LogPartition::LogPartition(const char *name) : mNextLog(headLog), mMinSeverity(lsWARNING) +{ + const char *ptr = strrchr(name, '/'); + mName = (ptr == NULL) ? name : (ptr + 1); + + size_t p = mName.find(".cpp"); + if (p != std::string::npos) + mName.erase(mName.begin() + p, mName.end()); + + headLog = this; +} + +std::vector< std::pair > LogPartition::getSeverities() +{ + std::vector< std::pair > sevs; + + for (LogPartition *l = headLog; l != NULL; l = l->mNextLog) + sevs.push_back(std::make_pair(l->mName, Log::severityToString(l->mMinSeverity))); + + return sevs; +} + Log::~Log() { std::string logMsg = boost::posix_time::to_simple_string(boost::posix_time::second_clock::universal_time()); @@ -26,6 +49,7 @@ Log::~Log() case lsWARNING: logMsg += " WARN "; break; case lsERROR: logMsg += " EROR "; break; case lsFATAL: logMsg += " FATL "; break; + case lsINVALID: assert(false); return; } logMsg += oss.str(); boost::recursive_mutex::scoped_lock sl(sLock); @@ -77,11 +101,50 @@ std::string Log::rotateLog(void) } -void Log::setMinSeverity(LogSeverity s) +void Log::setMinSeverity(LogSeverity s, bool all) { boost::recursive_mutex::scoped_lock sl(sLock); sMinSeverity = s; - LogPartition::setSeverity(s); + if (all) + LogPartition::setSeverity(s); +} + +LogSeverity Log::getMinSeverity() +{ + boost::recursive_mutex::scoped_lock sl(sLock); + return sMinSeverity; +} + +std::string Log::severityToString(LogSeverity s) +{ + switch (s) + { + case lsTRACE: return "Trace"; + case lsDEBUG: return "Debug"; + case lsINFO: return "Info"; + case lsWARNING: return "Warning"; + case lsERROR: return "Error"; + case lsFATAL: return "Fatal"; + default: assert(false); return "Unknown"; + } + +} + +LogSeverity Log::stringToSeverity(const std::string& s) +{ + if (boost::iequals(s, "trace")) + return lsTRACE; + if (boost::iequals(s, "debug")) + return lsDEBUG; + if (boost::iequals(s, "info") || boost::iequals(s, "information")) + return lsINFO; + if (boost::iequals(s, "warn") || boost::iequals(s, "warning") || boost::iequals(s, "warnings")) + return lsWARNING; + if (boost::iequals(s, "error") || boost::iequals(s, "errors")) + return lsERROR; + if (boost::iequals(s, "fatal") || boost::iequals(s, "fatals")) + return lsFATAL; + return lsINVALID; } void Log::setLogFile(boost::filesystem::path path) @@ -103,14 +166,15 @@ void Log::setLogFile(boost::filesystem::path path) pathToLog = new boost::filesystem::path(path); } -void LogPartition::setSeverity(const char *partition, LogSeverity severity) +bool LogPartition::setSeverity(const std::string& partition, LogSeverity severity) { for (LogPartition *p = headLog; p != NULL; p = p->mNextLog) - if (p->mName == partition) + if (boost::iequals(p->mName, partition)) { p->mMinSeverity = severity; - return; + return true; } + return false; } void LogPartition::setSeverity(LogSeverity severity) diff --git a/src/Log.h b/src/Log.h index 10c18fd0e..7dbc2c833 100644 --- a/src/Log.h +++ b/src/Log.h @@ -28,6 +28,7 @@ enum LogSeverity { + lsINVALID = -1, // used to indicate an invalid severity lsTRACE = 0, // Very low-level progress information, details inside an operation lsDEBUG = 1, // Function-level progress information, operations lsINFO = 2, // Server-level progress information, major operations @@ -46,20 +47,16 @@ protected: std::string mName; public: - LogPartition(const char *name) : mNextLog(headLog), mMinSeverity(lsWARNING) - { - const char *ptr = strrchr(name, '/'); - mName = (ptr == NULL) ? name : ptr; - headLog = this; - } + LogPartition(const char *name); - bool doLog(enum LogSeverity s) + bool doLog(LogSeverity s) { return s >= mMinSeverity; } - static void setSeverity(const char *partition, LogSeverity severity); + static bool setSeverity(const std::string& partition, LogSeverity severity); static void setSeverity(LogSeverity severity); + static std::vector< std::pair > getSeverities(); }; class Log @@ -95,7 +92,11 @@ public: return oss; } - static void setMinSeverity(LogSeverity); + static std::string severityToString(LogSeverity); + static LogSeverity stringToSeverity(const std::string&); + + static LogSeverity getMinSeverity(); + static void setMinSeverity(LogSeverity, bool all); static void setLogFile(boost::filesystem::path); static std::string rotateLog(void); }; diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index 477cca227..b3ee6079b 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -7,7 +7,7 @@ #include "LedgerConsensus.h" #include "LedgerTiming.h" #include "Log.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include #include @@ -193,7 +193,7 @@ Transaction::pointer NetworkOPs::findTransactionByID(const uint256& transactionI } int NetworkOPs::findTransactionsBySource(const uint256& uLedger, std::list& txns, - const NewcoinAddress& sourceAccount, uint32 minSeq, uint32 maxSeq) + const RippleAddress& sourceAccount, uint32 minSeq, uint32 maxSeq) { AccountState::pointer state = getAccountState(uLedger, sourceAccount); if (!state) return 0; @@ -215,7 +215,7 @@ int NetworkOPs::findTransactionsBySource(const uint256& uLedger, std::list& txns, - const NewcoinAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions) + const RippleAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions) { // WRITEME return 0; @@ -225,7 +225,7 @@ int NetworkOPs::findTransactionsByDestination(std::list& t // Account functions // -AccountState::pointer NetworkOPs::getAccountState(const uint256& uLedger, const NewcoinAddress& accountID) +AccountState::pointer NetworkOPs::getAccountState(const uint256& uLedger, const RippleAddress& accountID) { return mLedgerMaster->getLedgerByHash(uLedger)->getAccountState(accountID); } @@ -294,12 +294,12 @@ NicknameState::pointer NetworkOPs::getNicknameState(const uint256& uLedger, cons // Owner functions // -Json::Value NetworkOPs::getOwnerInfo(const uint256& uLedger, const NewcoinAddress& naAccount) +Json::Value NetworkOPs::getOwnerInfo(const uint256& uLedger, const RippleAddress& naAccount) { return getOwnerInfo(mLedgerMaster->getLedgerByHash(uLedger), naAccount); } -Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const NewcoinAddress& naAccount) +Json::Value NetworkOPs::getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount) { Json::Value jvObjects(Json::objectValue); @@ -496,7 +496,7 @@ bool NetworkOPs::checkLastClosedLedger(const std::vector& peerLis ValidationCount& ourVC = ledgers[closedLedger]; - if ((theConfig.LEDGER_CREATOR) && (mMode >= omTRACKING)) + if (mMode >= omTRACKING) { ++ourVC.nodesUsing; uint160 ourAddress = theApp->getWallet().getNodePublic().getNodeID(); @@ -638,7 +638,7 @@ void NetworkOPs::switchLastClosedLedger(Ledger::pointer newLedger, bool duringCo theApp->getConnectionPool().relayMessage(NULL, packet); } -int NetworkOPs::beginConsensus(const uint256& networkClosed, Ledger::pointer closingLedger) +int NetworkOPs::beginConsensus(const uint256& networkClosed, Ledger::ref closingLedger) { cLog(lsINFO) << "Consensus time for ledger " << closingLedger->getLedgerSeq(); cLog(lsINFO) << " LCL is " << closingLedger->getParentHash(); @@ -678,7 +678,7 @@ bool NetworkOPs::haveConsensusObject() bool ledgerChange = checkLastClosedLedger(peerList, networkClosed); if (!ledgerChange) { - cLog(lsWARNING) << "Beginning consensus due to peer action"; + cLog(lsINFO) << "Beginning consensus due to peer action"; beginConsensus(networkClosed, mLedgerMaster->getCurrentLedger()); } return mConsensus; @@ -686,7 +686,7 @@ bool NetworkOPs::haveConsensusObject() // <-- bool: true to relay bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger, - uint32 closeTime, const std::string& pubKey, const std::string& signature, const NewcoinAddress& nodePublic) + uint32 closeTime, const std::string& pubKey, const std::string& signature, const RippleAddress& nodePublic) { // JED: does mConsensus need to be locked? @@ -704,7 +704,7 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, cons if (!theApp->isNew(s.getSHA512Half())) return false; - NewcoinAddress naPeerPublic = NewcoinAddress::createNodePublic(strCopy(pubKey)); + RippleAddress naPeerPublic = RippleAddress::createNodePublic(strCopy(pubKey)); if (!haveConsensusObject()) { @@ -712,6 +712,12 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, cons return mMode != omFULL; } + if (mConsensus->isOurPubKey(naPeerPublic)) + { + cLog(lsTRACE) << "Received our own validation"; + return false; + } + // Is this node on our UNL? if (!theApp->getUNL().nodeInUNL(naPeerPublic)) { @@ -814,7 +820,7 @@ void NetworkOPs::setMode(OperatingMode om) } std::vector< std::pair > - NetworkOPs::getAffectedAccounts(const NewcoinAddress& account, uint32 minLedger, uint32 maxLedger) + NetworkOPs::getAffectedAccounts(const RippleAddress& account, uint32 minLedger, uint32 maxLedger) { std::vector< std::pair > affectedAccounts; @@ -836,14 +842,14 @@ std::vector< std::pair > return affectedAccounts; } -std::vector +std::vector NetworkOPs::getLedgerAffectedAccounts(uint32 ledgerSeq) { - std::vector accounts; + std::vector accounts; std::string sql = str(boost::format ("SELECT DISTINCT Account FROM AccountTransactions INDEXED BY AcctLgrIndex WHERE LedgerSeq = '%d';") % ledgerSeq); - NewcoinAddress acct; + RippleAddress acct; { Database* db = theApp->getTxnDB()->getDB(); ScopedLock dblock = theApp->getTxnDB()->getDBLock(); @@ -858,7 +864,7 @@ std::vector bool NetworkOPs::recvValidation(const SerializedValidation::pointer& val) { - cLog(lsINFO) << "recvValidation " << val->getLedgerHash(); + cLog(lsDEBUG) << "recvValidation " << val->getLedgerHash(); return theApp->getValidations().addValidation(val); } @@ -875,14 +881,16 @@ Json::Value NetworkOPs::getServerInfo() default: info["serverState"] = "unknown"; } - if (!theConfig.VALIDATION_SEED.isValid()) + if (!theConfig.VALIDATION_PUB.isValid()) info["serverState"] = "none"; else - info["validationPKey"] = NewcoinAddress::createNodePublic(theConfig.VALIDATION_SEED).humanNodePublic(); + info["validationPKey"] = theConfig.VALIDATION_PUB.humanNodePublic(); if (mNeedNetworkLedger) info["networkLedger"] = "waiting"; + info["completeLedgers"] = theApp->getMasterLedger().getCompleteLedgers(); + Json::Value lastClose = Json::objectValue; lastClose["proposers"] = theApp->getOPs().getPreviousProposers(); lastClose["convergeTime"] = theApp->getOPs().getPreviousConvergeTime(); @@ -898,7 +906,7 @@ Json::Value NetworkOPs::getServerInfo() // Monitoring: publisher side // -Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const NewcoinAddress& naAccountID) +Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID) { Json::Value jvObj(Json::objectValue); @@ -912,7 +920,7 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ne return jvObj; } -void NetworkOPs::pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj) +void NetworkOPs::pubAccountInfo(const RippleAddress& naAccountID, const Json::Value& jvObj) { boost::interprocess::sharable_lock sl(mMonitorLock); @@ -935,6 +943,10 @@ void NetworkOPs::pubAccountInfo(const NewcoinAddress& naAccountID, const Json::V void NetworkOPs::pubLedger(Ledger::ref lpAccepted) { + // Don't publish to clients ledgers we don't trust. + if (NetworkOPs::omDISCONNECTED == getOperatingMode()) + return; + { boost::interprocess::sharable_lock sl(mMonitorLock); @@ -960,7 +972,7 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) { Json::Value jvAccounts(Json::arrayValue); - BOOST_FOREACH(const NewcoinAddress& naAccountID, getLedgerAffectedAccounts(lpAccepted->getLedgerSeq())) + BOOST_FOREACH(const RippleAddress& naAccountID, getLedgerAffectedAccounts(lpAccepted->getLedgerSeq())) { jvAccounts.append(Json::Value(naAccountID.humanAccountID())); } @@ -1009,13 +1021,13 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted) } } - // Publish bootsrap information for accounts. + // Publish bootstrap information for accounts. { boost::interprocess::scoped_lock sl(mMonitorLock); BOOST_FOREACH(const subInfoMapType::iterator::value_type& it, mBootAccountInfo) { - Json::Value jvObj = pubBootstrapAccountInfo(lpAccepted, NewcoinAddress::createAccountID(it.first)); + Json::Value jvObj = pubBootstrapAccountInfo(lpAccepted, RippleAddress::createAccountID(it.first)); BOOST_FOREACH(InfoSub* ispListener, it.second) { @@ -1073,7 +1085,7 @@ void NetworkOPs::pubTransactionAccounts(Ledger::ref lpCurrent, const SerializedT if (!mSubAccountTransaction.empty()) { - BOOST_FOREACH(const NewcoinAddress& naAccountPublic, stTxn.getAffectedAccounts()) + BOOST_FOREACH(const RippleAddress& naAccountPublic, stTxn.getAffectedAccounts()) { subInfoMapIterator simiIt = mSubAccountTransaction.find(naAccountPublic.getAccountID()); @@ -1118,11 +1130,11 @@ void NetworkOPs::pubTransaction(Ledger::ref lpCurrent, const SerializedTransacti // Monitoring // -void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) +void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) + BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { // Register for bootstrap info. subInfoMapType::iterator simIterator; @@ -1160,11 +1172,11 @@ void NetworkOPs::subAccountInfo(InfoSub* ispListener, const boost::unordered_set } } -void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) +void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) + BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { subInfoMapType::iterator simIterator = mSubAccountInfo.find(naAccountID.getAccountID()); if (simIterator == mSubAccountInfo.end()) @@ -1186,11 +1198,11 @@ void NetworkOPs::unsubAccountInfo(InfoSub* ispListener, const boost::unordered_s } } -void NetworkOPs::subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) +void NetworkOPs::subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) + BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { subInfoMapType::iterator simIterator = mSubAccountTransaction.find(naAccountID.getAccountID()); if (simIterator == mSubAccountTransaction.end()) @@ -1209,11 +1221,11 @@ void NetworkOPs::subAccountTransaction(InfoSub* ispListener, const boost::unorde } } -void NetworkOPs::unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) +void NetworkOPs::unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs) { boost::interprocess::scoped_lock sl(mMonitorLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, vnaAccountIDs) + BOOST_FOREACH(const RippleAddress& naAccountID, vnaAccountIDs) { subInfoMapType::iterator simIterator = mSubAccountTransaction.find(naAccountID.getAccountID()); if (simIterator == mSubAccountTransaction.end()) @@ -1250,7 +1262,7 @@ uint32 NetworkOPs::acceptLedger() return mLedgerMaster->getCurrentLedger()->getLedgerSeq(); } -void NetworkOPs::storeProposal(const LedgerProposal::pointer& proposal, const NewcoinAddress& peerPublic) +void NetworkOPs::storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic) { std::list& props = mStoredProposals[peerPublic.getNodeID()]; if (props.size() >= (mLastCloseProposers + 10)) diff --git a/src/NetworkOPs.h b/src/NetworkOPs.h index cd2e8bede..5c54c3a16 100644 --- a/src/NetworkOPs.h +++ b/src/NetworkOPs.h @@ -78,6 +78,7 @@ protected: boost::unordered_set mSubLedger; // ledger accepteds boost::unordered_set mSubLedgerAccounts; // ledger accepteds + affected accounts boost::unordered_set mSubTransaction; // all transactions + boost::unordered_set mSubTxMeta; // all transaction meta // subInfoMapType mSubTransactionAccounts; void setMode(OperatingMode); @@ -87,7 +88,7 @@ protected: void pubTransactionAccounts(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted); bool haveConsensusObject(); - Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const NewcoinAddress& naAccountID); + Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID); public: NetworkOPs(boost::asio::io_service& io_service, LedgerMaster* pLedgerMaster); @@ -122,16 +123,16 @@ public: Transaction::pointer processTransaction(Transaction::pointer transaction, Peer* source = NULL); Transaction::pointer findTransactionByID(const uint256& transactionID); - int findTransactionsBySource(const uint256& uLedger, std::list&, const NewcoinAddress& sourceAccount, + int findTransactionsBySource(const uint256& uLedger, std::list&, const RippleAddress& sourceAccount, uint32 minSeq, uint32 maxSeq); - int findTransactionsByDestination(std::list&, const NewcoinAddress& destinationAccount, + int findTransactionsByDestination(std::list&, const RippleAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions); // // Account functions // - AccountState::pointer getAccountState(const uint256& uLedger, const NewcoinAddress& accountID); + AccountState::pointer getAccountState(const uint256& uLedger, const RippleAddress& accountID); SLE::pointer getGenerator(const uint256& uLedger, const uint160& uGeneratorID); // @@ -151,8 +152,8 @@ public: // Owner functions // - Json::Value getOwnerInfo(const uint256& uLedger, const NewcoinAddress& naAccount); - Json::Value getOwnerInfo(Ledger::pointer lpLedger, const NewcoinAddress& naAccount); + Json::Value getOwnerInfo(const uint256& uLedger, const RippleAddress& naAccount); + Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount); // raw object operations bool findRawLedger(const uint256& ledgerHash, std::vector& rawLedger); @@ -168,7 +169,7 @@ public: // ledger proposal/close functions bool recvPropose(uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger, uint32 closeTime, - const std::string& pubKey, const std::string& signature, const NewcoinAddress& nodePublic); + const std::string& pubKey, const std::string& signature, const RippleAddress& nodePublic); bool gotTXData(const boost::shared_ptr& peer, const uint256& hash, const std::list& nodeIDs, const std::list< std::vector >& nodeData); bool recvValidation(const SerializedValidation::pointer& val); @@ -180,7 +181,7 @@ public: void checkState(const boost::system::error_code& result); void switchLastClosedLedger(Ledger::pointer newLedger, bool duringConsensus); // Used for the "jump" case bool checkLastClosedLedger(const std::vector&, uint256& networkClosed); - int beginConsensus(const uint256& networkClosed, Ledger::pointer closingLedger); + int beginConsensus(const uint256& networkClosed, Ledger::ref closingLedger); void endConsensus(bool correctLCL); void setStandAlone() { setMode(omFULL); } void setStateTimer(); @@ -197,19 +198,19 @@ public: uint32 acceptLedger(); boost::unordered_map >& peekStoredProposals() { return mStoredProposals; } - void storeProposal(const LedgerProposal::pointer& proposal, const NewcoinAddress& peerPublic); + void storeProposal(const LedgerProposal::pointer& proposal, const RippleAddress& peerPublic); // client information retrieval functions std::vector< std::pair > - getAffectedAccounts(const NewcoinAddress& account, uint32 minLedger, uint32 maxLedger); - std::vector getLedgerAffectedAccounts(uint32 ledgerSeq); + getAffectedAccounts(const RippleAddress& account, uint32 minLedger, uint32 maxLedger); + std::vector getLedgerAffectedAccounts(uint32 ledgerSeq); std::vector getLedgerTransactions(uint32 ledgerSeq); // // Monitoring: publisher side // - void pubAccountInfo(const NewcoinAddress& naAccountID, const Json::Value& jvObj); + void pubAccountInfo(const RippleAddress& naAccountID, const Json::Value& jvObj); void pubLedger(Ledger::ref lpAccepted); void pubTransaction(Ledger::ref lpLedger, const SerializedTransaction& stTxn, TER terResult); @@ -218,11 +219,11 @@ public: // // --> vnaAddress: empty = all - void subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); + void subAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); + void unsubAccountInfo(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); - void unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); + void subAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); + void unsubAccountTransaction(InfoSub* ispListener, const boost::unordered_set& vnaAccountIDs); // void subAccountChanges(InfoSub* ispListener, const uint256 uLedgerHash); // void unsubAccountChanges(InfoSub* ispListener); diff --git a/src/NicknameState.cpp b/src/NicknameState.cpp index 72fbaa662..83d7c1bc1 100644 --- a/src/NicknameState.cpp +++ b/src/NicknameState.cpp @@ -18,7 +18,7 @@ STAmount NicknameState::getMinimumOffer() const : STAmount(); } -NewcoinAddress NicknameState::getAccountID() const +RippleAddress NicknameState::getAccountID() const { return mLedgerEntry->getFieldAccount(sfAccount); } diff --git a/src/NicknameState.h b/src/NicknameState.h index fe116f4c5..adc0eeb45 100644 --- a/src/NicknameState.h +++ b/src/NicknameState.h @@ -23,7 +23,7 @@ public: bool haveMinimumOffer() const; STAmount getMinimumOffer() const; - NewcoinAddress getAccountID() const; + RippleAddress getAccountID() const; SerializedLedgerEntry::pointer getSLE() { return mLedgerEntry; } const SerializedLedgerEntry& peekSLE() const { return *mLedgerEntry; } diff --git a/src/Pathfinder.cpp b/src/Pathfinder.cpp index 0904f30cd..407d2f894 100644 --- a/src/Pathfinder.cpp +++ b/src/Pathfinder.cpp @@ -76,19 +76,17 @@ PathOption::PathOption(PathOption::pointer other) } -Pathfinder::Pathfinder(NewcoinAddress& srcAccountID, NewcoinAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount) : - mSrcAccountID(srcAccountID.getAccountID()), mDstAccountID(dstAccountID.getAccountID()), mDstAmount(dstAmount), - mSrcCurrencyID(srcCurrencyID), mOrderBook(theApp->getMasterLedger().getCurrentLedger()) +Pathfinder::Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount) : + mSrcAccountID(srcAccountID.getAccountID()), mDstAccountID(dstAccountID.getAccountID()), mDstAmount(dstAmount), mSrcCurrencyID(srcCurrencyID), mOrderBook(theApp->getMasterLedger().getCurrentLedger()) { mLedger=theApp->getMasterLedger().getCurrentLedger(); } bool Pathfinder::findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet) { - if(mLedger) - { - std::queue pqueue; - STPathElement ele(mSrcAccountID, + if(mLedger) { + std::queue pqueue; + STPathElement ele(mSrcAccountID, mSrcCurrencyID, uint160()); STPath path; diff --git a/src/Pathfinder.h b/src/Pathfinder.h index cc900e1e1..6a5fe6987 100644 --- a/src/Pathfinder.h +++ b/src/Pathfinder.h @@ -1,5 +1,5 @@ #include "SerializedTypes.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "OrderBookDB.h" #include @@ -44,7 +44,7 @@ class Pathfinder void addPathOption(PathOption::pointer pathOption); public: - Pathfinder(NewcoinAddress& srcAccountID, NewcoinAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount); + Pathfinder(RippleAddress& srcAccountID, RippleAddress& dstAccountID, uint160& srcCurrencyID, STAmount dstAmount); // returns false if there is no path. otherwise fills out retPath bool findPaths(int maxSearchSteps, int maxPay, STPathSet& retPathSet); diff --git a/src/Peer.cpp b/src/Peer.cpp index 99558dd84..8dd7a5c8c 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -219,7 +219,7 @@ void Peer::handleConnect(const boost::system::error_code& error, boost::asio::ip } else { - std::cerr << "Connect peer: success." << std::endl; + cLog(lsINFO) << "Connect peer: success."; mSocketSsl.set_verify_mode(boost::asio::ssl::verify_none); @@ -384,7 +384,8 @@ void Peer::processReadBuffer() ripple::TMHello msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvHello(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -393,7 +394,8 @@ void Peer::processReadBuffer() ripple::TMErrorMsg msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvErrorMessage(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -402,7 +404,8 @@ void Peer::processReadBuffer() ripple::TMPing msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvPing(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -411,7 +414,8 @@ void Peer::processReadBuffer() ripple::TMGetContacts msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvGetContacts(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -420,7 +424,8 @@ void Peer::processReadBuffer() ripple::TMContact msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvContact(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; case ripple::mtGET_PEERS: @@ -428,7 +433,8 @@ void Peer::processReadBuffer() ripple::TMGetPeers msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvGetPeers(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; case ripple::mtPEERS: @@ -436,7 +442,8 @@ void Peer::processReadBuffer() ripple::TMPeers msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvPeers(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -445,7 +452,8 @@ void Peer::processReadBuffer() ripple::TMSearchTransaction msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvSearchTransaction(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -454,7 +462,8 @@ void Peer::processReadBuffer() ripple::TMGetAccount msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvGetAccount(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -463,7 +472,8 @@ void Peer::processReadBuffer() ripple::TMAccount msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvAccount(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -472,7 +482,8 @@ void Peer::processReadBuffer() ripple::TMTransaction msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvTransaction(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -481,7 +492,8 @@ void Peer::processReadBuffer() ripple::TMStatusChange msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvStatus(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -490,7 +502,8 @@ void Peer::processReadBuffer() ripple::TMProposeSet msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvPropose(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -499,7 +512,8 @@ void Peer::processReadBuffer() ripple::TMGetLedger msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvGetLedger(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -508,7 +522,8 @@ void Peer::processReadBuffer() ripple::TMLedgerData msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvLedger(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -517,7 +532,8 @@ void Peer::processReadBuffer() ripple::TMHaveTransactionSet msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvHaveTxSet(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -526,7 +542,8 @@ void Peer::processReadBuffer() ripple::TMValidation msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvValidation(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; #if 0 @@ -535,7 +552,8 @@ void Peer::processReadBuffer() ripple::TM msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recv(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; @@ -545,13 +563,14 @@ void Peer::processReadBuffer() ripple::TMGetObjectByHash msg; if (msg.ParseFromArray(&mReadbuf[HEADER_SIZE], mReadbuf.size() - HEADER_SIZE)) recvGetObjectByHash(msg); - else std::cerr << "parse error: " << type << std::endl; + else + cLog(lsWARNING) << "parse error: " << type; } break; default: - std::cerr << "Unknown Msg: " << type << std::endl; - std::cerr << strHex(&mReadbuf[0], mReadbuf.size()); + cLog(lsWARNING) << "Unknown Msg: " << type; + cLog(lsWARNING) << strHex(&mReadbuf[0], mReadbuf.size()); } } } @@ -669,9 +688,7 @@ void Peer::recvHello(ripple::TMHello& packet) void Peer::recvTransaction(ripple::TMTransaction& packet) { -#ifdef DEBUG - std::cerr << "Got transaction from peer" << std::endl; -#endif + cLog(lsDEBUG) << "Got transaction from peer"; Transaction::pointer tx; #ifndef TRUST_NETWORK @@ -758,11 +775,12 @@ void Peer::recvValidation(ripple::TMValidation& packet) 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 +// OPTIMIZEME: Should just defer validation checking to another thread +// checking the signature is expensive (but should do 'isNew' check here) + +#ifndef TRUST_NETWORK try -//#endif +#endif { Serializer s(packet.validation()); SerializerIterator sit(s); @@ -788,13 +806,13 @@ void Peer::recvValidation(ripple::TMValidation& packet) theApp->getConnectionPool().relayMessage(this, message); } } -//#ifndef TRUST_NETWORK +#ifndef TRUST_NETWORK catch (...) { cLog(lsWARNING) << "Exception processing validation"; punishPeer(PP_UNKNOWN_REQUEST); } -//#endif +#endif } void Peer::recvGetValidation(ripple::TMGetValidations& packet) @@ -877,7 +895,7 @@ void Peer::recvGetObjectByHash(ripple::TMGetObjectByHash& packet) reply.set_ledgerhash(packet.ledgerhash()); // This is a very minimal implementation - for (unsigned i = 0; i < packet.objects_size(); ++i) + for (int i = 0; i < packet.objects_size(); ++i) { uint256 hash; const ripple::TMIndexedObject& obj = packet.objects(i); @@ -1055,25 +1073,22 @@ void Peer::recvGetLedger(ripple::TMGetLedger& packet) ledger->addRaw(nData); reply.add_nodes()->set_nodedata(nData.getDataPtr(), nData.getLength()); - if (packet.nodeids().size() != 0) - { // new-style root request - cLog(lsINFO) << "Ledger root w/map roots request"; - SHAMap::pointer map = ledger->peekAccountStateMap(); - if (map && map->getHash().isNonZero()) - { // return account state root node if possible - Serializer rootNode(768); - if (map->getRootNode(rootNode, snfWIRE)) + cLog(lsINFO) << "Ledger root w/map roots request"; + SHAMap::pointer map = ledger->peekAccountStateMap(); + if (map && map->getHash().isNonZero()) + { // return account state root node if possible + Serializer rootNode(768); + if (map->getRootNode(rootNode, snfWIRE)) + { + reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength()); + if (ledger->getTransHash().isNonZero()) { - reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength()); - if (ledger->getTransHash().isNonZero()) + map = ledger->peekTransactionMap(); + if (map && map->getHash().isNonZero()) { - map = ledger->peekTransactionMap(); - if (map && map->getHash().isNonZero()) - { - rootNode.resize(0); - if (map->getRootNode(rootNode, snfWIRE)) - reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength()); - } + rootNode.resize(0); + if (map->getRootNode(rootNode, snfWIRE)) + reply.add_nodes()->set_nodedata(rootNode.getDataPtr(), rootNode.getLength()); } } } diff --git a/src/Peer.h b/src/Peer.h index c2fabfc59..6636a51ae 100644 --- a/src/Peer.h +++ b/src/Peer.h @@ -39,7 +39,7 @@ private: bool mClientConnect; // In process of connecting as client. bool mHelloed; // True, if hello accepted. bool mDetaching; // True, if detaching. - NewcoinAddress mNodePublic; // Node public key of peer. + RippleAddress mNodePublic; // Node public key of peer. ipPort mIpPort; ipPort mIpPortConnect; uint256 mCookieHash; @@ -158,7 +158,7 @@ public: uint256 getClosedLedgerHash() const { return mClosedLedgerHash; } bool hasLedger(const uint256& hash) const; bool hasTxSet(const uint256& hash) const; - NewcoinAddress getNodePublic() const { return mNodePublic; } + RippleAddress getNodePublic() const { return mNodePublic; } void cycleStatus() { mPreviousLedgerHash = mClosedLedgerHash; mClosedLedgerHash.zero(); } }; diff --git a/src/PubKeyCache.cpp b/src/PubKeyCache.cpp index 8d11c74c5..d3ae59c57 100644 --- a/src/PubKeyCache.cpp +++ b/src/PubKeyCache.cpp @@ -1,11 +1,11 @@ #include "PubKeyCache.h" #include "Application.h" -CKey::pointer PubKeyCache::locate(const NewcoinAddress& id) +CKey::pointer PubKeyCache::locate(const RippleAddress& id) { { // is it in cache boost::mutex::scoped_lock sl(mLock); - std::map::iterator it(mCache.find(id)); + std::map::iterator it(mCache.find(id)); if(it!=mCache.end()) return it->second; } @@ -39,11 +39,11 @@ CKey::pointer PubKeyCache::locate(const NewcoinAddress& id) return ckp; } -CKey::pointer PubKeyCache::store(const NewcoinAddress& id, const CKey::pointer& key) +CKey::pointer PubKeyCache::store(const RippleAddress& id, const CKey::pointer& key) { // stored if needed, returns cached copy (possibly the original) { boost::mutex::scoped_lock sl(mLock); - std::pair::iterator, bool> pit(mCache.insert(std::make_pair(id, key))); + std::pair::iterator, bool> pit(mCache.insert(std::make_pair(id, key))); if(!pit.second) // there was an existing key return pit.first->second; } diff --git a/src/PubKeyCache.h b/src/PubKeyCache.h index 69c19b2be..91023cfe9 100644 --- a/src/PubKeyCache.h +++ b/src/PubKeyCache.h @@ -5,20 +5,20 @@ #include -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "key.h" class PubKeyCache { private: boost::mutex mLock; - std::map mCache; + std::map mCache; public: PubKeyCache() { ; } - CKey::pointer locate(const NewcoinAddress& id); - CKey::pointer store(const NewcoinAddress& id, const CKey::pointer& key); + CKey::pointer locate(const RippleAddress& id); + CKey::pointer store(const RippleAddress& id, const CKey::pointer& key); void clear(); }; diff --git a/src/RPCHandler.cpp b/src/RPCHandler.cpp new file mode 100644 index 000000000..c6c8e9dba --- /dev/null +++ b/src/RPCHandler.cpp @@ -0,0 +1,2720 @@ +#include "NetworkOPs.h" +#include "RPCHandler.h" +#include "Application.h" +#include "Log.h" +#include "RippleLines.h" +#include "Wallet.h" +#include "RippleAddress.h" +#include "AccountState.h" +#include "NicknameState.h" + +#include "Pathfinder.h" +#include +#include +/* +carries out the RPC + +*/ + +SETUP_LOG(); + + +Json::Value RPCHandler::rpcError(int iError) +{ + static struct { + int iError; + const char* pToken; + const char* pMessage; + } errorInfoA[] = { + { rpcACT_EXISTS, "actExists", "Account already exists." }, + { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, + { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, + { rpcBAD_SEED, "badSeed", "Disallowed seed." }, + { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, + { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exists." }, + { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, + { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, + { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, + { rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." }, + { rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." }, + { rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." }, + { rpcINTERNAL, "internal", "Internal error." }, + { rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." }, + { rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." }, + { rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." }, + { rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." }, + { rpcNICKNAME_MALFORMED, "nicknameMalformed","Nickname is malformed." }, + { rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." }, + { rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." }, + { rpcNOT_IMPL, "notImpl", "Not implemented." }, + { rpcNO_ACCOUNT, "noAccount", "No such account." }, + { rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." }, + { rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." }, + { rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." }, + { rpcNO_NETWORK, "noNetwork", "Network not available." }, + { rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." }, + { rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." }, + { rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." }, + { rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." }, + { rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." }, + { rpcPORT_MALFORMED, "portMalformed", "Port is malformed." }, + { rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." }, + { rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." }, + { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, + { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, + { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, + { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, + { rpcSUCCESS, "success", "Success." }, + { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, + { rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown command." }, + { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, + }; + + int i; + + for (i=NUMBER(errorInfoA); i-- && errorInfoA[i].iError != iError;) + ; + + Json::Value jsonResult = Json::Value(Json::objectValue); + + jsonResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i(iError); + jsonResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i(iError); + jsonResult["error_code"] = iError; + if (i >= 0) + std::cerr << "rpcError: " + << errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl; + + return jsonResult; +} + + +RPCHandler::RPCHandler(NetworkOPs* netOps) +{ + mNetOps=netOps; +} + +int RPCHandler::getParamCount(const Json::Value& params) +{ // If non-array, only counts strings + if (params.isNull()) return 0; + if (params.isArray()) return params.size(); + if (!params.isConvertibleTo(Json::stringValue)) + return 0; + return 1; +} +bool RPCHandler::extractString(std::string& param, const Json::Value& params, int index) +{ + if (params.isNull()) return false; + + if (index!=0) + { + if (!params.isArray() || !params.isValidIndex(index)) + return false; + Json::Value p(params.get(index, Json::nullValue)); + if (p.isNull() || !p.isConvertibleTo(Json::stringValue)) + return false; + param = p.asString(); + return true; + } + + if (params.isArray()) + { + if ( (!params.isValidIndex(0)) || (!params[0u].isConvertibleTo(Json::stringValue)) ) + return false; + param = params[0u].asString(); + return true; + } + + if (!params.isConvertibleTo(Json::stringValue)) + return false; + param = params.asString(); + return true; +} + +// Look up the master public generator for a regular seed so we may index source accounts ids. +// --> naRegularSeed +// <-- naMasterGenerator +Json::Value RPCHandler::getMasterGenerator(const uint256& uLedger, const RippleAddress& naRegularSeed, RippleAddress& naMasterGenerator) +{ + RippleAddress na0Public; // To find the generator's index. + RippleAddress na0Private; // To decrypt the master generator's cipher. + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + + na0Public.setAccountPublic(naGenerator, 0); + na0Private.setAccountPrivate(naGenerator, naRegularSeed, 0); + + SLE::pointer sleGen = mNetOps->getGenerator(uLedger, na0Public.getAccountID()); + + if (!sleGen) + { + // No account has been claimed or has had it password set for seed. + return rpcError(rpcNO_ACCOUNT); + } + + std::vector vucCipher = sleGen->getFieldVL(sfGenerator); + std::vector vucMasterGenerator = na0Private.accountPrivateDecrypt(na0Public, vucCipher); + if (vucMasterGenerator.empty()) + { + return rpcError(rpcFAIL_GEN_DECRPYT); + } + + naMasterGenerator.setGenerator(vucMasterGenerator); + + return Json::Value(Json::objectValue); +} + +// Given a seed and a source account get the regular public and private key for authorizing transactions. +// - Make sure the source account can pay. +// --> naRegularSeed : To find the generator +// --> naSrcAccountID : Account we want the public and private regular keys to. +// <-- naAccountPublic : Regular public key for naSrcAccountID +// <-- naAccountPrivate : Regular private key for naSrcAccountID +// <-- saSrcBalance: Balance minus fee. +// --> naVerifyGenerator : If provided, the found master public generator must match. +// XXX Be more lenient, allow use of master generator on claimed accounts. +Json::Value RPCHandler::authorize(const uint256& uLedger, + const RippleAddress& naRegularSeed, const RippleAddress& naSrcAccountID, + RippleAddress& naAccountPublic, RippleAddress& naAccountPrivate, + STAmount& saSrcBalance, const STAmount& saFee, AccountState::pointer& asSrc, + const RippleAddress& naVerifyGenerator) +{ + // Source/paying account must exist. + asSrc = mNetOps->getAccountState(uLedger, naSrcAccountID); + if (!asSrc) + { + return rpcError(rpcSRC_ACT_MISSING); + } + + RippleAddress naMasterGenerator; + + if (asSrc->bHaveAuthorizedKey()) + { + Json::Value obj = getMasterGenerator(uLedger, naRegularSeed, naMasterGenerator); + + if (!obj.empty()) + return obj; + } + else + { + // Try the seed as a master seed. + naMasterGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + } + + // If naVerifyGenerator is provided, make sure it is the master generator. + if (naVerifyGenerator.isValid() && naMasterGenerator != naVerifyGenerator) + { + return rpcError(rpcWRONG_SEED); + } + + // Find the index of the account from the master generator, so we can generate the public and private keys. + RippleAddress naMasterAccountPublic; + unsigned int iIndex = 0; + bool bFound = false; + + // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are + // related. + while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX) + { + naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); + + Log(lsDEBUG) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naSrcAccountID.humanAccountID(); + + bFound = naSrcAccountID.getAccountID() == naMasterAccountPublic.getAccountID(); + if (!bFound) + ++iIndex; + } + + if (!bFound) + { + return rpcError(rpcACT_NOT_FOUND); + } + + // Use the regular generator to determine the associated public and private keys. + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + + naAccountPublic.setAccountPublic(naGenerator, iIndex); + naAccountPrivate.setAccountPrivate(naGenerator, naRegularSeed, iIndex); + + if (asSrc->bHaveAuthorizedKey() && (asSrc->getAuthorizedKey().getAccountID() != naAccountPublic.getAccountID())) + { + // std::cerr << "iIndex: " << iIndex << std::endl; + // std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl; + // std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl; + + return rpcError(rpcPASSWD_CHANGED); + } + + saSrcBalance = asSrc->getBalance(); + + if (saSrcBalance < saFee) + { + Log(lsINFO) << "authorize: Insufficent funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); + + return rpcError(rpcINSUF_FUNDS); + } + else + { + saSrcBalance -= saFee; + } + + return Json::Value(); +} + +// --> strIdent: public key, account ID, or regular seed. +// <-- bIndex: true if iIndex > 0 and used the index. +Json::Value RPCHandler::accountFromString(const uint256& uLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex) +{ + RippleAddress naSeed; + + if (naAccount.setAccountPublic(strIdent) || naAccount.setAccountID(strIdent)) + { + // Got the account. + bIndex = false; + } + // Must be a seed. + else if (!naSeed.setSeedGeneric(strIdent)) + { + return rpcError(rpcBAD_SEED); + } + else + { + // We allow the use of the seeds to access #0. + // This is poor practice and merely for debuging convenience. + RippleAddress naRegular0Public; + RippleAddress naRegular0Private; + + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSeed); + + naRegular0Public.setAccountPublic(naGenerator, 0); + naRegular0Private.setAccountPrivate(naGenerator, naSeed, 0); + + // uint160 uGeneratorID = naRegular0Public.getAccountID(); + SLE::pointer sleGen = mNetOps->getGenerator(uLedger, naRegular0Public.getAccountID()); + if (!sleGen) + { + // Didn't find a generator map, assume it is a master generator. + nothing(); + } + else + { + // Found master public key. + std::vector vucCipher = sleGen->getFieldVL(sfGenerator); + std::vector vucMasterGenerator = naRegular0Private.accountPrivateDecrypt(naRegular0Public, vucCipher); + if (vucMasterGenerator.empty()) + { + rpcError(rpcNO_GEN_DECRPYT); + } + + naGenerator.setGenerator(vucMasterGenerator); + } + + bIndex = !iIndex; + + naAccount.setAccountPublic(naGenerator, iIndex); + } + + return Json::Value(Json::objectValue); +} + +Json::Value RPCHandler::doAcceptLedger(const Json::Value ¶ms) +{ + if (!theConfig.RUN_STANDALONE) + return rpcError(rpcNOT_STANDALONE); + + Json::Value obj(Json::objectValue); + obj["newLedger"] = theApp->getOPs().acceptLedger(); + return obj; +} + +// account_domain_set [] +Json::Value RPCHandler::doAccountDomainSet(const Json::Value ¶ms) +{ + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); + + if (!obj.empty()) + return obj; + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + false, + uint128(), + false, + 0, + RippleAddress(), + true, + strCopy(params[2u].asString()), + false, + 0, + false, + uint256(), + 0); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return Json::Value(Json::objectValue); +} + +// account_email_set [] +Json::Value RPCHandler::doAccountEmailSet(const Json::Value ¶ms) +{ + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); + + if (!obj.empty()) + return obj; + + // Hash as per: http://en.gravatar.com/site/implement/hash/ + std::string strEmail = 3 == params.size() ? params[2u].asString() : ""; + boost::trim(strEmail); + boost::to_lower(strEmail); + + std::vector vucMD5(128/8, 0); + MD5(reinterpret_cast(strEmail.c_str()), strEmail.size(), &vucMD5.front()); + + uint128 uEmailHash(vucMD5); + std::vector vucDomain; + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + true, + strEmail.empty() ? uint128() : uEmailHash, + false, + uint256(), + RippleAddress(), + false, + vucDomain, + false, + 0, + false, + uint256(), + 0); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + if (!strEmail.empty()) + { + obj["Email"] = strEmail; + obj["EmailHash"] = strHex(vucMD5); + obj["UrlGravatar"] = AccountState::createGravatarUrl(uEmailHash); + } + + return obj; +} + +// account_info || +// account_info || [] +Json::Value RPCHandler::doAccountInfo(const Json::Value ¶ms) +{ + std::string strIdent = params[0u].asString(); + bool bIndex; + int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; + RippleAddress naAccount; + + Json::Value ret; + + // Get info on account. + + uint256 uAccepted = mNetOps->getClosedLedger(); + Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); + + if (jAccepted.empty()) + { + AccountState::pointer asAccepted = mNetOps->getAccountState(uAccepted, naAccount); + + if (asAccepted) + asAccepted->addJson(jAccepted); + } + + ret["accepted"] = jAccepted; + + Json::Value jCurrent = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); + + if (jCurrent.empty()) + { + AccountState::pointer asCurrent = mNetOps->getAccountState(uint256(0), naAccount); + + if (asCurrent) + asCurrent->addJson(jCurrent); + } + + ret["current"] = jCurrent; + +#if 0 + if (!jAccepted && !asCurrent) + { + ret["account"] = naAccount.humanAccountID(); + ret["status"] = "NotFound"; + if (bIndex) + ret["index"] = iIndex; + } +#endif + return ret; +} + + + +// account_message_set +Json::Value RPCHandler::doAccountMessageSet(const Json::Value& params) { + RippleAddress naSrcAccountID; + RippleAddress naSeed; + RippleAddress naMessagePubKey; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naMessagePubKey.setAccountPublic(params[2u].asString())) + { + return rpcError(rpcPUBLIC_MALFORMED); + } + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); + std::vector vucDomain; + + if (!obj.empty()) + return obj; + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + false, + uint128(), + false, + uint256(), + naMessagePubKey, + false, + vucDomain, + false, + 0, + false, + uint256(), + 0); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + obj["MessageKey"] = naMessagePubKey.humanAccountPublic(); + + return obj; +} + +// account_publish_set +Json::Value RPCHandler::doAccountPublishSet(const Json::Value ¶ms) +{ + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); + + if (!obj.empty()) + return obj; + + uint256 uPublishHash(params[2u].asString()); + uint32 uPublishSize = lexical_cast_s(params[3u].asString()); + std::vector vucDomain; + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + false, + uint128(), + false, + 0, + RippleAddress(), + false, + vucDomain, + false, + 0, + true, + uPublishHash, + uPublishSize); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return Json::Value(Json::objectValue); +} + +// account_rate_set +Json::Value RPCHandler::doAccountRateSet(const Json::Value ¶ms) +{ + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); + + if (!obj.empty()) + return obj; + + uint32 uRate = lexical_cast_s(params[2u].asString()); + std::vector vucDomain; + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + false, + uint128(), + false, + 0, + RippleAddress(), + false, + vucDomain, + true, + uRate, + false, + uint256(), + 0); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return Json::Value(Json::objectValue); +} + +// account_wallet_set [] +Json::Value RPCHandler::doAccountWalletSet(const Json::Value& params) { + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); + std::vector vucDomain; + + if (!obj.empty()) + return obj; + + std::string strWalletLocator = params.size() == 3 ? params[2u].asString() : ""; + uint256 uWalletLocator; + + if (!strWalletLocator.empty()) + uWalletLocator.SetHex(strWalletLocator); + + Transaction::pointer trans = Transaction::sharedAccountSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + false, + uint128(), + true, + uWalletLocator, + RippleAddress(), + false, + vucDomain, + false, + 0, + false, + uint256(), + 0); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + if (!strWalletLocator.empty()) + obj["WalletLocator"] = uWalletLocator.GetHex(); + + return obj; +} + +Json::Value RPCHandler::doConnect(const Json::Value& params) +{ + if (theConfig.RUN_STANDALONE) + return "cannot connect in standalone mode"; + + // connect [port] + std::string strIp; + int iPort = -1; + + // XXX Might allow domain for manual connections. + if (!extractString(strIp, params, 0)) + return rpcError(rpcHOST_IP_MALFORMED); + + if (params.size() == 2) + { + std::string strPort; + + // YYY Should make an extract int. + if (!extractString(strPort, params, 1)) + return rpcError(rpcPORT_MALFORMED); + + iPort = lexical_cast_s(strPort); + } + + // XXX Validate legal IP and port + theApp->getConnectionPool().connectTo(strIp, iPort); + + return "connecting"; +} + +// data_delete +Json::Value RPCHandler::doDataDelete(const Json::Value& params) +{ + std::string strKey = params[0u].asString(); + + Json::Value ret = Json::Value(Json::objectValue); + + if (theApp->getWallet().dataDelete(strKey)) + { + ret["key"] = strKey; + } + else + { + ret = rpcError(rpcINTERNAL); + } + + return ret; +} + +// data_fetch +Json::Value RPCHandler::doDataFetch(const Json::Value& params) +{ + std::string strKey = params[0u].asString(); + std::string strValue; + + Json::Value ret = Json::Value(Json::objectValue); + + ret["key"] = strKey; + if (theApp->getWallet().dataFetch(strKey, strValue)) + ret["value"] = strValue; + + return ret; +} + +// data_store +Json::Value RPCHandler::doDataStore(const Json::Value& params) +{ + std::string strKey = params[0u].asString(); + std::string strValue = params[1u].asString(); + + Json::Value ret = Json::Value(Json::objectValue); + + if (theApp->getWallet().dataStore(strKey, strValue)) + { + ret["key"] = strKey; + ret["value"] = strValue; + } + else + { + ret = rpcError(rpcINTERNAL); + } + + return ret; +} + +// nickname_info +// Note: Nicknames are not automatically looked up by commands as they are advisory and can be changed. +Json::Value RPCHandler::doNicknameInfo(const Json::Value& params) +{ + std::string strNickname = params[0u].asString(); + boost::trim(strNickname); + + if (strNickname.empty()) + { + return rpcError(rpcNICKNAME_MALFORMED); + } + + NicknameState::pointer nsSrc = mNetOps->getNicknameState(uint256(0), strNickname); + if (!nsSrc) + { + return rpcError(rpcNICKNAME_MISSING); + } + + Json::Value ret(Json::objectValue); + + ret["nickname"] = strNickname; + + nsSrc->addJson(ret); + + return ret; +} + +// nickname_set [] [] +Json::Value RPCHandler::doNicknameSet(const Json::Value& params) +{ + RippleAddress naSrcAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + STAmount saMinimumOffer; + bool bSetOffer = params.size() >= 4; + std::string strOfferCurrency; + std::string strNickname = params[2u].asString(); + boost::trim(strNickname); + + if (strNickname.empty()) + { + return rpcError(rpcNICKNAME_MALFORMED); + } + else if (params.size() >= 4 && !saMinimumOffer.setFullValue(params[3u].asString(), strOfferCurrency)) + { + return rpcError(rpcDST_AMT_MALFORMED); + } + + STAmount saFee; + NicknameState::pointer nsSrc = mNetOps->getNicknameState(uint256(0), strNickname); + + if (!nsSrc) + { + // Creating nickname. + saFee = theConfig.FEE_NICKNAME_CREATE; + } + else if (naSrcAccountID != nsSrc->getAccountID()) + { + // We don't own the nickname. + return rpcError(rpcNICKNAME_PERM); + } + else + { + // Setting the minimum offer. + saFee = theConfig.FEE_DEFAULT; + } + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, saFee, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + // YYY Could verify nickname does not exist or points to paying account. + // XXX Adjust fee for nickname create. + + Transaction::pointer trans = Transaction::sharedNicknameSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + saFee, + 0, // YYY No source tag + Ledger::getNicknameHash(strNickname), + bSetOffer, + saMinimumOffer); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; +} + +// offer_create [passive] +// *offering* for *wants* +Json::Value RPCHandler::doOfferCreate(const Json::Value ¶ms) +{ + RippleAddress naSeed; + RippleAddress naSrcAccountID; + STAmount saTakerPays; + STAmount saTakerGets; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!saTakerGets.setFullValue(params[2u].asString(), params[3u].asString(), params[4u].asString())) + { + return rpcError(rpcGETS_AMT_MALFORMED); + } + else if (!saTakerPays.setFullValue(params[5u].asString(), params[6u].asString(), params[7u].asString())) + { + return rpcError(rpcPAYS_AMT_MALFORMED); + } + else if (params.size() == 10 && params[9u].asString() != "passive") + { + return rpcError(rpcINVALID_PARAMS); + } + + uint32 uExpiration = lexical_cast_s(params[8u].asString()); + bool bPassive = params.size() == 10; + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + Transaction::pointer trans = Transaction::sharedOfferCreate( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + bPassive, + saTakerPays, + saTakerGets, + uExpiration); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; +} + +// offer_cancel +Json::Value RPCHandler::doOfferCancel(const Json::Value ¶ms) +{ + RippleAddress naSeed; + RippleAddress naSrcAccountID; + uint32 uSequence = lexical_cast_s(params[2u].asString()); + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + Transaction::pointer trans = Transaction::sharedOfferCancel( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + uSequence); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; +} + +// owner_info || +// owner_info || [] +Json::Value RPCHandler::doOwnerInfo(const Json::Value& params) +{ + std::string strIdent = params[0u].asString(); + bool bIndex; + int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; + RippleAddress naAccount; + + Json::Value ret; + + // Get info on account. + + uint256 uAccepted = mNetOps->getClosedLedger(); + Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); + + ret["accepted"] = jAccepted.empty() ? mNetOps->getOwnerInfo(uAccepted, naAccount) : jAccepted; + + Json::Value jCurrent = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); + + ret["current"] = jCurrent.empty() ? mNetOps->getOwnerInfo(uint256(0), naAccount) : jCurrent; + + return ret; +} +// password_fund [] +// YYY Make making account default to first account for seed. +Json::Value RPCHandler::doPasswordFund(const Json::Value ¶ms) +{ + RippleAddress naSrcAccountID; + RippleAddress naDstAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naDstAccountID.setAccountID(params[params.size() == 3 ? 2u : 1u].asString())) + { + return rpcError(rpcDST_ACT_MALFORMED); + } + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + // YYY Could verify dst exists and isn't already funded. + + Transaction::pointer trans = Transaction::sharedPasswordFund( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + naDstAccountID); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; +} + +// password_set [] +Json::Value RPCHandler::doPasswordSet(const Json::Value& params) +{ + RippleAddress naMasterSeed; + RippleAddress naRegularSeed; + RippleAddress naAccountID; + + if (!naMasterSeed.setSeedGeneric(params[0u].asString())) + { + // Should also not allow account id's as seeds. + return rpcError(rpcBAD_SEED); + } + else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) + { + // Should also not allow account id's as seeds. + return rpcError(rpcBAD_SEED); + } + // YYY Might use account from string to be more flexible. + else if (params.size() >= 3 && !naAccountID.setAccountID(params[2u].asString())) + { + return rpcError(rpcACT_MALFORMED); + } + else + { + RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); + RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + RippleAddress naRegular0Public; + RippleAddress naRegular0Private; + + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + + naAccountPublic.setAccountPublic(naMasterGenerator, 0); + naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); + + naRegular0Public.setAccountPublic(naRegularGenerator, 0); + naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); + + // Hash of regular account #0 public key. + // uint160 uGeneratorID = naRegular0Public.getAccountID(); + std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); + std::vector vucGeneratorSig; + + // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. + // XXX Check result. + naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); + + RippleAddress naMasterXPublic; + RippleAddress naRegularXPublic; + unsigned int iIndex = -1; // Compensate for initial increment. + int iMax = theConfig.ACCOUNT_PROBE_MAX; + + // YYY Could probe periodically to see if accounts exists. + // YYY Max could be set randomly. + // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are + // related. + do { + ++iIndex; + naMasterXPublic.setAccountPublic(naMasterGenerator, iIndex); + naRegularXPublic.setAccountPublic(naRegularGenerator, iIndex); + + std::cerr << iIndex << ": " << naRegularXPublic.humanAccountID() << std::endl; + + } while (naAccountID.getAccountID() != naMasterXPublic.getAccountID() && --iMax); + + if (!iMax) + { + return rpcError(rpcACT_NOT_FOUND); + } + + Transaction::pointer trans = Transaction::sharedPasswordSet( + naAccountPublic, naAccountPrivate, + 0, + naRegularXPublic, + vucGeneratorCipher, + naRegular0Public.getAccountPublic(), + vucGeneratorSig); + + trans = mNetOps->submitTransaction(trans); + + Json::Value obj(Json::objectValue); + + // We "echo" the seeds so they can be checked. + obj["master_seed"] = naMasterSeed.humanSeed(); + obj["master_key"] = naMasterSeed.humanSeed1751(); + obj["regular_seed"] = naRegularSeed.humanSeed(); + obj["regular_key"] = naRegularSeed.humanSeed1751(); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; + } +} + +Json::Value RPCHandler::doPeers(const Json::Value& params) +{ + // peers + Json::Value obj(Json::objectValue); + obj["peers"]=theApp->getConnectionPool().getPeersJson(); + return obj; +} + +// profile offers [submit] +// profile 0:offers 1:pass_a 2:account_a 3:currency_offer_a 4:account_b 5:currency_offer_b 6: 7:[submit] +// issuer is the offering account +// --> submit: 'submit|true|false': defaults to false +// Prior to running allow each to have a credit line of what they will be getting from the other account. +Json::Value RPCHandler::doProfile(const Json::Value ¶ms) +{ + int iArgs = params.size(); + RippleAddress naSeedA; + RippleAddress naAccountA; + uint160 uCurrencyOfferA; + RippleAddress naSeedB; + RippleAddress naAccountB; + uint160 uCurrencyOfferB; + uint32 iCount = 100; + bool bSubmit = false; + + if (iArgs < 6 || "offers" != params[0u].asString()) + { + return rpcError(rpcINVALID_PARAMS); + } + + if (!naSeedA.setSeedGeneric(params[1u].asString())) // + return rpcError(rpcINVALID_PARAMS); + + naAccountA.setAccountID(params[2u].asString()); // + + if (!STAmount::currencyFromString(uCurrencyOfferA, params[3u].asString())) // + return rpcError(rpcINVALID_PARAMS); + + naAccountB.setAccountID(params[4u].asString()); // + if (!STAmount::currencyFromString(uCurrencyOfferB, params[5u].asString())) // + return rpcError(rpcINVALID_PARAMS); + + iCount = lexical_cast_s(params[6u].asString()); + + if (iArgs >= 8 && "false" != params[7u].asString()) + bSubmit = true; + + Log::setMinSeverity(lsFATAL,true); + + boost::posix_time::ptime ptStart(boost::posix_time::microsec_clock::local_time()); + + for(unsigned int n=0; ngetSeq(), // uSeq + theConfig.FEE_DEFAULT, + 0, // uSourceTag, + false, // bPassive + STAmount(uCurrencyOfferA, naAccountA.getAccountID(), 1), // saTakerPays + STAmount(uCurrencyOfferB, naAccountB.getAccountID(), 1+n), // saTakerGets + 0); // uExpiration + + if(bSubmit) + tpOfferA = mNetOps->submitTransaction(tpOfferA); + } + + boost::posix_time::ptime ptEnd(boost::posix_time::microsec_clock::local_time()); + boost::posix_time::time_duration tdInterval = ptEnd-ptStart; + long lMicroseconds = tdInterval.total_microseconds(); + int iTransactions = iCount; + float fRate = lMicroseconds ? iTransactions/(lMicroseconds/1000000.0) : 0.0; + + Json::Value obj(Json::objectValue); + + obj["transactions"] = iTransactions; + obj["submit"] = bSubmit; + obj["start"] = boost::posix_time::to_simple_string(ptStart); + obj["end"] = boost::posix_time::to_simple_string(ptEnd); + obj["interval"] = boost::posix_time::to_simple_string(tdInterval); + obj["rate_per_second"] = fRate; + + return obj; +} + +// ripple +// [] +// + +// full|partial limit|average [] +// +// path: +// path + +// +// path_element: +// account [] [] +// offer [] +Json::Value RPCHandler::doRipple(const Json::Value ¶ms) +{ + RippleAddress naSeed; + STAmount saSrcAmountMax; + uint160 uSrcCurrencyID; + RippleAddress naSrcAccountID; + RippleAddress naSrcIssuerID; + bool bPartial; + bool bFull; + bool bLimit; + bool bAverage; + RippleAddress naDstAccountID; + STAmount saDstAmount; + uint160 uDstCurrencyID; + + STPathSet spsPaths; + + naSrcIssuerID.setAccountID(params[4u].asString()); // + + if (!naSeed.setSeedGeneric(params[0u].asString())) // + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) // + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + // [] + else if (!saSrcAmountMax.setFullValue(params[2u].asString(), params[3u].asString(), params[naSrcIssuerID.isValid() ? 4u : 1u].asString())) + { + // Log(lsINFO) << "naSrcIssuerID.isValid(): " << naSrcIssuerID.isValid(); + // Log(lsINFO) << "source_max: " << params[2u].asString(); + // Log(lsINFO) << "source_currency: " << params[3u].asString(); + // Log(lsINFO) << "source_issuer: " << params[naSrcIssuerID.isValid() ? 4u : 2u].asString(); + + return rpcError(rpcSRC_AMT_MALFORMED); + } + + int iArg = 4 + naSrcIssuerID.isValid(); + + // XXX bSrcRedeem & bSrcIssue not used. + STPath spPath; + + while (params.size() != iArg && params[iArg].asString() == "path") // path + { + Log(lsINFO) << "Path>"; + ++iArg; + + while (params.size() != iArg + && (params[iArg].asString() == "offer" || params[iArg].asString() == "account")) + { + if (params.size() >= iArg + 3 && params[iArg].asString() == "offer") // offer + { + Log(lsINFO) << "Offer>"; + uint160 uCurrencyID; + RippleAddress naIssuerID; + + ++iArg; + + if (!STAmount::currencyFromString(uCurrencyID, params[iArg++].asString())) // + { + return rpcError(rpcINVALID_PARAMS); + } + else if (naIssuerID.setAccountID(params[iArg].asString())) // [] + { + ++iArg; + } + + spPath.addElement(STPathElement( + uint160(0), + uCurrencyID, + naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); + } + else if (params.size() >= iArg + 2 && params[iArg].asString() == "account") // account + { + Log(lsINFO) << "Account>"; + RippleAddress naAccountID; + uint160 uCurrencyID; + RippleAddress naIssuerID; + + ++iArg; + + if (!naAccountID.setAccountID(params[iArg++].asString())) // + { + return rpcError(rpcINVALID_PARAMS); + } + + if (params.size() != iArg && STAmount::currencyFromString(uCurrencyID, params[iArg].asString())) // [] + { + ++iArg; + } + + if (params.size() != iArg && naIssuerID.setAccountID(params[iArg].asString())) // [] + { + ++iArg; + } + + spPath.addElement(STPathElement( + naAccountID.getAccountID(), + uCurrencyID, + naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); + } + else + { + return rpcError(rpcINVALID_PARAMS); + } + } + + if (spPath.isEmpty()) + { + return rpcError(rpcINVALID_PARAMS); + } + else + { + spsPaths.addPath(spPath); + spPath.clear(); + } + } + + // full|partial + bPartial = params.size() != iArg ? params[iArg].asString() == "partial" : false; + bFull = params.size() != iArg ? params[iArg].asString() == "full" : false; + + if (!bPartial && !bFull) + { + return rpcError(rpcINVALID_PARAMS); + } + else + { + ++iArg; + } + + // limit|average + bLimit = params.size() != iArg ? params[iArg].asString() == "limit" : false; + bAverage = params.size() != iArg ? params[iArg].asString() == "average" : false; + + if (!bLimit && !bAverage) + { + return rpcError(rpcINVALID_PARAMS); + } + else + { + ++iArg; + } + + if (params.size() != iArg && !naDstAccountID.setAccountID(params[iArg++].asString())) // + { + return rpcError(rpcDST_ACT_MALFORMED); + } + + const unsigned int uDstIssuer = params.size() == iArg + 3 ? iArg+2 : iArg-1; + + // + if (params.size() != iArg + 2 && params.size() != iArg + 3) + { + // Log(lsINFO) << "params.size(): " << params.size(); + + return rpcError(rpcDST_AMT_MALFORMED); + } + else if (!saDstAmount.setFullValue(params[iArg].asString(), params[iArg+1].asString(), params[uDstIssuer].asString())) + { + // Log(lsINFO) << " Amount: " << params[iArg].asString(); + // Log(lsINFO) << "Currency: " << params[iArg+1].asString(); + // Log(lsINFO) << " Issuer: " << params[uDstIssuer].asString(); + + return rpcError(rpcDST_AMT_MALFORMED); + } + + AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); + STAmount saFee = theConfig.FEE_DEFAULT; + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, saFee, asSrc, naVerifyGenerator); + + if (!obj.empty()) + return obj; + + // YYY Could do some checking: source has funds or credit, dst exists and has sufficent credit limit. + // YYY Currency from same source or loops not allowed. + // YYY Limit paths length and count. + if (!asDst) + { + Log(lsINFO) << "naDstAccountID: " << naDstAccountID.humanAccountID(); + + return rpcError(rpcDST_ACT_MISSING); + } + + Transaction::pointer trans = Transaction::sharedPayment( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + saFee, + 0, // YYY No source tag + naDstAccountID, + saDstAmount, + saSrcAmountMax, + spsPaths, + bPartial, + bLimit); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + obj["seed"] = naSeed.humanSeed(); + obj["fee"] = saFee.getText(); + obj["srcAccountID"] = naSrcAccountID.humanAccountID(); + obj["dstAccountID"] = naDstAccountID.humanAccountID(); + obj["srcAmountMax"] = saSrcAmountMax.getText(); + obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); + obj["dstAmount"] = saDstAmount.getText(); + obj["dstISO"] = saDstAmount.getHumanCurrency(); + obj["paths"] = spsPaths.getText(); + + return obj; +} + +// ripple_line_set [] [] [] +Json::Value RPCHandler::doRippleLineSet(const Json::Value& params) +{ + RippleAddress naSeed; + RippleAddress naSrcAccountID; + RippleAddress naDstAccountID; + STAmount saLimitAmount; + bool bQualityIn = params.size() >= 6; + bool bQualityOut = params.size() >= 7; + uint32 uQualityIn = 0; + uint32 uQualityOut = 0; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naDstAccountID.setAccountID(params[2u].asString())) + { + return rpcError(rpcDST_ACT_MALFORMED); + } + else if (!saLimitAmount.setFullValue(params[3u].asString(), params.size() >= 5 ? params[4u].asString() : "", params[2u].asString())) + { + return rpcError(rpcSRC_AMT_MALFORMED); + } + else if (bQualityIn && !parseQuality(params[5u].asString(), uQualityIn)) + { + return rpcError(rpcQUALITY_MALFORMED); + } + else if (bQualityOut && !parseQuality(params[6u].asString(), uQualityOut)) + { + return rpcError(rpcQUALITY_MALFORMED); + } + else + { + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + Transaction::pointer trans = Transaction::sharedCreditSet( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_DEFAULT, + 0, // YYY No source tag + saLimitAmount, + bQualityIn, uQualityIn, + bQualityOut, uQualityOut); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + obj["seed"] = naSeed.humanSeed(); + obj["srcAccountID"] = naSrcAccountID.humanAccountID(); + obj["dstAccountID"] = naDstAccountID.humanAccountID(); + + return obj; + } +} + +// ripple_lines_get || [] +Json::Value RPCHandler::doRippleLinesGet(const Json::Value ¶ms) +{ + // uint256 uAccepted = mNetOps->getClosedLedger(); + + std::string strIdent = params[0u].asString(); + bool bIndex; + int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; + + RippleAddress naAccount; + + Json::Value ret; + + ret = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); + + if (!ret.empty()) + return ret; + + // Get info on account. + ret = Json::Value(Json::objectValue); + + ret["account"] = naAccount.humanAccountID(); + if (bIndex) + ret["index"] = iIndex; + + AccountState::pointer as = mNetOps->getAccountState(uint256(0), naAccount); + if (as) + { + Json::Value jsonLines(Json::arrayValue); + + ret["account"] = naAccount.humanAccountID(); + + // XXX This is wrong, we do access the current ledger and do need to worry about changes. + // We access a committed ledger and need not worry about changes. + + RippleLines rippleLines(naAccount.getAccountID()); + BOOST_FOREACH(RippleState::pointer line, rippleLines.getLines()) + { + STAmount saBalance = line->getBalance(); + STAmount saLimit = line->getLimit(); + STAmount saLimitPeer = line->getLimitPeer(); + + Json::Value jPeer = Json::Value(Json::objectValue); + + //jPeer["node"] = uNode.ToString(); + + jPeer["account"] = line->getAccountIDPeer().humanAccountID(); + // Amount reported is positive if current account holds other account's IOUs. + // Amount reported is negative if other account holds current account's IOUs. + jPeer["balance"] = saBalance.getText(); + jPeer["currency"] = saBalance.getHumanCurrency(); + jPeer["limit"] = saLimit.getText(); + jPeer["limit_peer"] = saLimitPeer.getText(); + jPeer["quality_in"] = static_cast(line->getQualityIn()); + jPeer["quality_out"] = static_cast(line->getQualityOut()); + + jsonLines.append(jPeer); + } + ret["lines"] = jsonLines; + } + else + { + ret = rpcError(rpcACT_NOT_FOUND); + } + + return ret; +} + +// submit any transaction to the network +Json::Value RPCHandler::doSubmit(const Json::Value& params) +{ + // TODO + return rpcError(rpcSRC_ACT_MALFORMED); +} + +// send regular_seed paying_account account_id amount [currency] [issuer] [send_max] [send_currency] [send_issuer] +Json::Value RPCHandler::doSend(const Json::Value& params) +{ + RippleAddress naSeed; + RippleAddress naSrcAccountID; + RippleAddress naDstAccountID; + STAmount saSrcAmountMax; + STAmount saDstAmount; + std::string sSrcCurrency; + std::string sDstCurrency; + std::string sSrcIssuer; + std::string sDstIssuer; + + if (params.size() >= 5) + sDstCurrency = params[4u].asString(); + + if (params.size() >= 6) + sDstIssuer = params[5u].asString(); + + if (params.size() >= 8) + sSrcCurrency = params[7u].asString(); + + if (params.size() >= 9) + sSrcIssuer = params[8u].asString(); + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naDstAccountID.setAccountID(params[2u].asString())) + { + return rpcError(rpcDST_ACT_MALFORMED); + } + else if (!saDstAmount.setFullValue(params[3u].asString(), sDstCurrency, sDstIssuer)) + { + return rpcError(rpcDST_AMT_MALFORMED); + } + else if (params.size() >= 7 && !saSrcAmountMax.setFullValue(params[6u].asString(), sSrcCurrency, sSrcIssuer)) + { + return rpcError(rpcSRC_AMT_MALFORMED); + } + else + { + AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); + bool bCreate = !asDst; + STAmount saFee = bCreate ? theConfig.FEE_ACCOUNT_CREATE : theConfig.FEE_DEFAULT; + + RippleAddress naVerifyGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, saFee, asSrc, naVerifyGenerator); + + // Log(lsINFO) << boost::str(boost::format("doSend: sSrcIssuer=%s sDstIssuer=%s saSrcAmountMax=%s saDstAmount=%s") + // % sSrcIssuer + // % sDstIssuer + // % saSrcAmountMax.getFullText() + // % saDstAmount.getFullText()); + + if (!obj.empty()) + return obj; + + if (params.size() < 7) + saSrcAmountMax = saDstAmount; + + // Do a few simple checks. + if (!saSrcAmountMax.isNative()) + { + Log(lsINFO) << "doSend: Ripple"; + + nothing(); + } + else if (!saSrcBalance.isPositive()) + { + // No native currency to send. + Log(lsINFO) << "doSend: No native currency to send: " << saSrcBalance.getText(); + + return rpcError(rpcINSUF_FUNDS); + } + else if (saDstAmount.isNative() && saSrcAmountMax < saDstAmount) + { + // Not enough native currency. + + Log(lsINFO) << "doSend: Insufficient funds: src=" << saSrcAmountMax.getText() << " dst=" << saDstAmount.getText(); + + return rpcError(rpcINSUF_FUNDS); + } + // XXX Don't allow send to self of same currency. + + Transaction::pointer trans; + if (asDst) { + // Destination exists, ordinary send. + + STPathSet spsPaths; + uint160 srcCurrencyID; + + if (!saSrcAmountMax.isNative() || !saDstAmount.isNative()) + { + STAmount::currencyFromString(srcCurrencyID, sSrcCurrency); + Pathfinder pf(naSrcAccountID, naDstAccountID, srcCurrencyID, saDstAmount); + pf.findPaths(5, 1, spsPaths); + } + + trans = Transaction::sharedPayment( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + saFee, + 0, // YYY No source tag + naDstAccountID, + saDstAmount, + saSrcAmountMax, + spsPaths); + } + else + { + // Create destination and send. + + trans = Transaction::sharedCreate( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + saFee, + 0, // YYY No source tag + naDstAccountID, + saDstAmount); // Initial funds in XNS. + } + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + obj["seed"] = naSeed.humanSeed(); + obj["fee"] = saFee.getText(); + obj["create"] = bCreate; + obj["srcAccountID"] = naSrcAccountID.humanAccountID(); + obj["dstAccountID"] = naDstAccountID.humanAccountID(); + obj["srcAmountMax"] = saSrcAmountMax.getText(); + obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); + obj["dstAmount"] = saDstAmount.getText(); + obj["dstISO"] = saDstAmount.getHumanCurrency(); + + return obj; + } +} + +Json::Value RPCHandler::doServerInfo(const Json::Value& params) +{ + Json::Value ret(Json::objectValue); + + ret["info"] = theApp->getOPs().getServerInfo(); + + return ret; +} + +Json::Value RPCHandler::doTxHistory(const Json::Value& params) +{ + if (params.size() == 1) + { + unsigned int startIndex = params[0u].asInt(); + Json::Value obj; + Json::Value txs; + + obj["index"]=startIndex; + + std::string sql = + str(boost::format("SELECT * FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20") + % startIndex); + + { + Database* db = theApp->getTxnDB()->getDB(); + ScopedLock dbLock = theApp->getTxnDB()->getDBLock(); + + SQL_FOREACH(db, sql) + { + Transaction::pointer trans=Transaction::transactionFromSQL(db, false); + if(trans) txs.append(trans->getJson(0)); + } + } + + obj["txs"]=txs; + + return obj; + } + + return rpcError(rpcSRC_ACT_MALFORMED); +} + +Json::Value RPCHandler::doTx(const Json::Value& params) +{ + // tx + // tx + + std::string param1, param2; + if (!extractString(param1, params, 0)) + { + return rpcError(rpcINVALID_PARAMS); + } + + if (Transaction::isHexTxID(param1)) + { // transaction by ID + Json::Value ret; + uint256 txid(param1); + + Transaction::pointer txn = theApp->getMasterTransaction().fetch(txid, true); + + if (!txn) return rpcError(rpcTXN_NOT_FOUND); + + return txn->getJson(0); + } + + return rpcError(rpcNOT_IMPL); +} + +// ledger [id|current|lastclosed] [full] +Json::Value RPCHandler::doLedger(const Json::Value& params) +{ + if (getParamCount(params) == 0) + { + Json::Value ret(Json::objectValue), current(Json::objectValue), closed(Json::objectValue); + theApp->getMasterLedger().getCurrentLedger()->addJson(current, 0); + theApp->getMasterLedger().getClosedLedger()->addJson(closed, 0); + ret["open"] = current; + ret["closed"] = closed; + return ret; + } + + std::string param; + if (!extractString(param, params, 0)) + { + return "bad params"; + } + + Ledger::pointer ledger; + if (param == "current") + ledger = theApp->getMasterLedger().getCurrentLedger(); + else if ((param == "lastclosed") || (param == "lastaccepted")) + ledger = theApp->getMasterLedger().getClosedLedger(); + else if (param.size() > 12) + ledger = theApp->getMasterLedger().getLedgerByHash(uint256(param)); + else + ledger = theApp->getMasterLedger().getLedgerBySeq(lexical_cast_s(param)); + + if (!ledger) + return rpcError(rpcLGR_NOT_FOUND); + + bool full = extractString(param, params, 1) && (param == "full"); + Json::Value ret(Json::objectValue); + ledger->addJson(ret, full ? LEDGER_JSON_FULL : 0); + return ret; +} + +// account_tx +// account_tx +Json::Value RPCHandler::doAccountTransactions(const Json::Value& params) +{ + std::string param; + uint32 minLedger, maxLedger; + + if (!extractString(param, params, 0)) + return rpcError(rpcINVALID_PARAMS); + + RippleAddress account; + if (!account.setAccountID(param)) + return rpcError(rpcACT_MALFORMED); + + if (!extractString(param, params, 1)) + return rpcError(rpcLGR_IDX_MALFORMED); + + minLedger = lexical_cast_s(param); + + if ((params.size() == 3) && extractString(param, params, 2)) + maxLedger = lexical_cast_s(param); + else + maxLedger = minLedger; + + if ((maxLedger < minLedger) || (maxLedger == 0)) + { + std::cerr << "minL=" << minLedger << ", maxL=" << maxLedger << std::endl; + + return rpcError(rpcLGR_IDXS_INVALID); + } + +#ifndef DEBUG + try + { +#endif + std::vector< std::pair > txns = mNetOps->getAffectedAccounts(account, minLedger, maxLedger); + Json::Value ret(Json::objectValue); + ret["account"] = account.humanAccountID(); + Json::Value ledgers(Json::arrayValue); + + // uint32 currentLedger = 0; + for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) + { + Transaction::pointer txn = theApp->getMasterTransaction().fetch(it->second, true); + if (!txn) + { + ret["transactions"].append(it->second.GetHex()); + } + else + { + txn->setLedger(it->first); + ret["transactions"].append(txn->getJson(0)); + } + + } + return ret; +#ifndef DEBUG + } + catch (...) + { + return rpcError(rpcINTERNAL); + } +#endif +} + +// unl_add | [] +Json::Value RPCHandler::doUnlAdd(const Json::Value& params) +{ + std::string strNode = params[0u].asString(); + std::string strComment = (params.size() == 2) ? params[1u].asString() : ""; + + RippleAddress naNodePublic; + + if (naNodePublic.setNodePublic(strNode)) + { + theApp->getUNL().nodeAddPublic(naNodePublic, UniqueNodeList::vsManual, strComment); + + return "adding node by public key"; + } + else + { + theApp->getUNL().nodeAddDomain(strNode, UniqueNodeList::vsManual, strComment); + + return "adding node by domain"; + } +} + +// validation_create [||] +// +// NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command +// shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). +Json::Value RPCHandler::doValidationCreate(const Json::Value& params) { + RippleAddress naSeed; + Json::Value obj(Json::objectValue); + + if (params.empty()) + { + std::cerr << "Creating random validation seed." << std::endl; + + naSeed.setSeedRandom(); // Get a random seed. + } + else if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + + obj["validation_public_key"] = RippleAddress::createNodePublic(naSeed).humanNodePublic(); + obj["validation_seed"] = naSeed.humanSeed(); + obj["validation_key"] = naSeed.humanSeed1751(); + + return obj; +} + +// validation_seed [||] +// +// NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command +// shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). +Json::Value RPCHandler::doValidationSeed(const Json::Value& params) { + Json::Value obj(Json::objectValue); + + if (params.empty()) + { + std::cerr << "Unset validation seed." << std::endl; + + theConfig.VALIDATION_SEED.clear(); + theConfig.VALIDATION_PUB.clear(); + theConfig.VALIDATION_PRIV.clear(); + } + else if (!theConfig.VALIDATION_SEED.setSeedGeneric(params[0u].asString())) + { + theConfig.VALIDATION_PUB.clear(); + theConfig.VALIDATION_PRIV.clear(); + return rpcError(rpcBAD_SEED); + } + else + { + theConfig.VALIDATION_PUB = RippleAddress::createNodePublic(theConfig.VALIDATION_SEED); + theConfig.VALIDATION_PRIV = RippleAddress::createNodePrivate(theConfig.VALIDATION_SEED); + obj["validation_public_key"] = theConfig.VALIDATION_PUB.humanNodePublic(); + obj["validation_seed"] = theConfig.VALIDATION_SEED.humanSeed(); + obj["validation_key"] = theConfig.VALIDATION_SEED.humanSeed1751(); + } + + return obj; +} + +Json::Value RPCHandler::accounts(const uint256& uLedger, const RippleAddress& naMasterGenerator) +{ + Json::Value jsonAccounts(Json::arrayValue); + + // YYY Don't want to leak to thin server that these accounts are related. + // YYY Would be best to alternate requests to servers and to cache results. + unsigned int uIndex = 0; + + do { + RippleAddress naAccount; + + naAccount.setAccountPublic(naMasterGenerator, uIndex++); + + AccountState::pointer as = mNetOps->getAccountState(uLedger, naAccount); + if (as) + { + Json::Value jsonAccount(Json::objectValue); + + as->addJson(jsonAccount); + + jsonAccounts.append(jsonAccount); + } + else + { + uIndex = 0; + } + } while (uIndex); + + return jsonAccounts; +} + +// wallet_accounts +Json::Value RPCHandler::doWalletAccounts(const Json::Value& params) +{ + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + + // Try the seed as a master seed. + RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naSeed); + + Json::Value jsonAccounts = accounts(uint256(0), naMasterGenerator); + + if (jsonAccounts.empty()) + { + // No account via seed as master, try seed a regular. + Json::Value ret = getMasterGenerator(uint256(0), naSeed, naMasterGenerator); + + if (!ret.empty()) + return ret; + + ret["accounts"] = accounts(uint256(0), naMasterGenerator); + + return ret; + } + else + { + // Had accounts via seed as master, return them. + Json::Value ret(Json::objectValue); + + ret["accounts"] = jsonAccounts; + + return ret; + } +} + +// wallet_add [] [] +Json::Value RPCHandler::doWalletAdd(const Json::Value& params) +{ + RippleAddress naMasterSeed; + RippleAddress naRegularSeed; + RippleAddress naSrcAccountID; + STAmount saAmount; + std::string sDstCurrency; + + if (!naRegularSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naMasterSeed.setSeedGeneric(params[2u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (params.size() >= 4 && !saAmount.setFullValue(params[3u].asString(), sDstCurrency)) + { + return rpcError(rpcDST_AMT_MALFORMED); + } + else + { + RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); + RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naRegularSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + if (saSrcBalance < saAmount) + { + return rpcError(rpcINSUF_FUNDS); + } + else + { + RippleAddress naNewAccountPublic; + RippleAddress naNewAccountPrivate; + RippleAddress naAuthKeyID; + uint160 uAuthKeyID; + AccountState::pointer asNew; + std::vector vucSignature; + bool bAgain = true; + int iIndex = -1; + + // Find an unmade account. + do { + ++iIndex; + naNewAccountPublic.setAccountPublic(naMasterGenerator, iIndex); + + asNew = mNetOps->getAccountState(uint256(0), naNewAccountPublic); + if (!asNew) + bAgain = false; + } while (bAgain); + + // XXX Have a maximum number of accounts per wallet? + + // Determine corrisponding master private key. + naNewAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, iIndex); + + // Determine new accounts authorized regular key. + naAuthKeyID.setAccountPublic(naRegularGenerator, iIndex); + + uAuthKeyID = naAuthKeyID.getAccountID(); + + // Sign anything (naAuthKeyID) to prove we know new master private key. + naNewAccountPrivate.accountPrivateSign(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature); + + Transaction::pointer trans = Transaction::sharedWalletAdd( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_ACCOUNT_CREATE, + 0, // YYY No source tag + saAmount, + naAuthKeyID, + naNewAccountPublic, + vucSignature); + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + obj["srcAccountID"] = naSrcAccountID.humanAccountID(); + obj["newAccountID"] = naNewAccountPublic.humanAccountID(); + obj["amount"] = saAmount.getText(); + + return obj; + } + } +} + +// wallet_claim [] [] +// +// To provide an example to client writers, we do everything we expect a client to do here. +Json::Value RPCHandler::doWalletClaim(const Json::Value& params) +{ + RippleAddress naMasterSeed; + RippleAddress naRegularSeed; + + if (!naMasterSeed.setSeedGeneric(params[0u].asString())) + { + // Should also not allow account id's as seeds. + return rpcError(rpcBAD_SEED); + } + else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) + { + // Should also not allow account id's as seeds. + return rpcError(rpcBAD_SEED); + } + else + { + // Building: + // peer_wallet_claim + // [] + // + // + // Which has no confidential information. + + // XXX Need better parsing. + uint32 uSourceTag = (params.size() == 2) ? 0 : lexical_cast_s(params[2u].asString()); + // XXX Annotation is ignored. + std::string strAnnotation = (params.size() == 3) ? "" : params[3u].asString(); + + RippleAddress naMasterGenerator = RippleAddress::createGeneratorPublic(naMasterSeed); + RippleAddress naRegularGenerator = RippleAddress::createGeneratorPublic(naRegularSeed); + RippleAddress naRegular0Public; + RippleAddress naRegular0Private; + + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + + naAccountPublic.setAccountPublic(naMasterGenerator, 0); + naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); + + naRegular0Public.setAccountPublic(naRegularGenerator, 0); + naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); + + // Hash of regular account #0 public key. + uint160 uGeneratorID = naRegular0Public.getAccountID(); + std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); + std::vector vucGeneratorSig; + + // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. + // XXX Check result. + naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); + + Transaction::pointer trans = Transaction::sharedClaim( + naAccountPublic, naAccountPrivate, + uSourceTag, + vucGeneratorCipher, + naRegular0Public.getAccountPublic(), + vucGeneratorSig); + + trans = mNetOps->submitTransaction(trans); + + Json::Value obj(Json::objectValue); + + // We "echo" the seeds so they can be checked. + obj["master_seed"] = naMasterSeed.humanSeed(); + obj["master_key"] = naMasterSeed.humanSeed1751(); + obj["regular_seed"] = naRegularSeed.humanSeed(); + obj["regular_key"] = naRegularSeed.humanSeed1751(); + + obj["account_id"] = naAccountPublic.humanAccountID(); + obj["generator_id"] = strHex(uGeneratorID); + obj["generator"] = strHex(vucGeneratorCipher); + obj["annotation"] = strAnnotation; + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; + } +} + +// wallet_create regular_seed paying_account account_id [initial_funds] +// We don't allow creating an account_id by default here because we want to make sure the person has a chance to write down the +// master seed of the account to be created. +// YYY Need annotation and source tag +Json::Value RPCHandler::doWalletCreate(const Json::Value& params) +{ + RippleAddress naSrcAccountID; + RippleAddress naDstAccountID; + RippleAddress naSeed; + + if (!naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else if (!naSrcAccountID.setAccountID(params[1u].asString())) + { + return rpcError(rpcSRC_ACT_MALFORMED); + } + else if (!naDstAccountID.setAccountID(params[2u].asString())) + { + return rpcError(rpcDST_ACT_MALFORMED); + } + else if (mNetOps->getAccountState(uint256(0), naDstAccountID)) + { + return rpcError(rpcACT_EXISTS); + } + + // Trying to build: + // peer_wallet_create [] [] + + RippleAddress naMasterGenerator; + RippleAddress naAccountPublic; + RippleAddress naAccountPrivate; + AccountState::pointer asSrc; + STAmount saSrcBalance; + Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, + saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); + + if (!obj.empty()) + return obj; + + STAmount saInitialFunds = (params.size() < 4) ? 0 : lexical_cast_s(params[3u].asString()); + + if (saSrcBalance < saInitialFunds) + return rpcError(rpcINSUF_FUNDS); + + Transaction::pointer trans = Transaction::sharedCreate( + naAccountPublic, naAccountPrivate, + naSrcAccountID, + asSrc->getSeq(), + theConfig.FEE_ACCOUNT_CREATE, + 0, // YYY No source tag + naDstAccountID, + saInitialFunds); // Initial funds in XNC. + + trans = mNetOps->submitTransaction(trans); + + obj["transaction"] = trans->getSTransaction()->getJson(0); + obj["status"] = trans->getStatus(); + + return obj; +} + + +Json::Value RPCHandler::doLogRotate(const Json::Value& params) +{ + return Log::rotateLog(); +} + +Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& params,int role) +{ + Log(lsTRACE) << "RPC:" << command; + + static struct { + const char* pCommand; + doFuncPtr dfpFunc; + int iMinParams; + int iMaxParams; + bool mAdminRequired; + unsigned int iOptions; + } commandsA[] = { + { "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true }, + { "account_domain_set", &RPCHandler::doAccountDomainSet, 2, 3, false, optCurrent }, + { "account_email_set", &RPCHandler::doAccountEmailSet, 2, 3, false, optCurrent }, + { "account_info", &RPCHandler::doAccountInfo, 1, 2, false, optCurrent }, + { "account_message_set", &RPCHandler::doAccountMessageSet, 3, 3, false, optCurrent }, + { "account_publish_set", &RPCHandler::doAccountPublishSet, 4, 4, false, optCurrent }, + { "account_rate_set", &RPCHandler::doAccountRateSet, 3, 3, false, optCurrent }, + { "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, optNetwork }, + { "account_wallet_set", &RPCHandler::doAccountWalletSet, 2, 3, false, optCurrent }, + { "connect", &RPCHandler::doConnect, 1, 2, true }, + { "data_delete", &RPCHandler::doDataDelete, 1, 1, true }, + { "data_fetch", &RPCHandler::doDataFetch, 1, 1, true }, + { "data_store", &RPCHandler::doDataStore, 2, 2, true }, + { "ledger", &RPCHandler::doLedger, 0, 2, false, optNetwork }, + { "log_level", &RPCHandler::doLogLevel, 0, 2, true }, + { "logrotate", &RPCHandler::doLogRotate, 0, 0, true }, + { "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent }, + { "nickname_set", &RPCHandler::doNicknameSet, 2, 3, false, optCurrent }, + { "offer_create", &RPCHandler::doOfferCreate, 9, 10, false, optCurrent }, + { "offer_cancel", &RPCHandler::doOfferCancel, 3, 3, false, optCurrent }, + { "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, optCurrent }, + { "password_fund", &RPCHandler::doPasswordFund, 2, 3, false, optCurrent }, + { "password_set", &RPCHandler::doPasswordSet, 2, 3, false, optNetwork }, + { "peers", &RPCHandler::doPeers, 0, 0, true }, + { "profile", &RPCHandler::doProfile, 1, 9, false, optCurrent }, + { "ripple", &RPCHandler::doRipple, 9, -1, false, optCurrent|optClosed }, + { "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, optCurrent }, + { "ripple_line_set", &RPCHandler::doRippleLineSet, 4, 7, false, optCurrent }, + { "send", &RPCHandler::doSend, 3, 9, false, optCurrent }, + { "server_info", &RPCHandler::doServerInfo, 0, 0, true }, + { "stop", &RPCHandler::doStop, 0, 0, true }, + { "tx", &RPCHandler::doTx, 1, 1, true }, + { "tx_history", &RPCHandler::doTxHistory, 1, 1, false, }, + + { "unl_add", &RPCHandler::doUnlAdd, 1, 2, true }, + { "unl_delete", &RPCHandler::doUnlDelete, 1, 1, true }, + { "unl_list", &RPCHandler::doUnlList, 0, 0, true }, + { "unl_load", &RPCHandler::doUnlLoad, 0, 0, true }, + { "unl_network", &RPCHandler::doUnlNetwork, 0, 0, true }, + { "unl_reset", &RPCHandler::doUnlReset, 0, 0, true }, + { "unl_score", &RPCHandler::doUnlScore, 0, 0, true }, + + { "validation_create", &RPCHandler::doValidationCreate, 0, 1, false }, + { "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false }, + + { "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, optCurrent }, + { "wallet_add", &RPCHandler::doWalletAdd, 3, 5, false, optCurrent }, + { "wallet_claim", &RPCHandler::doWalletClaim, 2, 4, false, optNetwork }, + { "wallet_create", &RPCHandler::doWalletCreate, 3, 4, false, optCurrent }, + { "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, }, + { "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, }, + + { "login", &RPCHandler::doLogin, 2, 2, true }, + }; + + int i = NUMBER(commandsA); + + while (i-- && command != commandsA[i].pCommand) + ; + + if (i < 0) + { + return rpcError(rpcUNKNOWN_COMMAND); + } + else if (commandsA[i].mAdminRequired && role != ADMIN) + { + return rpcError(rpcNO_PERMISSION); + } + else if (params.size() < commandsA[i].iMinParams + || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) + { + return rpcError(rpcINVALID_PARAMS); + } + else if ((commandsA[i].iOptions & optNetwork) && !mNetOps->available()) + { + return rpcError(rpcNO_NETWORK); + } + // XXX Should verify we have a current ledger. + else if ((commandsA[i].iOptions & optCurrent) && false) + { + return rpcError(rpcNO_CURRENT); + } + else if ((commandsA[i].iOptions & optClosed) && mNetOps->getClosedLedger().isZero()) + { + return rpcError(rpcNO_CLOSED); + } + else + { + try { + return (this->*(commandsA[i].dfpFunc))(params); + } + catch (std::exception& e) + { + cLog(lsINFO) << "Caught throw: " << e.what(); + + return rpcError(rpcINTERNAL); + } + } +} + + + + +// wallet_propose [] +// is only for testing. Master seeds should only be generated randomly. +Json::Value RPCHandler::doWalletPropose(const Json::Value& params) +{ + RippleAddress naSeed; + RippleAddress naAccount; + + if (params.empty()) + { + naSeed.setSeedRandom(); + } + else + { + naSeed = RippleAddress::createSeedGeneric(params[0u].asString()); + } + + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSeed); + naAccount.setAccountPublic(naGenerator, 0); + + Json::Value obj(Json::objectValue); + + obj["master_seed"] = naSeed.humanSeed(); + //obj["master_key"] = naSeed.humanSeed1751(); + obj["account_id"] = naAccount.humanAccountID(); + + return obj; +} + +// wallet_seed [||] +Json::Value RPCHandler::doWalletSeed(const Json::Value& params) +{ + RippleAddress naSeed; + + if (params.size() + && !naSeed.setSeedGeneric(params[0u].asString())) + { + return rpcError(rpcBAD_SEED); + } + else + { + RippleAddress naAccount; + + if (!params.size()) + { + naSeed.setSeedRandom(); + } + + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSeed); + + naAccount.setAccountPublic(naGenerator, 0); + + Json::Value obj(Json::objectValue); + + obj["seed"] = naSeed.humanSeed(); + obj["key"] = naSeed.humanSeed1751(); + + return obj; + } +} + + +// TODO: for now this simply checks if this is the admin account +// TODO: need to prevent them hammering this over and over +// TODO: maybe a better way is only allow admin from local host +Json::Value RPCHandler::doLogin(const Json::Value& params) +{ + std::string username = params[0u].asString(); + std::string password = params[1u].asString(); + + if (username == theConfig.RPC_USER && password == theConfig.RPC_PASSWORD) + { + //mRole=ADMIN; + return "logged in"; + } + else + { + return "nope"; + } +} + +Json::Value RPCHandler::doLogLevel(const Json::Value& params) +{ + if (params.size() == 0) + { // get log severities + Json::Value ret = Json::objectValue; + + ret["base"] = Log::severityToString(Log::getMinSeverity()); + + std::vector< std::pair > logTable = LogPartition::getSeverities(); + typedef std::pair stringPair; + BOOST_FOREACH(const stringPair& it, logTable) + ret[it.first] = it.second; + return ret; + } + + if (params.size() == 1) + { // set base log severity + LogSeverity sv = Log::stringToSeverity(params[0u].asString()); + if (sv == lsINVALID) + return rpcError(rpcINVALID_PARAMS); + Log::setMinSeverity(sv,true); + return rpcError(rpcSUCCESS); + } + + if (params.size() == 2) + { // set partition severity + LogSeverity sv = Log::stringToSeverity(params[1u].asString()); + if (sv == lsINVALID) + return rpcError(rpcINVALID_PARAMS); + if (params[2u].asString() == "base") + Log::setMinSeverity(sv,false); + else if (!LogPartition::setSeverity(params[0u].asString(), sv)) + return rpcError(rpcINVALID_PARAMS); + return rpcError(rpcSUCCESS); + } + + assert(false); + return rpcError(rpcINVALID_PARAMS); +} + + + +// Populate the UNL from ripple.com's validators.txt file. +Json::Value RPCHandler::doUnlNetwork(const Json::Value& params) +{ + theApp->getUNL().nodeNetwork(); + + return "fetching"; +} + +// unl_reset +Json::Value RPCHandler::doUnlReset(const Json::Value& params) +{ + theApp->getUNL().nodeReset(); + + return "removing nodes"; +} + +// unl_score +Json::Value RPCHandler::doUnlScore(const Json::Value& params) +{ + theApp->getUNL().nodeScore(); + + return "scoring requested"; +} + +Json::Value RPCHandler::doStop(const Json::Value& params) +{ + theApp->stop(); + + return SYSTEM_NAME " server stopping"; +} + +// unl_delete | +Json::Value RPCHandler::doUnlDelete(const Json::Value& params) +{ + std::string strNode = params[0u].asString(); + + RippleAddress naNodePublic; + + if (naNodePublic.setNodePublic(strNode)) + { + theApp->getUNL().nodeRemovePublic(naNodePublic); + + return "removing node by public key"; + } + else + { + theApp->getUNL().nodeRemoveDomain(strNode); + + return "removing node by domain"; + } +} + +Json::Value RPCHandler::doUnlList(const Json::Value& params) +{ + Json::Value obj(Json::objectValue); + + obj["unl"]=theApp->getUNL().getUnlJson(); + + return obj; +} + +// Populate the UNL from a local validators.txt file. +Json::Value RPCHandler::doUnlLoad(const Json::Value& params) +{ + if (theConfig.UNL_DEFAULT.empty() || !theApp->getUNL().nodeLoad(theConfig.UNL_DEFAULT)) + { + return rpcError(rpcLOAD_FAILED); + } + + return "loading"; +} + +// vim:ts=4 diff --git a/src/RPCHandler.h b/src/RPCHandler.h new file mode 100644 index 000000000..4dfd7f80b --- /dev/null +++ b/src/RPCHandler.h @@ -0,0 +1,159 @@ +// used by the RPCServer or WSDoor to carry out these RPC commands +class NetworkOPs; + +class RPCHandler +{ + NetworkOPs* mNetOps; + + typedef Json::Value (RPCHandler::*doFuncPtr)(const Json::Value ¶ms); + enum { + optNetwork = 1, // Need network + optCurrent = 2+optNetwork, // Need current ledger + optClosed = 4+optNetwork, // Need closed ledger + }; + + int getParamCount(const Json::Value& params); + bool extractString(std::string& param, const Json::Value& params, int index); + + Json::Value getMasterGenerator(const uint256& uLedger, const RippleAddress& naRegularSeed, RippleAddress& naMasterGenerator); + Json::Value authorize(const uint256& uLedger, const RippleAddress& naRegularSeed, const RippleAddress& naSrcAccountID, + RippleAddress& naAccountPublic, RippleAddress& naAccountPrivate, + STAmount& saSrcBalance, const STAmount& saFee, AccountState::pointer& asSrc, + const RippleAddress& naVerifyGenerator); + Json::Value accounts(const uint256& uLedger, const RippleAddress& naMasterGenerator); + + 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 doAccountDomainSet(const Json::Value ¶ms); + Json::Value doAccountEmailSet(const Json::Value ¶ms); + Json::Value doAccountInfo(const Json::Value& params); + Json::Value doAccountMessageSet(const Json::Value ¶ms); + Json::Value doAccountPublishSet(const Json::Value ¶ms); + Json::Value doAccountRateSet(const Json::Value ¶ms); + Json::Value doAccountTransactions(const Json::Value& params); + Json::Value doAccountWalletSet(const Json::Value ¶ms); + Json::Value doConnect(const Json::Value& params); + Json::Value doDataDelete(const Json::Value& params); + Json::Value doDataFetch(const Json::Value& params); + Json::Value doDataStore(const Json::Value& params); + Json::Value doLedger(const Json::Value& params); + Json::Value doLogRotate(const Json::Value& params); + Json::Value doNicknameInfo(const Json::Value& params); + Json::Value doNicknameSet(const Json::Value& params); + Json::Value doOfferCreate(const Json::Value& params); + Json::Value doOfferCancel(const Json::Value& params); + Json::Value doOwnerInfo(const Json::Value& params); + Json::Value doPasswordFund(const Json::Value& params); + Json::Value doPasswordSet(const Json::Value& params); + Json::Value doProfile(const Json::Value& params); + Json::Value doPeers(const Json::Value& params); + Json::Value doRipple(const Json::Value ¶ms); + Json::Value doRippleLinesGet(const Json::Value ¶ms); + Json::Value doRippleLineSet(const Json::Value& params); + Json::Value doSend(const Json::Value& params); + 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 doTransitSet(const Json::Value& params); + 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); + Json::Value doUnlDelete(const Json::Value& params); + Json::Value doUnlFetch(const Json::Value& params); + Json::Value doUnlList(const Json::Value& params); + Json::Value doUnlLoad(const Json::Value& params); + Json::Value doUnlNetwork(const Json::Value& params); + Json::Value doUnlReset(const Json::Value& params); + Json::Value doUnlScore(const Json::Value& params); + + Json::Value doValidationCreate(const Json::Value& params); + Json::Value doValidationSeed(const Json::Value& params); + + Json::Value doWalletAccounts(const Json::Value& params); + Json::Value doWalletAdd(const Json::Value& params); + Json::Value doWalletClaim(const Json::Value& params); + Json::Value doWalletCreate(const Json::Value& params); + Json::Value doWalletLock(const Json::Value& params); + Json::Value doWalletPropose(const Json::Value& params); + Json::Value doWalletSeed(const Json::Value& params); + Json::Value doWalletUnlock(const Json::Value& params); + Json::Value doWalletVerify(const Json::Value& params); + + Json::Value doLogin(const Json::Value& params); + + +public: + + enum { + rpcSUCCESS, + + // Misc failure + rpcLOAD_FAILED, + rpcNO_PERMISSION, + rpcNOT_STANDALONE, + + // Networking + rpcNO_CLOSED, + rpcNO_CURRENT, + rpcNO_NETWORK, + + // Ledger state + rpcACT_EXISTS, + rpcACT_NOT_FOUND, + rpcINSUF_FUNDS, + rpcLGR_NOT_FOUND, + rpcNICKNAME_MISSING, + rpcNO_ACCOUNT, + rpcPASSWD_CHANGED, + rpcSRC_MISSING, + rpcSRC_UNCLAIMED, + rpcTXN_NOT_FOUND, + rpcWRONG_SEED, + + // Malformed command + rpcINVALID_PARAMS, + rpcUNKNOWN_COMMAND, + + // Bad parameter + rpcACT_MALFORMED, + rpcQUALITY_MALFORMED, + rpcBAD_SEED, + rpcDST_ACT_MALFORMED, + rpcDST_ACT_MISSING, + rpcDST_AMT_MALFORMED, + rpcGETS_ACT_MALFORMED, + rpcGETS_AMT_MALFORMED, + rpcHOST_IP_MALFORMED, + rpcLGR_IDXS_INVALID, + rpcLGR_IDX_MALFORMED, + rpcNICKNAME_MALFORMED, + rpcNICKNAME_PERM, + rpcPAYS_ACT_MALFORMED, + rpcPAYS_AMT_MALFORMED, + rpcPORT_MALFORMED, + rpcPUBLIC_MALFORMED, + rpcSRC_ACT_MALFORMED, + rpcSRC_ACT_MISSING, + rpcSRC_AMT_MALFORMED, + + // Internal error (should never happen) + rpcINTERNAL, // Generic internal error. + rpcFAIL_GEN_DECRPYT, + rpcNOT_IMPL, + rpcNO_GEN_DECRPYT, + }; + + enum { GUEST, USER, ADMIN }; + + RPCHandler(NetworkOPs* netOps); + + Json::Value doCommand(const std::string& command, Json::Value& params,int role); + Json::Value rpcError(int iError); + +}; \ No newline at end of file diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 07d8376cb..01568d999 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1,19 +1,9 @@ - #include "RPCServer.h" -#include "RequestParser.h" -#include "HttpReply.h" -#include "HttpsClient.h" -#include "Application.h" -#include "RPC.h" -#include "Wallet.h" -#include "NewcoinAddress.h" -#include "AccountState.h" -#include "NicknameState.h" -#include "utils.h" #include "Log.h" -#include "RippleLines.h" -#include "Pathfinder.h" +#include "HttpsClient.h" +#include "RPC.h" +#include "utils.h" #include @@ -22,136 +12,110 @@ #include #include #include +#include + -#include #include "../json/reader.h" #include "../json/writer.h" SETUP_LOG(); +#ifndef RPC_MAXIMUM_QUERY +#define RPC_MAXIMUM_QUERY (1024*1024) +#endif + RPCServer::RPCServer(boost::asio::io_service& io_service , NetworkOPs* nopNetwork) - : mNetOps(nopNetwork), mSocket(io_service) + : mNetOps(nopNetwork), mRPCHandler(nopNetwork), mSocket(io_service) { - mRole = GUEST; + + mRole = RPCHandler::GUEST; } -Json::Value RPCServer::RPCError(int iError) -{ - static struct { - int iError; - const char* pToken; - const char* pMessage; - } errorInfoA[] = { - { rpcACT_EXISTS, "actExists", "Account already exists." }, - { rpcACT_MALFORMED, "actMalformed", "Account malformed." }, - { rpcACT_NOT_FOUND, "actNotFound", "Account not found." }, - { rpcBAD_SEED, "badSeed", "Disallowed seed." }, - { rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." }, - { rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exists." }, - { rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." }, - { rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." }, - { rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." }, - { rpcGETS_AMT_MALFORMED, "getsAmtMalformed", "Gets amount malformed." }, - { rpcHOST_IP_MALFORMED, "hostIpMalformed", "Host IP is malformed." }, - { rpcINSUF_FUNDS, "insufFunds", "Insufficient funds." }, - { rpcINTERNAL, "internal", "Internal error." }, - { rpcINVALID_PARAMS, "invalidParams", "Invalid parameters." }, - { rpcLGR_IDXS_INVALID, "lgrIdxsInvalid", "Ledger indexes invalid." }, - { rpcLGR_IDX_MALFORMED, "lgrIdxMalformed", "Ledger index malformed." }, - { rpcLGR_NOT_FOUND, "lgrNotFound", "Ledger not found." }, - { rpcNICKNAME_MALFORMED, "nicknameMalformed","Nickname is malformed." }, - { rpcNICKNAME_MISSING, "nicknameMissing", "Nickname does not exist." }, - { rpcNICKNAME_PERM, "nicknamePerm", "Account does not control nickname." }, - { rpcNOT_IMPL, "notImpl", "Not implemented." }, - { rpcNO_ACCOUNT, "noAccount", "No such account." }, - { rpcNO_CLOSED, "noClosed", "Closed ledger is unavailable." }, - { rpcNO_CURRENT, "noCurrent", "Current ledger is unavailable." }, - { rpcNO_GEN_DECRPYT, "noGenDectypt", "Password failed to decrypt master public generator." }, - { rpcNO_NETWORK, "noNetwork", "Network not available." }, - { rpcNO_PERMISSION, "noPermission", "You don't have permission for this command." }, - { rpcNOT_STANDALONE, "notStandAlone", "Operation valid in debug mode only." }, - { rpcPASSWD_CHANGED, "passwdChanged", "Wrong key, password changed." }, - { rpcPAYS_ACT_MALFORMED, "paysActMalformed", "Pays account malformed." }, - { rpcPAYS_AMT_MALFORMED, "paysAmtMalformed", "Pays amount malformed." }, - { rpcPORT_MALFORMED, "portMalformed", "Port is malformed." }, - { rpcPUBLIC_MALFORMED, "publicMalformed", "Public key is malformed." }, - { rpcQUALITY_MALFORMED, "qualityMalformed", "Quality malformed." }, - { rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." }, - { rpcSRC_ACT_MISSING, "srcActMissing", "Source account does not exist." }, - { rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." }, - { rpcSRC_UNCLAIMED, "srcUnclaimed", "Source account is not claimed." }, - { rpcSUCCESS, "success", "Success." }, - { rpcTXN_NOT_FOUND, "txnNotFound", "Transaction not found." }, - { rpcUNKNOWN_COMMAND, "unknownCmd", "Unknown command." }, - { rpcWRONG_SEED, "wrongSeed", "The regular key does not point as the master key." }, - }; - int i; - - for (i=NUMBER(errorInfoA); i-- && errorInfoA[i].iError != iError;) - ; - - Json::Value jsonResult = Json::Value(Json::objectValue); - - jsonResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i(iError); - jsonResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i(iError); - jsonResult["error_code"] = iError; - if (i >= 0) - std::cerr << "RPCError: " - << errorInfoA[i].pToken << ": " << errorInfoA[i].pMessage << std::endl; - - return jsonResult; -} void RPCServer::connected() { //std::cout << "RPC request" << std::endl; - if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole=ADMIN; - else mRole=GUEST; + if (mSocket.remote_endpoint().address().to_string()=="127.0.0.1") mRole = RPCHandler::ADMIN; + else mRole = RPCHandler::GUEST; - mSocket.async_read_some(boost::asio::buffer(mReadBuffer), - boost::bind(&RPCServer::Shandle_read, shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n", + boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error)); } -void RPCServer::handle_read(const boost::system::error_code& e, - std::size_t bytes_transferred) +void RPCServer::handle_read_req(const boost::system::error_code& e) { - if (!e) + std::string req; + + if (mLineBuffer.size()) { - boost::tribool result; - result = mRequestParser.parse( - mIncomingRequest, mReadBuffer.data(), mReadBuffer.data() + bytes_transferred); + req.assign(boost::asio::buffer_cast(mLineBuffer.data()), mLineBuffer.size()); + mLineBuffer.consume(mLineBuffer.size()); + } - if (result) + req += strCopy(mQueryVec); + mReplyStr = handleRequest(req); + boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr), + boost::bind(&RPCServer::handle_write, shared_from_this(), boost::asio::placeholders::error)); +} + +void RPCServer::handle_read_line(const boost::system::error_code& e) +{ + if (e) + return; + + HTTPRequestAction action = mHTTPRequest.consume(mLineBuffer); + + if (action == haDO_REQUEST) + { // request with no body + cLog(lsWARNING) << "RPC HTTP request with no body"; + boost::system::error_code ignore_ec; + mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec); + return; + } + else if (action == haREAD_LINE) + { + boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n", + boost::bind(&RPCServer::handle_read_line, shared_from_this(), + boost::asio::placeholders::error)); + } + else if (action == haREAD_RAW) + { + int rLen = mHTTPRequest.getDataSize(); + if ((rLen < 0) || (rLen > RPC_MAXIMUM_QUERY)) { - mReplyStr = handleRequest(mIncomingRequest.mBody); - - sendReply(); + cLog(lsWARNING) << "Illegal RPC request length " << rLen; + boost::system::error_code ignore_ec; + mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignore_ec); + return; } - else if (!result) - { // bad request - std::cout << "bad request: " << mIncomingRequest.mBody < naRegularSeed -// <-- naMasterGenerator -Json::Value RPCServer::getMasterGenerator(const uint256& uLedger, const NewcoinAddress& naRegularSeed, NewcoinAddress& naMasterGenerator) -{ - NewcoinAddress na0Public; // To find the generator's index. - NewcoinAddress na0Private; // To decrypt the master generator's cipher. - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - - na0Public.setAccountPublic(naGenerator, 0); - na0Private.setAccountPrivate(naGenerator, naRegularSeed, 0); - - SLE::pointer sleGen = mNetOps->getGenerator(uLedger, na0Public.getAccountID()); - - if (!sleGen) - { - // No account has been claimed or has had it password set for seed. - return RPCError(rpcNO_ACCOUNT); - } - - std::vector vucCipher = sleGen->getFieldVL(sfGenerator); - std::vector vucMasterGenerator = na0Private.accountPrivateDecrypt(na0Public, vucCipher); - if (vucMasterGenerator.empty()) - { - return RPCError(rpcFAIL_GEN_DECRPYT); - } - - naMasterGenerator.setGenerator(vucMasterGenerator); - - return Json::Value(Json::objectValue); -} - -// Given a seed and a source account get the regular public and private key for authorizing transactions. -// - Make sure the source account can pay. -// --> naRegularSeed : To find the generator -// --> naSrcAccountID : Account we want the public and private regular keys to. -// <-- naAccountPublic : Regular public key for naSrcAccountID -// <-- naAccountPrivate : Regular private key for naSrcAccountID -// <-- saSrcBalance: Balance minus fee. -// --> naVerifyGenerator : If provided, the found master public generator must match. -// XXX Be more lenient, allow use of master generator on claimed accounts. -Json::Value RPCServer::authorize(const uint256& uLedger, - const NewcoinAddress& naRegularSeed, const NewcoinAddress& naSrcAccountID, - NewcoinAddress& naAccountPublic, NewcoinAddress& naAccountPrivate, - STAmount& saSrcBalance, const STAmount& saFee, AccountState::pointer& asSrc, - const NewcoinAddress& naVerifyGenerator) -{ - // Source/paying account must exist. - asSrc = mNetOps->getAccountState(uLedger, naSrcAccountID); - if (!asSrc) - { - return RPCError(rpcSRC_ACT_MISSING); - } - - NewcoinAddress naMasterGenerator; - - if (asSrc->bHaveAuthorizedKey()) - { - Json::Value obj = getMasterGenerator(uLedger, naRegularSeed, naMasterGenerator); - - if (!obj.empty()) - return obj; - } - else - { - // Try the seed as a master seed. - naMasterGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - } - - // If naVerifyGenerator is provided, make sure it is the master generator. - if (naVerifyGenerator.isValid() && naMasterGenerator != naVerifyGenerator) - { - return RPCError(rpcWRONG_SEED); - } - - // Find the index of the account from the master generator, so we can generate the public and private keys. - NewcoinAddress naMasterAccountPublic; - unsigned int iIndex = 0; - bool bFound = false; - - // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are - // related. - while (!bFound && iIndex != theConfig.ACCOUNT_PROBE_MAX) - { - naMasterAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - - Log(lsDEBUG) << "authorize: " << iIndex << " : " << naMasterAccountPublic.humanAccountID() << " : " << naSrcAccountID.humanAccountID(); - - bFound = naSrcAccountID.getAccountID() == naMasterAccountPublic.getAccountID(); - if (!bFound) - ++iIndex; - } - - if (!bFound) - { - return RPCError(rpcACT_NOT_FOUND); - } - - // Use the regular generator to determine the associated public and private keys. - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - - naAccountPublic.setAccountPublic(naGenerator, iIndex); - naAccountPrivate.setAccountPrivate(naGenerator, naRegularSeed, iIndex); - - if (asSrc->bHaveAuthorizedKey() && (asSrc->getAuthorizedKey().getAccountID() != naAccountPublic.getAccountID())) - { - // std::cerr << "iIndex: " << iIndex << std::endl; - // std::cerr << "sfAuthorizedKey: " << strHex(asSrc->getAuthorizedKey().getAccountID()) << std::endl; - // std::cerr << "naAccountPublic: " << strHex(naAccountPublic.getAccountID()) << std::endl; - - return RPCError(rpcPASSWD_CHANGED); - } - - saSrcBalance = asSrc->getBalance(); - - if (saSrcBalance < saFee) - { - Log(lsINFO) << "authorize: Insufficent funds for fees: fee=" << saFee.getText() << " balance=" << saSrcBalance.getText(); - - return RPCError(rpcINSUF_FUNDS); - } - else - { - saSrcBalance -= saFee; - } - - return Json::Value(); -} - -// --> strIdent: public key, account ID, or regular seed. -// <-- bIndex: true if iIndex > 0 and used the index. -Json::Value RPCServer::accountFromString(const uint256& uLedger, NewcoinAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex) -{ - NewcoinAddress naSeed; - - if (naAccount.setAccountPublic(strIdent) || naAccount.setAccountID(strIdent)) - { - // Got the account. - bIndex = false; - } - // Must be a seed. - else if (!naSeed.setSeedGeneric(strIdent)) - { - return RPCError(rpcBAD_SEED); - } - else - { - // We allow the use of the seeds to access #0. - // This is poor practice and merely for debuging convenience. - NewcoinAddress naRegular0Public; - NewcoinAddress naRegular0Private; - - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSeed); - - naRegular0Public.setAccountPublic(naGenerator, 0); - naRegular0Private.setAccountPrivate(naGenerator, naSeed, 0); - -// uint160 uGeneratorID = naRegular0Public.getAccountID(); - SLE::pointer sleGen = mNetOps->getGenerator(uLedger, naRegular0Public.getAccountID()); - if (!sleGen) - { - // Didn't find a generator map, assume it is a master generator. - nothing(); - } - else - { - // Found master public key. - std::vector vucCipher = sleGen->getFieldVL(sfGenerator); - std::vector vucMasterGenerator = naRegular0Private.accountPrivateDecrypt(naRegular0Public, vucCipher); - if (vucMasterGenerator.empty()) - { - RPCError(rpcNO_GEN_DECRPYT); - } - - naGenerator.setGenerator(vucMasterGenerator); - } - - bIndex = !iIndex; - - naAccount.setAccountPublic(naGenerator, iIndex); - } - - return Json::Value(Json::objectValue); -} - -Json::Value RPCServer::doAcceptLedger(const Json::Value ¶ms) -{ - if (!theConfig.RUN_STANDALONE) - return RPCError(rpcNOT_STANDALONE); - - Json::Value obj(Json::objectValue); - obj["newLedger"] = theApp->getOPs().acceptLedger(); - return obj; -} - -// account_domain_set [] -Json::Value RPCServer::doAccountDomainSet(const Json::Value ¶ms) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - 0, - NewcoinAddress(), - true, - strCopy(params[2u].asString()), - false, - 0, - false, - uint256(), - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return Json::Value(Json::objectValue); -} - -// account_email_set [] -Json::Value RPCServer::doAccountEmailSet(const Json::Value ¶ms) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - // Hash as per: http://en.gravatar.com/site/implement/hash/ - std::string strEmail = 3 == params.size() ? params[2u].asString() : ""; - boost::trim(strEmail); - boost::to_lower(strEmail); - - std::vector vucMD5(128/8, 0); - MD5(reinterpret_cast(strEmail.c_str()), strEmail.size(), &vucMD5.front()); - - uint128 uEmailHash(vucMD5); - std::vector vucDomain; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - true, - strEmail.empty() ? uint128() : uEmailHash, - false, - uint256(), - NewcoinAddress(), - false, - vucDomain, - false, - 0, - false, - uint256(), - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - if (!strEmail.empty()) - { - obj["Email"] = strEmail; - obj["EmailHash"] = strHex(vucMD5); - obj["UrlGravatar"] = AccountState::createGravatarUrl(uEmailHash); - } - - return obj; -} - -// account_info || -// account_info || [] -Json::Value RPCServer::doAccountInfo(const Json::Value ¶ms) -{ - std::string strIdent = params[0u].asString(); - bool bIndex; - int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; - NewcoinAddress naAccount; - - Json::Value ret; - - // Get info on account. - - uint256 uAccepted = mNetOps->getClosedLedger(); - Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); - - if (jAccepted.empty()) - { - AccountState::pointer asAccepted = mNetOps->getAccountState(uAccepted, naAccount); - - if (asAccepted) - asAccepted->addJson(jAccepted); - } - - ret["accepted"] = jAccepted; - - Json::Value jCurrent = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); - - if (jCurrent.empty()) - { - AccountState::pointer asCurrent = mNetOps->getAccountState(uint256(0), naAccount); - - if (asCurrent) - asCurrent->addJson(jCurrent); - } - - ret["current"] = jCurrent; - -#if 0 - if (!jAccepted && !asCurrent) - { - ret["account"] = naAccount.humanAccountID(); - ret["status"] = "NotFound"; - if (bIndex) - ret["index"] = iIndex; - } -#endif - return ret; -} - -// account_message_set -Json::Value RPCServer::doAccountMessageSet(const Json::Value& params) { - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - NewcoinAddress naMessagePubKey; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naMessagePubKey.setAccountPublic(params[2u].asString())) - { - return RPCError(rpcPUBLIC_MALFORMED); - } - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - std::vector vucDomain; - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - uint256(), - naMessagePubKey, - false, - vucDomain, - false, - 0, - false, - uint256(), - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["MessageKey"] = naMessagePubKey.humanAccountPublic(); - - return obj; -} - -// account_publish_set -Json::Value RPCServer::doAccountPublishSet(const Json::Value ¶ms) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - uint256 uPublishHash(params[2u].asString()); - uint32 uPublishSize = lexical_cast_s(params[3u].asString()); - std::vector vucDomain; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - 0, - NewcoinAddress(), - false, - vucDomain, - false, - 0, - true, - uPublishHash, - uPublishSize); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return Json::Value(Json::objectValue); -} - -// account_rate_set -Json::Value RPCServer::doAccountRateSet(const Json::Value ¶ms) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - uint32 uRate = lexical_cast_s(params[2u].asString()); - std::vector vucDomain; - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - false, - 0, - NewcoinAddress(), - false, - vucDomain, - true, - uRate, - false, - uint256(), - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return Json::Value(Json::objectValue); -} - -// account_wallet_set [] -Json::Value RPCServer::doAccountWalletSet(const Json::Value& params) { - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - std::vector vucDomain; - - if (!obj.empty()) - return obj; - - std::string strWalletLocator = params.size() == 3 ? params[2u].asString() : ""; - uint256 uWalletLocator; - - if (!strWalletLocator.empty()) - uWalletLocator.SetHex(strWalletLocator); - - Transaction::pointer trans = Transaction::sharedAccountSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - false, - uint128(), - true, - uWalletLocator, - NewcoinAddress(), - false, - vucDomain, - false, - 0, - false, - uint256(), - 0); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - if (!strWalletLocator.empty()) - obj["WalletLocator"] = uWalletLocator.GetHex(); - - return obj; -} - -Json::Value RPCServer::doConnect(const Json::Value& params) -{ - if (theConfig.RUN_STANDALONE) - return "cannot connect in standalone mode"; - - // connect [port] - std::string strIp; - int iPort = -1; - - // XXX Might allow domain for manual connections. - if (!extractString(strIp, params, 0)) - return RPCError(rpcHOST_IP_MALFORMED); - - if (params.size() == 2) - { - std::string strPort; - - // YYY Should make an extract int. - if (!extractString(strPort, params, 1)) - return RPCError(rpcPORT_MALFORMED); - - iPort = lexical_cast_s(strPort); - } - - // XXX Validate legal IP and port - theApp->getConnectionPool().connectTo(strIp, iPort); - - return "connecting"; -} - -// data_delete -Json::Value RPCServer::doDataDelete(const Json::Value& params) -{ - std::string strKey = params[0u].asString(); - - Json::Value ret = Json::Value(Json::objectValue); - - if (theApp->getWallet().dataDelete(strKey)) - { - ret["key"] = strKey; - } - else - { - ret = RPCError(rpcINTERNAL); - } - - return ret; -} - -// data_fetch -Json::Value RPCServer::doDataFetch(const Json::Value& params) -{ - std::string strKey = params[0u].asString(); - std::string strValue; - - Json::Value ret = Json::Value(Json::objectValue); - - ret["key"] = strKey; - if (theApp->getWallet().dataFetch(strKey, strValue)) - ret["value"] = strValue; - - return ret; -} - -// data_store -Json::Value RPCServer::doDataStore(const Json::Value& params) -{ - std::string strKey = params[0u].asString(); - std::string strValue = params[1u].asString(); - - Json::Value ret = Json::Value(Json::objectValue); - - if (theApp->getWallet().dataStore(strKey, strValue)) - { - ret["key"] = strKey; - ret["value"] = strValue; - } - else - { - ret = RPCError(rpcINTERNAL); - } - - return ret; -} - -// nickname_info -// Note: Nicknames are not automatically looked up by commands as they are advisory and can be changed. -Json::Value RPCServer::doNicknameInfo(const Json::Value& params) -{ - std::string strNickname = params[0u].asString(); - boost::trim(strNickname); - - if (strNickname.empty()) - { - return RPCError(rpcNICKNAME_MALFORMED); - } - - NicknameState::pointer nsSrc = mNetOps->getNicknameState(uint256(0), strNickname); - if (!nsSrc) - { - return RPCError(rpcNICKNAME_MISSING); - } - - Json::Value ret(Json::objectValue); - - ret["nickname"] = strNickname; - - nsSrc->addJson(ret); - - return ret; -} - -// nickname_set [] [] -Json::Value RPCServer::doNicknameSet(const Json::Value& params) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - STAmount saMinimumOffer; - bool bSetOffer = params.size() >= 4; - std::string strOfferCurrency; - std::string strNickname = params[2u].asString(); - boost::trim(strNickname); - - if (strNickname.empty()) - { - return RPCError(rpcNICKNAME_MALFORMED); - } - else if (params.size() >= 4 && !saMinimumOffer.setFullValue(params[3u].asString(), strOfferCurrency)) - { - return RPCError(rpcDST_AMT_MALFORMED); - } - - STAmount saFee; - NicknameState::pointer nsSrc = mNetOps->getNicknameState(uint256(0), strNickname); - - if (!nsSrc) - { - // Creating nickname. - saFee = theConfig.FEE_NICKNAME_CREATE; - } - else if (naSrcAccountID != nsSrc->getAccountID()) - { - // We don't own the nickname. - return RPCError(rpcNICKNAME_PERM); - } - else - { - // Setting the minimum offer. - saFee = theConfig.FEE_DEFAULT; - } - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could verify nickname does not exist or points to paying account. - // XXX Adjust fee for nickname create. - - Transaction::pointer trans = Transaction::sharedNicknameSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - Ledger::getNicknameHash(strNickname), - bSetOffer, - saMinimumOffer); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// offer_create [passive] -// *offering* for *wants* -Json::Value RPCServer::doOfferCreate(const Json::Value ¶ms) -{ - NewcoinAddress naSeed; - NewcoinAddress naSrcAccountID; - STAmount saTakerPays; - STAmount saTakerGets; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!saTakerGets.setFullValue(params[2u].asString(), params[3u].asString(), params[4u].asString())) - { - return RPCError(rpcGETS_AMT_MALFORMED); - } - else if (!saTakerPays.setFullValue(params[5u].asString(), params[6u].asString(), params[7u].asString())) - { - return RPCError(rpcPAYS_AMT_MALFORMED); - } - else if (params.size() == 10 && params[9u].asString() != "passive") - { - return RPCError(rpcINVALID_PARAMS); - } - - uint32 uExpiration = lexical_cast_s(params[8u].asString()); - bool bPassive = params.size() == 10; - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedOfferCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - bPassive, - saTakerPays, - saTakerGets, - uExpiration); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// offer_cancel -Json::Value RPCServer::doOfferCancel(const Json::Value ¶ms) -{ - NewcoinAddress naSeed; - NewcoinAddress naSrcAccountID; - uint32 uSequence = lexical_cast_s(params[2u].asString()); - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedOfferCancel( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - uSequence); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// owner_info || -// owner_info || [] -Json::Value RPCServer::doOwnerInfo(const Json::Value& params) -{ - std::string strIdent = params[0u].asString(); - bool bIndex; - int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; - NewcoinAddress naAccount; - - Json::Value ret; - - // Get info on account. - - uint256 uAccepted = mNetOps->getClosedLedger(); - Json::Value jAccepted = accountFromString(uAccepted, naAccount, bIndex, strIdent, iIndex); - - ret["accepted"] = jAccepted.empty() ? mNetOps->getOwnerInfo(uAccepted, naAccount) : jAccepted; - - Json::Value jCurrent = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); - - ret["current"] = jCurrent.empty() ? mNetOps->getOwnerInfo(uint256(0), naAccount) : jCurrent; - - return ret; -} -// password_fund [] -// YYY Make making account default to first account for seed. -Json::Value RPCServer::doPasswordFund(const Json::Value ¶ms) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naDstAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[params.size() == 3 ? 2u : 1u].asString())) - { - return RPCError(rpcDST_ACT_MALFORMED); - } - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could verify dst exists and isn't already funded. - - Transaction::pointer trans = Transaction::sharedPasswordFund( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - naDstAccountID); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// password_set [] -Json::Value RPCServer::doPasswordSet(const Json::Value& params) -{ - NewcoinAddress naMasterSeed; - NewcoinAddress naRegularSeed; - NewcoinAddress naAccountID; - - if (!naMasterSeed.setSeedGeneric(params[0u].asString())) - { - // Should also not allow account id's as seeds. - return RPCError(rpcBAD_SEED); - } - else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) - { - // Should also not allow account id's as seeds. - return RPCError(rpcBAD_SEED); - } - // YYY Might use account from string to be more flexible. - else if (params.size() >= 3 && !naAccountID.setAccountID(params[2u].asString())) - { - return RPCError(rpcACT_MALFORMED); - } - else - { - NewcoinAddress naMasterGenerator = NewcoinAddress::createGeneratorPublic(naMasterSeed); - NewcoinAddress naRegularGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - NewcoinAddress naRegular0Public; - NewcoinAddress naRegular0Private; - - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - - naAccountPublic.setAccountPublic(naMasterGenerator, 0); - naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); - - naRegular0Public.setAccountPublic(naRegularGenerator, 0); - naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); - - // Hash of regular account #0 public key. -// uint160 uGeneratorID = naRegular0Public.getAccountID(); - std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); - std::vector vucGeneratorSig; - - // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. - // XXX Check result. - naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); - - NewcoinAddress naMasterXPublic; - NewcoinAddress naRegularXPublic; - unsigned int iIndex = -1; // Compensate for initial increment. - int iMax = theConfig.ACCOUNT_PROBE_MAX; - - // YYY Could probe periodically to see if accounts exists. - // YYY Max could be set randomly. - // Don't look at ledger entries to determine if the account exists. Don't want to leak to thin server that these accounts are - // related. - do { - ++iIndex; - naMasterXPublic.setAccountPublic(naMasterGenerator, iIndex); - naRegularXPublic.setAccountPublic(naRegularGenerator, iIndex); - - std::cerr << iIndex << ": " << naRegularXPublic.humanAccountID() << std::endl; - - } while (naAccountID.getAccountID() != naMasterXPublic.getAccountID() && --iMax); - - if (!iMax) - { - return RPCError(rpcACT_NOT_FOUND); - } - - Transaction::pointer trans = Transaction::sharedPasswordSet( - naAccountPublic, naAccountPrivate, - 0, - naRegularXPublic, - vucGeneratorCipher, - naRegular0Public.getAccountPublic(), - vucGeneratorSig); - - trans = mNetOps->submitTransaction(trans); - - Json::Value obj(Json::objectValue); - - // We "echo" the seeds so they can be checked. - obj["master_seed"] = naMasterSeed.humanSeed(); - obj["master_key"] = naMasterSeed.humanSeed1751(); - obj["regular_seed"] = naRegularSeed.humanSeed(); - obj["regular_key"] = naRegularSeed.humanSeed1751(); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; - } -} - -Json::Value RPCServer::doPeers(const Json::Value& params) -{ - // peers - Json::Value obj(Json::objectValue); - obj["peers"]=theApp->getConnectionPool().getPeersJson(); - return obj; -} - -// profile offers [submit] -// profile 0:offers 1:pass_a 2:account_a 3:currency_offer_a 4:account_b 5:currency_offer_b 6: 7:[submit] -// issuer is the offering account -// --> submit: 'submit|true|false': defaults to false -// Prior to running allow each to have a credit line of what they will be getting from the other account. -Json::Value RPCServer::doProfile(const Json::Value ¶ms) -{ - int iArgs = params.size(); - NewcoinAddress naSeedA; - NewcoinAddress naAccountA; - uint160 uCurrencyOfferA; - NewcoinAddress naSeedB; - NewcoinAddress naAccountB; - uint160 uCurrencyOfferB; - uint32 iCount = 100; - bool bSubmit = false; - - if (iArgs < 6 || "offers" != params[0u].asString()) - { - return RPCError(rpcINVALID_PARAMS); - } - - if (!naSeedA.setSeedGeneric(params[1u].asString())) // - return RPCError(rpcINVALID_PARAMS); - - naAccountA.setAccountID(params[2u].asString()); // - - if (!STAmount::currencyFromString(uCurrencyOfferA, params[3u].asString())) // - return RPCError(rpcINVALID_PARAMS); - - naAccountB.setAccountID(params[4u].asString()); // - if (!STAmount::currencyFromString(uCurrencyOfferB, params[5u].asString())) // - return RPCError(rpcINVALID_PARAMS); - - iCount = lexical_cast_s(params[6u].asString()); - - if (iArgs >= 8 && "false" != params[7u].asString()) - bSubmit = true; - - Log::setMinSeverity(lsFATAL); - - boost::posix_time::ptime ptStart(boost::posix_time::microsec_clock::local_time()); - - for(int n=0; ngetSeq(), // uSeq - theConfig.FEE_DEFAULT, - 0, // uSourceTag, - false, // bPassive - STAmount(uCurrencyOfferA, naAccountA.getAccountID(), 1), // saTakerPays - STAmount(uCurrencyOfferB, naAccountB.getAccountID(), 1+n), // saTakerGets - 0); // uExpiration - - if(bSubmit) - tpOfferA = mNetOps->submitTransaction(tpOfferA); - } - - boost::posix_time::ptime ptEnd(boost::posix_time::microsec_clock::local_time()); - boost::posix_time::time_duration tdInterval = ptEnd-ptStart; - long lMicroseconds = tdInterval.total_microseconds(); - int iTransactions = iCount; - float fRate = lMicroseconds ? iTransactions/(lMicroseconds/1000000.0) : 0.0; - - Json::Value obj(Json::objectValue); - - obj["transactions"] = iTransactions; - obj["submit"] = bSubmit; - obj["start"] = boost::posix_time::to_simple_string(ptStart); - obj["end"] = boost::posix_time::to_simple_string(ptEnd); - obj["interval"] = boost::posix_time::to_simple_string(tdInterval); - obj["rate_per_second"] = fRate; - - return obj; -} - -// ripple -// [] -// + -// full|partial limit|average [] -// -// path: -// path + -// -// path_element: -// account [] [] -// offer [] -Json::Value RPCServer::doRipple(const Json::Value ¶ms) -{ - NewcoinAddress naSeed; - STAmount saSrcAmountMax; - uint160 uSrcCurrencyID; - NewcoinAddress naSrcAccountID; - NewcoinAddress naSrcIssuerID; - bool bPartial; - bool bFull; - bool bLimit; - bool bAverage; - NewcoinAddress naDstAccountID; - STAmount saDstAmount; - uint160 uDstCurrencyID; - - STPathSet spsPaths; - - naSrcIssuerID.setAccountID(params[4u].asString()); // - - if (!naSeed.setSeedGeneric(params[0u].asString())) // - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) // - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - // [] - else if (!saSrcAmountMax.setFullValue(params[2u].asString(), params[3u].asString(), params[naSrcIssuerID.isValid() ? 4u : 1u].asString())) - { - // Log(lsINFO) << "naSrcIssuerID.isValid(): " << naSrcIssuerID.isValid(); - // Log(lsINFO) << "source_max: " << params[2u].asString(); - // Log(lsINFO) << "source_currency: " << params[3u].asString(); - // Log(lsINFO) << "source_issuer: " << params[naSrcIssuerID.isValid() ? 4u : 2u].asString(); - - return RPCError(rpcSRC_AMT_MALFORMED); - } - - int iArg = 4 + naSrcIssuerID.isValid(); - - // XXX bSrcRedeem & bSrcIssue not used. - STPath spPath; - - while (params.size() != iArg && params[iArg].asString() == "path") // path - { - Log(lsINFO) << "Path>"; - ++iArg; - - while (params.size() != iArg - && (params[iArg].asString() == "offer" || params[iArg].asString() == "account")) - { - if (params.size() >= iArg + 3 && params[iArg].asString() == "offer") // offer - { - Log(lsINFO) << "Offer>"; - uint160 uCurrencyID; - NewcoinAddress naIssuerID; - - ++iArg; - - if (!STAmount::currencyFromString(uCurrencyID, params[iArg++].asString())) // - { - return RPCError(rpcINVALID_PARAMS); - } - else if (naIssuerID.setAccountID(params[iArg].asString())) // [] - { - ++iArg; - } - - spPath.addElement(STPathElement( - uint160(0), - uCurrencyID, - naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); - } - else if (params.size() >= iArg + 2 && params[iArg].asString() == "account") // account - { - Log(lsINFO) << "Account>"; - NewcoinAddress naAccountID; - uint160 uCurrencyID; - NewcoinAddress naIssuerID; - - ++iArg; - - if (!naAccountID.setAccountID(params[iArg++].asString())) // - { - return RPCError(rpcINVALID_PARAMS); - } - - if (params.size() != iArg && STAmount::currencyFromString(uCurrencyID, params[iArg].asString())) // [] - { - ++iArg; - } - - if (params.size() != iArg && naIssuerID.setAccountID(params[iArg].asString())) // [] - { - ++iArg; - } - - spPath.addElement(STPathElement( - naAccountID.getAccountID(), - uCurrencyID, - naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); - } - else - { - return RPCError(rpcINVALID_PARAMS); - } - } - - if (spPath.isEmpty()) - { - return RPCError(rpcINVALID_PARAMS); - } - else - { - spsPaths.addPath(spPath); - spPath.clear(); - } - } - - // full|partial - bPartial = params.size() != iArg ? params[iArg].asString() == "partial" : false; - bFull = params.size() != iArg ? params[iArg].asString() == "full" : false; - - if (!bPartial && !bFull) - { - return RPCError(rpcINVALID_PARAMS); - } - else - { - ++iArg; - } - - // limit|average - bLimit = params.size() != iArg ? params[iArg].asString() == "limit" : false; - bAverage = params.size() != iArg ? params[iArg].asString() == "average" : false; - - if (!bLimit && !bAverage) - { - return RPCError(rpcINVALID_PARAMS); - } - else - { - ++iArg; - } - - if (params.size() != iArg && !naDstAccountID.setAccountID(params[iArg++].asString())) // - { - return RPCError(rpcDST_ACT_MALFORMED); - } - - const unsigned int uDstIssuer = params.size() == iArg + 3 ? iArg+2 : iArg-1; - - // - if (params.size() != iArg + 2 && params.size() != iArg + 3) - { - // Log(lsINFO) << "params.size(): " << params.size(); - - return RPCError(rpcDST_AMT_MALFORMED); - } - else if (!saDstAmount.setFullValue(params[iArg].asString(), params[iArg+1].asString(), params[uDstIssuer].asString())) - { - // Log(lsINFO) << " Amount: " << params[iArg].asString(); - // Log(lsINFO) << "Currency: " << params[iArg+1].asString(); - // Log(lsINFO) << " Issuer: " << params[uDstIssuer].asString(); - - return RPCError(rpcDST_AMT_MALFORMED); - } - - AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); - STAmount saFee = theConfig.FEE_DEFAULT; - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naVerifyGenerator); - - if (!obj.empty()) - return obj; - - // YYY Could do some checking: source has funds or credit, dst exists and has sufficent credit limit. - // YYY Currency from same source or loops not allowed. - // YYY Limit paths length and count. - if (!asDst) - { - Log(lsINFO) << "naDstAccountID: " << naDstAccountID.humanAccountID(); - - return RPCError(rpcDST_ACT_MISSING); - } - - Transaction::pointer trans = Transaction::sharedPayment( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount, - saSrcAmountMax, - spsPaths, - bPartial, - bLimit); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["fee"] = saFee.getText(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - obj["srcAmountMax"] = saSrcAmountMax.getText(); - obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); - obj["dstAmount"] = saDstAmount.getText(); - obj["dstISO"] = saDstAmount.getHumanCurrency(); - obj["paths"] = spsPaths.getText(); - - return obj; -} - -// ripple_line_set [] [] [] -Json::Value RPCServer::doRippleLineSet(const Json::Value& params) -{ - NewcoinAddress naSeed; - NewcoinAddress naSrcAccountID; - NewcoinAddress naDstAccountID; - STAmount saLimitAmount; - bool bQualityIn = params.size() >= 6; - bool bQualityOut = params.size() >= 7; - uint32 uQualityIn = 0; - uint32 uQualityOut = 0; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return RPCError(rpcDST_ACT_MALFORMED); - } - else if (!saLimitAmount.setFullValue(params[3u].asString(), params.size() >= 5 ? params[4u].asString() : "", params[2u].asString())) - { - return RPCError(rpcSRC_AMT_MALFORMED); - } - else if (bQualityIn && !parseQuality(params[5u].asString(), uQualityIn)) - { - return RPCError(rpcQUALITY_MALFORMED); - } - else if (bQualityOut && !parseQuality(params[6u].asString(), uQualityOut)) - { - return RPCError(rpcQUALITY_MALFORMED); - } - else - { - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_DEFAULT, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - Transaction::pointer trans = Transaction::sharedCreditSet( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_DEFAULT, - 0, // YYY No source tag - saLimitAmount, - bQualityIn, uQualityIn, - bQualityOut, uQualityOut); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - - return obj; - } -} - -// ripple_lines_get || [] -Json::Value RPCServer::doRippleLinesGet(const Json::Value ¶ms) -{ -// uint256 uAccepted = mNetOps->getClosedLedger(); - - std::string strIdent = params[0u].asString(); - bool bIndex; - int iIndex = 2 == params.size() ? lexical_cast_s(params[1u].asString()) : 0; - - NewcoinAddress naAccount; - - Json::Value ret; - - ret = accountFromString(uint256(0), naAccount, bIndex, strIdent, iIndex); - - if (!ret.empty()) - return ret; - - // Get info on account. - ret = Json::Value(Json::objectValue); - - ret["account"] = naAccount.humanAccountID(); - if (bIndex) - ret["index"] = iIndex; - - AccountState::pointer as = mNetOps->getAccountState(uint256(0), naAccount); - if (as) - { - Json::Value jsonLines(Json::arrayValue); - - ret["account"] = naAccount.humanAccountID(); - - // XXX This is wrong, we do access the current ledger and do need to worry about changes. - // We access a committed ledger and need not worry about changes. - - RippleLines rippleLines(naAccount.getAccountID()); - BOOST_FOREACH(RippleState::pointer line, rippleLines.getLines()) - { - STAmount saBalance = line->getBalance(); - STAmount saLimit = line->getLimit(); - STAmount saLimitPeer = line->getLimitPeer(); - - Json::Value jPeer = Json::Value(Json::objectValue); - - //jPeer["node"] = uNode.ToString(); - - jPeer["account"] = line->getAccountIDPeer().humanAccountID(); - // Amount reported is positive if current account holds other account's IOUs. - // Amount reported is negative if other account holds current account's IOUs. - jPeer["balance"] = saBalance.getText(); - jPeer["currency"] = saBalance.getHumanCurrency(); - jPeer["limit"] = saLimit.getText(); - jPeer["limit_peer"] = saLimitPeer.getText(); - jPeer["quality_in"] = static_cast(line->getQualityIn()); - jPeer["quality_out"] = static_cast(line->getQualityOut()); - - jsonLines.append(jPeer); - } - ret["lines"] = jsonLines; - } - else - { - ret = RPCError(rpcACT_NOT_FOUND); - } - - return ret; -} - -// send regular_seed paying_account account_id amount [currency] [issuer] [send_max] [send_currency] [send_issuer] -Json::Value RPCServer::doSend(const Json::Value& params) -{ - NewcoinAddress naSeed; - NewcoinAddress naSrcAccountID; - NewcoinAddress naDstAccountID; - STAmount saSrcAmountMax; - STAmount saDstAmount; - std::string sSrcCurrency; - std::string sDstCurrency; - std::string sSrcIssuer; - std::string sDstIssuer; - - if (params.size() >= 5) - sDstCurrency = params[4u].asString(); - - if (params.size() >= 6) - sDstIssuer = params[5u].asString(); - - if (params.size() >= 8) - sSrcCurrency = params[7u].asString(); - - if (params.size() >= 9) - sSrcIssuer = params[8u].asString(); - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return RPCError(rpcDST_ACT_MALFORMED); - } - else if (!saDstAmount.setFullValue(params[3u].asString(), sDstCurrency, sDstIssuer)) - { - return RPCError(rpcDST_AMT_MALFORMED); - } - else if (params.size() >= 7 && !saSrcAmountMax.setFullValue(params[6u].asString(), sSrcCurrency, sSrcIssuer)) - { - return RPCError(rpcSRC_AMT_MALFORMED); - } - else - { - AccountState::pointer asDst = mNetOps->getAccountState(uint256(0), naDstAccountID); - bool bCreate = !asDst; - STAmount saFee = bCreate ? theConfig.FEE_ACCOUNT_CREATE : theConfig.FEE_DEFAULT; - - NewcoinAddress naVerifyGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, saFee, asSrc, naVerifyGenerator); - - // Log(lsINFO) << boost::str(boost::format("doSend: sSrcIssuer=%s sDstIssuer=%s saSrcAmountMax=%s saDstAmount=%s") - // % sSrcIssuer - // % sDstIssuer - // % saSrcAmountMax.getFullText() - // % saDstAmount.getFullText()); - - if (!obj.empty()) - return obj; - - if (params.size() < 7) - saSrcAmountMax = saDstAmount; - - // Do a few simple checks. - if (!saSrcAmountMax.isNative()) - { - Log(lsINFO) << "doSend: Ripple"; - - nothing(); - } - else if (!saSrcBalance.isPositive()) - { - // No native currency to send. - Log(lsINFO) << "doSend: No native currency to send: " << saSrcBalance.getText(); - - return RPCError(rpcINSUF_FUNDS); - } - else if (saDstAmount.isNative() && saSrcAmountMax < saDstAmount) - { - // Not enough native currency. - - Log(lsINFO) << "doSend: Insufficient funds: src=" << saSrcAmountMax.getText() << " dst=" << saDstAmount.getText(); - - return RPCError(rpcINSUF_FUNDS); - } - // XXX Don't allow send to self of same currency. - - Transaction::pointer trans; - if (asDst) { - // Destination exists, ordinary send. - - STPathSet spsPaths; - uint160 srcCurrencyID; - - if (!saSrcAmountMax.isNative() || !saDstAmount.isNative()) - { - STAmount::currencyFromString(srcCurrencyID, sSrcCurrency); - Pathfinder pf(naSrcAccountID, naDstAccountID, srcCurrencyID, saDstAmount); - pf.findPaths(5, 1, spsPaths); - } - - trans = Transaction::sharedPayment( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount, - saSrcAmountMax, - spsPaths); - } - else - { - // Create destination and send. - - trans = Transaction::sharedCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - saFee, - 0, // YYY No source tag - naDstAccountID, - saDstAmount); // Initial funds in XNS. - } - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["seed"] = naSeed.humanSeed(); - obj["fee"] = saFee.getText(); - obj["create"] = bCreate; - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["dstAccountID"] = naDstAccountID.humanAccountID(); - obj["srcAmountMax"] = saSrcAmountMax.getText(); - obj["srcISO"] = saSrcAmountMax.getHumanCurrency(); - obj["dstAmount"] = saDstAmount.getText(); - obj["dstISO"] = saDstAmount.getHumanCurrency(); - - return obj; - } -} - -Json::Value RPCServer::doServerInfo(const Json::Value& params) -{ - Json::Value ret(Json::objectValue); - - ret["info"] = theApp->getOPs().getServerInfo(); - - return ret; -} - -Json::Value RPCServer::doTx(const Json::Value& params) -{ - // tx - // tx - - std::string param1, param2; - if (!extractString(param1, params, 0)) - { - return RPCError(rpcINVALID_PARAMS); - } - - if (Transaction::isHexTxID(param1)) - { // transaction by ID - Json::Value ret; - uint256 txid(param1); - - Transaction::pointer txn = theApp->getMasterTransaction().fetch(txid, true); - - if (!txn) return RPCError(rpcTXN_NOT_FOUND); - - return txn->getJson(0); - } - - return RPCError(rpcNOT_IMPL); -} - -// ledger [id|current|lastclosed] [full] -Json::Value RPCServer::doLedger(const Json::Value& params) -{ - if (getParamCount(params) == 0) - { - Json::Value ret(Json::objectValue), current(Json::objectValue), closed(Json::objectValue); - theApp->getMasterLedger().getCurrentLedger()->addJson(current, 0); - theApp->getMasterLedger().getClosedLedger()->addJson(closed, 0); - ret["open"] = current; - ret["closed"] = closed; - return ret; - } - - std::string param; - if (!extractString(param, params, 0)) - { - return "bad params"; - } - - Ledger::pointer ledger; - if (param == "current") - ledger = theApp->getMasterLedger().getCurrentLedger(); - else if ((param == "lastclosed") || (param == "lastaccepted")) - ledger = theApp->getMasterLedger().getClosedLedger(); - else if (param.size() > 12) - ledger = theApp->getMasterLedger().getLedgerByHash(uint256(param)); - else - ledger = theApp->getMasterLedger().getLedgerBySeq(lexical_cast_s(param)); - - if (!ledger) - return RPCError(rpcLGR_NOT_FOUND); - - bool full = extractString(param, params, 1) && (param == "full"); - Json::Value ret(Json::objectValue); - ledger->addJson(ret, full ? LEDGER_JSON_FULL : 0); - return ret; -} - -// account_tx -// account_tx -Json::Value RPCServer::doAccountTransactions(const Json::Value& params) -{ - std::string param; - uint32 minLedger, maxLedger; - - if (!extractString(param, params, 0)) - return RPCError(rpcINVALID_PARAMS); - - NewcoinAddress account; - if (!account.setAccountID(param)) - return RPCError(rpcACT_MALFORMED); - - if (!extractString(param, params, 1)) - return RPCError(rpcLGR_IDX_MALFORMED); - - minLedger = lexical_cast_s(param); - - if ((params.size() == 3) && extractString(param, params, 2)) - maxLedger = lexical_cast_s(param); - else - maxLedger = minLedger; - - if ((maxLedger < minLedger) || (maxLedger == 0)) - { - std::cerr << "minL=" << minLedger << ", maxL=" << maxLedger << std::endl; - - return RPCError(rpcLGR_IDXS_INVALID); - } - -#ifndef DEBUG - try - { -#endif - std::vector< std::pair > txns = mNetOps->getAffectedAccounts(account, minLedger, maxLedger); - Json::Value ret(Json::objectValue); - ret["account"] = account.humanAccountID(); - Json::Value ledgers(Json::arrayValue); - -// uint32 currentLedger = 0; - for (std::vector< std::pair >::iterator it = txns.begin(), end = txns.end(); it != end; ++it) - { - Transaction::pointer txn = theApp->getMasterTransaction().fetch(it->second, true); - if (!txn) - { - ret["transactions"].append(it->second.GetHex()); - } - else - { - txn->setLedger(it->first); - ret["transactions"].append(txn->getJson(0)); - } - - } - return ret; -#ifndef DEBUG - } - catch (...) - { - return RPCError(rpcINTERNAL); - } -#endif -} - -// unl_add | [] -Json::Value RPCServer::doUnlAdd(const Json::Value& params) -{ - std::string strNode = params[0u].asString(); - std::string strComment = (params.size() == 2) ? params[1u].asString() : ""; - - NewcoinAddress naNodePublic; - - if (naNodePublic.setNodePublic(strNode)) - { - theApp->getUNL().nodeAddPublic(naNodePublic, UniqueNodeList::vsManual, strComment); - - return "adding node by public key"; - } - else - { - theApp->getUNL().nodeAddDomain(strNode, UniqueNodeList::vsManual, strComment); - - return "adding node by domain"; - } -} - -// validation_create [||] -// -// NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command -// shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCServer::doValidationCreate(const Json::Value& params) { - NewcoinAddress naSeed; - Json::Value obj(Json::objectValue); - - if (params.empty()) - { - std::cerr << "Creating random validation seed." << std::endl; - - naSeed.setSeedRandom(); // Get a random seed. - } - else if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - - obj["validation_public_key"] = NewcoinAddress::createNodePublic(naSeed).humanNodePublic(); - obj["validation_seed"] = naSeed.humanSeed(); - obj["validation_key"] = naSeed.humanSeed1751(); - - return obj; -} - -// validation_seed [||] -// -// NOTE: It is poor security to specify secret information on the command line. This information might be saved in the command -// shell history file (e.g. .bash_history) and it may be leaked via the process status command (i.e. ps). -Json::Value RPCServer::doValidationSeed(const Json::Value& params) { - Json::Value obj(Json::objectValue); - - if (params.empty()) - { - std::cerr << "Unset validation seed." << std::endl; - - theConfig.VALIDATION_SEED.clear(); - } - else if (!theConfig.VALIDATION_SEED.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else - { - obj["validation_public_key"] = NewcoinAddress::createNodePublic(theConfig.VALIDATION_SEED).humanNodePublic(); - obj["validation_seed"] = theConfig.VALIDATION_SEED.humanSeed(); - obj["validation_key"] = theConfig.VALIDATION_SEED.humanSeed1751(); - } - - return obj; -} - -Json::Value RPCServer::accounts(const uint256& uLedger, const NewcoinAddress& naMasterGenerator) -{ - Json::Value jsonAccounts(Json::arrayValue); - - // YYY Don't want to leak to thin server that these accounts are related. - // YYY Would be best to alternate requests to servers and to cache results. - unsigned int uIndex = 0; - - do { - NewcoinAddress naAccount; - - naAccount.setAccountPublic(naMasterGenerator, uIndex++); - - AccountState::pointer as = mNetOps->getAccountState(uLedger, naAccount); - if (as) - { - Json::Value jsonAccount(Json::objectValue); - - as->addJson(jsonAccount); - - jsonAccounts.append(jsonAccount); - } - else - { - uIndex = 0; - } - } while (uIndex); - - return jsonAccounts; -} - -// wallet_accounts -Json::Value RPCServer::doWalletAccounts(const Json::Value& params) -{ - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - - // Try the seed as a master seed. - NewcoinAddress naMasterGenerator = NewcoinAddress::createGeneratorPublic(naSeed); - - Json::Value jsonAccounts = accounts(uint256(0), naMasterGenerator); - - if (jsonAccounts.empty()) - { - // No account via seed as master, try seed a regular. - Json::Value ret = getMasterGenerator(uint256(0), naSeed, naMasterGenerator); - - if (!ret.empty()) - return ret; - - ret["accounts"] = accounts(uint256(0), naMasterGenerator); - - return ret; - } - else - { - // Had accounts via seed as master, return them. - Json::Value ret(Json::objectValue); - - ret["accounts"] = jsonAccounts; - - return ret; - } -} - -// wallet_add [] [] -Json::Value RPCServer::doWalletAdd(const Json::Value& params) -{ - NewcoinAddress naMasterSeed; - NewcoinAddress naRegularSeed; - NewcoinAddress naSrcAccountID; - STAmount saAmount; - std::string sDstCurrency; - - if (!naRegularSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naMasterSeed.setSeedGeneric(params[2u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (params.size() >= 4 && !saAmount.setFullValue(params[3u].asString(), sDstCurrency)) - { - return RPCError(rpcDST_AMT_MALFORMED); - } - else - { - NewcoinAddress naMasterGenerator = NewcoinAddress::createGeneratorPublic(naMasterSeed); - NewcoinAddress naRegularGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naRegularSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - if (saSrcBalance < saAmount) - { - return RPCError(rpcINSUF_FUNDS); - } - else - { - NewcoinAddress naNewAccountPublic; - NewcoinAddress naNewAccountPrivate; - NewcoinAddress naAuthKeyID; - uint160 uAuthKeyID; - AccountState::pointer asNew; - std::vector vucSignature; - bool bAgain = true; - int iIndex = -1; - - // Find an unmade account. - do { - ++iIndex; - naNewAccountPublic.setAccountPublic(naMasterGenerator, iIndex); - - asNew = mNetOps->getAccountState(uint256(0), naNewAccountPublic); - if (!asNew) - bAgain = false; - } while (bAgain); - - // XXX Have a maximum number of accounts per wallet? - - // Determine corrisponding master private key. - naNewAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, iIndex); - - // Determine new accounts authorized regular key. - naAuthKeyID.setAccountPublic(naRegularGenerator, iIndex); - - uAuthKeyID = naAuthKeyID.getAccountID(); - - // Sign anything (naAuthKeyID) to prove we know new master private key. - naNewAccountPrivate.accountPrivateSign(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature); - - Transaction::pointer trans = Transaction::sharedWalletAdd( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_ACCOUNT_CREATE, - 0, // YYY No source tag - saAmount, - naAuthKeyID, - naNewAccountPublic, - vucSignature); - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - obj["srcAccountID"] = naSrcAccountID.humanAccountID(); - obj["newAccountID"] = naNewAccountPublic.humanAccountID(); - obj["amount"] = saAmount.getText(); - - return obj; - } - } -} - -// wallet_claim [] [] -// -// To provide an example to client writers, we do everything we expect a client to do here. -Json::Value RPCServer::doWalletClaim(const Json::Value& params) -{ - NewcoinAddress naMasterSeed; - NewcoinAddress naRegularSeed; - - if (!naMasterSeed.setSeedGeneric(params[0u].asString())) - { - // Should also not allow account id's as seeds. - return RPCError(rpcBAD_SEED); - } - else if (!naRegularSeed.setSeedGeneric(params[1u].asString())) - { - // Should also not allow account id's as seeds. - return RPCError(rpcBAD_SEED); - } - else - { - // Building: - // peer_wallet_claim - // [] - // - // - // Which has no confidential information. - - // XXX Need better parsing. - uint32 uSourceTag = (params.size() == 2) ? 0 : lexical_cast_s(params[2u].asString()); - // XXX Annotation is ignored. - std::string strAnnotation = (params.size() == 3) ? "" : params[3u].asString(); - - NewcoinAddress naMasterGenerator = NewcoinAddress::createGeneratorPublic(naMasterSeed); - NewcoinAddress naRegularGenerator = NewcoinAddress::createGeneratorPublic(naRegularSeed); - NewcoinAddress naRegular0Public; - NewcoinAddress naRegular0Private; - - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - - naAccountPublic.setAccountPublic(naMasterGenerator, 0); - naAccountPrivate.setAccountPrivate(naMasterGenerator, naMasterSeed, 0); - - naRegular0Public.setAccountPublic(naRegularGenerator, 0); - naRegular0Private.setAccountPrivate(naRegularGenerator, naRegularSeed, 0); - - // Hash of regular account #0 public key. - uint160 uGeneratorID = naRegular0Public.getAccountID(); - std::vector vucGeneratorCipher = naRegular0Private.accountPrivateEncrypt(naRegular0Public, naMasterGenerator.getGenerator()); - std::vector vucGeneratorSig; - - // Prove that we have the corresponding private key to the generator id. So, we can get the generator id. - // XXX Check result. - naRegular0Private.accountPrivateSign(Serializer::getSHA512Half(vucGeneratorCipher), vucGeneratorSig); - - Transaction::pointer trans = Transaction::sharedClaim( - naAccountPublic, naAccountPrivate, - uSourceTag, - vucGeneratorCipher, - naRegular0Public.getAccountPublic(), - vucGeneratorSig); - - trans = mNetOps->submitTransaction(trans); - - Json::Value obj(Json::objectValue); - - // We "echo" the seeds so they can be checked. - obj["master_seed"] = naMasterSeed.humanSeed(); - obj["master_key"] = naMasterSeed.humanSeed1751(); - obj["regular_seed"] = naRegularSeed.humanSeed(); - obj["regular_key"] = naRegularSeed.humanSeed1751(); - - obj["account_id"] = naAccountPublic.humanAccountID(); - obj["generator_id"] = strHex(uGeneratorID); - obj["generator"] = strHex(vucGeneratorCipher); - obj["annotation"] = strAnnotation; - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; - } -} - -// wallet_create regular_seed paying_account account_id [initial_funds] -// We don't allow creating an account_id by default here because we want to make sure the person has a chance to write down the -// master seed of the account to be created. -// YYY Need annotation and source tag -Json::Value RPCServer::doWalletCreate(const Json::Value& params) -{ - NewcoinAddress naSrcAccountID; - NewcoinAddress naDstAccountID; - NewcoinAddress naSeed; - - if (!naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else if (!naSrcAccountID.setAccountID(params[1u].asString())) - { - return RPCError(rpcSRC_ACT_MALFORMED); - } - else if (!naDstAccountID.setAccountID(params[2u].asString())) - { - return RPCError(rpcDST_ACT_MALFORMED); - } - else if (mNetOps->getAccountState(uint256(0), naDstAccountID)) - { - return RPCError(rpcACT_EXISTS); - } - - // Trying to build: - // peer_wallet_create [] [] - - NewcoinAddress naMasterGenerator; - NewcoinAddress naAccountPublic; - NewcoinAddress naAccountPrivate; - AccountState::pointer asSrc; - STAmount saSrcBalance; - Json::Value obj = authorize(uint256(0), naSeed, naSrcAccountID, naAccountPublic, naAccountPrivate, - saSrcBalance, theConfig.FEE_ACCOUNT_CREATE, asSrc, naMasterGenerator); - - if (!obj.empty()) - return obj; - - STAmount saInitialFunds = (params.size() < 4) ? 0 : lexical_cast_s(params[3u].asString()); - - if (saSrcBalance < saInitialFunds) - return RPCError(rpcINSUF_FUNDS); - - Transaction::pointer trans = Transaction::sharedCreate( - naAccountPublic, naAccountPrivate, - naSrcAccountID, - asSrc->getSeq(), - theConfig.FEE_ACCOUNT_CREATE, - 0, // YYY No source tag - naDstAccountID, - saInitialFunds); // Initial funds in XNC. - - trans = mNetOps->submitTransaction(trans); - - obj["transaction"] = trans->getSTransaction()->getJson(0); - obj["status"] = trans->getStatus(); - - return obj; -} - -// wallet_propose [] -// is only for testing. Master seeds should only be generated randomly. -Json::Value RPCServer::doWalletPropose(const Json::Value& params) -{ - NewcoinAddress naSeed; - NewcoinAddress naAccount; - - if (params.empty()) - { - naSeed.setSeedRandom(); - } - else - { - naSeed = NewcoinAddress::createSeedGeneric(params[0u].asString()); - } - - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSeed); - naAccount.setAccountPublic(naGenerator, 0); - - Json::Value obj(Json::objectValue); - - obj["master_seed"] = naSeed.humanSeed(); - obj["master_key"] = naSeed.humanSeed1751(); - obj["account_id"] = naAccount.humanAccountID(); - - return obj; -} - -// wallet_seed [||] -Json::Value RPCServer::doWalletSeed(const Json::Value& params) -{ - NewcoinAddress naSeed; - - if (params.size() - && !naSeed.setSeedGeneric(params[0u].asString())) - { - return RPCError(rpcBAD_SEED); - } - else - { - NewcoinAddress naAccount; - - if (!params.size()) - { - naSeed.setSeedRandom(); - } - - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSeed); - - naAccount.setAccountPublic(naGenerator, 0); - - Json::Value obj(Json::objectValue); - - obj["seed"] = naSeed.humanSeed(); - obj["key"] = naSeed.humanSeed1751(); - - return obj; - } -} - -// unl_delete | -Json::Value RPCServer::doUnlDelete(const Json::Value& params) -{ - std::string strNode = params[0u].asString(); - - NewcoinAddress naNodePublic; - - if (naNodePublic.setNodePublic(strNode)) - { - theApp->getUNL().nodeRemovePublic(naNodePublic); - - return "removing node by public key"; - } - else - { - theApp->getUNL().nodeRemoveDomain(strNode); - - return "removing node by domain"; - } -} - -Json::Value RPCServer::doUnlList(const Json::Value& params) -{ - Json::Value obj(Json::objectValue); - - obj["unl"]=theApp->getUNL().getUnlJson(); - - return obj; -} - -// Populate the UNL from a local validators.txt file. -Json::Value RPCServer::doUnlLoad(const Json::Value& params) -{ - if (theConfig.UNL_DEFAULT.empty() || !theApp->getUNL().nodeLoad(theConfig.UNL_DEFAULT)) - { - return RPCError(rpcLOAD_FAILED); - } - - return "loading"; -} - -// Populate the UNL from ripple.com's validators.txt file. -Json::Value RPCServer::doUnlNetwork(const Json::Value& params) -{ - theApp->getUNL().nodeNetwork(); - - return "fetching"; -} - -// unl_reset -Json::Value RPCServer::doUnlReset(const Json::Value& params) -{ - theApp->getUNL().nodeReset(); - - return "removing nodes"; -} - -// unl_score -Json::Value RPCServer::doUnlScore(const Json::Value& params) -{ - theApp->getUNL().nodeScore(); - - return "scoring requested"; -} - -Json::Value RPCServer::doStop(const Json::Value& params) -{ - theApp->stop(); - - return SYSTEM_NAME " server stopping"; -} - -// TODO: for now this simply checks if this is the admin account -// TODO: need to prevent them hammering this over and over -// TODO: maybe a better way is only allow admin from local host -Json::Value RPCServer::doLogin(const Json::Value& params) -{ - std::string username = params[0u].asString(); - std::string password = params[1u].asString(); - - if (username == theConfig.RPC_USER && password == theConfig.RPC_PASSWORD) - { - //mRole=ADMIN; - return "logged in"; - } - else - { - return "nope"; - } -} - -Json::Value RPCServer::doLogRotate(const Json::Value& params) -{ - return Log::rotateLog(); -} - -Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params) -{ - Log(lsTRACE) << "RPC:" << command; - - static struct { - const char* pCommand; - doFuncPtr dfpFunc; - int iMinParams; - int iMaxParams; - bool mAdminRequired; - unsigned int iOptions; - } commandsA[] = { - { "accept_ledger", &RPCServer::doAcceptLedger, 0, 0, true }, - { "account_domain_set", &RPCServer::doAccountDomainSet, 2, 3, false, optCurrent }, - { "account_email_set", &RPCServer::doAccountEmailSet, 2, 3, false, optCurrent }, - { "account_info", &RPCServer::doAccountInfo, 1, 2, false, optCurrent }, - { "account_message_set", &RPCServer::doAccountMessageSet, 3, 3, false, optCurrent }, - { "account_publish_set", &RPCServer::doAccountPublishSet, 4, 4, false, optCurrent }, - { "account_rate_set", &RPCServer::doAccountRateSet, 3, 3, false, optCurrent }, - { "account_tx", &RPCServer::doAccountTransactions, 2, 3, false, optNetwork }, - { "account_wallet_set", &RPCServer::doAccountWalletSet, 2, 3, false, optCurrent }, - { "connect", &RPCServer::doConnect, 1, 2, true }, - { "data_delete", &RPCServer::doDataDelete, 1, 1, true }, - { "data_fetch", &RPCServer::doDataFetch, 1, 1, true }, - { "data_store", &RPCServer::doDataStore, 2, 2, true }, - { "ledger", &RPCServer::doLedger, 0, 2, false, optNetwork }, - { "logrotate", &RPCServer::doLogRotate, 0, 0, true }, - { "nickname_info", &RPCServer::doNicknameInfo, 1, 1, false, optCurrent }, - { "nickname_set", &RPCServer::doNicknameSet, 2, 3, false, optCurrent }, - { "offer_create", &RPCServer::doOfferCreate, 9, 10, false, optCurrent }, - { "offer_cancel", &RPCServer::doOfferCancel, 3, 3, false, optCurrent }, - { "owner_info", &RPCServer::doOwnerInfo, 1, 2, false, optCurrent }, - { "password_fund", &RPCServer::doPasswordFund, 2, 3, false, optCurrent }, - { "password_set", &RPCServer::doPasswordSet, 2, 3, false, optNetwork }, - { "peers", &RPCServer::doPeers, 0, 0, true }, - { "profile", &RPCServer::doProfile, 1, 9, false, optCurrent }, - { "ripple", &RPCServer::doRipple, 9, -1, false, optCurrent|optClosed }, - { "ripple_lines_get", &RPCServer::doRippleLinesGet, 1, 2, false, optCurrent }, - { "ripple_line_set", &RPCServer::doRippleLineSet, 4, 7, false, optCurrent }, - { "send", &RPCServer::doSend, 3, 9, false, optCurrent }, - { "server_info", &RPCServer::doServerInfo, 0, 0, true }, - { "stop", &RPCServer::doStop, 0, 0, true }, - { "tx", &RPCServer::doTx, 1, 1, true }, - - { "unl_add", &RPCServer::doUnlAdd, 1, 2, true }, - { "unl_delete", &RPCServer::doUnlDelete, 1, 1, true }, - { "unl_list", &RPCServer::doUnlList, 0, 0, true }, - { "unl_load", &RPCServer::doUnlLoad, 0, 0, true }, - { "unl_network", &RPCServer::doUnlNetwork, 0, 0, true }, - { "unl_reset", &RPCServer::doUnlReset, 0, 0, true }, - { "unl_score", &RPCServer::doUnlScore, 0, 0, true }, - - { "validation_create", &RPCServer::doValidationCreate, 0, 1, false }, - { "validation_seed", &RPCServer::doValidationSeed, 0, 1, false }, - - { "wallet_accounts", &RPCServer::doWalletAccounts, 1, 1, false, optCurrent }, - { "wallet_add", &RPCServer::doWalletAdd, 3, 5, false, optCurrent }, - { "wallet_claim", &RPCServer::doWalletClaim, 2, 4, false, optNetwork }, - { "wallet_create", &RPCServer::doWalletCreate, 3, 4, false, optCurrent }, - { "wallet_propose", &RPCServer::doWalletPropose, 0, 1, false, }, - { "wallet_seed", &RPCServer::doWalletSeed, 0, 1, false, }, - - { "login", &RPCServer::doLogin, 2, 2, true }, - }; - - int i = NUMBER(commandsA); - - while (i-- && command != commandsA[i].pCommand) - ; - - if (i < 0) - { - return RPCError(rpcUNKNOWN_COMMAND); - } - else if (commandsA[i].mAdminRequired && mRole != ADMIN) - { - return RPCError(rpcNO_PERMISSION); - } - else if (params.size() < commandsA[i].iMinParams - || (commandsA[i].iMaxParams >= 0 && params.size() > commandsA[i].iMaxParams)) - { - return RPCError(rpcINVALID_PARAMS); - } - else if ((commandsA[i].iOptions & optNetwork) && !mNetOps->available()) - { - return RPCError(rpcNO_NETWORK); - } - // XXX Should verify we have a current ledger. - else if ((commandsA[i].iOptions & optCurrent) && false) - { - return RPCError(rpcNO_CURRENT); - } - else if ((commandsA[i].iOptions & optClosed) && mNetOps->getClosedLedger().isZero()) - { - return RPCError(rpcNO_CLOSED); - } - else - { - try { - return (this->*(commandsA[i].dfpFunc))(params); - } - catch (std::exception& e) - { - cLog(lsINFO) << "Caught throw: " << e.what(); - - return RPCError(rpcINTERNAL); - } - } -} - -void RPCServer::sendReply() -{ - //std::cout << "RPC reply: " << mReplyStr << std::endl; - boost::asio::async_write(mSocket, boost::asio::buffer(mReplyStr), - boost::bind(&RPCServer::Shandle_write, shared_from_this(), - boost::asio::placeholders::error)); -} void RPCServer::handle_write(const boost::system::error_code& e) { @@ -2739,34 +169,17 @@ void RPCServer::handle_write(const boost::system::error_code& e) if (!e) { - bool keep_alive = (mIncomingRequest.http_version_major == 1) && (mIncomingRequest.http_version_minor >= 1); - BOOST_FOREACH(HttpHeader& h, mIncomingRequest.headers) - { - if (boost::iequals(h.name, "connection")) - { - if (boost::iequals(h.value, "keep-alive")) - keep_alive = true; - if (boost::iequals(h.value, "close")) - keep_alive = false; - } - } - if (keep_alive) - { - mIncomingRequest.method.clear(); - mIncomingRequest.uri.clear(); - mIncomingRequest.mBody.clear(); - mIncomingRequest.headers.clear(); - mRequestParser.reset(); - mSocket.async_read_some(boost::asio::buffer(mReadBuffer), - boost::bind(&RPCServer::Shandle_read, shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - else + HTTPRequestAction action = mHTTPRequest.requestDone(false); + if (action == haCLOSE_CONN) { boost::system::error_code ignored_ec; mSocket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec); } + else + { + boost::asio::async_read_until(mSocket, mLineBuffer, "\r\n", + boost::bind(&RPCServer::handle_read_line, shared_from_this(), boost::asio::placeholders::error)); + } } if (e != boost::asio::error::operation_aborted) diff --git a/src/RPCServer.h b/src/RPCServer.h index ce859c843..a2b51caab 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -8,96 +8,33 @@ #include "../json/value.h" -#include "HttpRequest.h" -#include "RequestParser.h" -#include "NewcoinAddress.h" +#include "HTTPRequest.h" +#include "RippleAddress.h" #include "NetworkOPs.h" #include "SerializedLedger.h" +#include "RPCHandler.h" class RPCServer : public boost::enable_shared_from_this { public: - enum { - rpcSUCCESS, - - // Misc failure - rpcLOAD_FAILED, - rpcNO_PERMISSION, - rpcNOT_STANDALONE, - - // Networking - rpcNO_CLOSED, - rpcNO_CURRENT, - rpcNO_NETWORK, - - // Ledger state - rpcACT_EXISTS, - rpcACT_NOT_FOUND, - rpcINSUF_FUNDS, - rpcLGR_NOT_FOUND, - rpcNICKNAME_MISSING, - rpcNO_ACCOUNT, - rpcPASSWD_CHANGED, - rpcSRC_MISSING, - rpcSRC_UNCLAIMED, - rpcTXN_NOT_FOUND, - rpcWRONG_SEED, - - // Malformed command - rpcINVALID_PARAMS, - rpcUNKNOWN_COMMAND, - - // Bad parameter - rpcACT_MALFORMED, - rpcQUALITY_MALFORMED, - rpcBAD_SEED, - rpcDST_ACT_MALFORMED, - rpcDST_ACT_MISSING, - rpcDST_AMT_MALFORMED, - rpcGETS_ACT_MALFORMED, - rpcGETS_AMT_MALFORMED, - rpcHOST_IP_MALFORMED, - rpcLGR_IDXS_INVALID, - rpcLGR_IDX_MALFORMED, - rpcNICKNAME_MALFORMED, - rpcNICKNAME_PERM, - rpcPAYS_ACT_MALFORMED, - rpcPAYS_AMT_MALFORMED, - rpcPORT_MALFORMED, - rpcPUBLIC_MALFORMED, - rpcSRC_ACT_MALFORMED, - rpcSRC_ACT_MISSING, - rpcSRC_AMT_MALFORMED, - - // Internal error (should never happen) - rpcINTERNAL, // Generic internal error. - rpcFAIL_GEN_DECRPYT, - rpcNOT_IMPL, - rpcNO_GEN_DECRPYT, - }; - - Json::Value RPCError(int iError); - + typedef boost::shared_ptr pointer; private: - typedef Json::Value (RPCServer::*doFuncPtr)(const Json::Value ¶ms); - enum { - optNetwork = 1, // Need network - optCurrent = 2+optNetwork, // Need current ledger - optClosed = 4+optNetwork, // Need closed ledger - }; + NetworkOPs* mNetOps; + RPCHandler mRPCHandler; boost::asio::ip::tcp::socket mSocket; - boost::array mReadBuffer; + + boost::asio::streambuf mLineBuffer; + std::vector mQueryVec; std::string mReplyStr; - HttpRequest mIncomingRequest; - HttpRequestParser mRequestParser; + HTTPRequest mHTTPRequest; - enum { GUEST, USER, ADMIN }; + int mRole; RPCServer(boost::asio::io_service& io_service, NetworkOPs* nopNetwork); @@ -106,88 +43,10 @@ private: RPCServer& operator=(const RPCServer&); // no implementation void handle_write(const boost::system::error_code& ec); - static void Shandle_write(pointer This, const boost::system::error_code& ec) - { This->handle_write(ec); } - - void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred); - static void Shandle_read(pointer This, const boost::system::error_code& ec, std::size_t bytes_transferred) - { This->handle_read(ec, bytes_transferred); } + void handle_read_line(const boost::system::error_code& ec); + void handle_read_req(const boost::system::error_code& ec); std::string handleRequest(const std::string& requestStr); - void sendReply(); - - Json::Value doCommand(const std::string& command, Json::Value& params); - int getParamCount(const Json::Value& params); - bool extractString(std::string& param, const Json::Value& params, int index); - - Json::Value getMasterGenerator(const uint256& uLedger, const NewcoinAddress& naRegularSeed, NewcoinAddress& naMasterGenerator); - Json::Value authorize(const uint256& uLedger, const NewcoinAddress& naRegularSeed, const NewcoinAddress& naSrcAccountID, - NewcoinAddress& naAccountPublic, NewcoinAddress& naAccountPrivate, - STAmount& saSrcBalance, const STAmount& saFee, AccountState::pointer& asSrc, - const NewcoinAddress& naVerifyGenerator); - Json::Value accounts(const uint256& uLedger, const NewcoinAddress& naMasterGenerator); - - Json::Value accountFromString(const uint256& uLedger, NewcoinAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex); - - Json::Value doAcceptLedger(const Json::Value ¶ms); - Json::Value doAccountDomainSet(const Json::Value ¶ms); - Json::Value doAccountEmailSet(const Json::Value ¶ms); - Json::Value doAccountInfo(const Json::Value& params); - Json::Value doAccountMessageSet(const Json::Value ¶ms); - Json::Value doAccountPublishSet(const Json::Value ¶ms); - Json::Value doAccountRateSet(const Json::Value ¶ms); - Json::Value doAccountTransactions(const Json::Value& params); - Json::Value doAccountWalletSet(const Json::Value ¶ms); - Json::Value doConnect(const Json::Value& params); - Json::Value doDataDelete(const Json::Value& params); - Json::Value doDataFetch(const Json::Value& params); - Json::Value doDataStore(const Json::Value& params); - Json::Value doLedger(const Json::Value& params); - Json::Value doLogRotate(const Json::Value& params); - Json::Value doNicknameInfo(const Json::Value& params); - Json::Value doNicknameSet(const Json::Value& params); - Json::Value doOfferCreate(const Json::Value& params); - Json::Value doOfferCancel(const Json::Value& params); - Json::Value doOwnerInfo(const Json::Value& params); - Json::Value doPasswordFund(const Json::Value& params); - Json::Value doPasswordSet(const Json::Value& params); - Json::Value doProfile(const Json::Value& params); - Json::Value doPeers(const Json::Value& params); - Json::Value doRipple(const Json::Value ¶ms); - Json::Value doRippleLinesGet(const Json::Value ¶ms); - Json::Value doRippleLineSet(const Json::Value& params); - Json::Value doSend(const Json::Value& params); - Json::Value doServerInfo(const Json::Value& params); - Json::Value doSessionClose(const Json::Value& params); - Json::Value doSessionOpen(const Json::Value& params); - Json::Value doStop(const Json::Value& params); - Json::Value doTransitSet(const Json::Value& params); - Json::Value doTx(const Json::Value& params); - - Json::Value doUnlAdd(const Json::Value& params); - Json::Value doUnlDelete(const Json::Value& params); - Json::Value doUnlFetch(const Json::Value& params); - Json::Value doUnlList(const Json::Value& params); - Json::Value doUnlLoad(const Json::Value& params); - Json::Value doUnlNetwork(const Json::Value& params); - Json::Value doUnlReset(const Json::Value& params); - Json::Value doUnlScore(const Json::Value& params); - - Json::Value doValidationCreate(const Json::Value& params); - Json::Value doValidationSeed(const Json::Value& params); - - Json::Value doWalletAccounts(const Json::Value& params); - Json::Value doWalletAdd(const Json::Value& params); - Json::Value doWalletClaim(const Json::Value& params); - Json::Value doWalletCreate(const Json::Value& params); - Json::Value doWalletLock(const Json::Value& params); - Json::Value doWalletPropose(const Json::Value& params); - Json::Value doWalletSeed(const Json::Value& params); - Json::Value doWalletUnlock(const Json::Value& params); - Json::Value doWalletVerify(const Json::Value& params); - - Json::Value doLogin(const Json::Value& params); - public: static pointer create(boost::asio::io_service& io_service, NetworkOPs* mNetOps) diff --git a/src/RangeSet.cpp b/src/RangeSet.cpp new file mode 100644 index 000000000..e9c4b38f9 --- /dev/null +++ b/src/RangeSet.cpp @@ -0,0 +1,145 @@ + +#include "RangeSet.h" + +#include +#include + +#include "Log.h" + +SETUP_LOG(); + +inline uint32 min(uint32 x, uint32 y) { return (x < y) ? x : y; } +inline uint32 max(uint32 x, uint32 y) { return (x > y) ? x : y; } + +bool RangeSet::hasValue(uint32 v) const +{ + return mRanges.find(v) != mRanges.end(); +} + +uint32 RangeSet::getFirst() const +{ + const_iterator it = begin(); + if (it == end()) + return RangeSetAbsent; + return lower(it); +} + +uint32 RangeSet::getNext(uint32 v) const +{ + for (const_iterator it = begin(); it != end(); ++it) + { + if (upper(it) > v) + return max(v + 1, lower(it)); + } + return RangeSetAbsent; +} + +uint32 RangeSet::getLast() const +{ + const_reverse_iterator it = rbegin(); + if (it == rend()) + return RangeSetAbsent; + return upper(it); +} + +uint32 RangeSet::getPrev(uint32 v) const +{ + for (const_reverse_iterator it = rbegin(); it != rend(); ++it) + { + if (lower(it) < v) + return min(v - 1, upper(it)); + } + return RangeSetAbsent; +} + +uint32 RangeSet::prevMissing(uint32 v) const +{ // largest number not in the set that is less than the given number + cLog(lsTRACE) << "prevMissing(" << v << ") " << toString(); + for (const_reverse_iterator it = rbegin(); it != rend(); ++it) + { + if ((upper(it) + 1) < v) + return upper(it) + 1; + if (lower(it) == 0) + return RangeSetAbsent; + if ((lower(it) - 1) < v) + return lower(it) - 1; + } + if (v > 0) + return v - 1; + return RangeSetAbsent; +} + +void RangeSet::setValue(uint32 v) +{ + setRange(v, v); +} + +void RangeSet::setRange(uint32 minV, uint32 maxV) +{ + mRanges.add(boost::icl::discrete_interval(minV, maxV + 1)); +} + +void RangeSet::clearValue(uint32 v) +{ + clearRange(v, v); +} + +void RangeSet::clearRange(uint32 minV, uint32 maxV) +{ + mRanges.erase(boost::icl::discrete_interval(minV, maxV + 1)); +} + +std::string RangeSet::toString() const +{ + std::string ret; + for (const_iterator it = begin(); it != end(); ++it) + { + if (!ret.empty()) + ret += ","; + if (lower(it) == upper(it)) + ret += boost::lexical_cast(lower(it)); + else + ret += boost::lexical_cast(lower(it)) + "-" + + boost::lexical_cast(upper(it)); + } + if (ret.empty()) + return "empty"; + return ret; +} + +BOOST_AUTO_TEST_SUITE(RangeSet_suite) + +BOOST_AUTO_TEST_CASE(RangeSet_test) +{ + cLog(lsTRACE) << "RangeSet test begins"; + + RangeSet r1, r2; + + if (r1 != r2) BOOST_FAIL("RangeSet fail"); + + r1.setValue(1); + if (r1 == r2) BOOST_FAIL("RangeSet fail"); + r2.setRange(1, 1); + if (r1 != r2) BOOST_FAIL("RangeSet fail"); + + r1.clear(); + r1.setRange(1,10); + r1.clearValue(5); + r1.setRange(11, 20); + + r2.clear(); + r2.setRange(1, 4); + r2.setRange(6, 10); + r2.setRange(10, 20); + if (r1 != r2) BOOST_FAIL("RangeSet fail"); + if (r1.hasValue(5)) BOOST_FAIL("RangeSet fail"); + if (!r2.hasValue(9)) BOOST_FAIL("RangeSet fail"); + + // TODO: Traverse functions must be tested + + cLog(lsTRACE) << "RangeSet test complete"; +} + +BOOST_AUTO_TEST_SUITE_END() + +// vim:ts=4 diff --git a/src/RangeSet.h b/src/RangeSet.h new file mode 100644 index 000000000..5ccf89f07 --- /dev/null +++ b/src/RangeSet.h @@ -0,0 +1,89 @@ +#ifndef RANGESET__H +#define RANGESET__H + +#include +#include + +#include +#include + +#include "types.h" + +class RangeSet +{ + +public: + + typedef boost::icl::interval_set iRangeSet; + typedef iRangeSet::iterator iterator; + typedef iRangeSet::const_iterator const_iterator; + typedef iRangeSet::reverse_iterator reverse_iterator; + typedef iRangeSet::const_reverse_iterator const_reverse_iterator; + static const uint32 RangeSetAbsent = static_cast(-1); + +protected: + + iRangeSet mRanges; + +public: + + RangeSet() { ; } + + bool hasValue(uint32) const; + uint32 getFirst() const; + uint32 getNext(uint32) const; + uint32 getLast() const; + uint32 getPrev(uint32) const; + + uint32 prevMissing(uint32) const; // largest number not in the set that is less than the given number + + void setValue(uint32); + void setRange(uint32, uint32); + void clearValue(uint32); + void clearRange(uint32, uint32); + + + void clear() { mRanges.clear(); } + + // iterator stuff + iterator begin() { return mRanges.begin(); } + iterator end() { return mRanges.end(); } + const_iterator begin() const { return mRanges.begin(); } + const_iterator end() const { return mRanges.end(); } + reverse_iterator rbegin() { return mRanges.rbegin(); } + reverse_iterator rend() { return mRanges.rend(); } + const_reverse_iterator rbegin() const { return mRanges.rbegin(); } + const_reverse_iterator rend() const { return mRanges.rend(); } + + static uint32 lower(const_iterator& it) { return it->lower(); } + static uint32 upper(const_iterator& it) { return it->upper() - 1; } + static uint32 lower(const_reverse_iterator& it) { return it->lower(); } + static uint32 upper(const_reverse_iterator& it) { return it->upper() - 1; } + + + bool operator!=(const RangeSet& r) const { return mRanges != r.mRanges; } + bool operator==(const RangeSet& r) const { return mRanges == r.mRanges; } + + std::string toString() const; +}; + +inline RangeSet::const_iterator range_begin(const RangeSet& r) { return r.begin(); } +inline RangeSet::iterator range_begin(RangeSet& r) { return r.begin(); } +inline RangeSet::const_iterator range_end(const RangeSet& r) { return r.end(); } +inline RangeSet::iterator range_end(RangeSet& r) { return r.end(); } + +namespace boost +{ + template<> struct range_mutable_iterator + { + typedef RangeSet::iterator type; + }; + template<> struct range_const_iterator + { + typedef RangeSet::const_iterator type; + }; +} + +#endif + +// vim:ts=4 diff --git a/src/RequestParser.cpp b/src/RequestParser.cpp deleted file mode 100644 index 8384cd0e1..000000000 --- a/src/RequestParser.cpp +++ /dev/null @@ -1,330 +0,0 @@ -#include "RequestParser.h" -#include "HttpRequest.h" - - - -HttpRequestParser::HttpRequestParser() - : state_(method_start) -{ -} - -void HttpRequestParser::reset() -{ - state_ = method_start; -} - -//template -boost::tribool HttpRequestParser::parse(HttpRequest& req, - char* begin, char* end) -{ - while (begin != end) - { - boost::tribool result = consume(req, *begin++); - if (result || !result) - { - std::string temp(begin,end); - req.mBody=temp; - return result; - } - } - boost::tribool result = boost::indeterminate; - return result; -} - -boost::tribool HttpRequestParser::consume(HttpRequest& req, char input) -{ - switch (state_) - { - case method_start: - if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return false; - } - else - { - state_ = method; - req.method.push_back(input); - return boost::indeterminate; - } - case method: - if (input == ' ') - { - state_ = uri; - return boost::indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return false; - } - else - { - req.method.push_back(input); - return boost::indeterminate; - } - case uri_start: - if (is_ctl(input)) - { - return false; - } - else - { - state_ = uri; - req.uri.push_back(input); - return boost::indeterminate; - } - case uri: - if (input == ' ') - { - state_ = http_version_h; - return boost::indeterminate; - } - else if (is_ctl(input)) - { - return false; - } - else - { - req.uri.push_back(input); - return boost::indeterminate; - } - case http_version_h: - if (input == 'H') - { - state_ = http_version_t_1; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_t_1: - if (input == 'T') - { - state_ = http_version_t_2; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_t_2: - if (input == 'T') - { - state_ = http_version_p; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_p: - if (input == 'P') - { - state_ = http_version_slash; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_slash: - if (input == '/') - { - req.http_version_major = 0; - req.http_version_minor = 0; - state_ = http_version_major_start; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_major_start: - if (is_digit(input)) - { - req.http_version_major = req.http_version_major * 10 + input - '0'; - state_ = http_version_major; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_major: - if (input == '.') - { - state_ = http_version_minor_start; - return boost::indeterminate; - } - else if (is_digit(input)) - { - req.http_version_major = req.http_version_major * 10 + input - '0'; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_minor_start: - if (is_digit(input)) - { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; - state_ = http_version_minor; - return boost::indeterminate; - } - else - { - return false; - } - case http_version_minor: - if (input == '\r') - { - state_ = expecting_newline_1; - return boost::indeterminate; - } - else if (is_digit(input)) - { - req.http_version_minor = req.http_version_minor * 10 + input - '0'; - return boost::indeterminate; - } - else - { - return false; - } - case expecting_newline_1: - if (input == '\n') - { - state_ = header_line_start; - return boost::indeterminate; - } - else - { - return false; - } - case header_line_start: - if (input == '\r') - { - state_ = expecting_newline_3; - return boost::indeterminate; - } - else if (!req.headers.empty() && (input == ' ' || input == '\t')) - { - state_ = header_lws; - return boost::indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return false; - } - else - { - req.headers.push_back(HttpHeader()); - req.headers.back().name.push_back(input); - state_ = header_name; - return boost::indeterminate; - } - case header_lws: - if (input == '\r') - { - state_ = expecting_newline_2; - return boost::indeterminate; - } - else if (input == ' ' || input == '\t') - { - return boost::indeterminate; - } - else if (is_ctl(input)) - { - return false; - } - else - { - state_ = header_value; - req.headers.back().value.push_back(input); - return boost::indeterminate; - } - case header_name: - if (input == ':') - { - state_ = space_before_header_value; - return boost::indeterminate; - } - else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) - { - return false; - } - else - { - req.headers.back().name.push_back(input); - return boost::indeterminate; - } - case space_before_header_value: - if (input == ' ') - { - state_ = header_value; - return boost::indeterminate; - } - else - { - return false; - } - case header_value: - if (input == '\r') - { - state_ = expecting_newline_2; - return boost::indeterminate; - } - else if (is_ctl(input)) - { - return false; - } - else - { - req.headers.back().value.push_back(input); - return boost::indeterminate; - } - case expecting_newline_2: - if (input == '\n') - { - state_ = header_line_start; - return boost::indeterminate; - } - else - { - return false; - } - case expecting_newline_3: - return (input == '\n'); - default: - return false; - } -} - -bool HttpRequestParser::is_char(int c) -{ - return c >= 0 && c <= 127; -} - -bool HttpRequestParser::is_ctl(int c) -{ - return (c >= 0 && c <= 31) || (c == 127); -} - -bool HttpRequestParser::is_tspecial(int c) -{ - switch (c) - { - case '(': case ')': case '<': case '>': case '@': - case ',': case ';': case ':': case '\\': case '"': - case '/': case '[': case ']': case '?': case '=': - case '{': case '}': case ' ': case '\t': - return true; - default: - return false; - } -} - -bool HttpRequestParser::is_digit(int c) -{ - return c >= '0' && c <= '9'; -} diff --git a/src/RequestParser.h b/src/RequestParser.h deleted file mode 100644 index f0906daa8..000000000 --- a/src/RequestParser.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef HTTP_REQUEST_PARSER_HPP -#define HTTP_REQUEST_PARSER_HPP - -#include -#include - - - -struct HttpRequest; - -/// Parser for incoming requests. -class HttpRequestParser -{ - /// Handle the next character of input. - boost::tribool consume(HttpRequest& req, char input); - - /// Check if a byte is an HTTP character. - static bool is_char(int c); - - /// Check if a byte is an HTTP control character. - static bool is_ctl(int c); - - /// Check if a byte is defined as an HTTP special character. - static bool is_tspecial(int c); - - /// Check if a byte is a digit. - static bool is_digit(int c); - - /// The current state of the parser. - enum state - { - method_start, - method, - uri_start, - uri, - http_version_h, - http_version_t_1, - http_version_t_2, - http_version_p, - http_version_slash, - http_version_major_start, - http_version_major, - http_version_minor_start, - http_version_minor, - expecting_newline_1, - header_line_start, - header_lws, - header_name, - space_before_header_value, - header_value, - expecting_newline_2, - expecting_newline_3 - } state_; -public: - /// Construct ready to parse the request method. - HttpRequestParser(); - - /// Reset to initial parser state. - void reset(); - - /// Parse some data. The tribool return value is true when a complete request - /// has been parsed, false if the data is invalid, indeterminate when more - /// data is required. The InputIterator return value indicates how much of the - /// input has been consumed. - //template - boost::tribool parse(HttpRequest& req, char*, char*); - - -}; - - -#endif // HTTP_REQUEST_PARSER_HPP diff --git a/src/NewcoinAddress.cpp b/src/RippleAddress.cpp similarity index 72% rename from src/NewcoinAddress.cpp rename to src/RippleAddress.cpp index a8f538963..46d769d30 100644 --- a/src/NewcoinAddress.cpp +++ b/src/RippleAddress.cpp @@ -1,24 +1,31 @@ -#include "NewcoinAddress.h" +#include "RippleAddress.h" + +#include +#include +#include + +#include +#include +#include + +#include + #include "key.h" #include "Config.h" #include "BitcoinUtil.h" #include "rfc1751.h" #include "utils.h" +#include "Log.h" -#include -#include -#include -#include -#include -#include -#include +SETUP_LOG(); -NewcoinAddress::NewcoinAddress() + +RippleAddress::RippleAddress() { nVersion = VER_NONE; } -bool NewcoinAddress::isValid() const +bool RippleAddress::isValid() const { bool bValid = false; @@ -48,13 +55,13 @@ bool NewcoinAddress::isValid() const return bValid; } -void NewcoinAddress::clear() +void RippleAddress::clear() { nVersion = VER_NONE; vchData.clear(); } -std::string NewcoinAddress::humanAddressType() const +std::string RippleAddress::humanAddressType() const { switch (nVersion) { @@ -75,10 +82,10 @@ std::string NewcoinAddress::humanAddressType() const // NodePublic // -NewcoinAddress NewcoinAddress::createNodePublic(const NewcoinAddress& naSeed) +RippleAddress RippleAddress::createNodePublic(const RippleAddress& naSeed) { CKey ckSeed(naSeed.getSeed()); - NewcoinAddress naNew; + RippleAddress naNew; // YYY Should there be a GetPubKey() equiv that returns a uint256? naNew.setNodePublic(ckSeed.GetPubKey()); @@ -86,25 +93,25 @@ NewcoinAddress NewcoinAddress::createNodePublic(const NewcoinAddress& naSeed) return naNew; } -NewcoinAddress NewcoinAddress::createNodePublic(const std::vector& vPublic) +RippleAddress RippleAddress::createNodePublic(const std::vector& vPublic) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setNodePublic(vPublic); return naNew; } -NewcoinAddress NewcoinAddress::createNodePublic(const std::string& strPublic) +RippleAddress RippleAddress::createNodePublic(const std::string& strPublic) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setNodePublic(strPublic); return naNew; } -uint160 NewcoinAddress::getNodeID() const +uint160 RippleAddress::getNodeID() const { switch (nVersion) { case VER_NONE: @@ -118,7 +125,7 @@ uint160 NewcoinAddress::getNodeID() const throw std::runtime_error(str(boost::format("bad source: %d") % int(nVersion))); } } -const std::vector& NewcoinAddress::getNodePublic() const +const std::vector& RippleAddress::getNodePublic() const { switch (nVersion) { case VER_NONE: @@ -132,7 +139,7 @@ const std::vector& NewcoinAddress::getNodePublic() const } } -std::string NewcoinAddress::humanNodePublic() const +std::string RippleAddress::humanNodePublic() const { switch (nVersion) { case VER_NONE: @@ -146,17 +153,17 @@ std::string NewcoinAddress::humanNodePublic() const } } -bool NewcoinAddress::setNodePublic(const std::string& strPublic) +bool RippleAddress::setNodePublic(const std::string& strPublic) { return SetString(strPublic.c_str(), VER_NODE_PUBLIC); } -void NewcoinAddress::setNodePublic(const std::vector& vPublic) +void RippleAddress::setNodePublic(const std::vector& vPublic) { SetData(VER_NODE_PUBLIC, vPublic); } -bool NewcoinAddress::verifyNodePublic(const uint256& hash, const std::vector& vchSig) const +bool RippleAddress::verifyNodePublic(const uint256& hash, const std::vector& vchSig) const { CKey pubkey = CKey(); bool bVerified; @@ -174,7 +181,7 @@ bool NewcoinAddress::verifyNodePublic(const uint256& hash, const std::vector vchSig(strSig.begin(), strSig.end()); @@ -185,10 +192,10 @@ bool NewcoinAddress::verifyNodePublic(const uint256& hash, const std::string& st // NodePrivate // -NewcoinAddress NewcoinAddress::createNodePrivate(const NewcoinAddress& naSeed) +RippleAddress RippleAddress::createNodePrivate(const RippleAddress& naSeed) { uint256 uPrivKey; - NewcoinAddress naNew; + RippleAddress naNew; CKey ckSeed(naSeed.getSeed()); ckSeed.GetPrivateKeyU(uPrivKey); @@ -198,7 +205,7 @@ NewcoinAddress NewcoinAddress::createNodePrivate(const NewcoinAddress& naSeed) return naNew; } -const std::vector& NewcoinAddress::getNodePrivateData() const +const std::vector& RippleAddress::getNodePrivateData() const { switch (nVersion) { case VER_NONE: @@ -212,7 +219,7 @@ const std::vector& NewcoinAddress::getNodePrivateData() const } } -uint256 NewcoinAddress::getNodePrivate() const +uint256 RippleAddress::getNodePrivate() const { switch (nVersion) { case VER_NONE: @@ -226,7 +233,7 @@ uint256 NewcoinAddress::getNodePrivate() const } } -std::string NewcoinAddress::humanNodePrivate() const +std::string RippleAddress::humanNodePrivate() const { switch (nVersion) { case VER_NONE: @@ -240,21 +247,21 @@ std::string NewcoinAddress::humanNodePrivate() const } } -bool NewcoinAddress::setNodePrivate(const std::string& strPrivate) +bool RippleAddress::setNodePrivate(const std::string& strPrivate) { return SetString(strPrivate.c_str(), VER_NODE_PRIVATE); } -void NewcoinAddress::setNodePrivate(const std::vector& vPrivate) { +void RippleAddress::setNodePrivate(const std::vector& vPrivate) { SetData(VER_NODE_PRIVATE, vPrivate); } -void NewcoinAddress::setNodePrivate(uint256 hash256) +void RippleAddress::setNodePrivate(uint256 hash256) { SetData(VER_NODE_PRIVATE, hash256.begin(), 32); } -void NewcoinAddress::signNodePrivate(const uint256& hash, std::vector& vchSig) const +void RippleAddress::signNodePrivate(const uint256& hash, std::vector& vchSig) const { CKey ckPrivKey; @@ -268,7 +275,7 @@ void NewcoinAddress::signNodePrivate(const uint256& hash, std::vector& NewcoinAddress::getAccountPublic() const +const std::vector& RippleAddress::getAccountPublic() const { switch (nVersion) { case VER_NONE: @@ -360,7 +367,7 @@ const std::vector& NewcoinAddress::getAccountPublic() const } } -std::string NewcoinAddress::humanAccountPublic() const +std::string RippleAddress::humanAccountPublic() const { switch (nVersion) { case VER_NONE: @@ -377,24 +384,24 @@ std::string NewcoinAddress::humanAccountPublic() const } } -bool NewcoinAddress::setAccountPublic(const std::string& strPublic) +bool RippleAddress::setAccountPublic(const std::string& strPublic) { return SetString(strPublic.c_str(), VER_ACCOUNT_PUBLIC); } -void NewcoinAddress::setAccountPublic(const std::vector& vPublic) +void RippleAddress::setAccountPublic(const std::vector& vPublic) { SetData(VER_ACCOUNT_PUBLIC, vPublic); } -void NewcoinAddress::setAccountPublic(const NewcoinAddress& generator, int seq) +void RippleAddress::setAccountPublic(const RippleAddress& generator, int seq) { CKey pubkey = CKey(generator, seq); setAccountPublic(pubkey.GetPubKey()); } -bool NewcoinAddress::accountPublicVerify(const uint256& uHash, const std::vector& vucSig) const +bool RippleAddress::accountPublicVerify(const uint256& uHash, const std::vector& vucSig) const { CKey ckPublic; bool bVerified; @@ -402,7 +409,7 @@ bool NewcoinAddress::accountPublicVerify(const uint256& uHash, const std::vector if (!ckPublic.SetPubKey(getAccountPublic())) { // Bad private key. - std::cerr << "accountPublicVerify: Bad private key." << std::endl; + cLog(lsWARNING) << "accountPublicVerify: Bad private key."; bVerified = false; } else @@ -413,9 +420,9 @@ bool NewcoinAddress::accountPublicVerify(const uint256& uHash, const std::vector return bVerified; } -NewcoinAddress NewcoinAddress::createAccountID(const uint160& uiAccountID) +RippleAddress RippleAddress::createAccountID(const uint160& uiAccountID) { - NewcoinAddress na; + RippleAddress na; na.setAccountID(uiAccountID); @@ -426,16 +433,16 @@ NewcoinAddress NewcoinAddress::createAccountID(const uint160& uiAccountID) // AccountPrivate // -NewcoinAddress NewcoinAddress::createAccountPrivate(const NewcoinAddress& naGenerator, const NewcoinAddress& naSeed, int iSeq) +RippleAddress RippleAddress::createAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int iSeq) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setAccountPrivate(naGenerator, naSeed, iSeq); return naNew; } -uint256 NewcoinAddress::getAccountPrivate() const +uint256 RippleAddress::getAccountPrivate() const { switch (nVersion) { case VER_NONE: @@ -449,7 +456,7 @@ uint256 NewcoinAddress::getAccountPrivate() const } } -std::string NewcoinAddress::humanAccountPrivate() const +std::string RippleAddress::humanAccountPrivate() const { switch (nVersion) { case VER_NONE: @@ -463,22 +470,22 @@ std::string NewcoinAddress::humanAccountPrivate() const } } -bool NewcoinAddress::setAccountPrivate(const std::string& strPrivate) +bool RippleAddress::setAccountPrivate(const std::string& strPrivate) { return SetString(strPrivate.c_str(), VER_ACCOUNT_PRIVATE); } -void NewcoinAddress::setAccountPrivate(const std::vector& vPrivate) +void RippleAddress::setAccountPrivate(const std::vector& vPrivate) { SetData(VER_ACCOUNT_PRIVATE, vPrivate); } -void NewcoinAddress::setAccountPrivate(uint256 hash256) +void RippleAddress::setAccountPrivate(uint256 hash256) { SetData(VER_ACCOUNT_PRIVATE, hash256.begin(), 32); } -void NewcoinAddress::setAccountPrivate(const NewcoinAddress& naGenerator, const NewcoinAddress& naSeed, int seq) +void RippleAddress::setAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int seq) { CKey ckPubkey = CKey(naSeed.getSeed()); CKey ckPrivkey = CKey(naGenerator, ckPubkey.GetSecretBN(), seq); @@ -489,7 +496,7 @@ void NewcoinAddress::setAccountPrivate(const NewcoinAddress& naGenerator, const setAccountPrivate(uPrivKey); } -bool NewcoinAddress::accountPrivateSign(const uint256& uHash, std::vector& vucSig) const +bool RippleAddress::accountPrivateSign(const uint256& uHash, std::vector& vucSig) const { CKey ckPrivate; bool bResult; @@ -497,21 +504,20 @@ bool NewcoinAddress::accountPrivateSign(const uint256& uHash, std::vector& vucSig) const +bool RippleAddress::accountPrivateVerify(const uint256& uHash, const std::vector& vucSig) const { CKey ckPrivate; bool bVerified; @@ -519,7 +525,7 @@ bool NewcoinAddress::accountPrivateVerify(const uint256& uHash, const std::vecto if (!ckPrivate.SetPrivateKeyU(getAccountPrivate())) { // Bad private key. - std::cerr << "accountPrivateVerify: Bad private key." << std::endl; + cLog(lsWARNING) << "accountPrivateVerify: Bad private key."; bVerified = false; } else @@ -531,7 +537,7 @@ bool NewcoinAddress::accountPrivateVerify(const uint256& uHash, const std::vecto } #endif -std::vector NewcoinAddress::accountPrivateEncrypt(const NewcoinAddress& naPublicTo, const std::vector& vucPlainText) const +std::vector RippleAddress::accountPrivateEncrypt(const RippleAddress& naPublicTo, const std::vector& vucPlainText) const { CKey ckPrivate; CKey ckPublic; @@ -540,16 +546,17 @@ std::vector NewcoinAddress::accountPrivateEncrypt(const NewcoinAd if (!ckPublic.SetPubKey(naPublicTo.getAccountPublic())) { // Bad public key. - std::cerr << "accountPrivateEncrypt: Bad public key." << std::endl; + cLog(lsWARNING) << "accountPrivateEncrypt: Bad public key."; } else if (!ckPrivate.SetPrivateKeyU(getAccountPrivate())) { // Bad private key. - std::cerr << "accountPrivateEncrypt: Bad private key." << std::endl; + cLog(lsWARNING) << "accountPrivateEncrypt: Bad private key."; } else { - try { + try + { vucCipherText = ckPrivate.encryptECIES(ckPublic, vucPlainText); } catch (...) @@ -561,7 +568,7 @@ std::vector NewcoinAddress::accountPrivateEncrypt(const NewcoinAd return vucCipherText; } -std::vector NewcoinAddress::accountPrivateDecrypt(const NewcoinAddress& naPublicFrom, const std::vector& vucCipherText) const +std::vector RippleAddress::accountPrivateDecrypt(const RippleAddress& naPublicFrom, const std::vector& vucCipherText) const { CKey ckPrivate; CKey ckPublic; @@ -570,12 +577,12 @@ std::vector NewcoinAddress::accountPrivateDecrypt(const NewcoinAd if (!ckPublic.SetPubKey(naPublicFrom.getAccountPublic())) { // Bad public key. - std::cerr << "accountPrivateDecrypt: Bad public key." << std::endl; + cLog(lsWARNING) << "accountPrivateDecrypt: Bad public key."; } else if (!ckPrivate.SetPrivateKeyU(getAccountPrivate())) { // Bad private key. - std::cerr << "accountPrivateDecrypt: Bad private key." << std::endl; + cLog(lsWARNING) << "accountPrivateDecrypt: Bad private key."; } else { @@ -595,7 +602,7 @@ std::vector NewcoinAddress::accountPrivateDecrypt(const NewcoinAd // Generators // -BIGNUM* NewcoinAddress::getGeneratorBN() const +BIGNUM* RippleAddress::getGeneratorBN() const { // returns the public generator switch (nVersion) { case VER_NONE: @@ -614,7 +621,7 @@ BIGNUM* NewcoinAddress::getGeneratorBN() const return ret; } -const std::vector& NewcoinAddress::getGenerator() const +const std::vector& RippleAddress::getGenerator() const { // returns the public generator switch (nVersion) { case VER_NONE: @@ -629,7 +636,7 @@ const std::vector& NewcoinAddress::getGenerator() const } } -std::string NewcoinAddress::humanGenerator() const +std::string RippleAddress::humanGenerator() const { switch (nVersion) { case VER_NONE: @@ -643,20 +650,20 @@ std::string NewcoinAddress::humanGenerator() const } } -bool NewcoinAddress::setGenerator(const std::string& strGenerator) +bool RippleAddress::setGenerator(const std::string& strGenerator) { return SetString(strGenerator.c_str(), VER_FAMILY_GENERATOR); } -void NewcoinAddress::setGenerator(const std::vector& vPublic) +void RippleAddress::setGenerator(const std::vector& vPublic) { SetData(VER_FAMILY_GENERATOR, vPublic); } -NewcoinAddress NewcoinAddress::createGeneratorPublic(const NewcoinAddress& naSeed) +RippleAddress RippleAddress::createGeneratorPublic(const RippleAddress& naSeed) { CKey ckSeed(naSeed.getSeed()); - NewcoinAddress naNew; + RippleAddress naNew; naNew.setGenerator(ckSeed.GetPubKey()); @@ -667,7 +674,7 @@ NewcoinAddress NewcoinAddress::createGeneratorPublic(const NewcoinAddress& naSee // Seed // -uint128 NewcoinAddress::getSeed() const +uint128 RippleAddress::getSeed() const { switch (nVersion) { case VER_NONE: @@ -681,7 +688,7 @@ uint128 NewcoinAddress::getSeed() const } } -std::string NewcoinAddress::humanSeed1751() const +std::string RippleAddress::humanSeed1751() const { switch (nVersion) { case VER_NONE: @@ -708,7 +715,7 @@ std::string NewcoinAddress::humanSeed1751() const } } -std::string NewcoinAddress::humanSeed() const +std::string RippleAddress::humanSeed() const { switch (nVersion) { case VER_NONE: @@ -722,7 +729,7 @@ std::string NewcoinAddress::humanSeed() const } } -int NewcoinAddress::setSeed1751(const std::string& strHuman1751) +int RippleAddress::setSeed1751(const std::string& strHuman1751) { std::string strKey; int iResult = eng2key(strKey, strHuman1751); @@ -738,14 +745,14 @@ int NewcoinAddress::setSeed1751(const std::string& strHuman1751) return iResult; } -bool NewcoinAddress::setSeed(const std::string& strSeed) +bool RippleAddress::setSeed(const std::string& strSeed) { return SetString(strSeed.c_str(), VER_FAMILY_SEED); } -bool NewcoinAddress::setSeedGeneric(const std::string& strText) +bool RippleAddress::setSeedGeneric(const std::string& strText) { - NewcoinAddress naTemp; + RippleAddress naTemp; bool bResult = true; if (strText.empty() @@ -776,32 +783,32 @@ bool NewcoinAddress::setSeedGeneric(const std::string& strText) return bResult; } -void NewcoinAddress::setSeed(uint128 hash128) { +void RippleAddress::setSeed(uint128 hash128) { SetData(VER_FAMILY_SEED, hash128.begin(), 16); } -void NewcoinAddress::setSeedRandom() +void RippleAddress::setSeedRandom() { // XXX Maybe we should call MakeNewKey uint128 key; RAND_bytes((unsigned char *) &key, sizeof(key)); - NewcoinAddress::setSeed(key); + RippleAddress::setSeed(key); } -NewcoinAddress NewcoinAddress::createSeedRandom() +RippleAddress RippleAddress::createSeedRandom() { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setSeedRandom(); return naNew; } -NewcoinAddress NewcoinAddress::createSeedGeneric(const std::string& strText) +RippleAddress RippleAddress::createSeedGeneric(const std::string& strText) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setSeedGeneric(strText); @@ -813,14 +820,14 @@ BOOST_AUTO_TEST_SUITE(ripple_address) BOOST_AUTO_TEST_CASE( check_crypto ) { // Construct a seed. - NewcoinAddress naSeed; + RippleAddress naSeed; BOOST_CHECK(naSeed.setSeedGeneric("masterpassphrase")); BOOST_CHECK_MESSAGE(naSeed.humanSeed() == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", naSeed.humanSeed()); // Create node public/private key pair - NewcoinAddress naNodePublic = NewcoinAddress::createNodePublic(naSeed); - NewcoinAddress naNodePrivate = NewcoinAddress::createNodePrivate(naSeed); + RippleAddress naNodePublic = RippleAddress::createNodePublic(naSeed); + RippleAddress naNodePrivate = RippleAddress::createNodePrivate(naSeed); BOOST_CHECK_MESSAGE(naNodePublic.humanNodePublic() == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", naNodePublic.humanNodePublic()); BOOST_CHECK_MESSAGE(naNodePrivate.humanNodePrivate() == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate()); @@ -834,21 +841,21 @@ BOOST_AUTO_TEST_CASE( check_crypto ) BOOST_CHECK_MESSAGE(naNodePublic.verifyNodePublic(uHash, vucTextSig), "Verify failed."); // Construct a public generator from the seed. - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSeed); + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSeed); BOOST_CHECK_MESSAGE(naGenerator.humanGenerator() == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", naGenerator.humanGenerator()); // Create account #0 public/private key pair. - NewcoinAddress naAccountPublic0 = NewcoinAddress::createAccountPublic(naGenerator, 0); - NewcoinAddress naAccountPrivate0 = NewcoinAddress::createAccountPrivate(naGenerator, naSeed, 0); + RippleAddress naAccountPublic0 = RippleAddress::createAccountPublic(naGenerator, 0); + RippleAddress naAccountPrivate0 = RippleAddress::createAccountPrivate(naGenerator, naSeed, 0); BOOST_CHECK_MESSAGE(naAccountPublic0.humanAccountID() == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", naAccountPublic0.humanAccountID()); BOOST_CHECK_MESSAGE(naAccountPublic0.humanAccountPublic() == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw", naAccountPublic0.humanAccountPublic()); BOOST_CHECK_MESSAGE(naAccountPrivate0.humanAccountPrivate() == "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh", naAccountPrivate0.humanAccountPrivate()); // Create account #1 public/private key pair. - NewcoinAddress naAccountPublic1 = NewcoinAddress::createAccountPublic(naGenerator, 1); - NewcoinAddress naAccountPrivate1 = NewcoinAddress::createAccountPrivate(naGenerator, naSeed, 1); + RippleAddress naAccountPublic1 = RippleAddress::createAccountPublic(naGenerator, 1); + RippleAddress naAccountPrivate1 = RippleAddress::createAccountPrivate(naGenerator, naSeed, 1); BOOST_CHECK_MESSAGE(naAccountPublic1.humanAccountID() == "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP", naAccountPublic1.humanAccountID()); BOOST_CHECK_MESSAGE(naAccountPublic1.humanAccountPublic() == "aBPXpTfuLy1Bhk3HnGTTAqnovpKWQ23NpFMNkAF6F1Atg5vDyPrw", naAccountPublic1.humanAccountPublic()); diff --git a/src/NewcoinAddress.h b/src/RippleAddress.h similarity index 71% rename from src/NewcoinAddress.h rename to src/RippleAddress.h index 707a976c5..61a71a28e 100644 --- a/src/NewcoinAddress.h +++ b/src/RippleAddress.h @@ -8,7 +8,7 @@ // Used to hold addresses and parse and produce human formats. // // XXX This needs to be reworked to store data in uint160 and uint256. Conversion to CBase58Data should happen as needed. -class NewcoinAddress : public CBase58Data +class RippleAddress : public CBase58Data { private: typedef enum { @@ -23,7 +23,7 @@ private: } VersionEncoding; public: - NewcoinAddress(); + RippleAddress(); // For public and private key, checks if they are legal. bool isValid() const; @@ -44,9 +44,9 @@ public: bool verifyNodePublic(const uint256& hash, const std::vector& vchSig) const; bool verifyNodePublic(const uint256& hash, const std::string& strSig) const; - static NewcoinAddress createNodePublic(const NewcoinAddress& naSeed); - static NewcoinAddress createNodePublic(const std::vector& vPublic); - static NewcoinAddress createNodePublic(const std::string& strPublic); + static RippleAddress createNodePublic(const RippleAddress& naSeed); + static RippleAddress createNodePublic(const std::vector& vPublic); + static RippleAddress createNodePublic(const std::string& strPublic); // // Node Private @@ -61,7 +61,7 @@ public: void setNodePrivate(uint256 hash256); void signNodePrivate(const uint256& hash, std::vector& vchSig) const; - static NewcoinAddress createNodePrivate(const NewcoinAddress& naSeed); + static RippleAddress createNodePrivate(const RippleAddress& naSeed); // // Accounts IDs @@ -73,10 +73,10 @@ public: bool setAccountID(const std::string& strAccountID); void setAccountID(const uint160& hash160In); - static NewcoinAddress createAccountID(const std::string& strAccountID) - { NewcoinAddress na; na.setAccountID(strAccountID); return na; } + static RippleAddress createAccountID(const std::string& strAccountID) + { RippleAddress na; na.setAccountID(strAccountID); return na; } - static NewcoinAddress createAccountID(const uint160& uiAccountID); + static RippleAddress createAccountID(const uint160& uiAccountID); static std::string createHumanAccountID(const uint160& uiAccountID) { return createAccountID(uiAccountID).humanAccountID(); } @@ -93,13 +93,13 @@ public: bool setAccountPublic(const std::string& strPublic); void setAccountPublic(const std::vector& vPublic); - void setAccountPublic(const NewcoinAddress& generator, int seq); + void setAccountPublic(const RippleAddress& generator, int seq); bool accountPublicVerify(const uint256& uHash, const std::vector& vucSig) const; - static NewcoinAddress createAccountPublic(const std::vector& vPublic) + static RippleAddress createAccountPublic(const std::vector& vPublic) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setAccountPublic(vPublic); @@ -111,7 +111,7 @@ public: } // Create a deterministic public key from a public generator. - static NewcoinAddress createAccountPublic(const NewcoinAddress& naGenerator, int iSeq); + static RippleAddress createAccountPublic(const RippleAddress& naGenerator, int iSeq); // // Accounts Private @@ -123,22 +123,22 @@ public: bool setAccountPrivate(const std::string& strPrivate); void setAccountPrivate(const std::vector& vPrivate); void setAccountPrivate(uint256 hash256); - void setAccountPrivate(const NewcoinAddress& naGenerator, const NewcoinAddress& naSeed, int seq); + void setAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int seq); bool accountPrivateSign(const uint256& uHash, std::vector& vucSig) const; // bool accountPrivateVerify(const uint256& uHash, const std::vector& vucSig) const; // Encrypt a message. - std::vector accountPrivateEncrypt(const NewcoinAddress& naPublicTo, const std::vector& vucPlainText) const; + std::vector accountPrivateEncrypt(const RippleAddress& naPublicTo, const std::vector& vucPlainText) const; // Decrypt a message. - std::vector accountPrivateDecrypt(const NewcoinAddress& naPublicFrom, const std::vector& vucCipherText) const; + std::vector accountPrivateDecrypt(const RippleAddress& naPublicFrom, const std::vector& vucCipherText) const; - static NewcoinAddress createAccountPrivate(const NewcoinAddress& naGenerator, const NewcoinAddress& naSeed, int iSeq); + static RippleAddress createAccountPrivate(const RippleAddress& naGenerator, const RippleAddress& naSeed, int iSeq); - static NewcoinAddress createAccountPrivate(const std::vector& vPrivate) + static RippleAddress createAccountPrivate(const std::vector& vPrivate) { - NewcoinAddress naNew; + RippleAddress naNew; naNew.setAccountPrivate(vPrivate); @@ -160,10 +160,10 @@ public: bool setGenerator(const std::string& strGenerator); void setGenerator(const std::vector& vPublic); - // void setGenerator(const NewcoinAddress& seed); + // void setGenerator(const RippleAddress& seed); // Create generator for making public deterministic keys. - static NewcoinAddress createGeneratorPublic(const NewcoinAddress& naSeed); + static RippleAddress createGeneratorPublic(const RippleAddress& naSeed); // // Seeds @@ -179,8 +179,8 @@ public: void setSeed(uint128 hash128); void setSeedRandom(); - static NewcoinAddress createSeedRandom(); - static NewcoinAddress createSeedGeneric(const std::string& strText); + static RippleAddress createSeedRandom(); + static RippleAddress createSeedGeneric(const std::string& strText); }; #endif diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index c660e4dd8..c951f3143 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -27,7 +27,7 @@ std::size_t hash_value(const aciSource& asValue) // <-- uOfferIndex : 0=end of list. TER RippleCalc::calcNodeAdvance( const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality, const bool bReverse) { @@ -159,7 +159,7 @@ TER RippleCalc::calcNodeAdvance( const aciSource asLine = boost::make_tuple(uOfrOwnerID, uCurCurrencyID, uCurIssuerID); - cLog(lsINFO) << boost::str(boost::format("calcNodeAdvance: uOfrOwnerID=%s") % NewcoinAddress::createHumanAccountID(uOfrOwnerID)); + cLog(lsINFO) << boost::str(boost::format("calcNodeAdvance: uOfrOwnerID=%s") % RippleAddress::createHumanAccountID(uOfrOwnerID)); if (sleOffer->isFieldPresent(sfExpiration) && sleOffer->getFieldU32(sfExpiration) <= lesActive.getLedger()->getParentCloseTimeNC()) { @@ -236,9 +236,9 @@ TER RippleCalc::calcNodeAdvance( { // Consider source mentioned by current path state. cLog(lsINFO) << boost::str(boost::format("calcNodeAdvance: remember=%s/%s/%s") - % NewcoinAddress::createHumanAccountID(uOfrOwnerID) + % RippleAddress::createHumanAccountID(uOfrOwnerID) % STAmount::createHumanCurrency(uCurCurrencyID) - % NewcoinAddress::createHumanAccountID(uCurIssuerID)); + % RippleAddress::createHumanAccountID(uCurIssuerID)); pspCur->umReverse.insert(std::make_pair(asLine, uIndex)); } @@ -267,7 +267,7 @@ TER RippleCalc::calcNodeAdvance( // Continue process till request is satisified while we the rate does not increase past the initial rate. TER RippleCalc::calcNodeDeliverRev( const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality, const uint160& uOutAccountID, // --> Output owner's account. const STAmount& saOutReq, // --> Funds wanted. @@ -311,9 +311,9 @@ TER RippleCalc::calcNodeDeliverRev( ? saOne // No fee. : saTransferRate; // Transfer rate of issuer. cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: uOfrOwnerID=%s uOutAccountID=%s uCurIssuerID=%s saTransferRate=%s saOutFeeRate=%s") - % NewcoinAddress::createHumanAccountID(uOfrOwnerID) - % NewcoinAddress::createHumanAccountID(uOutAccountID) - % NewcoinAddress::createHumanAccountID(uCurIssuerID) + % RippleAddress::createHumanAccountID(uOfrOwnerID) + % RippleAddress::createHumanAccountID(uOutAccountID) + % RippleAddress::createHumanAccountID(uCurIssuerID) % saTransferRate.getFullText() % saOutFeeRate.getFullText()); @@ -459,7 +459,7 @@ TER RippleCalc::calcNodeDeliverRev( // Goal: Make progress consuming the offer. TER RippleCalc::calcNodeDeliverFwd( const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality, const uint160& uInAccountID, // --> Input owner's account. const STAmount& saInFunds, // --> Funds available for delivery and fees. @@ -610,7 +610,7 @@ TER RippleCalc::calcNodeDeliverFwd( // Called to drive from the last offer node in a chain. TER RippleCalc::calcNodeOfferRev( const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality) { TER terResult; @@ -650,7 +650,7 @@ TER RippleCalc::calcNodeOfferRev( // - Deliver is set without transfer fees. TER RippleCalc::calcNodeOfferFwd( const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality ) { @@ -789,7 +789,7 @@ void RippleCalc::calcNodeRipple( // Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur... // <-- tesSUCCESS or tepPATH_DRY -TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) +TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality) { TER terResult = tesSUCCESS; const unsigned int uLast = pspCur->vpnNodes.size() - 1; @@ -829,9 +829,9 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s") % uIndex % uLast - % NewcoinAddress::createHumanAccountID(uPrvAccountID) - % NewcoinAddress::createHumanAccountID(uCurAccountID) - % NewcoinAddress::createHumanAccountID(uNxtAccountID) + % RippleAddress::createHumanAccountID(uPrvAccountID) + % RippleAddress::createHumanAccountID(uCurAccountID) + % RippleAddress::createHumanAccountID(uNxtAccountID) % STAmount::createHumanCurrency(uCurrencyID) % uQualityIn % uQualityOut @@ -1100,7 +1100,7 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uIndex, const PathState::p // - Output to next node is computed as input minus quality or transfer fee. TER RippleCalc::calcNodeAccountFwd( const unsigned int uIndex, // 0 <= uIndex <= uLast - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality) { TER terResult = tesSUCCESS; @@ -1215,8 +1215,8 @@ TER RippleCalc::calcNodeAccountFwd( { // account --> ACCOUNT --> $ cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> $ : uPrvAccountID=%s uCurAccountID=%s saPrvRedeemReq=%s saPrvIssueReq=%s") - % NewcoinAddress::createHumanAccountID(uPrvAccountID) - % NewcoinAddress::createHumanAccountID(uCurAccountID) + % RippleAddress::createHumanAccountID(uPrvAccountID) + % RippleAddress::createHumanAccountID(uCurAccountID) % saPrvRedeemReq.getFullText() % saPrvIssueReq.getFullText()); @@ -1362,7 +1362,7 @@ TER RippleCalc::calcNodeAccountFwd( } // Return true, iff lhs has less priority than rhs. -bool PathState::lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs) +bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs) { if (lhs->uQuality != rhs->uQuality) return lhs->uQuality > rhs->uQuality; // Bigger is worse. @@ -1391,39 +1391,39 @@ TER PathState::pushImply( TER terResult = tesSUCCESS; cLog(lsINFO) << "pushImply> " - << NewcoinAddress::createHumanAccountID(uAccountID) + << RippleAddress::createHumanAccountID(uAccountID) << " " << STAmount::createHumanCurrency(uCurrencyID) - << " " << NewcoinAddress::createHumanAccountID(uIssuerID); + << " " << RippleAddress::createHumanAccountID(uIssuerID); if (pnPrv.uCurrencyID != uCurrencyID) { // Currency is different, need to convert via an offer. terResult = pushNode( - STPathElement::typeCurrency // Offer. - | STPathElement::typeIssuer, - ACCOUNT_ONE, // Placeholder for offers. - uCurrencyID, // The offer's output is what is now wanted. - uIssuerID); + STPathElement::typeCurrency // Offer. + | STPathElement::typeIssuer, + ACCOUNT_ONE, // Placeholder for offers. + uCurrencyID, // The offer's output is what is now wanted. + uIssuerID); } // For ripple, non-stamps, ensure the issuer is on at least one side of the transaction. if (tesSUCCESS == terResult - && !!uCurrencyID // Not stamps. - && (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs. - && uAccountID != uIssuerID)) // Current is not receiving own IOUs. + && !!uCurrencyID // Not stamps. + && (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs. + && uAccountID != uIssuerID)) // Current is not receiving own IOUs. { // Need to ripple through uIssuerID's account. terResult = pushNode( - STPathElement::typeAccount, - uIssuerID, // Intermediate account is the needed issuer. - uCurrencyID, - uIssuerID); + STPathElement::typeAccount, + uIssuerID, // Intermediate account is the needed issuer. + uCurrencyID, + uIssuerID); } - cLog(lsINFO) << "pushImply< " << terResult; + cLog(lsDEBUG) << boost::str(boost::format("pushImply< : %s") % transToken(terResult)); return terResult; } @@ -1437,9 +1437,9 @@ TER PathState::pushNode( const uint160& uIssuerID) { cLog(lsINFO) << "pushNode> " - << NewcoinAddress::createHumanAccountID(uAccountID) + << RippleAddress::createHumanAccountID(uAccountID) << " " << STAmount::createHumanCurrency(uCurrencyID) - << "/" << NewcoinAddress::createHumanAccountID(uIssuerID); + << "/" << RippleAddress::createHumanAccountID(uIssuerID); PaymentNode pnCur; const bool bFirst = vpnNodes.empty(); const PaymentNode& pnPrv = bFirst ? PaymentNode() : vpnNodes.back(); @@ -1491,9 +1491,9 @@ TER PathState::pushNode( if (!sleRippleState) { cLog(lsINFO) << "pushNode: No credit line between " - << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << RippleAddress::createHumanAccountID(pnBck.uAccountID) << " and " - << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << RippleAddress::createHumanAccountID(pnCur.uAccountID) << " for " << STAmount::createHumanCurrency(pnPrv.uCurrencyID) << "." ; @@ -1505,9 +1505,9 @@ TER PathState::pushNode( else { cLog(lsINFO) << "pushNode: Credit line found between " - << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << RippleAddress::createHumanAccountID(pnBck.uAccountID) << " and " - << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << RippleAddress::createHumanAccountID(pnCur.uAccountID) << " for " << STAmount::createHumanCurrency(pnPrv.uCurrencyID) << "." ; @@ -1544,11 +1544,12 @@ TER PathState::pushNode( vpnNodes.push_back(pnCur); } } - cLog(lsINFO) << "pushNode< " << terResult; + cLog(lsDEBUG) << boost::str(boost::format("pushNode< : %s") % transToken(terResult)); return terResult; } +// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, or temBAD_PATH_LOOP PathState::PathState( const int iIndex, const LedgerEntrySet& lesSource, @@ -1632,9 +1633,9 @@ PathState::PathState( cLog(lsINFO) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s") % STAmount::createHumanCurrency(uInCurrencyID) - % NewcoinAddress::createHumanAccountID(uInIssuerID) + % RippleAddress::createHumanAccountID(uInIssuerID) % STAmount::createHumanCurrency(uOutCurrencyID) - % NewcoinAddress::createHumanAccountID(uOutIssuerID) + % RippleAddress::createHumanAccountID(uOutIssuerID) % getJson()); } @@ -1655,13 +1656,13 @@ Json::Value PathState::getJson() const jvNode["flags"] = jvFlags; if (pnNode.uFlags & STPathElement::typeAccount) - jvNode["account"] = NewcoinAddress::createHumanAccountID(pnNode.uAccountID); + jvNode["account"] = RippleAddress::createHumanAccountID(pnNode.uAccountID); if (!!pnNode.uCurrencyID) jvNode["currency"] = STAmount::createHumanCurrency(pnNode.uCurrencyID); if (!!pnNode.uIssuerID) - jvNode["issuer"] = NewcoinAddress::createHumanAccountID(pnNode.uIssuerID); + jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID); // if (pnNode.saRevRedeem) jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText(); @@ -1712,7 +1713,7 @@ Json::Value PathState::getJson() const return jvPathState; } -TER RippleCalc::calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) +TER RippleCalc::calcNodeFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality) { const PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); @@ -1742,7 +1743,7 @@ TER RippleCalc::calcNodeFwd(const unsigned int uIndex, const PathState::pointer& // --> [all]saWanted.mCurrency // --> [all]saAccount // <-> [0]saWanted.mAmount : --> limit, <-- actual -TER RippleCalc::calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) +TER RippleCalc::calcNodeRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality) { PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); @@ -1756,7 +1757,7 @@ TER RippleCalc::calcNodeRev(const unsigned int uIndex, const PathState::pointer& cLog(lsINFO) << boost::str(boost::format("calcNodeRev> uIndex=%d uIssuerID=%s saTransferRate=%s") % uIndex - % NewcoinAddress::createHumanAccountID(uCurIssuerID) + % RippleAddress::createHumanAccountID(uCurIssuerID) % saTransferRate.getFullText()); terResult = bCurAccount @@ -1784,7 +1785,7 @@ TER RippleCalc::calcNodeRev(const unsigned int uIndex, const PathState::pointer& // Calculate the next increment of a path. // The increment is what can satisfy a portion or all of the requested output at the best quality. // <-- pspCur->uQuality -void RippleCalc::pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent) +void RippleCalc::pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent) { // The next state is what is available in preference order. // This is calculated when referenced accounts changed. @@ -1886,7 +1887,7 @@ TER RippleCalc::rippleCalc( if (pspDirect) { // Return if malformed. - if (pspDirect->terStatus >= temMALFORMED && pspDirect->terStatus < tefFAILURE) + if (isTemMalformed(pspDirect->terStatus)) return pspDirect->terStatus; if (tesSUCCESS == pspDirect->terStatus) @@ -1917,14 +1918,11 @@ TER RippleCalc::rippleCalc( if (pspExpanded) { // Return if malformed. - if (pspExpanded->terStatus >= temMALFORMED && pspExpanded->terStatus < tefFAILURE) + if (isTemMalformed(pspExpanded->terStatus)) return pspExpanded->terStatus; if (tesSUCCESS == pspExpanded->terStatus) - { - // Had a success. - terResult = tesSUCCESS; - } + terResult = tesSUCCESS; // Had a success. vpsPaths.push_back(pspExpanded); } @@ -1932,7 +1930,8 @@ TER RippleCalc::rippleCalc( if (vpsPaths.empty()) { - return tefEXCEPTION; + // No paths. Missing credit lines. + return terNO_LINE; } else if (tesSUCCESS != terResult) { @@ -1945,7 +1944,7 @@ TER RippleCalc::rippleCalc( terResult = temUNCERTAIN; } - STAmount saInAct; + STAmount saInAct; // XXX Verify don't need to set currency/issuer. STAmount saOutAct; const LedgerEntrySet lesBase = lesActive; // Checkpoint with just fees paid. const uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmountReq, saMaxAmountReq) : 0; diff --git a/src/RippleCalc.h b/src/RippleCalc.h index 14d07f4aa..b212c162e 100644 --- a/src/RippleCalc.h +++ b/src/RippleCalc.h @@ -65,13 +65,14 @@ extern std::size_t hash_value(const aciSource& asValue); class PathState { protected: - const Ledger::pointer& mLedger; + Ledger::ref mLedger; TER pushNode(const int iType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID); TER pushImply(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID); public: - typedef boost::shared_ptr pointer; + typedef boost::shared_ptr pointer; + typedef const boost::shared_ptr& ref; TER terStatus; std::vector vpnNodes; @@ -123,7 +124,7 @@ public: return boost::make_shared(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax); } - static bool lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs); + static bool lessPriority(PathState::ref lhs, PathState::ref rhs); }; class RippleCalc @@ -139,18 +140,18 @@ public: boost::unordered_set musUnfundedFound; // Offers that were found unfunded. PathState::pointer pathCreate(const STPath& spPath); - void pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent); - TER calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeOfferRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeOfferFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeAccountFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality, const bool bReverse); + void pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent); + TER calcNode(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeOfferRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeOfferFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeAccountRev(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeAccountFwd(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality); + TER calcNodeAdvance(const unsigned int uIndex, PathState::ref pspCur, const bool bMultiQuality, const bool bReverse); TER calcNodeDeliverRev( const unsigned int uIndex, - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality, const uint160& uOutAccountID, const STAmount& saOutReq, @@ -158,7 +159,7 @@ public: TER calcNodeDeliverFwd( const unsigned int uIndex, - const PathState::pointer& pspCur, + PathState::ref pspCur, const bool bMultiQuality, const uint160& uInAccountID, const STAmount& saInFunds, diff --git a/src/RippleLines.cpp b/src/RippleLines.cpp index 46b88d75f..fece4f429 100644 --- a/src/RippleLines.cpp +++ b/src/RippleLines.cpp @@ -7,16 +7,17 @@ SETUP_LOG(); -RippleLines::RippleLines(const uint160& accountID, Ledger::pointer ledger) +RippleLines::RippleLines(const uint160& accountID, Ledger::ref ledger) { - fillLines(accountID,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; +void RippleLines::printRippleLines() +{ + for (unsigned 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 ) @@ -24,7 +25,7 @@ RippleLines::RippleLines(const uint160& accountID ) fillLines(accountID,theApp->getMasterLedger().getCurrentLedger()); } -void RippleLines::fillLines(const uint160& accountID, Ledger::pointer ledger) +void RippleLines::fillLines(const uint160& accountID, Ledger::ref ledger) { uint256 rootIndex = Ledger::getOwnerDirIndex(accountID); uint256 currentIndex = rootIndex; diff --git a/src/RippleLines.h b/src/RippleLines.h index 00373838d..5ddd47e04 100644 --- a/src/RippleLines.h +++ b/src/RippleLines.h @@ -9,12 +9,12 @@ It provides a vector so you to easily iterate through them class RippleLines { std::vector mLines; - void fillLines(const uint160& accountID, Ledger::pointer ledger); + void fillLines(const uint160& accountID, Ledger::ref ledger); public: - RippleLines(const uint160& accountID, Ledger::pointer ledger); + RippleLines(const uint160& accountID, Ledger::ref ledger); RippleLines(const uint160& accountID ); // looks in the current ledger - std::vector& getLines(){ return(mLines); } + std::vector& getLines() { return(mLines); } void printRippleLines(); }; diff --git a/src/RippleState.cpp b/src/RippleState.cpp index 673d563b3..ad814d376 100644 --- a/src/RippleState.cpp +++ b/src/RippleState.cpp @@ -10,8 +10,8 @@ RippleState::RippleState(SerializedLedgerEntry::pointer ledgerEntry) : mLowLimit = mLedgerEntry->getFieldAmount(sfLowLimit); mHighLimit = mLedgerEntry->getFieldAmount(sfHighLimit); - mLowID = NewcoinAddress::createAccountID(mLowLimit.getIssuer()); - mHighID = NewcoinAddress::createAccountID(mHighLimit.getIssuer()); + mLowID = RippleAddress::createAccountID(mLowLimit.getIssuer()); + mHighID = RippleAddress::createAccountID(mHighLimit.getIssuer()); mLowQualityIn = mLedgerEntry->getFieldU32(sfLowQualityIn); mLowQualityOut = mLedgerEntry->getFieldU32(sfLowQualityOut); diff --git a/src/RippleState.h b/src/RippleState.h index f17daf31c..047c661df 100644 --- a/src/RippleState.h +++ b/src/RippleState.h @@ -18,8 +18,8 @@ public: private: SerializedLedgerEntry::pointer mLedgerEntry; - NewcoinAddress mLowID; - NewcoinAddress mHighID; + RippleAddress mLowID; + RippleAddress mHighID; STAmount mLowLimit; STAmount mHighLimit; @@ -39,8 +39,8 @@ public: void setViewAccount(const uint160& accountID); - const NewcoinAddress getAccountID() const { return mViewLowest ? mLowID : mHighID; } - const NewcoinAddress getAccountIDPeer() const { return mViewLowest ? mHighID : mLowID; } + const RippleAddress getAccountID() const { return mViewLowest ? mLowID : mHighID; } + const RippleAddress getAccountIDPeer() const { return mViewLowest ? mHighID : mLowID; } STAmount getBalance() const { return mBalance; } diff --git a/src/SHAMap.cpp b/src/SHAMap.cpp index 7bb86ac30..b7a775778 100644 --- a/src/SHAMap.cpp +++ b/src/SHAMap.cpp @@ -123,12 +123,12 @@ void SHAMap::dirtyUp(std::stack& stack, const uint256& if (!node->setChildHash(branch, prevHash)) { - std::cerr << "dirtyUp terminates early" << std::endl; + cLog(lsFATAL) << "dirtyUp terminates early"; assert(false); return; } #ifdef ST_DEBUG - std::cerr << "dirtyUp sets branch " << branch << " to " << prevHash << std::endl; + cLog(lsTRACE) << "dirtyUp sets branch " << branch << " to " << prevHash; #endif prevHash = node->getNodeHash(); assert(prevHash.isNonZero()); @@ -814,6 +814,38 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& nodeID) return node; } +bool SHAMap::getPath(const uint256& index, std::vector< std::vector >& nodes, SHANodeFormat format) +{ + // Return the path of nodes to the specified index in the specified format + // Return value: true = node present, false = node not present + + boost::recursive_mutex::scoped_lock sl(mLock); + SHAMapTreeNode* inNode = root.get(); + + while (!inNode->isLeaf()) + { + Serializer s; + inNode->addRaw(s, format); + nodes.push_back(s.peekData()); + + int branch = inNode->selectBranch(index); + if (inNode->isEmptyBranch(branch)) // paths leads to empty branch + return false; + inNode = getNodePointer(inNode->getChildNodeID(branch), inNode->getChildHash(branch)); + if (!inNode) + throw SHAMapMissingNode(mType, inNode->getChildNodeID(branch), inNode->getChildHash(branch), index); + } + + if (inNode->getTag() != index) // path leads to different leaf + return false; + + // path lead to the requested leaf + Serializer s; + inNode->addRaw(s, format); + nodes.push_back(s.peekData()); + return true; +} + void SHAMap::dump(bool hash) { #if 0 diff --git a/src/SHAMap.h b/src/SHAMap.h index 2518669b2..107d3d8b6 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -125,6 +125,7 @@ enum SHANodeFormat { snfPREFIX = 1, // Form that hashes to its official hash snfWIRE = 2, // Compressed form used on the wire + snfHASH = 3, // just the hash }; enum SHAMapType @@ -207,7 +208,7 @@ public: bool hasItem() const { return !!mItem; } SHAMapItem::ref peekItem() { return mItem; } SHAMapItem::pointer getItem() const; - bool setItem(const SHAMapItem::pointer& i, TNType type); + bool setItem(SHAMapItem::ref i, TNType type); const uint256& getTag() const { return mItem->getTag(); } const std::vector& peekData() { return mItem->peekData(); } std::vector getData() const { return mItem->getData(); } @@ -236,7 +237,7 @@ public: virtual ~SHAMapSyncFilter() { ; } virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash, - const std::vector& nodeData, bool isLeaf) + const std::vector& nodeData, SHAMapTreeNode::TNType type) { ; } virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector& nodeData) @@ -281,7 +282,8 @@ public: typedef boost::shared_ptr pointer; typedef const boost::shared_ptr& ref; - typedef std::map > SHAMapDiff; + typedef std::pair SHAMapDiffItem; + typedef std::map SHAMapDiff; typedef boost::unordered_map SHADirtyMap; private: @@ -367,8 +369,10 @@ public: bool getNodeFat(const SHAMapNode& node, std::vector& nodeIDs, std::list >& rawNode, bool fatRoot, bool fatLeaves); bool getRootNode(Serializer& s, SHANodeFormat format); - bool addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format); - bool addRootNode(const std::vector& rootNode, SHANodeFormat format); + bool addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format, + SHAMapSyncFilter* filter); + bool addRootNode(const std::vector& rootNode, SHANodeFormat format, + SHAMapSyncFilter* filter); bool addKnownNode(const SHAMapNode& nodeID, const std::vector& rawNode, SHAMapSyncFilter* filter); @@ -404,6 +408,8 @@ public: void walkMap(std::vector& missingNodes, int maxMissing); + bool getPath(const uint256& index, std::vector< std::vector >& nodes, SHANodeFormat format); + bool deepCompare(SHAMap& other); virtual void dump(bool withHashes = false); }; diff --git a/src/SHAMapNodes.cpp b/src/SHAMapNodes.cpp index b14516813..9db9f4e2c 100644 --- a/src/SHAMapNodes.cpp +++ b/src/SHAMapNodes.cpp @@ -185,7 +185,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapTreeNode& node, uint32 seq) : SHAMapN memcpy(mHashes, node.mHashes, sizeof(mHashes)); } -SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, const SHAMapItem::pointer& item, TNType type, uint32 seq) : +SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& node, SHAMapItem::ref item, TNType type, uint32 seq) : SHAMapNode(node), mItem(item), mSeq(seq), mType(type), mFullBelow(true) { assert(item->peekData().size() >= 12); @@ -259,7 +259,7 @@ SHAMapTreeNode::SHAMapTreeNode(const SHAMapNode& id, const std::vector(mHashes), sizeof(mHashes)); +#ifdef DEBUG + Serializer s; + s.add32(sHP_InnerNode); + for(int i = 0; i < 16; ++i) + s.add256(mHashes[i]); + assert(nh == s.getSHA512Half()); +#endif + } } else if (mType == tnTRANSACTION_NM) { @@ -366,11 +381,15 @@ bool SHAMapTreeNode::updateHash() void SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format) { - assert((format == snfPREFIX) || (format == snfWIRE)); + assert((format == snfPREFIX) || (format == snfWIRE) || (format == snfHASH)); if (mType == tnERROR) throw std::runtime_error("invalid I node type"); - if (mType == tnINNER) + if (format == snfHASH) + { + s.add256(getNodeHash()); + } + else if (mType == tnINNER) { assert(!isEmpty()); if (format == snfPREFIX) @@ -446,7 +465,7 @@ void SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format) assert(false); } -bool SHAMapTreeNode::setItem(const SHAMapItem::pointer& i, TNType type) +bool SHAMapTreeNode::setItem(SHAMapItem::ref i, TNType type) { uint256 hash = getNodeHash(); mType = type; diff --git a/src/SHAMapSync.cpp b/src/SHAMapSync.cpp index 028a5db1c..b8202fb96 100644 --- a/src/SHAMapSync.cpp +++ b/src/SHAMapSync.cpp @@ -131,7 +131,7 @@ bool SHAMap::getRootNode(Serializer& s, SHANodeFormat format) return true; } -bool SHAMap::addRootNode(const std::vector& rootNode, SHANodeFormat format) +bool SHAMap::addRootNode(const std::vector& rootNode, SHANodeFormat format, SHAMapSyncFilter* filter) { boost::recursive_mutex::scoped_lock sl(mLock); @@ -154,16 +154,23 @@ bool SHAMap::addRootNode(const std::vector& rootNode, SHANodeForm root = node; mTNByID[*root] = root; - if (!root->getNodeHash()) + if (root->getNodeHash().isZero()) { root->setFullBelow(); clearSynching(); } + else if (filter) + { + Serializer s; + root->addRaw(s, snfPREFIX); + filter->gotNode(*root, root->getNodeHash(), s.peekData(), root->getType()); + } return true; } -bool SHAMap::addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format) +bool SHAMap::addRootNode(const uint256& hash, const std::vector& rootNode, SHANodeFormat format, + SHAMapSyncFilter* filter) { boost::recursive_mutex::scoped_lock sl(mLock); @@ -184,11 +191,17 @@ bool SHAMap::addRootNode(const uint256& hash, const std::vector& returnNode(root, true); root = node; mTNByID[*root] = root; - if (!root->getNodeHash()) + if (root->getNodeHash().isZero()) { root->setFullBelow(); clearSynching(); } + else if (filter) + { + Serializer s; + root->addRaw(s, snfPREFIX); + filter->gotNode(*root, root->getNodeHash(), s.peekData(), root->getType()); + } return true; } @@ -216,7 +229,7 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectorisLeaf() || (iNode->getDepth() == node.getDepth())) + if (iNode->isLeaf() || (iNode->getDepth() >= node.getDepth())) { cLog(lsTRACE) << "got inner node, already had it (late)"; return true; @@ -247,7 +260,7 @@ bool SHAMap::addKnownNode(const SHAMapNode& node, const std::vectoraddRaw(s, snfPREFIX); - filter->gotNode(node, hash, s.peekData(), newNode->isLeaf()); + filter->gotNode(node, hash, s.peekData(), newNode->getType()); } mTNByID[*newNode] = newNode; @@ -458,7 +471,7 @@ BOOST_AUTO_TEST_CASE( SHAMapSync_test ) cLog(lsFATAL) << "Didn't get root node " << gotNodes.size(); BOOST_FAIL("NodeSize"); } - if (!destination.addRootNode(*gotNodes.begin(), snfWIRE)) + if (!destination.addRootNode(*gotNodes.begin(), snfWIRE, NULL)) { cLog(lsFATAL) << "AddRootNode fails"; BOOST_FAIL("AddRootNode"); diff --git a/src/SHAMapSync.h b/src/SHAMapSync.h index 3d673e37a..1431ccc12 100644 --- a/src/SHAMapSync.h +++ b/src/SHAMapSync.h @@ -12,7 +12,7 @@ class ConsensusTransSetSF : public SHAMapSyncFilter public: ConsensusTransSetSF() { ; } virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash, - const std::vector& nodeData, bool isLeaf) + const std::vector& nodeData, SHAMapTreeNode::TNType) { // WRITEME: If 'isLeaf' is true, this is a transaction theApp->getTempNodeCache().store(nodeHash, nodeData); @@ -35,7 +35,7 @@ public: { ; } virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash, - const std::vector& nodeData, bool isLeaf) + const std::vector& nodeData, SHAMapTreeNode::TNType) { theApp->getHashedObjectStore().store(hotACCOUNT_NODE, mLedgerSeq, nodeData, nodeHash); } @@ -56,10 +56,11 @@ public: { ; } virtual void gotNode(const SHAMapNode& id, const uint256& nodeHash, - const std::vector& nodeData, bool isLeaf) + const std::vector& nodeData, SHAMapTreeNode::TNType type) { - theApp->getHashedObjectStore().store(isLeaf ? hotTRANSACTION : hotTRANSACTION_NODE, mLedgerSeq, - nodeData, nodeHash); + theApp->getHashedObjectStore().store( + (type == SHAMapTreeNode::tnTRANSACTION_NM) ? hotTRANSACTION : hotTRANSACTION_NODE, + mLedgerSeq, nodeData, nodeHash); } virtual bool haveNode(const SHAMapNode& id, const uint256& nodeHash, std::vector& nodeData) { // fetchNodeExternal already tried diff --git a/src/SNTPClient.cpp b/src/SNTPClient.cpp index cdafc0698..d9663d134 100644 --- a/src/SNTPClient.cpp +++ b/src/SNTPClient.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -200,9 +201,8 @@ void SNTPClient::init(const std::vector& servers) Log(lsINFO) << "SNTP: no server specified"; return; } - do - addServer(*it++); - while (it != servers.end()); + BOOST_FOREACH(const std::string& it, servers) + addServer(it); queryAll(); } diff --git a/src/SerializeProto.h b/src/SerializeProto.h index f7d46a50f..2fa8dbf00 100644 --- a/src/SerializeProto.h +++ b/src/SerializeProto.h @@ -35,7 +35,7 @@ FIELD(Flags, UINT32, 2) FIELD(SourceTag, UINT32, 3) FIELD(Sequence, UINT32, 4) - FIELD(LastTxnSeq, UINT32, 5) + FIELD(PreviousTxnLgrSeq, UINT32, 5) FIELD(LedgerSequence, UINT32, 6) FIELD(CloseTime, UINT32, 7) FIELD(ParentCloseTime, UINT32, 8) @@ -71,7 +71,7 @@ FIELD(ParentHash, HASH256, 2) FIELD(TransactionHash, HASH256, 3) FIELD(AccountHash, HASH256, 4) - FIELD(LastTxnID, HASH256, 5) + FIELD(PreviousTxnID, HASH256, 5) FIELD(LedgerIndex, HASH256, 6) FIELD(WalletLocator, HASH256, 7) FIELD(PublishHash, HASH256, 8) @@ -95,12 +95,6 @@ // currency amount (uncommon) FIELD(MinimumOffer, AMOUNT, 16) FIELD(RippleEscrow, AMOUNT, 17) - FIELD(PreviousBalance, AMOUNT, 18) - FIELD(FinalBalance, AMOUNT, 19) - FIELD(PreviousTakerPays, AMOUNT, 20) - FIELD(PreviousTakerGets, AMOUNT, 21) - FIELD(FinalTakerPays, AMOUNT, 22) - FIELD(FinalTakerGets, AMOUNT, 23) // variable length FIELD(PublicKey, VL, 1) @@ -123,11 +117,6 @@ FIELD(Target, ACCOUNT, 7) FIELD(AuthorizedKey, ACCOUNT, 8) - // account (uncommon) - FIELD(PreviousAccount, ACCOUNT, 16) - FIELD(LowID, ACCOUNT, 17) - FIELD(HighID, ACCOUNT, 18) - // path set FIELD(Paths, PATHSET, 1) @@ -141,6 +130,8 @@ FIELD(CreatedNode, OBJECT, 3) FIELD(DeletedNode, OBJECT, 4) FIELD(ModifiedNode, OBJECT, 5) + FIELD(PreviousFields, OBJECT, 6) + FIELD(FinalFields, OBJECT, 7) // array of objects // ARRAY/1 is reserved for end of array @@ -151,3 +142,5 @@ FIELD(Template, ARRAY, 5) FIELD(Necessary, ARRAY, 6) FIELD(Sufficient, ARRAY, 7) + +// vim:ts=4 diff --git a/src/SerializedLedger.cpp b/src/SerializedLedger.cpp index 4ffecf231..359a93fa5 100644 --- a/src/SerializedLedger.cpp +++ b/src/SerializedLedger.cpp @@ -75,37 +75,37 @@ Json::Value SerializedLedgerEntry::getJson(int options) const bool SerializedLedgerEntry::isThreadedType() { - return getFieldIndex(sfLastTxnID) != -1; + return getFieldIndex(sfPreviousTxnID) != -1; } bool SerializedLedgerEntry::isThreaded() { - return isFieldPresent(sfLastTxnID); + return isFieldPresent(sfPreviousTxnID); } uint256 SerializedLedgerEntry::getThreadedTransaction() { - return getFieldH256(sfLastTxnID); + return getFieldH256(sfPreviousTxnID); } uint32 SerializedLedgerEntry::getThreadedLedger() { - return getFieldU32(sfLastTxnSeq); + return getFieldU32(sfPreviousTxnLgrSeq); } bool SerializedLedgerEntry::thread(const uint256& txID, uint32 ledgerSeq, uint256& prevTxID, uint32& prevLedgerID) { - uint256 oldPrevTxID = getFieldH256(sfLastTxnID); + uint256 oldPrevTxID = getFieldH256(sfPreviousTxnID); Log(lsTRACE) << "Thread Tx:" << txID << " prev:" << oldPrevTxID; if (oldPrevTxID == txID) { // this transaction is already threaded - assert(getFieldU32(sfLastTxnSeq) == ledgerSeq); + assert(getFieldU32(sfPreviousTxnLgrSeq) == ledgerSeq); return false; } prevTxID = oldPrevTxID; - prevLedgerID = getFieldU32(sfLastTxnSeq); - setFieldH256(sfLastTxnID, txID); - setFieldU32(sfLastTxnSeq, ledgerSeq); + prevLedgerID = getFieldU32(sfPreviousTxnLgrSeq); + setFieldH256(sfPreviousTxnID, txID); + setFieldU32(sfPreviousTxnLgrSeq, ledgerSeq); return true; } @@ -119,19 +119,19 @@ bool SerializedLedgerEntry::hasTwoOwners() return mType == ltRIPPLE_STATE; } -NewcoinAddress SerializedLedgerEntry::getOwner() +RippleAddress SerializedLedgerEntry::getOwner() { return getFieldAccount(sfAccount); } -NewcoinAddress SerializedLedgerEntry::getFirstOwner() +RippleAddress SerializedLedgerEntry::getFirstOwner() { - return NewcoinAddress::createAccountID(getFieldAmount(sfLowLimit).getIssuer()); + return RippleAddress::createAccountID(getFieldAmount(sfLowLimit).getIssuer()); } -NewcoinAddress SerializedLedgerEntry::getSecondOwner() +RippleAddress SerializedLedgerEntry::getSecondOwner() { - return NewcoinAddress::createAccountID(getFieldAmount(sfHighLimit).getIssuer()); + return RippleAddress::createAccountID(getFieldAmount(sfHighLimit).getIssuer()); } std::vector SerializedLedgerEntry::getOwners() diff --git a/src/SerializedLedger.h b/src/SerializedLedger.h index fb5bad0dc..c02126db1 100644 --- a/src/SerializedLedger.h +++ b/src/SerializedLedger.h @@ -3,7 +3,7 @@ #include "SerializedObject.h" #include "LedgerFormats.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" class SerializedLedgerEntry : public STObject { @@ -39,9 +39,9 @@ public: bool isThreaded(); // is this ledger entry actually threaded bool hasOneOwner(); // This node has one other node that owns it (like nickname) bool hasTwoOwners(); // This node has two nodes that own it (like ripple balance) - NewcoinAddress getOwner(); - NewcoinAddress getFirstOwner(); - NewcoinAddress getSecondOwner(); + RippleAddress getOwner(); + RippleAddress getFirstOwner(); + RippleAddress getSecondOwner(); uint256 getThreadedTransaction(); uint32 getThreadedLedger(); bool thread(const uint256& txID, uint32 ledgerSeq, uint256& prevTxID, uint32& prevLedgerID); diff --git a/src/SerializedObject.cpp b/src/SerializedObject.cpp index d6bce836c..1477a2393 100644 --- a/src/SerializedObject.cpp +++ b/src/SerializedObject.cpp @@ -164,7 +164,8 @@ bool STObject::setType(const std::vector &type) { if (elem->flags != SOE_OPTIONAL) { - cLog(lsWARNING) << "setType !valid missing " << elem->e_field.fieldName; + cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid missing " + << elem->e_field.fieldName; valid = false; } newData.push_back(makeNonPresentObject(elem->e_field)); @@ -178,7 +179,8 @@ bool STObject::setType(const std::vector &type) { if (!t.getFName().isDiscardable()) { - cLog(lsWARNING) << "setType !valid leftover: " << t.getFName().getName(); + cLog(lsWARNING) << "setType( " << getFName().getName() << ") invalid leftover " + << t.getFName().getName(); valid = false; } } @@ -204,6 +206,8 @@ bool STObject::isValidForType() bool STObject::isFieldAllowed(SField::ref field) { + if (isFree()) + return true; BOOST_FOREACH(SOElement::ptr elem, mType) { // are any required elemnents missing if (elem->e_field == field) @@ -240,6 +244,14 @@ std::auto_ptr STObject::deserialize(SerializerIterator& sit, SFi return object; } +bool STObject::hasMatchingEntry(const SerializedType& t) +{ + const SerializedType* o = peekAtPField(t.getFName()); + if (!o) + return false; + return t == *o; +} + std::string STObject::getFullText() const { std::string ret; @@ -563,7 +575,8 @@ uint160 STObject::getFieldH160(SField::ref field) const uint256 STObject::getFieldH256(SField::ref field) const { const SerializedType* rf = peekAtPField(field); - if (!rf) throw std::runtime_error("Field not found"); + if (!rf) + throw std::runtime_error("Field not found"); SerializedTypeID id = rf->getSType(); if (id == STI_NOTPRESENT) return uint256(); // optional field not present const STHash256 *cf = dynamic_cast(rf); @@ -571,21 +584,16 @@ uint256 STObject::getFieldH256(SField::ref field) const return cf->getValue(); } -NewcoinAddress STObject::getFieldAccount(SField::ref field) const +RippleAddress STObject::getFieldAccount(SField::ref field) const { const SerializedType* rf = peekAtPField(field); if (!rf) - { -#ifdef DEBUG - std::cerr << "Account field not found" << std::endl; - std::cerr << getFullText() << std::endl; -#endif throw std::runtime_error("Field not found"); - } SerializedTypeID id = rf->getSType(); - if (id == STI_NOTPRESENT) return NewcoinAddress(); // optional field not present + if (id == STI_NOTPRESENT) return RippleAddress(); // optional field not present const STAccount* cf = dynamic_cast(rf); - if (!cf) throw std::runtime_error("Wrong field type"); + if (!cf) + throw std::runtime_error("Wrong field type"); return cf->getValueNCA(); } @@ -594,18 +602,13 @@ uint160 STObject::getFieldAccount160(SField::ref field) const uint160 a; const SerializedType* rf = peekAtPField(field); if (!rf) - { -#ifdef DEBUG - std::cerr << "Account field not found" << std::endl; - std::cerr << getFullText() << std::endl; -#endif throw std::runtime_error("Field not found"); - } SerializedTypeID id = rf->getSType(); if (id != STI_NOTPRESENT) { const STAccount* cf = dynamic_cast(rf); - if (!cf) throw std::runtime_error("Wrong field type"); + if (!cf) + throw std::runtime_error("Wrong field type"); cf->getValueH160(a); } return a; @@ -792,6 +795,42 @@ Json::Value STObject::getJson(int options) const return ret; } +bool STObject::operator==(const STObject& obj) const +{ // This is not particularly efficient, and only compares data elements with binary representations + int matches = 0; + BOOST_FOREACH(const SerializedType& t, mData) + if ((t.getSType() != STI_NOTPRESENT) && t.getFName().isBinary()) + { // each present field must have a matching field + bool match = false; + BOOST_FOREACH(const SerializedType& t2, obj.mData) + if (t.getFName() == t2.getFName()) + { + if (t2 != t) + return false; + match = true; + ++matches; + break; + } + if (!match) + { + Log(lsTRACE) << "STObject::operator==: no match for " << t.getFName().getName(); + return false; + } + } + + int fields = 0; + BOOST_FOREACH(const SerializedType& t2, obj.mData) + if ((t2.getSType() != STI_NOTPRESENT) && t2.getFName().isBinary()) + ++fields; + + if (fields != matches) + { + Log(lsTRACE) << "STObject::operator==: " << fields << " fields, " << matches << " matches"; + return false; + } + return true; +} + Json::Value STVector256::getJson(int options) const { Json::Value ret(Json::arrayValue); @@ -1096,7 +1135,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r if (value.size() == 40) // 160-bit hex account value uAccount.SetHex(strValue); { - NewcoinAddress a; + RippleAddress a; if (!a.setAccountPublic(strValue)) throw std::runtime_error("Account in path element invalid"); uAccount = a.getAccountID(); @@ -1120,7 +1159,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r uIssuer.SetHex(issuer.asString()); else { - NewcoinAddress a; + RippleAddress a; if (!a.setAccountPublic(issuer.asString())) throw std::runtime_error("path element issuer invalid"); uIssuer = a.getAccountID(); @@ -1146,7 +1185,7 @@ std::auto_ptr STObject::parseJson(const Json::Value& object, SField::r } else { // ripple address - NewcoinAddress a; + RippleAddress a; if (!a.setAccountID(strValue)) { cLog(lsINFO) << "Invalid acccount JSON: " << fieldName << ": " << strValue; @@ -1246,7 +1285,6 @@ BOOST_AUTO_TEST_CASE( FieldManipulation_test ) if (object1.getFieldVL(sfTestVL) != j) BOOST_FAIL("STObject error"); if (object3.getFieldVL(sfTestVL) != j) BOOST_FAIL("STObject error"); } - } BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/SerializedObject.h b/src/SerializedObject.h index 69683ed49..00c89ed1e 100644 --- a/src/SerializedObject.h +++ b/src/SerializedObject.h @@ -58,6 +58,7 @@ public: virtual SerializedTypeID getSType() const { return STI_OBJECT; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mData.empty(); } virtual void add(Serializer& s) const { add(s, true); } // just inner elements void add(Serializer& s, bool withSignature) const; @@ -108,7 +109,7 @@ public: uint128 getFieldH128(SField::ref field) const; uint160 getFieldH160(SField::ref field) const; uint256 getFieldH256(SField::ref field) const; - NewcoinAddress getFieldAccount(SField::ref field) const; + RippleAddress getFieldAccount(SField::ref field) const; uint160 getFieldAccount160(SField::ref field) const; std::vector getFieldVL(SField::ref field) const; std::vector getFieldTL(SField::ref field) const; @@ -126,7 +127,7 @@ public: void setFieldVL(SField::ref field, const std::vector&); void setFieldTL(SField::ref field, const std::vector&); void setFieldAccount(SField::ref field, const uint160&); - void setFieldAccount(SField::ref field, const NewcoinAddress& addr) + void setFieldAccount(SField::ref field, const RippleAddress& addr) { setFieldAccount(field, addr.getAccountID()); } void setFieldAmount(SField::ref field, const STAmount&); void setFieldPathSet(SField::ref field, const STPathSet&); @@ -148,8 +149,32 @@ public: { return makeDefaultObject(STI_NOTPRESENT, name); } static std::auto_ptr makeDefaultObject(SField::ref name) { return makeDefaultObject(name.fieldType, name); } + + // field iterator stuff + typedef boost::ptr_vector::iterator iterator; + typedef boost::ptr_vector::const_iterator const_iterator; + iterator begin() { return mData.begin(); } + iterator end() { return mData.end(); } + const_iterator begin() const { return mData.begin(); } + const_iterator end() const { return mData.end(); } + bool empty() const { return mData.empty(); } + + bool hasMatchingEntry(const SerializedType&); + + bool operator==(const STObject& o) const; + bool operator!=(const STObject& o) const { return ! (*this == o); } }; +inline STObject::iterator range_begin(STObject& x) { return x.begin(); } +inline STObject::iterator range_end(STObject &x) { return x.end(); } +namespace boost +{ + template<> struct range_mutable_iterator { typedef STObject::iterator type; }; + template<> struct range_const_iterator { typedef STObject::const_iterator type; }; +} + + + class STArray : public SerializedType { public: @@ -170,7 +195,9 @@ protected: public: STArray() { ; } + STArray(int n) { value.reserve(n); } STArray(SField::ref f) : SerializedType(f) { ; } + STArray(SField::ref f, int n) : SerializedType(f) { value.reserve(n); } STArray(SField::ref f, const vector& v) : SerializedType(f), value(v) { ; } STArray(vector& v) : value(v) { ; } @@ -215,7 +242,16 @@ public: virtual SerializedTypeID getSType() const { return STI_ARRAY; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } }; +inline STArray::iterator range_begin(STArray& x) { return x.begin(); } +inline STArray::iterator range_end(STArray &x) { return x.end(); } +namespace boost +{ + template<> struct range_mutable_iterator { typedef STArray::iterator type; }; + template<> struct range_const_iterator { typedef STArray::const_iterator type; }; +} + #endif // vim:ts=4 diff --git a/src/SerializedTransaction.cpp b/src/SerializedTransaction.cpp index dbe3ffbf2..d6b7a0291 100644 --- a/src/SerializedTransaction.cpp +++ b/src/SerializedTransaction.cpp @@ -25,7 +25,6 @@ SerializedTransaction::SerializedTransaction(const STObject& object) : STObject( throw std::runtime_error("invalid transaction type"); if (!setType(mFormat->elements)) { - assert(false); throw std::runtime_error("transaction not valid"); } } @@ -67,9 +66,9 @@ std::string SerializedTransaction::getText() const return STObject::getText(); } -std::vector SerializedTransaction::getAffectedAccounts() const +std::vector SerializedTransaction::getAffectedAccounts() const { - std::vector accounts; + std::vector accounts; BOOST_FOREACH(const SerializedType& it, peekData()) { @@ -77,11 +76,10 @@ std::vector SerializedTransaction::getAffectedAccounts() const if (sa != NULL) { bool found = false; - NewcoinAddress na = sa->getValueNCA(); - for (std::vector::iterator it = accounts.begin(), end = accounts.end(); - it != end; ++it) + RippleAddress na = sa->getValueNCA(); + BOOST_FOREACH(const RippleAddress& it, accounts) { - if (*it == na) + if (it == na) { found = true; break; @@ -116,7 +114,7 @@ std::vector SerializedTransaction::getSignature() const } } -void SerializedTransaction::sign(const NewcoinAddress& naAccountPrivate) +void SerializedTransaction::sign(const RippleAddress& naAccountPrivate) { std::vector signature; naAccountPrivate.accountPrivateSign(getSigningHash(), signature); @@ -127,7 +125,7 @@ bool SerializedTransaction::checkSign() const { try { - NewcoinAddress n; + RippleAddress n; n.setAccountPublic(getFieldVL(sfSigningPubKey)); return checkSign(n); } @@ -137,7 +135,7 @@ bool SerializedTransaction::checkSign() const } } -bool SerializedTransaction::checkSign(const NewcoinAddress& naAccountPublic) const +bool SerializedTransaction::checkSign(const RippleAddress& naAccountPublic) const { try { @@ -149,12 +147,12 @@ bool SerializedTransaction::checkSign(const NewcoinAddress& naAccountPublic) con } } -void SerializedTransaction::setSigningPubKey(const NewcoinAddress& naSignPubKey) +void SerializedTransaction::setSigningPubKey(const RippleAddress& naSignPubKey) { setFieldVL(sfSigningPubKey, naSignPubKey.getAccountPublic()); } -void SerializedTransaction::setSourceAccount(const NewcoinAddress& naSource) +void SerializedTransaction::setSourceAccount(const RippleAddress& naSource) { setFieldAccount(sfAccount, naSource); } @@ -200,11 +198,11 @@ BOOST_AUTO_TEST_SUITE(SerializedTransactionTS) BOOST_AUTO_TEST_CASE( STrans_test ) { - NewcoinAddress seed; + RippleAddress seed; seed.setSeedRandom(); - NewcoinAddress generator = NewcoinAddress::createGeneratorPublic(seed); - NewcoinAddress publicAcct = NewcoinAddress::createAccountPublic(generator, 1); - NewcoinAddress privateAcct = NewcoinAddress::createAccountPrivate(generator, seed, 1); + RippleAddress generator = RippleAddress::createGeneratorPublic(seed); + RippleAddress publicAcct = RippleAddress::createAccountPublic(generator, 1); + RippleAddress privateAcct = RippleAddress::createAccountPrivate(generator, seed, 1); SerializedTransaction j(ttCLAIM); j.setSourceAccount(publicAcct); @@ -224,10 +222,15 @@ BOOST_AUTO_TEST_CASE( STrans_test ) Log(lsFATAL) << copy.getJson(0); BOOST_FAIL("Transaction fails serialize/deserialize test"); } - Log(lsINFO) << "ORIG: " << j.getJson(0); 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) << "BUILT " << new_obj->getJson(0); + + if (STObject(j) != *new_obj) + { + Log(lsINFO) << "ORIG: " << j.getJson(0); + Log(lsINFO) << "BUILT " << new_obj->getJson(0); + BOOST_FAIL("Built a different transaction"); + } } BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/SerializedTransaction.h b/src/SerializedTransaction.h index e049a455d..185ca6cda 100644 --- a/src/SerializedTransaction.h +++ b/src/SerializedTransaction.h @@ -8,7 +8,7 @@ #include "uint256.h" #include "SerializedObject.h" #include "TransactionFormats.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #define TXN_SQL_NEW 'N' #define TXN_SQL_CONFLICT 'C' @@ -20,7 +20,8 @@ class SerializedTransaction : public STObject { public: - typedef boost::shared_ptr pointer; + typedef boost::shared_ptr pointer; + typedef const boost::shared_ptr& ref; protected: TransactionType mType; @@ -47,23 +48,23 @@ public: STAmount getTransactionFee() const { return getFieldAmount(sfFee); } void setTransactionFee(const STAmount& fee) { setFieldAmount(sfFee, fee); } - NewcoinAddress getSourceAccount() const { return getFieldAccount(sfAccount); } + RippleAddress getSourceAccount() const { return getFieldAccount(sfAccount); } std::vector getSigningPubKey() const { return getFieldVL(sfSigningPubKey); } - void setSigningPubKey(const NewcoinAddress& naSignPubKey); - void setSourceAccount(const NewcoinAddress& naSource); + void setSigningPubKey(const RippleAddress& naSignPubKey); + void setSourceAccount(const RippleAddress& naSource); std::string getTransactionType() const { return mFormat->t_name; } uint32 getSequence() const { return getFieldU32(sfSequence); } void setSequence(uint32 seq) { return setFieldU32(sfSequence, seq); } - std::vector getAffectedAccounts() const; + std::vector getAffectedAccounts() const; uint256 getTransactionID() const; virtual Json::Value getJson(int options) const; - void sign(const NewcoinAddress& naAccountPrivate); - bool checkSign(const NewcoinAddress& naAccountPublic) const; + void sign(const RippleAddress& naAccountPrivate); + bool checkSign(const RippleAddress& naAccountPublic) const; bool checkSign() const; // SQL Functions diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index 73d2e440f..55fc7039b 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -8,9 +8,9 @@ #include "LedgerFormats.h" #include "FieldNames.h" #include "Log.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "utils.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "TransactionErr.h" SETUP_LOG(); @@ -23,7 +23,7 @@ void STPathSet::printDebug() { std::cout << i << ": "; for (int j = 0; j < value[i].mPath.size(); j++) { //STPathElement pe = value[i].mPath[j]; - NewcoinAddress nad; + RippleAddress nad; nad.setAccountID(value[i].mPath[j].mAccountID); std::cout << " " << nad.humanAccountID(); //std::cout << " " << pe.mAccountID.GetHex(); @@ -36,7 +36,7 @@ void STPathSet::printDebug() { void STPath::printDebug() { std::cout << "STPath:" << std::endl; for(int i =0; i < mPath.size(); i++) { - NewcoinAddress nad; + RippleAddress nad; nad.setAccountID(mPath[i].mAccountID); std::cout << " " << i << ": " << nad.humanAccountID() << std::endl; } @@ -251,7 +251,7 @@ bool STVariableLength::isEquivalent(const SerializedType& t) const std::string STAccount::getText() const { uint160 u; - NewcoinAddress a; + RippleAddress a; if (!getValueH160(u)) return STVariableLength::getText(); @@ -329,16 +329,16 @@ bool STAccount::getValueH160(uint160& v) const return true; } -NewcoinAddress STAccount::getValueNCA() const +RippleAddress STAccount::getValueNCA() const { - NewcoinAddress a; + RippleAddress a; uint160 v; if (getValueH160(v)) a.setAccountID(v); return a; } -void STAccount::setValueNCA(const NewcoinAddress& nca) +void STAccount::setValueNCA(const RippleAddress& nca) { setValueH160(nca.getAccountID()); } @@ -453,13 +453,13 @@ Json::Value STPath::getJson(int) const elem["type_hex"] = strHex(iType); if (iType & STPathElement::typeAccount) - elem["account"] = NewcoinAddress::createHumanAccountID(it.getAccountID()); + elem["account"] = RippleAddress::createHumanAccountID(it.getAccountID()); if (iType & STPathElement::typeCurrency) elem["currency"] = STAmount::createHumanCurrency(it.getCurrency()); if (iType & STPathElement::typeIssuer) - elem["issuer"] = NewcoinAddress::createHumanAccountID(it.getIssuerID()); + elem["issuer"] = RippleAddress::createHumanAccountID(it.getIssuerID()); ret.append(elem); } @@ -490,7 +490,7 @@ std::string STPath::getText() const { case STPathElement::typeAccount: { - ret += NewcoinAddress::createHumanAccountID(it.getNode()); + ret += RippleAddress::createHumanAccountID(it.getNode()); break; } diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index d7df1d1b7..783411059 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -36,13 +36,11 @@ protected: SField::ptr fName; virtual SerializedType* duplicate() const { return new SerializedType(*fName); } - SerializedType(SField::ptr n) : fName(n) { assert(fName); } public: SerializedType() : fName(&sfGeneric) { ; } SerializedType(SField::ref n) : fName(&n) { assert(fName); } - SerializedType(const SerializedType& n) : fName(n.fName) { ; } virtual ~SerializedType() { ; } static std::auto_ptr deserialize(SField::ref name) @@ -74,6 +72,8 @@ public: { return (getSType() == t.getSType()) && isEquivalent(t); } bool operator!=(const SerializedType& t) const { return (getSType() != t.getSType()) || !isEquivalent(t); } + + virtual bool isDefault() const { return true; } }; inline SerializedType* new_clone(const SerializedType& s) { return s.clone().release(); } @@ -105,6 +105,7 @@ public: operator unsigned char() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt16 : public SerializedType @@ -132,6 +133,7 @@ public: operator uint16() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt32 : public SerializedType @@ -159,6 +161,7 @@ public: operator uint32() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STUInt64 : public SerializedType @@ -186,6 +189,7 @@ public: operator uint64() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value == 0; } }; class STAmount : public SerializedType @@ -297,6 +301,7 @@ public: void setValue(const STAmount &); virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mValue == 0 && mIssuer.isZero() && mCurrency.isZero(); } bool operator==(const STAmount&) const; bool operator!=(const STAmount&) const; @@ -400,6 +405,7 @@ public: operator uint128() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STHash160 : public SerializedType @@ -430,6 +436,7 @@ public: operator uint160() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STHash256 : public SerializedType @@ -460,6 +467,7 @@ public: operator uint256() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.isZero(); } }; class STVariableLength : public SerializedType @@ -491,6 +499,7 @@ public: operator std::vector() const { return value; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } }; class STAccount : public STVariableLength @@ -512,8 +521,8 @@ public: SerializedTypeID getSType() const { return STI_ACCOUNT; } std::string getText() const; - NewcoinAddress getValueNCA() const; - void setValueNCA(const NewcoinAddress& nca); + RippleAddress getValueNCA() const; + void setValueNCA(const RippleAddress& nca); void setValueH160(const uint160& v); bool getValueH160(uint160&) const; @@ -670,6 +679,7 @@ public: void addPath(const STPath& e) { value.push_back(e); } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return value.empty(); } void printDebug(); @@ -737,6 +747,7 @@ public: const std::vector& peekValue() const { return mValue; } std::vector& peekValue() { return mValue; } virtual bool isEquivalent(const SerializedType& t) const; + virtual bool isDefault() const { return mValue.empty(); } std::vector getValue() const { return mValue; } bool isEmpty() const { return mValue.empty(); } diff --git a/src/SerializedValidation.cpp b/src/SerializedValidation.cpp index f95866a51..313577a2c 100644 --- a/src/SerializedValidation.cpp +++ b/src/SerializedValidation.cpp @@ -27,7 +27,7 @@ const uint32 SerializedValidation::sFullFlag = 0x1; SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature) : STObject(sValidationFormat, sit, sfValidation), mTrusted(false) { - mNodeID = NewcoinAddress::createNodePublic(getFieldVL(sfSigningPubKey)).getNodeID(); + mNodeID = RippleAddress::createNodePublic(getFieldVL(sfSigningPubKey)).getNodeID(); assert(mNodeID.isNonZero()); if (checkSignature && !isValid()) { @@ -37,26 +37,25 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi } SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 signTime, - const NewcoinAddress& naSeed, bool isFull) + const RippleAddress& naPub, const RippleAddress& naPriv, bool isFull, uint256& signingHash) : STObject(sValidationFormat, sfValidation), mTrusted(false) { setFieldH256(sfLedgerHash, ledgerHash); setFieldU32(sfSigningTime, signTime); - if (naSeed.isValid()) - { - NewcoinAddress np = NewcoinAddress::createNodePublic(naSeed); - setFieldVL(sfSigningPubKey, np.getNodePublic()); - mNodeID = np.getNodeID(); - assert(mNodeID.isNonZero()); - } + + setFieldVL(sfSigningPubKey, naPub.getNodePublic()); + mNodeID = naPub.getNodeID(); + assert(mNodeID.isNonZero()); + if (!isFull) setFlag(sFullFlag); + signingHash = getSigningHash(); std::vector signature; - NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), signature); + naPriv.signNodePrivate(signingHash, signature); setFieldVL(sfSignature, signature); // XXX Check if this can fail. - // if (!NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue())) + // if (!RippleAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue())) // throw std::runtime_error("Unable to sign validation"); } @@ -89,7 +88,7 @@ bool SerializedValidation::isValid(const uint256& signingHash) const { try { - NewcoinAddress naPublicKey = NewcoinAddress::createNodePublic(getFieldVL(sfSigningPubKey)); + RippleAddress naPublicKey = RippleAddress::createNodePublic(getFieldVL(sfSigningPubKey)); return naPublicKey.isValid() && naPublicKey.verifyNodePublic(signingHash, getFieldVL(sfSignature)); } catch (...) @@ -99,9 +98,9 @@ bool SerializedValidation::isValid(const uint256& signingHash) const } } -NewcoinAddress SerializedValidation::getSignerPublic() const +RippleAddress SerializedValidation::getSignerPublic() const { - NewcoinAddress a; + RippleAddress a; a.setNodePublic(getFieldVL(sfSigningPubKey)); return a; } diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index 9ce8fb6a2..eae4dffad 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -2,7 +2,7 @@ #define __VALIDATION__ #include "SerializedObject.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" class SerializedValidation : public STObject { @@ -21,12 +21,13 @@ public: // These throw if the object is not valid SerializedValidation(SerializerIterator& sit, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull); + SerializedValidation(const uint256& ledgerHash, uint32 signTime, const RippleAddress& naPub, + const RippleAddress& naPriv, bool isFull, uint256& signingHash); uint256 getLedgerHash() const; uint32 getSignTime() const; uint32 getFlags() const; - NewcoinAddress getSignerPublic() const; + RippleAddress getSignerPublic() const; uint160 getNodeID() const { return mNodeID; } bool isValid() const; bool isFull() const; diff --git a/src/Serializer.cpp b/src/Serializer.cpp index efce6138c..8b67d3b65 100644 --- a/src/Serializer.cpp +++ b/src/Serializer.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "key.h" #include "Log.h" @@ -376,11 +377,13 @@ int Serializer::addTaggedList(const std::list& list) if (size > 255) return -1; int ret = add8(size); if (size != 0) - for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) + { + BOOST_FOREACH(const TaggedListItem& it, list) { - add8(it->first); - addVL(it->second); + add8(it.first); + addVL(it.second); } + } return ret; } @@ -390,11 +393,13 @@ int Serializer::addTaggedList(const std::vector& list) if (size > 255) return -1; int ret = add8(size); if (size != 0) - for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) + { + BOOST_FOREACH(const TaggedListItem& it, list) { - add8(it->first); - addVL(it->second); + add8(it.first); + addVL(it.second); } + } return ret; } @@ -404,8 +409,10 @@ int Serializer::getTaggedListLength(const std::list& list) if (size > 255) return -1; int ret = 1; if (size != 0) - for (std::list::const_iterator it = list.begin(); it != list.end(); ++it) - ret += 1 + it->second.size() + Serializer::encodeLengthLength(it->second.size()); + { + BOOST_FOREACH(const TaggedListItem& it, list) + ret += 1 + it.second.size() + Serializer::encodeLengthLength(it.second.size()); + } return ret; } @@ -415,8 +422,10 @@ int Serializer::getTaggedListLength(const std::vector& list) if (size > 255) return -1; int ret = 1; if (size != 0) - for (std::vector::const_iterator it = list.begin(); it != list.end(); ++it) - ret += 1 + it->second.size() + Serializer::encodeLengthLength(it->second.size()); + { + BOOST_FOREACH(const TaggedListItem& it, list) + ret += 1 + it.second.size() + Serializer::encodeLengthLength(it.second.size()); + } return ret; } diff --git a/src/Transaction.cpp b/src/Transaction.cpp index 6ef87abaf..2fca62742 100644 --- a/src/Transaction.cpp +++ b/src/Transaction.cpp @@ -13,7 +13,7 @@ #include "SerializedTransaction.h" #include "Log.h" -Transaction::Transaction(const SerializedTransaction::pointer& sit, bool bValidate) +Transaction::Transaction(SerializedTransaction::ref sit, bool bValidate) : mInLedger(0), mStatus(INVALID), mResult(temUNCERTAIN), mTransaction(sit) { try @@ -55,8 +55,8 @@ Transaction::pointer Transaction::sharedTransaction(const std::vector& vucDomain, bool bTransferRate, @@ -151,8 +151,8 @@ Transaction::pointer Transaction::setAccountSet( } Transaction::pointer Transaction::sharedAccountSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -160,7 +160,7 @@ Transaction::pointer Transaction::sharedAccountSet( const uint128& uEmailHash, bool bWalletLocator, const uint256& uWalletLocator, - const NewcoinAddress& naMessagePublic, + const RippleAddress& naMessagePublic, bool bDomain, const std::vector& vucDomain, bool bTransferRate, @@ -181,7 +181,7 @@ Transaction::pointer Transaction::sharedAccountSet( // Transaction::pointer Transaction::setClaim( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature) @@ -196,7 +196,7 @@ Transaction::pointer Transaction::setClaim( } Transaction::pointer Transaction::sharedClaim( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, uint32 uSourceTag, const std::vector& vucGenerator, const std::vector& vucPubKey, @@ -216,8 +216,8 @@ Transaction::pointer Transaction::sharedClaim( // Transaction::pointer Transaction::setCreate( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naCreateAccountID, + const RippleAddress& naPrivateKey, + const RippleAddress& naCreateAccountID, const STAmount& saFund) { mTransaction->setFieldU32(sfFlags, tfCreateAccount); @@ -230,12 +230,12 @@ Transaction::pointer Transaction::setCreate( } Transaction::pointer Transaction::sharedCreate( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naCreateAccountID, + const RippleAddress& naCreateAccountID, const STAmount& saFund) { pointer tResult = boost::make_shared(ttPAYMENT, naPublicKey, naSourceAccount, uSeq, saFee, uSourceTag); @@ -248,7 +248,7 @@ Transaction::pointer Transaction::sharedCreate( // Transaction::pointer Transaction::setCreditSet( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const STAmount& saLimitAmount, bool bQualityIn, uint32 uQualityIn, @@ -269,8 +269,8 @@ Transaction::pointer Transaction::setCreditSet( } Transaction::pointer Transaction::sharedCreditSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -293,7 +293,7 @@ Transaction::pointer Transaction::sharedCreditSet( // Transaction::pointer Transaction::setNicknameSet( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const uint256& uNickname, bool bSetOffer, const STAmount& saMinimumOffer) @@ -312,8 +312,8 @@ Transaction::pointer Transaction::setNicknameSet( // --> bSetOffer: true, change offer // --> saMinimumOffer: 0 to remove. Transaction::pointer Transaction::sharedNicknameSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -331,7 +331,7 @@ Transaction::pointer Transaction::sharedNicknameSet( // Transaction::pointer Transaction::setOfferCreate( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, bool bPassive, const STAmount& saTakerPays, const STAmount& saTakerGets, @@ -352,8 +352,8 @@ Transaction::pointer Transaction::setOfferCreate( } Transaction::pointer Transaction::sharedOfferCreate( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -372,7 +372,7 @@ Transaction::pointer Transaction::sharedOfferCreate( // Transaction::pointer Transaction::setOfferCancel( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, uint32 uSequence) { mTransaction->setFieldU32(sfOfferSequence, uSequence); @@ -383,8 +383,8 @@ Transaction::pointer Transaction::setOfferCancel( } Transaction::pointer Transaction::sharedOfferCancel( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -400,8 +400,8 @@ Transaction::pointer Transaction::sharedOfferCancel( // Transaction::pointer Transaction::setPasswordFund( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naDstAccountID) + const RippleAddress& naPrivateKey, + const RippleAddress& naDstAccountID) { mTransaction->setFieldAccount(sfDestination, naDstAccountID); @@ -411,12 +411,12 @@ Transaction::pointer Transaction::setPasswordFund( } Transaction::pointer Transaction::sharedPasswordFund( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naDstAccountID) + const RippleAddress& naDstAccountID) { pointer tResult = boost::make_shared(ttPASSWORD_FUND, naPublicKey, naSourceAccount, uSeq, saFee, uSourceTag); @@ -428,8 +428,8 @@ Transaction::pointer Transaction::sharedPasswordFund( // Transaction::pointer Transaction::setPasswordSet( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naAuthKeyID, + const RippleAddress& naPrivateKey, + const RippleAddress& naAuthKeyID, const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature) @@ -445,9 +445,9 @@ Transaction::pointer Transaction::setPasswordSet( } Transaction::pointer Transaction::sharedPasswordSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, uint32 uSourceTag, - const NewcoinAddress& naAuthKeyID, + const RippleAddress& naAuthKeyID, const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature) @@ -466,8 +466,8 @@ Transaction::pointer Transaction::sharedPasswordSet( // Transaction::pointer Transaction::setPayment( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naDstAccountID, + const RippleAddress& naPrivateKey, + const RippleAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, const STPathSet& spsPaths, @@ -493,12 +493,12 @@ Transaction::pointer Transaction::setPayment( } Transaction::pointer Transaction::sharedPayment( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naDstAccountID, + const RippleAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, const STPathSet& spsPaths, @@ -515,10 +515,10 @@ Transaction::pointer Transaction::sharedPayment( // Transaction::pointer Transaction::setWalletAdd( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const STAmount& saAmount, - const NewcoinAddress& naAuthKeyID, - const NewcoinAddress& naNewPubKey, + const RippleAddress& naAuthKeyID, + const RippleAddress& naNewPubKey, const std::vector& vucSignature) { mTransaction->setFieldAmount(sfAmount, saAmount); @@ -532,14 +532,14 @@ Transaction::pointer Transaction::setWalletAdd( } Transaction::pointer Transaction::sharedWalletAdd( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, const STAmount& saAmount, - const NewcoinAddress& naAuthKeyID, - const NewcoinAddress& naNewPubKey, + const RippleAddress& naAuthKeyID, + const RippleAddress& naNewPubKey, const std::vector& vucSignature) { pointer tResult = boost::make_shared(ttWALLET_ADD, naPublicKey, naSourceAccount, uSeq, saFee, uSourceTag); @@ -593,6 +593,47 @@ bool Transaction::save() db->executeSQL(mTransaction->getSQLInsertHeader() + mTransaction->getSQL(getLedger(), status) + ";"); } +Transaction::pointer Transaction::transactionFromSQL(Database* db, bool bValidate) +{ + Serializer rawTxn; + std::string status; + uint32 inLedger; + + int txSize = 2048; + rawTxn.resize(txSize); + + db->getStr("Status", status); + inLedger = db->getInt("LedgerSeq"); + txSize = db->getBinary("RawTxn", &*rawTxn.begin(), rawTxn.getLength()); + if (txSize > rawTxn.getLength()) + { + rawTxn.resize(txSize); + db->getBinary("RawTxn", &*rawTxn.begin(), rawTxn.getLength()); + } + + rawTxn.resize(txSize); + + SerializerIterator it(rawTxn); + SerializedTransaction::pointer txn = boost::make_shared(boost::ref(it)); + Transaction::pointer tr = boost::make_shared(txn, bValidate); + + TransStatus st(INVALID); + switch (status[0]) + { + case TXN_SQL_NEW: st = NEW; break; + case TXN_SQL_CONFLICT: st = CONFLICTED; break; + case TXN_SQL_HELD: st = HELD; break; + case TXN_SQL_VALIDATED: st = COMMITTED; break; + case TXN_SQL_INCLUDED: st = INCLUDED; break; + case TXN_SQL_UNKNOWN: break; + default: assert(false); + } + tr->setStatus(st); + tr->setLedger(inLedger); + return tr; +} + +// DAVID: would you rather duplicate this code or keep the lock longer? Transaction::pointer Transaction::transactionFromSQL(const std::string& sql) { Serializer rawTxn; @@ -649,7 +690,7 @@ Transaction::pointer Transaction::load(const uint256& id) return transactionFromSQL(sql); } -Transaction::pointer Transaction::findFrom(const NewcoinAddress& fromID, uint32 seq) +Transaction::pointer Transaction::findFrom(const RippleAddress& fromID, uint32 seq) { std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE FromID='"; sql.append(fromID.humanAccountID()); @@ -664,12 +705,12 @@ bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedg std::map >& outMap) { // convert a straight SHAMap payload difference to a transaction difference table // return value: true=ledgers are valid, false=a ledger is invalid - std::map >::const_iterator it; + SHAMap::SHAMapDiff::const_iterator it; for(it = inMap.begin(); it != inMap.end(); ++it) { const uint256& id = it->first; - const SHAMapItem::pointer& first = it->second.first; - const SHAMapItem::pointer& second = it->second.second; + SHAMapItem::ref first = it->second.first; + SHAMapItem::ref second = it->second.second; Transaction::pointer firstTrans, secondTrans; if (!!first) diff --git a/src/Transaction.h b/src/Transaction.h index 7cd94aedd..8d14654e2 100644 --- a/src/Transaction.h +++ b/src/Transaction.h @@ -1,6 +1,11 @@ #ifndef __TRANSACTION__ #define __TRANSACTION__ +// +// Notes: this code contains legacy constructored sharedXYZ and setXYZ. The intent is for these functions to go away. Transactions +// should now be constructed in JSON with. Use STObject::parseJson to obtain a binary version. +// + #include #include @@ -17,6 +22,8 @@ #include "SerializedTransaction.h" #include "TransactionErr.h" +class Database; + enum TransStatus { NEW = 0, // just received / generated @@ -38,9 +45,9 @@ public: private: uint256 mTransactionID; - NewcoinAddress mAccountFrom; - NewcoinAddress mFromPubKey; // Sign transaction with this. mSignPubKey - NewcoinAddress mSourcePrivate; // Sign transaction with this. + RippleAddress mAccountFrom; + RippleAddress mFromPubKey; // Sign transaction with this. mSignPubKey + RippleAddress mSourcePrivate; // Sign transaction with this. uint32 mInLedger; TransStatus mStatus; @@ -49,12 +56,12 @@ private: SerializedTransaction::pointer mTransaction; Transaction::pointer setAccountSet( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, bool bEmailHash, const uint128& uEmailHash, bool bWalletLocator, const uint256& uWalletLocator, - const NewcoinAddress& naMessagePublic, + const RippleAddress& naMessagePublic, bool bDomain, const std::vector& vucDomain, bool bTransferRate, @@ -64,18 +71,18 @@ private: const uint32 uPublishSize); Transaction::pointer setClaim( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature); Transaction::pointer setCreate( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naCreateAccountID, + const RippleAddress& naPrivateKey, + const RippleAddress& naCreateAccountID, const STAmount& saFund); Transaction::pointer setCreditSet( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const STAmount& saLimitAmount, bool bQualityIn, uint32 uQualityIn, @@ -83,36 +90,36 @@ private: uint32 uQualityOut); Transaction::pointer setNicknameSet( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const uint256& uNickname, bool bSetOffer, const STAmount& saMinimumOffer); Transaction::pointer setOfferCreate( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, bool bPassive, const STAmount& saTakerPays, const STAmount& saTakerGets, uint32 uExpiration); Transaction::pointer setOfferCancel( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, uint32 uSequence); Transaction::pointer setPasswordFund( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naDstAccountID); + const RippleAddress& naPrivateKey, + const RippleAddress& naDstAccountID); Transaction::pointer setPasswordSet( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naAuthKeyID, + const RippleAddress& naPrivateKey, + const RippleAddress& naAuthKeyID, const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature); Transaction::pointer setPayment( - const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naDstAccountID, + const RippleAddress& naPrivateKey, + const RippleAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, const STPathSet& spsPaths, @@ -120,29 +127,30 @@ private: const bool bLimit); Transaction::pointer setWalletAdd( - const NewcoinAddress& naPrivateKey, + const RippleAddress& naPrivateKey, const STAmount& saAmount, - const NewcoinAddress& naAuthKeyID, - const NewcoinAddress& naNewPubKey, + const RippleAddress& naAuthKeyID, + const RippleAddress& naNewPubKey, const std::vector& vucSignature); public: - Transaction(const SerializedTransaction::pointer& st, bool bValidate); + Transaction(SerializedTransaction::ref st, bool bValidate); static Transaction::pointer sharedTransaction(const std::vector&vucTransaction, bool bValidate); + static Transaction::pointer transactionFromSQL(Database* db, bool bValidate); Transaction( TransactionType ttKind, - const NewcoinAddress& naPublicKey, // To prove transaction is consistent and authorized. - const NewcoinAddress& naSourceAccount, // To identify the paying account. + const RippleAddress& naPublicKey, // To prove transaction is consistent and authorized. + const RippleAddress& naSourceAccount, // To identify the paying account. uint32 uSeq, // To order transactions. const STAmount& saFee, // Transaction fee. uint32 uSourceTag); // User call back value. // Change account settings. static Transaction::pointer sharedAccountSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -150,7 +158,7 @@ public: const uint128& uEmailHash, bool bWalletLocator, const uint256& uWalletLocator, - const NewcoinAddress& naMessagePublic, + const RippleAddress& naMessagePublic, bool bDomain, const std::vector& vucDomain, bool bTransferRate, @@ -161,7 +169,7 @@ public: // Claim a wallet. static Transaction::pointer sharedClaim( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, uint32 uSourceTag, const std::vector& vucGenerator, const std::vector& vucPubKey, @@ -169,18 +177,18 @@ public: // Create an account. static Transaction::pointer sharedCreate( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naCreateAccountID, // Account to create. + const RippleAddress& naCreateAccountID, // Account to create. const STAmount& saFund); // Initial funds in XNC. // Set credit limit and borrow fees. static Transaction::pointer sharedCreditSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -192,8 +200,8 @@ public: // Set Nickname static Transaction::pointer sharedNicknameSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -203,30 +211,30 @@ public: // Pre-fund password change. static Transaction::pointer sharedPasswordFund( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naDstAccountID); + const RippleAddress& naDstAccountID); // Change a password. static Transaction::pointer sharedPasswordSet( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, uint32 uSourceTag, - const NewcoinAddress& naAuthKeyID, // ID of regular public to auth. + const RippleAddress& naAuthKeyID, // ID of regular public to auth. const std::vector& vucGenerator, const std::vector& vucPubKey, const std::vector& vucSignature); // Make a payment. static Transaction::pointer sharedPayment( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, - const NewcoinAddress& naDstAccountID, + const RippleAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, const STPathSet& spsPaths, @@ -235,8 +243,8 @@ public: // Place an offer. static Transaction::pointer sharedOfferCreate( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -247,8 +255,8 @@ public: // Cancel an offer static Transaction::pointer sharedOfferCancel( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, @@ -256,24 +264,24 @@ public: // Add an account to a wallet. static Transaction::pointer sharedWalletAdd( - const NewcoinAddress& naPublicKey, const NewcoinAddress& naPrivateKey, - const NewcoinAddress& naSourceAccount, + const RippleAddress& naPublicKey, const RippleAddress& naPrivateKey, + const RippleAddress& naSourceAccount, uint32 uSeq, const STAmount& saFee, uint32 uSourceTag, const STAmount& saAmount, // Initial funds in XNC. - const NewcoinAddress& naAuthKeyID, // ID of regular public to auth. - const NewcoinAddress& naNewPubKey, // Public key of new account + const RippleAddress& naAuthKeyID, // ID of regular public to auth. + const RippleAddress& naNewPubKey, // Public key of new account const std::vector& vucSignature); // Proof know new account's private key. - bool sign(const NewcoinAddress& naAccountPrivate); + bool sign(const RippleAddress& naAccountPrivate); bool checkSign() const; void updateID() { mTransactionID=mTransaction->getTransactionID(); } SerializedTransaction::pointer getSTransaction() { return mTransaction; } const uint256& getID() const { return mTransactionID; } - const NewcoinAddress& getFromAccount() const { return mAccountFrom; } + const RippleAddress& getFromAccount() const { return mAccountFrom; } STAmount getAmount() const { return mTransaction->getFieldU64(sfAmount); } STAmount getFee() const { return mTransaction->getTransactionFee(); } uint32 getFromAccountSeq() const { return mTransaction->getSequence(); } @@ -293,7 +301,7 @@ public: static void saveTransaction(const Transaction::pointer&); bool save(); static Transaction::pointer load(const uint256& id); - static Transaction::pointer findFrom(const NewcoinAddress& fromID, uint32 seq); + static Transaction::pointer findFrom(const RippleAddress& fromID, uint32 seq); // conversion function static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq, diff --git a/src/TransactionAction.cpp b/src/TransactionAction.cpp index 81b9fe85d..538028150 100644 --- a/src/TransactionAction.cpp +++ b/src/TransactionAction.cpp @@ -32,7 +32,7 @@ TER TransactionEngine::setAuthorized(const SerializedTransaction& txn, bool bMus std::vector vucCipher = txn.getFieldVL(sfGenerator); std::vector vucPubKey = txn.getFieldVL(sfPublicKey); std::vector vucSignature = txn.getFieldVL(sfSignature); - NewcoinAddress naAccountPublic = NewcoinAddress::createAccountPublic(vucPubKey); + RippleAddress naAccountPublic = RippleAddress::createAccountPublic(vucPubKey); if (!naAccountPublic.accountPublicVerify(Serializer::getSHA512Half(vucCipher), vucSignature)) { @@ -641,7 +641,7 @@ TER TransactionEngine::doWalletAdd(const SerializedTransaction& txn) const std::vector vucPubKey = txn.getFieldVL(sfPublicKey); const std::vector vucSignature = txn.getFieldVL(sfSignature); const uint160 uAuthKeyID = txn.getFieldAccount160(sfAuthorizedKey); - const NewcoinAddress naMasterPubKey = NewcoinAddress::createAccountPublic(vucPubKey); + const RippleAddress naMasterPubKey = RippleAddress::createAccountPublic(vucPubKey); const uint160 uDstAccountID = naMasterPubKey.getAccountID(); if (!naMasterPubKey.accountPublicVerify(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature)) @@ -1018,7 +1018,7 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe if (!sleTakerPays) { - Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existant issuer: " << NewcoinAddress::createHumanAccountID(uPaysIssuerID); + Log(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existant issuer: " << RippleAddress::createHumanAccountID(uPaysIssuerID); terResult = terNO_ACCOUNT; } @@ -1066,11 +1066,11 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); Log(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); - Log(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << NewcoinAddress::createHumanAccountID(mTxnAccountID); + Log(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID); Log(lsWARNING) << "doOfferCreate: takeOffers: FUNDS=" << mNodes.accountFunds(mTxnAccountID, saTakerGets).getFullText(); - // Log(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); - // Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); + // Log(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); + // Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); if (tesSUCCESS == terResult && saTakerPays // Still wanting something. @@ -1092,9 +1092,9 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe Log(lsINFO) << boost::str(boost::format("doOfferCreate: adding to book: %s : %s/%s -> %s/%s") % uBookBase.ToString() % saTakerPays.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerPays.getIssuer()) + % RippleAddress::createHumanAccountID(saTakerPays.getIssuer()) % saTakerGets.getHumanCurrency() - % NewcoinAddress::createHumanAccountID(saTakerGets.getIssuer())); + % RippleAddress::createHumanAccountID(saTakerGets.getIssuer())); uDirectory = Ledger::getQualityIndex(uBookBase, uRate); // Use original rate. @@ -1104,9 +1104,9 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe if (tesSUCCESS == terResult) { - Log(lsWARNING) << "doOfferCreate: sfAccount=" << NewcoinAddress::createHumanAccountID(mTxnAccountID); - Log(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << NewcoinAddress::createHumanAccountID(uPaysIssuerID); - Log(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); + Log(lsWARNING) << "doOfferCreate: sfAccount=" << RippleAddress::createHumanAccountID(mTxnAccountID); + Log(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); + Log(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); Log(lsWARNING) << "doOfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative(); Log(lsWARNING) << "doOfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative(); Log(lsWARNING) << "doOfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency(); @@ -1136,24 +1136,37 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn) { TER terResult; - const uint32 uSequence = txn.getFieldU32(sfOfferSequence); - const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); + const uint32 uOfferSequence = txn.getFieldU32(sfOfferSequence); + const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32(sfSequence); - if (sleOffer) + Log(lsDEBUG) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; + + if (!uOfferSequence || uAccountSequenceNext-1 <= uOfferSequence) { - Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence; + Log(lsINFO) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; - terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); + terResult = temBAD_SEQUENCE; } else { - Log(lsWARNING) << "doOfferCancel: offer not found: " - << NewcoinAddress::createHumanAccountID(mTxnAccountID) - << " : " << uSequence - << " : " << uOfferIndex.ToString(); + const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uOfferSequence); + SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - terResult = terOFFER_NOT_FOUND; + if (sleOffer) + { + Log(lsWARNING) << "doOfferCancel: uOfferSequence=" << uOfferSequence; + + terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); + } + else + { + Log(lsWARNING) << "doOfferCancel: offer not found: " + << RippleAddress::createHumanAccountID(mTxnAccountID) + << " : " << uOfferSequence + << " : " << uOfferIndex.ToString(); + + terResult = tesSUCCESS; + } } return terResult; @@ -1179,7 +1192,6 @@ TER TransactionEngine::doContractAdd(const SerializedTransaction& txn) // place contract in ledger // run create code - if (mLedger->getParentCloseTimeNC() >= expiration) { diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 167b275ba..04f4ecf09 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -3,6 +3,7 @@ // #include +#include #include "TransactionEngine.h" @@ -18,12 +19,12 @@ SETUP_LOG(); void TransactionEngine::txnWrite() { // Write back the account states - for (std::map::iterator it = mNodes.begin(), end = mNodes.end(); - it != end; ++it) + typedef std::pair u256_LES_pair; + BOOST_FOREACH(u256_LES_pair& it, mNodes) { - const SLE::pointer& sleEntry = it->second.mEntry; + const SLE::pointer& sleEntry = it.second.mEntry; - switch (it->second.mAction) + switch (it.second.mAction) { case taaNONE: assert(false); @@ -54,7 +55,7 @@ void TransactionEngine::txnWrite() { cLog(lsINFO) << "applyTransaction: taaDELETE: " << sleEntry->getText(); - if (!mLedger->peekAccountStateMap()->delItem(it->first)) + if (!mLedger->peekAccountStateMap()->delItem(it.first)) assert(false); } break; @@ -104,10 +105,10 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa // without going to disk. Each transaction also notes a source account id. This is used to verify that the signing key is // associated with the account. // XXX This could be a lot cleaner to prevent unnecessary copying. - NewcoinAddress naSigningPubKey; + RippleAddress naSigningPubKey; if (tesSUCCESS == terResult) - naSigningPubKey = NewcoinAddress::createAccountPublic(txn.getSigningPubKey()); + naSigningPubKey = RippleAddress::createAccountPublic(txn.getSigningPubKey()); // Consistency: really signed. if ((tesSUCCESS == terResult) && !isSetBit(params, tapNO_CHECK_SIGN) && !txn.checkSign(naSigningPubKey)) diff --git a/src/TransactionErr.cpp b/src/TransactionErr.cpp index f85f0cda4..9f50b4d3a 100644 --- a/src/TransactionErr.cpp +++ b/src/TransactionErr.cpp @@ -33,6 +33,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: Bad publish." }, { temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" }, { temBAD_SET_ID, "temBAD_SET_ID", "Malformed." }, + { temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence in not in the past." }, { temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." }, { temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." }, { temDST_NEEDED, "temDST_NEEDED", "Destination not specified." }, @@ -53,7 +54,6 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { terNO_DST, "terNO_DST", "The destination does not exist." }, { terNO_LINE, "terNO_LINE", "No such line." }, { terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." }, - { terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." }, { terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." }, { terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." }, { terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." }, diff --git a/src/TransactionErr.h b/src/TransactionErr.h index adb381909..f672caf7f 100644 --- a/src/TransactionErr.h +++ b/src/TransactionErr.h @@ -34,6 +34,7 @@ enum TER // aka TransactionEngineResult temBAD_PATH_LOOP, temBAD_PUBLISH, temBAD_TRANSFER_RATE, + temBAD_SEQUENCE, temBAD_SET_ID, temCREATEXNS, temDST_IS_SRC, @@ -83,7 +84,6 @@ enum TER // aka TransactionEngineResult terNO_DST, terNO_LINE, terNO_LINE_NO_ZERO, - terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure. terPRE_SEQ, terSET_MISSING_DST, terUNFUNDED, @@ -121,3 +121,4 @@ std::string transToken(TER terCode); std::string transHuman(TER terCode); #endif +// vim:ts=4 diff --git a/src/TransactionMaster.cpp b/src/TransactionMaster.cpp index b59f54d22..eabf17622 100644 --- a/src/TransactionMaster.cpp +++ b/src/TransactionMaster.cpp @@ -31,7 +31,7 @@ Transaction::pointer TransactionMaster::fetch(const uint256& txnID, bool checkDi return txn; } -SerializedTransaction::pointer TransactionMaster::fetch(const SHAMapItem::pointer& item, bool checkDisk, uint32 uCommitLedger) +SerializedTransaction::pointer TransactionMaster::fetch(SHAMapItem::ref item, bool checkDisk, uint32 uCommitLedger) { SerializedTransaction::pointer txn; Transaction::pointer iTx = theApp->getMasterTransaction().fetch(item->getTag(), false); diff --git a/src/TransactionMaster.h b/src/TransactionMaster.h index c5e5f9a75..19fd8b64d 100644 --- a/src/TransactionMaster.h +++ b/src/TransactionMaster.h @@ -16,7 +16,7 @@ public: TransactionMaster(); Transaction::pointer fetch(const uint256&, bool checkDisk); - SerializedTransaction::pointer fetch(const SHAMapItem::pointer& item, bool checkDisk, uint32 uCommitLedger); + SerializedTransaction::pointer fetch(SHAMapItem::ref item, bool checkDisk, uint32 uCommitLedger); // return value: true = we had the transaction already bool canonicalize(Transaction::pointer& txn, bool maybeNew); diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index 30117e1e7..f8fdff08d 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -6,8 +6,10 @@ #include #include +#include "Log.h" + TransactionMetaSet::TransactionMetaSet(const uint256& txid, uint32 ledger, const std::vector& vec) : - mTransactionID(txid), mLedger(ledger), mNodes(sfAffectedNodes) + mTransactionID(txid), mLedger(ledger), mNodes(sfAffectedNodes, 32) { Serializer s(vec); SerializerIterator sit(s); @@ -23,21 +25,20 @@ TransactionMetaSet::TransactionMetaSet(const uint256& txid, uint32 ledger, const bool TransactionMetaSet::isNodeAffected(const uint256& node) const { - for (STArray::const_iterator it = mNodes.begin(); it != mNodes.end(); ++it) - if (it->getFieldH256(sfLedgerIndex) == node) + BOOST_FOREACH(const STObject& it, mNodes) + if (it.getFieldH256(sfLedgerIndex) == node) return true; return false; } -STObject& TransactionMetaSet::getAffectedNode(const uint256& node, SField::ref type, bool overrideType) -{ - for (STArray::iterator it = mNodes.begin(); it != mNodes.end(); ++it) +void TransactionMetaSet::setAffectedNode(const uint256& node, SField::ref type) +{ // make sure the node exists and force its type + BOOST_FOREACH(STObject& it, mNodes) { - if (it->getFieldH256(sfLedgerIndex) == node) + if (it.getFieldH256(sfLedgerIndex) == node) { - if (overrideType) - it->setFName(type); - return *it; + it.setFName(type); + return; } } @@ -46,15 +47,42 @@ STObject& TransactionMetaSet::getAffectedNode(const uint256& node, SField::ref t assert(obj.getFName() == type); obj.setFieldH256(sfLedgerIndex, node); +} - return mNodes.back(); +STObject& TransactionMetaSet::getAffectedNode(const uint256& node, SField::ref type) +{ + assert(&type); + BOOST_FOREACH(STObject& it, mNodes) + { + if (it.getFieldH256(sfLedgerIndex) == node) + return it; + } + + mNodes.push_back(STObject(sfModifiedNode)); + STObject& obj = mNodes.back(); + + assert(obj.getFName() == type); + obj.setFieldH256(sfLedgerIndex, node); + + return obj; +} + +STObject& TransactionMetaSet::getAffectedNode(const uint256& node) +{ + BOOST_FOREACH(STObject& it, mNodes) + { + if (it.getFieldH256(sfLedgerIndex) == node) + return it; + } + assert(false); + throw std::runtime_error("Affected node not found"); } const STObject& TransactionMetaSet::peekAffectedNode(const uint256& node) const { - for (STArray::const_iterator it = mNodes.begin(); it != mNodes.end(); ++it) - if (it->getFieldH256(sfLedgerIndex) == node) - return *it; + BOOST_FOREACH(const STObject& it, mNodes) + if (it.getFieldH256(sfLedgerIndex) == node) + return it; throw std::runtime_error("Affected node not found"); } @@ -62,7 +90,7 @@ void TransactionMetaSet::init(const uint256& id, uint32 ledger) { mTransactionID = id; mLedger = ledger; - mNodes = STArray(sfAffectedNodes); + mNodes = STArray(sfAffectedNodes, 32); } void TransactionMetaSet::swap(TransactionMetaSet& s) @@ -73,15 +101,15 @@ void TransactionMetaSet::swap(TransactionMetaSet& s) bool TransactionMetaSet::thread(STObject& node, const uint256& prevTxID, uint32 prevLgrID) { - if (node.getFieldIndex(sfLastTxnID) == -1) + if (node.getFieldIndex(sfPreviousTxnID) == -1) { - assert(node.getFieldIndex(sfLastTxnSeq) == -1); - node.setFieldH256(sfLastTxnID, prevTxID); - node.setFieldU32(sfLastTxnSeq, prevLgrID); + assert(node.getFieldIndex(sfPreviousTxnLgrSeq) == -1); + node.setFieldH256(sfPreviousTxnID, prevTxID); + node.setFieldU32(sfPreviousTxnLgrSeq, prevLgrID); return true; } - assert(node.getFieldH256(sfLastTxnID) == prevTxID); - assert(node.getFieldU32(sfLastTxnSeq) == prevLgrID); + assert(node.getFieldH256(sfPreviousTxnID) == prevTxID); + assert(node.getFieldU32(sfPreviousTxnLgrSeq) == prevLgrID); return false; } diff --git a/src/TransactionMeta.h b/src/TransactionMeta.h index 81b8c2a44..100b09900 100644 --- a/src/TransactionMeta.h +++ b/src/TransactionMeta.h @@ -39,7 +39,9 @@ public: uint32 getLgrSeq() { return mLedger; } bool isNodeAffected(const uint256&) const; - STObject& getAffectedNode(const uint256&, SField::ref type, bool overrideType); + void setAffectedNode(const uint256&, SField::ref type); + STObject& getAffectedNode(const uint256&, SField::ref type); + STObject& getAffectedNode(const uint256&); const STObject& peekAffectedNode(const uint256&) const; Json::Value getJson(int p) const { return getAsObject().getJson(p); } diff --git a/src/UniqueNodeList.cpp b/src/UniqueNodeList.cpp index cd55c89c0..d8f374993 100644 --- a/src/UniqueNodeList.cpp +++ b/src/UniqueNodeList.cpp @@ -1,13 +1,10 @@ // XXX Dynamically limit fetching by distance. // XXX Want a limit of 2000 validators. -#include "Application.h" -#include "HttpsClient.h" -#include "Log.h" -#include "ParseSection.h" -#include "Serializer.h" #include "UniqueNodeList.h" -#include "utils.h" + +#include +#include #include #include @@ -17,8 +14,14 @@ #include #include -#include -#include +#include "Application.h" +#include "HttpsClient.h" +#include "Log.h" +#include "ParseSection.h" +#include "Serializer.h" +#include "utils.h" + +SETUP_LOG(); #define VALIDATORS_FETCH_SECONDS 30 #define VALIDATORS_FILE_PATH "/" VALIDATORS_FILE_NAME @@ -42,8 +45,6 @@ #define MIN(x,y) ((x)<(y)?(x):(y)) #endif -SETUP_LOG(); - UniqueNodeList::UniqueNodeList(boost::asio::io_service& io_service) : mdtScoreTimer(io_service), mFetchActive(0), @@ -57,8 +58,8 @@ void UniqueNodeList::start() { miscLoad(); - std::cerr << "Validator fetch updated: " << mtpFetchUpdated << std::endl; - std::cerr << "Validator score updated: " << mtpScoreUpdated << std::endl; + Log(lsDEBUG) << "Validator fetch updated: " << mtpFetchUpdated; + Log(lsDEBUG) << "Validator score updated: " << mtpScoreUpdated; fetchNext(); // Start fetching. scoreNext(false); // Start scoring. @@ -136,17 +137,18 @@ bool UniqueNodeList::scoreRound(std::vector& vsnNodes) } } - std::cerr << "midway: " << std::endl; - - BOOST_FOREACH(scoreNode& sn, vsnNodes) + if (sLog(lsTRACE)) { - std::cerr << str(boost::format("%s| %d, %d, %d: [%s]") - % sn.strValidator - % sn.iScore - % sn.iRoundScore - % sn.iRoundSeed - % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")) - << std::endl; + Log(lsTRACE) << "midway: "; + BOOST_FOREACH(scoreNode& sn, vsnNodes) + { + Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + % sn.strValidator + % sn.iScore + % sn.iRoundScore + % sn.iRoundSeed + % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")); + } } // Add roundScore to score. @@ -160,17 +162,18 @@ bool UniqueNodeList::scoreRound(std::vector& vsnNodes) sn.iRoundScore = 0; } - std::cerr << "finish: " << std::endl; - - BOOST_FOREACH(scoreNode& sn, vsnNodes) + if (sLog(lsTRACE)) { - std::cerr << str(boost::format("%s| %d, %d, %d: [%s]") - % sn.strValidator - % sn.iScore - % sn.iRoundScore - % sn.iRoundSeed - % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")) - << std::endl; + Log(lsTRACE) << "finish: "; + BOOST_FOREACH(scoreNode& sn, vsnNodes) + { + Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + % sn.strValidator + % sn.iScore + % sn.iRoundScore + % sn.iRoundSeed + % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")); + } } return bDist; @@ -283,21 +286,23 @@ void UniqueNodeList::scoreCompute() } // For debugging, print out initial scores. - BOOST_FOREACH(scoreNode& sn, vsnNodes) + if (sLog(lsTRACE)) { - std::cerr << str(boost::format("%s| %d, %d, %d") - % sn.strValidator - % sn.iScore - % sn.iRoundScore - % sn.iRoundSeed) - << std::endl; + BOOST_FOREACH(scoreNode& sn, vsnNodes) + { + Log(lsTRACE) << str(boost::format("%s| %d, %d, %d") + % sn.strValidator + % sn.iScore + % sn.iRoundScore + % sn.iRoundSeed); + } } - // std::cerr << str(boost::format("vsnNodes.size=%d") % vsnNodes.size()) << std::endl; + // cLog(lsTRACE) << str(boost::format("vsnNodes.size=%d") % vsnNodes.size()); // Step through growing list of nodes adding each validation list. // - Each validator may have provided referals. Add those referals as validators. - for (int iNode=0; iNode != vsnNodes.size(); iNode++) + for (int iNode = 0; iNode != vsnNodes.size(); ++iNode) { scoreNode& sn = vsnNodes[iNode]; std::string& strValidator = sn.strValidator; @@ -313,7 +318,7 @@ void UniqueNodeList::scoreCompute() strIndex::iterator itEntry; - NewcoinAddress na; + RippleAddress na; if (na.setNodePublic(strReferral)) { @@ -342,7 +347,7 @@ void UniqueNodeList::scoreCompute() iReferral = itEntry->second; } - // std::cerr << str(boost::format("%s: Public=%s iReferral=%d") % strValidator % strReferral % iReferral) << std::endl; + // cLog(lsTRACE) << str(boost::format("%s: Public=%s iReferral=%d") % strValidator % strReferral % iReferral); } else @@ -353,7 +358,7 @@ void UniqueNodeList::scoreCompute() ? -1 // We ignore domains we can't find entires for. : itEntry->second; - // std::cerr << str(boost::format("%s: Domain=%s iReferral=%d") % strValidator % strReferral % iReferral) << std::endl; + // cLog(lsTRACE) << str(boost::format("%s: Domain=%s iReferral=%d") % strValidator % strReferral % iReferral); } if (iReferral >= 0 && iNode != iReferral) @@ -369,17 +374,18 @@ void UniqueNodeList::scoreCompute() for (int i = SCORE_ROUNDS; bDist && i--;) bDist = scoreRound(vsnNodes); - std::cerr << "Scored:" << std::endl; - - BOOST_FOREACH(scoreNode& sn, vsnNodes) + if (sLog(lsTRACE)) { - std::cerr << str(boost::format("%s| %d, %d, %d: [%s]") - % sn.strValidator - % sn.iScore - % sn.iRoundScore - % sn.iRoundSeed - % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")) - << std::endl; + Log(lsTRACE) << "Scored:"; + BOOST_FOREACH(scoreNode& sn, vsnNodes) + { + Log(lsTRACE) << str(boost::format("%s| %d, %d, %d: [%s]") + % sn.strValidator + % sn.iScore + % sn.iRoundScore + % sn.iRoundSeed + % strJoin(sn.viReferrals.begin(), sn.viReferrals.end(), ",")); + } } // Persist validator scores. @@ -454,7 +460,7 @@ void UniqueNodeList::scoreCompute() { umValidators[db->getStrBinary("Validator")] = db->getInt("Count"); - // std::cerr << strValidator << ":" << db->getInt("Count") << std::endl; + // cLog(lsTRACE) << strValidator << ":" << db->getInt("Count"); } } @@ -529,11 +535,11 @@ void UniqueNodeList::scoreTimerHandler(const boost::system::error_code& err) mtpScoreNext = boost::posix_time::ptime(boost::posix_time::not_a_date_time); // Timer not set. mtpScoreStart = boost::posix_time::second_clock::universal_time(); // Scoring. - std::cerr << "Scoring: Start" << std::endl; + cLog(lsTRACE) << "Scoring: Start"; scoreCompute(); - std::cerr << "Scoring: End" << std::endl; + cLog(lsTRACE) << "Scoring: End"; // Save update time. mtpScoreUpdated = mtpScoreStart; @@ -553,7 +559,7 @@ void UniqueNodeList::scoreTimerHandler(const boost::system::error_code& err) // <-- bNow: true, to force scoring for debugging. void UniqueNodeList::scoreNext(bool bNow) { - // std::cerr << str(boost::format("scoreNext: mtpFetchUpdated=%s mtpScoreStart=%s mtpScoreUpdated=%s mtpScoreNext=%s") % mtpFetchUpdated % mtpScoreStart % mtpScoreUpdated % mtpScoreNext) << std::endl; + // cLog(lsTRACE) << str(boost::format("scoreNext: mtpFetchUpdated=%s mtpScoreStart=%s mtpScoreUpdated=%s mtpScoreNext=%s") % mtpFetchUpdated % mtpScoreStart % mtpScoreUpdated % mtpScoreNext); bool bCanScore = mtpScoreStart.is_not_a_date_time() // Not scoring. && !mtpFetchUpdated.is_not_a_date_time(); // Something to score. @@ -572,7 +578,7 @@ void UniqueNodeList::scoreNext(bool bNow) mtpScoreNext = boost::posix_time::second_clock::universal_time() // Past now too. + boost::posix_time::seconds(bNow ? 0 : SCORE_DELAY_SECONDS); - // std::cerr << str(boost::format("scoreNext: @%s") % mtpScoreNext) << std::endl; + // cLog(lsTRACE) << str(boost::format("scoreNext: @%s") % mtpScoreNext); mdtScoreTimer.expires_at(mtpScoreNext); mdtScoreTimer.async_wait(boost::bind(&UniqueNodeList::scoreTimerHandler, this, _1)); } @@ -608,7 +614,7 @@ void UniqueNodeList::fetchDirty() // Persist the IPs refered to by a Validator. // --> strSite: source of the IPs (for debugging) // --> naNodePublic: public key of the validating node. -void UniqueNodeList::processIps(const std::string& strSite, const NewcoinAddress& naNodePublic, section::mapped_type* pmtVecStrIps) +void UniqueNodeList::processIps(const std::string& strSite, const RippleAddress& naNodePublic, section::mapped_type* pmtVecStrIps) { Database* db=theApp->getWalletDB()->getDB(); @@ -652,10 +658,9 @@ void UniqueNodeList::processIps(const std::string& strSite, const NewcoinAddress } else { - std::cerr + cLog(lsTRACE) << str(boost::format("Validator: '%s' ["SECTION_IPS"]: rejecting '%s'") - % strSite % strReferral) - << std::endl; + % strSite % strReferral); } } @@ -678,18 +683,17 @@ void UniqueNodeList::processIps(const std::string& strSite, const NewcoinAddress // --> strValidatorsSrc: source details for display // --> naNodePublic: remote source public key - not valid for local // --> vsWhy: reason for adding validator to SeedDomains or SeedNodes. -int UniqueNodeList::processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators) +int UniqueNodeList::processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators) { Database* db = theApp->getWalletDB()->getDB(); std::string strNodePublic = naNodePublic.isValid() ? naNodePublic.humanNodePublic() : strValidatorsSrc; int iValues = 0; - std::cerr + cLog(lsTRACE) << str(boost::format("Validator: '%s' : '%s' : processing %d validators.") % strSite % strValidatorsSrc - % ( pmtVecStrValidators ? pmtVecStrValidators->size() : 0)) - << std::endl; + % ( pmtVecStrValidators ? pmtVecStrValidators->size() : 0)); // Remove all current Validator's entries in ValidatorReferrals { @@ -724,7 +728,7 @@ int UniqueNodeList::processValidators(const std::string& strSite, const std::str { std::string strRefered = smMatch[1]; std::string strComment = smMatch[2]; - NewcoinAddress naValidator; + RippleAddress naValidator; if (naValidator.setSeedGeneric(strRefered)) { @@ -777,7 +781,7 @@ int UniqueNodeList::processValidators(const std::string& strSite, const std::str } // Given a section with IPs, parse and persist it for a validator. -void UniqueNodeList::responseIps(const std::string& strSite, const NewcoinAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile) +void UniqueNodeList::responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile) { if (!err) { @@ -791,7 +795,7 @@ void UniqueNodeList::responseIps(const std::string& strSite, const NewcoinAddres // Process section [ips_url]. // If we have a section with a single entry, fetch the url and process it. -void UniqueNodeList::getIpsUrl(const NewcoinAddress& naNodePublic, section secSite) +void UniqueNodeList::getIpsUrl(const RippleAddress& naNodePublic, section secSite) { std::string strIpsUrl; std::string strDomain; @@ -817,7 +821,7 @@ void UniqueNodeList::getIpsUrl(const NewcoinAddress& naNodePublic, section secSi } // After fetching a ripple.txt from a web site, given a section with validators, parse and persist it. -void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, const NewcoinAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile) +void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile) { if (!err) { @@ -830,7 +834,7 @@ void UniqueNodeList::responseValidators(const std::string& strValidatorsUrl, con } // Process section [validators_url]. -void UniqueNodeList::getValidatorsUrl(const NewcoinAddress& naNodePublic, section secSite) +void UniqueNodeList::getValidatorsUrl(const RippleAddress& naNodePublic, section secSite) { std::string strValidatorsUrl; std::string strDomain; @@ -856,7 +860,7 @@ void UniqueNodeList::getValidatorsUrl(const NewcoinAddress& naNodePublic, sectio } // Process a ripple.txt. -void UniqueNodeList::processFile(const std::string& strDomain, const NewcoinAddress& naNodePublic, section secSite) +void UniqueNodeList::processFile(const std::string& strDomain, const RippleAddress& naNodePublic, section secSite) { // // Process Validators @@ -876,7 +880,7 @@ void UniqueNodeList::processFile(const std::string& strDomain, const NewcoinAddr if ((pvCurrencies = sectionEntries(secSite, SECTION_CURRENCIES)) && pvCurrencies->size()) { // XXX Process currencies. - std::cerr << "Ignoring currencies: not implemented." << std::endl; + cLog(lsWARNING) << "Ignoring currencies: not implemented."; } getValidatorsUrl(naNodePublic, secSite); @@ -890,15 +894,14 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy if (bGood) { - std::cerr << boost::format("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain << std::endl; + cLog(lsTRACE) << boost::format("Validator: '%s' received " NODE_FILE_NAME ".") % strDomain; } else { - std::cerr + cLog(lsTRACE) << boost::format("Validator: '%s' unable to retrieve " NODE_FILE_NAME ": %s") % strDomain - % err.message() - << std::endl; + % err.message(); } // @@ -910,21 +913,19 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy { bGood = false; - std::cerr + cLog(lsTRACE) << boost::format("Validator: '%s' bad " NODE_FILE_NAME " missing single entry for " SECTION_DOMAIN ".") - % strDomain - << std::endl; + % strDomain; } if (bGood && strSite != strDomain) { bGood = false; - std::cerr + cLog(lsTRACE) << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_DOMAIN " does not match: %s") % strDomain - % strSite - << std::endl; + % strSite; } // @@ -937,29 +938,27 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy // Bad [validation_public_key] section. bGood = false; - std::cerr + cLog(lsTRACE) << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " does not have single entry.") - % strDomain - << std::endl; + % strDomain; } - NewcoinAddress naNodePublic; + RippleAddress naNodePublic; if (bGood && !naNodePublic.setNodePublic(strNodePublicKey)) { // Bad public key. bGood = false; - std::cerr + cLog(lsTRACE) << boost::format("Validator: '%s' bad " NODE_FILE_NAME " " SECTION_PUBLIC_KEY " is bad: ") % strDomain - % strNodePublicKey - << std::endl; + % strNodePublicKey; } if (bGood) { -// std::cerr << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic() << std::endl; +// cLog(lsTRACE) << boost::format("naNodePublic: '%s'") % naNodePublic.humanNodePublic(); seedDomain sdCurrent; @@ -975,7 +974,7 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy // XXX Only if no other refs to keep it arround, other wise we have an attack vector. sdCurrent.naPublicKey = naNodePublic; -// std::cerr << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic() << std::endl; +// cLog(lsTRACE) << boost::format("sdCurrent.naPublicKey: '%s'") % sdCurrent.naPublicKey.humanNodePublic(); sdCurrent.tpFetch = boost::posix_time::second_clock::universal_time(); sdCurrent.iSha256 = iSha256; @@ -984,12 +983,12 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy if (bChangedB) { - std::cerr << boost::format("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain << std::endl; + cLog(lsTRACE) << boost::format("Validator: '%s' processing new " NODE_FILE_NAME ".") % strDomain; processFile(strDomain, naNodePublic, secSite); } else { - std::cerr << boost::format("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain << std::endl; + cLog(lsTRACE) << boost::format("Validator: '%s' no change for " NODE_FILE_NAME ".") % strDomain; fetchFinish(); } } @@ -1005,7 +1004,7 @@ void UniqueNodeList::responseFetch(const std::string& strDomain, const boost::sy // Get the ripple.txt and process it. void UniqueNodeList::fetchProcess(std::string strDomain) { - std::cerr << "Fetching '" NODE_FILE_NAME "' from '" << strDomain << "'." << std::endl; + cLog(lsTRACE) << "Fetching '" NODE_FILE_NAME "' from '" << strDomain << "'."; std::deque deqSites; @@ -1030,7 +1029,7 @@ void UniqueNodeList::fetchTimerHandler(const boost::system::error_code& err) if (!err) { // Time to check for another fetch. - std::cerr << "fetchTimerHandler" << std::endl; + cLog(lsTRACE) << "fetchTimerHandler"; fetchNext(); } } @@ -1064,7 +1063,7 @@ void UniqueNodeList::fetchNext() tpNext = ptFromSeconds(iNext); tpNow = boost::posix_time::second_clock::universal_time(); - std::cerr << str(boost::format("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow) << std::endl; + cLog(lsTRACE) << str(boost::format("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow); strDomain = db->getStrBinary("Domain"); db->endIterRows(); @@ -1083,13 +1082,13 @@ void UniqueNodeList::fetchNext() if (strDomain.empty() || bFull) { - std::cerr << str(boost::format("fetchNext: strDomain=%s bFull=%d") % strDomain % bFull) << std::endl; + cLog(lsTRACE) << str(boost::format("fetchNext: strDomain=%s bFull=%d") % strDomain % bFull); nothing(); } else if (tpNext > tpNow) { - std::cerr << str(boost::format("fetchNext: set timer : strDomain=%s") % strDomain) << std::endl; + cLog(lsTRACE) << str(boost::format("fetchNext: set timer : strDomain=%s") % strDomain); // Fetch needs to happen in the future. Set a timer to wake us. mtpFetchNext = tpNext; @@ -1098,7 +1097,7 @@ void UniqueNodeList::fetchNext() } else { - std::cerr << str(boost::format("fetchNext: fetch now: strDomain=%s tpNext=%s tpNow=%s") % strDomain % tpNext %tpNow) << std::endl; + cLog(lsTRACE) << str(boost::format("fetchNext: fetch now: strDomain=%s tpNext=%s tpNow=%s") % strDomain % tpNext %tpNow); // Fetch needs to happen now. mtpFetchNext = boost::posix_time::ptime(boost::posix_time::not_a_date_time); @@ -1115,7 +1114,7 @@ void UniqueNodeList::fetchNext() setSeedDomains(sdCurrent, false); - std::cerr << "Validator: '" << strDomain << "' fetching " NODE_FILE_NAME "." << std::endl; + cLog(lsTRACE) << "Validator: '" << strDomain << "' fetching " NODE_FILE_NAME "."; fetchProcess(strDomain); // Go get it. @@ -1210,7 +1209,7 @@ void UniqueNodeList::setSeedDomains(const seedDomain& sdSource, bool bNext) int iScan = iToSeconds(sdSource.tpScan); int iFetch = iToSeconds(sdSource.tpFetch); - // std::cerr << str(boost::format("setSeedDomains: iNext=%s tpNext=%s") % iNext % sdSource.tpNext) << std::endl; + // cLog(lsTRACE) << str(boost::format("setSeedDomains: iNext=%s tpNext=%s") % iNext % sdSource.tpNext); std::string strSql = str(boost::format("REPLACE INTO SeedDomains (Domain,PublicKey,Source,Next,Scan,Fetch,Sha256,Comment) VALUES (%s, %s, %s, %d, %d, %d, '%s', %s);") % db->escape(sdSource.strDomain) @@ -1228,7 +1227,7 @@ void UniqueNodeList::setSeedDomains(const seedDomain& sdSource, bool bNext) if (!db->executeSQL(strSql)) { // XXX Check result. - std::cerr << "setSeedDomains: failed." << std::endl; + cLog(lsWARNING) << "setSeedDomains: failed."; } if (bNext && (mtpFetchNext.is_not_a_date_time() || mtpFetchNext > sdSource.tpNext)) @@ -1247,10 +1246,10 @@ void UniqueNodeList::nodeAddDomain(std::string strDomain, validatorSource vsWhy, boost::to_lower(strDomain); // YYY Would be best to verify strDomain is a valid domain. - // std::cerr << str(boost::format("nodeAddDomain: '%s' %c '%s'") + // cLog(lsTRACE) << str(boost::format("nodeAddDomain: '%s' %c '%s'") // % strDomain // % vsWhy - // % strComment) << std::endl; + // % strComment); seedDomain sdCurrent; @@ -1283,7 +1282,7 @@ void UniqueNodeList::nodeAddDomain(std::string strDomain, validatorSource vsWhy, } // Retrieve a SeedNode from DB. -bool UniqueNodeList::getSeedNodes(const NewcoinAddress& naNodePublic, seedNode& dstSeedNode) +bool UniqueNodeList::getSeedNodes(const RippleAddress& naNodePublic, seedNode& dstSeedNode) { bool bResult; Database* db=theApp->getWalletDB()->getDB(); @@ -1348,7 +1347,7 @@ void UniqueNodeList::setSeedNodes(const seedNode& snSource, bool bNext) int iScan = iToSeconds(snSource.tpScan); int iFetch = iToSeconds(snSource.tpFetch); - // std::cerr << str(boost::format("setSeedNodes: iNext=%s tpNext=%s") % iNext % sdSource.tpNext) << std::endl; + // cLog(lsTRACE) << str(boost::format("setSeedNodes: iNext=%s tpNext=%s") % iNext % sdSource.tpNext); assert(snSource.naPublicKey.isValid()); @@ -1368,7 +1367,7 @@ void UniqueNodeList::setSeedNodes(const seedNode& snSource, bool bNext) if (!db->executeSQL(strSql)) { // XXX Check result. - std::cerr << "setSeedNodes: failed." << std::endl; + cLog(lsTRACE) << "setSeedNodes: failed."; } } @@ -1385,7 +1384,7 @@ void UniqueNodeList::setSeedNodes(const seedNode& snSource, bool bNext) } // Add a trusted node. Called by RPC or other source. -void UniqueNodeList::nodeAddPublic(const NewcoinAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment) +void UniqueNodeList::nodeAddPublic(const RippleAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment) { seedNode snCurrent; @@ -1417,7 +1416,7 @@ void UniqueNodeList::nodeAddPublic(const NewcoinAddress& naNodePublic, validator setSeedNodes(snCurrent, true); } -void UniqueNodeList::nodeRemovePublic(const NewcoinAddress& naNodePublic) +void UniqueNodeList::nodeRemovePublic(const RippleAddress& naNodePublic) { { Database* db=theApp->getWalletDB()->getDB(); @@ -1485,21 +1484,21 @@ bool UniqueNodeList::nodeLoad(boost::filesystem::path pConfig) { if (pConfig.empty()) { - std::cerr << VALIDATORS_FILE_NAME " path not specified." << std::endl; + cLog(lsINFO) << VALIDATORS_FILE_NAME " path not specified."; return false; } if (!boost::filesystem::exists(pConfig)) { - std::cerr << str(boost::format(VALIDATORS_FILE_NAME " not found: %s") % pConfig) << std::endl; + cLog(lsWARNING) << str(boost::format(VALIDATORS_FILE_NAME " not found: %s") % pConfig); return false; } if (!boost::filesystem::is_regular_file(pConfig)) { - std::cerr << str(boost::format(VALIDATORS_FILE_NAME " not regular file: %s") % pConfig) << std::endl; + cLog(lsWARNING) << str(boost::format(VALIDATORS_FILE_NAME " not regular file: %s") % pConfig); return false; } @@ -1508,7 +1507,7 @@ bool UniqueNodeList::nodeLoad(boost::filesystem::path pConfig) if (!ifsDefault) { - std::cerr << str(boost::format(VALIDATORS_FILE_NAME " failed to open: %s") % pConfig) << std::endl; + cLog(lsFATAL) << str(boost::format(VALIDATORS_FILE_NAME " failed to open: %s") % pConfig); return false; } @@ -1520,21 +1519,21 @@ bool UniqueNodeList::nodeLoad(boost::filesystem::path pConfig) if (ifsDefault.bad()) { - std::cerr << str(boost::format("Failed to read: %s") % pConfig) << std::endl; + cLog(lsFATAL) << str(boost::format("Failed to read: %s") % pConfig); return false; } nodeProcess("local", strValidators, pConfig.string()); - std::cerr << str(boost::format("Processing: %s") % pConfig) << std::endl; + cLog(lsTRACE) << str(boost::format("Processing: %s") % pConfig); return true; } void UniqueNodeList::validatorsResponse(const boost::system::error_code& err, std::string strResponse) { - std::cerr << "Fetch '" VALIDATORS_FILE_NAME "' complete." << std::endl; + cLog(lsTRACE) << "Fetch '" VALIDATORS_FILE_NAME "' complete."; if (!err) { @@ -1542,7 +1541,7 @@ void UniqueNodeList::validatorsResponse(const boost::system::error_code& err, st } else { - std::cerr << "Error: " << err.message() << std::endl; + cLog(lsWARNING) << "Error: " << err.message(); } } @@ -1595,7 +1594,7 @@ void UniqueNodeList::nodeBootstrap() // Always load from rippled.cfg if (!theConfig.VALIDATORS.empty()) { - NewcoinAddress naInvalid; // Don't want a referrer on added entries. + RippleAddress naInvalid; // Don't want a referrer on added entries. cLog(lsINFO) << "Bootstrapping UNL: loading from " CONFIG_FILE_NAME "."; @@ -1650,18 +1649,18 @@ void UniqueNodeList::nodeProcess(const std::string& strSite, const std::string& section::mapped_type* pmtEntries = sectionEntries(secValidators, SECTION_VALIDATORS); if (pmtEntries) { - NewcoinAddress naInvalid; // Don't want a referrer on added entries. + RippleAddress naInvalid; // Don't want a referrer on added entries. // YYY Unspecified might be bootstrap or rpc command processValidators(strSite, strSource, naInvalid, vsValidator, pmtEntries); } else { - std::cerr << "WARNING: '" VALIDATORS_FILE_NAME "' missing [" SECTION_VALIDATORS "]." << std::endl; + cLog(lsWARNING) << "'" VALIDATORS_FILE_NAME "' missing [" SECTION_VALIDATORS "]."; } } -bool UniqueNodeList::nodeInUNL(const NewcoinAddress& naNodePublic) +bool UniqueNodeList::nodeInUNL(const RippleAddress& naNodePublic) { ScopedLock sl(mUNLLock); diff --git a/src/UniqueNodeList.h b/src/UniqueNodeList.h index 4040fe0f3..e9c3999dd 100644 --- a/src/UniqueNodeList.h +++ b/src/UniqueNodeList.h @@ -5,12 +5,12 @@ #include "../json/value.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "Config.h" #include "HttpsClient.h" #include "ParseSection.h" -#include +#include #include #include @@ -48,7 +48,7 @@ private: boost::posix_time::ptime mtpScoreUpdated; boost::posix_time::ptime mtpFetchUpdated; - boost::recursive_mutex mUNLLock; + boost::recursive_mutex mUNLLock; // XXX Make this faster, make this the contents vector unsigned char or raw public key. // XXX Contents needs to based on score. boost::unordered_set mUNL; @@ -58,7 +58,7 @@ private: typedef struct { std::string strDomain; - NewcoinAddress naPublicKey; + RippleAddress naPublicKey; validatorSource vsSource; boost::posix_time::ptime tpNext; boost::posix_time::ptime tpScan; @@ -68,7 +68,7 @@ private: } seedDomain; typedef struct { - NewcoinAddress naPublicKey; + RippleAddress naPublicKey; validatorSource vsSource; boost::posix_time::ptime tpNext; boost::posix_time::ptime tpScan; @@ -117,20 +117,20 @@ private: void fetchProcess(std::string strDomain); void fetchTimerHandler(const boost::system::error_code& err); - void getValidatorsUrl(const NewcoinAddress& naNodePublic, section secSite); - void getIpsUrl(const NewcoinAddress& naNodePublic, section secSite); - void responseIps(const std::string& strSite, const NewcoinAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile); - void responseValidators(const std::string& strValidatorsUrl, const NewcoinAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile); + void getValidatorsUrl(const RippleAddress& naNodePublic, section secSite); + void getIpsUrl(const RippleAddress& naNodePublic, section secSite); + void responseIps(const std::string& strSite, const RippleAddress& naNodePublic, const boost::system::error_code& err, const std::string& strIpsFile); + void responseValidators(const std::string& strValidatorsUrl, const RippleAddress& naNodePublic, section secSite, const std::string& strSite, const boost::system::error_code& err, const std::string& strValidatorsFile); - void processIps(const std::string& strSite, const NewcoinAddress& naNodePublic, section::mapped_type* pmtVecStrIps); - int processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const NewcoinAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators); + void processIps(const std::string& strSite, const RippleAddress& naNodePublic, section::mapped_type* pmtVecStrIps); + int processValidators(const std::string& strSite, const std::string& strValidatorsSrc, const RippleAddress& naNodePublic, validatorSource vsWhy, section::mapped_type* pmtVecStrValidators); - void processFile(const std::string& strDomain, const NewcoinAddress& naNodePublic, section secSite); + void processFile(const std::string& strDomain, const RippleAddress& naNodePublic, section secSite); bool getSeedDomains(const std::string& strDomain, seedDomain& dstSeedDomain); void setSeedDomains(const seedDomain& dstSeedDomain, bool bNext); - bool getSeedNodes(const NewcoinAddress& naNodePublic, seedNode& dstSeedNode); + bool getSeedNodes(const RippleAddress& naNodePublic, seedNode& dstSeedNode); void setSeedNodes(const seedNode& snSource, bool bNext); void validatorsResponse(const boost::system::error_code& err, std::string strResponse); @@ -142,15 +142,15 @@ public: // Begin processing. void start(); - void nodeAddPublic(const NewcoinAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment); + void nodeAddPublic(const RippleAddress& naNodePublic, validatorSource vsWhy, const std::string& strComment); void nodeAddDomain(std::string strDomain, validatorSource vsWhy, const std::string& strComment=""); - void nodeRemovePublic(const NewcoinAddress& naNodePublic); + void nodeRemovePublic(const RippleAddress& naNodePublic); void nodeRemoveDomain(std::string strDomain); void nodeReset(); void nodeScore(); - bool nodeInUNL(const NewcoinAddress& naNodePublic); + bool nodeInUNL(const RippleAddress& naNodePublic); void nodeBootstrap(); bool nodeLoad(boost::filesystem::path pConfig); diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 5f5fc17a5..70b83f0c5 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -9,9 +9,11 @@ SETUP_LOG(); +typedef std::pair u160_val_pair; + bool ValidationCollection::addValidation(const SerializedValidation::pointer& val) { - NewcoinAddress signer = val->getSignerPublic(); + RippleAddress signer = val->getSignerPublic(); bool isCurrent = false; if (theApp->getUNL().nodeInUNL(signer) || val->isTrusted()) { @@ -121,10 +123,9 @@ int ValidationCollection::getNodesAfter(const uint256& ledger) { // Number of trusted nodes that have moved past this ledger int count = 0; boost::mutex::scoped_lock sl(mValidationLock); - for (boost::unordered_map::iterator it = mCurrentValidations.begin(), - end = mCurrentValidations.end(); it != end; ++it) + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second->isTrusted() && it->second->isPreviousHash(ledger)) + if (it.second->isTrusted() && it.second->isPreviousHash(ledger)) ++count; } return count; @@ -136,12 +137,11 @@ int ValidationCollection::getLoadRatio(bool overLoaded) int badNodes = overLoaded ? 0 : 1; { boost::mutex::scoped_lock sl(mValidationLock); - for (boost::unordered_map::iterator it = mCurrentValidations.begin(), - end = mCurrentValidations.end(); it != end; ++it) + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second->isTrusted()) + if (it.second->isTrusted()) { - if (it->second->isFull()) + if (it.second->isFull()) ++goodNodes; else ++badNodes; @@ -190,14 +190,13 @@ ValidationCollection::getCurrentValidations(uint256 currentLedger) void ValidationCollection::flush() { - boost::mutex::scoped_lock sl(mValidationLock); - boost::unordered_map::iterator it = mCurrentValidations.begin(); bool anyNew = false; - while (it != mCurrentValidations.end()) + + boost::mutex::scoped_lock sl(mValidationLock); + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second) - mStaleValidations.push_back(it->second); - ++it; + if (it.second) + mStaleValidations.push_back(it.second); anyNew = true; } mCurrentValidations.clear(); @@ -243,7 +242,6 @@ void ValidationCollection::doWrite() % db->escape(strCopy(it->getSignature())))); db->executeSQL("END TRANSACTION;"); } - sl.lock(); } mWriting = false; diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index d5c4a03a3..91b17e8aa 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -56,8 +56,8 @@ protected: typedef void (WSConnection::*doFuncPtr)(Json::Value& jvResult, const Json::Value &jvRequest); boost::mutex mLock; - boost::unordered_set mSubAccountInfo; - boost::unordered_set mSubAccountTransaction; + boost::unordered_set mSubAccountInfo; + boost::unordered_set mSubAccountTransaction; WSServerHandler* mHandler; connection_ptr mConnection; @@ -78,17 +78,23 @@ public: // Utilities Json::Value invokeCommand(const Json::Value& jvRequest); - boost::unordered_set parseAccountIds(const Json::Value& jvArray); + boost::unordered_set parseAccountIds(const Json::Value& jvArray); - // Request-Response Commands + // Commands + void doSubmit(Json::Value& jvResult, const Json::Value& jvRequest); + void doRPC(Json::Value& jvResult, const Json::Value& jvRequest); + void doSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + void doUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); + + + + // deprecated void doLedgerAccept(Json::Value& jvResult, const Json::Value& jvRequest); void doLedgerClosed(Json::Value& jvResult, const Json::Value& jvRequest); void doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvRequest); void doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest); - void doSubmit(Json::Value& jvResult, const Json::Value& jvRequest); void doTransactionEntry(Json::Value& jvResult, const Json::Value& jvRequest); - // Streaming Commands void doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); void doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest); void doAccountTransactionSubscribe(Json::Value& jvResult, const Json::Value& jvRequest); @@ -313,8 +319,10 @@ Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) { "ledger_entry", &WSConnection::doLedgerEntry }, { "submit", &WSConnection::doSubmit }, { "transaction_entry", &WSConnection::doTransactionEntry }, + { "subscribe", &WSConnection::doSubscribe }, + { "unsubscribe", &WSConnection::doUnsubscribe }, - // Streaming commands: + // deprecated { "account_info_subscribe", &WSConnection::doAccountInfoSubscribe }, { "account_info_unsubscribe", &WSConnection::doAccountInfoUnsubscribe }, { "account_transaction_subscribe", &WSConnection::doAccountTransactionSubscribe }, @@ -377,13 +385,13 @@ Json::Value WSConnection::invokeCommand(const Json::Value& jvRequest) return jvResult; } -boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) +boost::unordered_set WSConnection::parseAccountIds(const Json::Value& jvArray) { - boost::unordered_set usnaResult; + boost::unordered_set usnaResult; for (Json::Value::const_iterator it = jvArray.begin(); it != jvArray.end(); it++) { - NewcoinAddress naString; + RippleAddress naString; if (!(*it).isString() || !naString.setAccountID((*it).asString())) { @@ -403,6 +411,92 @@ boost::unordered_set WSConnection::parseAccountIds(const Json::V // Commands // +/* +server : Sends a message anytime the server status changes such as network connectivity. +ledger : Sends a message at every ledger close. +transactions : Sends a message for every transaction that makes it into a ledger. +rt_transactions +*/ +// TODO +void WSConnection::doSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (jvRequest.isMember("streams")) + { + for (Json::Value::const_iterator it = jvRequest["streams"].begin(); it != jvRequest["streams"].end(); it++) + { + if ((*it).isString() ) + { + std::string streamName=(*it).asString(); + + if(streamName=="server") + { + mNetwork.subLedgerAccounts(this); + }else if(streamName=="ledger") + { + mNetwork.subLedgerAccounts(this); + }else if(streamName=="transactions") + { + mNetwork.subTransaction(this); + }else if(streamName=="rt_transactions") + { + mNetwork.subTransaction(this); // TODO + }else + { + jvResult["error"] = str(boost::format("Unknown stream: %s") % streamName); + } + }else + { + jvResult["error"] = "malformedSteam"; + } + } + } + + if (jvRequest.isMember("rt_accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["rt_accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.subAccountInfo(this, usnaAccoundIds); + } + } + + if (jvRequest.isMember("accounts")) + { + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + + if (usnaAccoundIds.empty()) + { + jvResult["error"] = "malformedAccount"; + }else + { + boost::mutex::scoped_lock sl(mLock); + + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) + { + mSubAccountInfo.insert(naAccountID); + } + + mNetwork.subAccountInfo(this, usnaAccoundIds); + } + } +} + +void WSConnection::doUnsubscribe(Json::Value& jvResult, const Json::Value& jvRequest) +{ + +} + void WSConnection::doAccountInfoSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) { if (!jvRequest.isMember("accounts")) @@ -415,23 +509,9 @@ void WSConnection::doAccountInfoSubscribe(Json::Value& jvResult, const Json::Val } else { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); - if (usnaAccoundIds.empty()) - { - jvResult["error"] = "malformedAccount"; - } - else - { - boost::mutex::scoped_lock sl(mLock); - - BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) - { - mSubAccountInfo.insert(naAccountID); - } - - mNetwork.subAccountInfo(this, usnaAccoundIds); - } + } } @@ -447,7 +527,7 @@ void WSConnection::doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::V } else { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); if (usnaAccoundIds.empty()) { @@ -457,7 +537,7 @@ void WSConnection::doAccountInfoUnsubscribe(Json::Value& jvResult, const Json::V { boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { mSubAccountInfo.erase(naAccountID); } @@ -479,7 +559,7 @@ void WSConnection::doAccountTransactionSubscribe(Json::Value& jvResult, const Js } else { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); if (usnaAccoundIds.empty()) { @@ -489,7 +569,7 @@ void WSConnection::doAccountTransactionSubscribe(Json::Value& jvResult, const Js { boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { mSubAccountTransaction.insert(naAccountID); } @@ -511,7 +591,7 @@ void WSConnection::doAccountTransactionUnsubscribe(Json::Value& jvResult, const } else { - boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); + boost::unordered_set usnaAccoundIds = parseAccountIds(jvRequest["accounts"]); if (usnaAccoundIds.empty()) { @@ -521,7 +601,7 @@ void WSConnection::doAccountTransactionUnsubscribe(Json::Value& jvResult, const { boost::mutex::scoped_lock sl(mLock); - BOOST_FOREACH(const NewcoinAddress& naAccountID, usnaAccoundIds) + BOOST_FOREACH(const RippleAddress& naAccountID, usnaAccoundIds) { mSubAccountTransaction.erase(naAccountID); } @@ -577,7 +657,7 @@ void WSConnection::doLedgerCurrent(Json::Value& jvResult, const Json::Value& jvR void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvRequest) { NetworkOPs& noNetwork = mNetwork; - uint256 uLedger = jvRequest.isMember("ledger") ? uint256(jvRequest["ledger"].asString()) : 0; + uint256 uLedger = jvRequest.isMember("ledger_closed") ? uint256(jvRequest["ledger_closed"].asString()) : 0; uint32 uLedgerIndex = jvRequest.isMember("ledger_index") && jvRequest["ledger_index"].isNumeric() ? jvRequest["ledger_index"].asUInt() : 0; Ledger::pointer lpLedger; @@ -635,9 +715,10 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else if (jvRequest.isMember("account_root")) { - NewcoinAddress naAccount; + RippleAddress naAccount; - if (!naAccount.setAccountID(jvRequest["account_root"].asString())) + if (!naAccount.setAccountID(jvRequest["account_root"].asString()) + || !naAccount.getAccountID()) { jvResult["error"] = "malformedAddress"; } @@ -674,7 +755,7 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else if (jvRequest["directory"].isMember("owner")) { - NewcoinAddress naOwnerID; + RippleAddress naOwnerID; if (!naOwnerID.setAccountID(jvRequest["directory"]["owner"].asString())) { @@ -695,7 +776,7 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else if (jvRequest.isMember("generator")) { - NewcoinAddress naGeneratorID; + RippleAddress naGeneratorID; if (!jvRequest.isObject()) { @@ -711,8 +792,8 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else { - NewcoinAddress na0Public; // To find the generator's index. - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naGeneratorID); + RippleAddress na0Public; // To find the generator's index. + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naGeneratorID); na0Public.setAccountPublic(naGenerator, 0); @@ -721,7 +802,7 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else if (jvRequest.isMember("offer")) { - NewcoinAddress naAccountID; + RippleAddress naAccountID; if (!jvRequest.isObject()) { @@ -746,21 +827,36 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } else if (jvRequest.isMember("ripple_state")) { - NewcoinAddress naA; - NewcoinAddress naB; + RippleAddress naA; + RippleAddress naB; uint160 uCurrency; + Json::Value jvRippleState = jvRequest["ripple_state"]; + + if (!jvRippleState.isMember("currency") + || !jvRippleState.isMember("accounts") + || !jvRippleState["accounts"].isArray() + || 2 != jvRippleState["accounts"].size() + || !jvRippleState["accounts"][0u].isString() + || !jvRippleState["accounts"][1u].isString() + || jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString() + ) { + + cLog(lsINFO) + << boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array: %d size: %d equal: %d") + % jvRippleState.isMember("accounts") + % jvRippleState.isMember("currency") + % jvRippleState["accounts"].isArray() + % jvRippleState["accounts"].size() + % (jvRippleState["accounts"][0u].asString() == jvRippleState["accounts"][1u].asString()) + ); - if (!jvRequest.isMember("accounts") - || !jvRequest.isMember("currency") - || !jvRequest["accounts"].isArray() - || 2 != jvRequest["accounts"].size()) { jvResult["error"] = "malformedRequest"; } - else if (!naA.setAccountID(jvRequest["accounts"][0u].asString()) - || !naB.setAccountID(jvRequest["accounts"][1u].asString())) { + else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString()) + || !naB.setAccountID(jvRippleState["accounts"][1u].asString())) { jvResult["error"] = "malformedAddress"; } - else if (!STAmount::currencyFromString(uCurrency, jvRequest["currency"].asString())) { + else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) { jvResult["error"] = "malformedCurrency"; } else @@ -801,6 +897,9 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq } } +// The objective is to allow the client to know the server's status. The only thing that show the server is fully operating is the +// stream of ledger_closeds. Therefore, that is all that is provided. A client can drop servers that do not provide recent +// ledger_closeds. void WSConnection::doServerSubscribe(Json::Value& jvResult, const Json::Value& jvRequest) { if (!mNetwork.subLedger(this)) @@ -812,10 +911,10 @@ void WSConnection::doServerSubscribe(Json::Value& jvResult, const Json::Value& j 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"] = mNetwork.getClosedLedger().ToString(); - jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID(); + if (NetworkOPs::omDISCONNECTED != mNetwork.getOperatingMode()) { + jvResult["ledger_closed"] = mNetwork.getClosedLedger().ToString(); + jvResult["ledger_current_index"] = mNetwork.getCurrentLedgerID(); + } } } @@ -827,10 +926,19 @@ void WSConnection::doServerUnsubscribe(Json::Value& jvResult, const Json::Value& } } +void WSConnection::doRPC(Json::Value& jvResult, const Json::Value& jvRequest) +{ + if (jvRequest.isMember("command")) + { + // TODO + }else jvResult["error"] = "fieldNotCommand"; + +} + // XXX Current requires secret. Allow signed transaction as an alternative. void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) { - NewcoinAddress naAccount; + RippleAddress naAccount; if (!jvRequest.isMember("transaction")) { @@ -862,7 +970,7 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) } bool bHaveAuthKey = false; - NewcoinAddress naAuthorizedPublic; + RippleAddress naAuthorizedPublic; #if 0 if (sleAccountRoot->isFieldPresent(sfAuthorizedKey)) @@ -872,11 +980,11 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) } #endif - NewcoinAddress naSecret = NewcoinAddress::createSeedGeneric(jvRequest["secret"].asString()); - NewcoinAddress naMasterGenerator = NewcoinAddress::createGeneratorPublic(naSecret); + 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. - NewcoinAddress naMasterAccountPublic; + RippleAddress naMasterAccountPublic; unsigned int iIndex = 0; bool bFound = false; @@ -900,9 +1008,9 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) } // Use the generator to determine the associated public and private keys. - NewcoinAddress naGenerator = NewcoinAddress::createGeneratorPublic(naSecret); - NewcoinAddress naAccountPublic = NewcoinAddress::createAccountPublic(naGenerator, iIndex); - NewcoinAddress naAccountPrivate = NewcoinAddress::createAccountPrivate(naGenerator, naSecret, iIndex); + RippleAddress naGenerator = RippleAddress::createGeneratorPublic(naSecret); + RippleAddress naAccountPublic = RippleAddress::createAccountPublic(naGenerator, iIndex); + RippleAddress naAccountPrivate = RippleAddress::createAccountPrivate(naGenerator, naSecret, iIndex); if (bHaveAuthKey // The generated pair must match authorized... @@ -933,7 +1041,18 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest) sopTrans->setFieldVL(sfSigningPubKey, naAccountPublic.getAccountPublic()); - SerializedTransaction::pointer stpTrans = boost::make_shared(*sopTrans); + SerializedTransaction::pointer stpTrans; + + try + { + stpTrans = boost::make_shared(*sopTrans); + } + catch (std::exception& e) + { + jvResult["error"] = "invalidTransaction"; + jvResult["error_exception"] = e.what(); + return; + } stpTrans->sign(naAccountPrivate); diff --git a/src/Wallet.cpp b/src/Wallet.cpp index ab9c866ef..1d21fb5eb 100644 --- a/src/Wallet.cpp +++ b/src/Wallet.cpp @@ -11,7 +11,7 @@ #include "Wallet.h" #include "Ledger.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "Application.h" #include "utils.h" @@ -69,9 +69,9 @@ bool Wallet::nodeIdentityCreate() { // // Generate the public and private key // - NewcoinAddress naSeed = NewcoinAddress::createSeedRandom(); - NewcoinAddress naNodePublic = NewcoinAddress::createNodePublic(naSeed); - NewcoinAddress naNodePrivate = NewcoinAddress::createNodePrivate(naSeed); + RippleAddress naSeed = RippleAddress::createSeedRandom(); + RippleAddress naNodePublic = RippleAddress::createNodePublic(naSeed); + RippleAddress naNodePrivate = RippleAddress::createNodePrivate(naSeed); // Make new key. diff --git a/src/Wallet.h b/src/Wallet.h index f1e9dc0af..5f9a06004 100644 --- a/src/Wallet.h +++ b/src/Wallet.h @@ -31,8 +31,8 @@ private: protected: boost::recursive_mutex mLock; - NewcoinAddress mNodePublicKey; - NewcoinAddress mNodePrivateKey; + RippleAddress mNodePublicKey; + RippleAddress mNodePrivateKey; DH* mDh512; DH* mDh1024; @@ -45,8 +45,8 @@ public: // - Maintain peer connectivity through validation and peer management. void start(); - const NewcoinAddress& getNodePublic() const { return mNodePublicKey; } - const NewcoinAddress& getNodePrivate() const { return mNodePrivateKey; } + const RippleAddress& getNodePublic() const { return mNodePublicKey; } + const RippleAddress& getNodePrivate() const { return mNodePrivateKey; } DH* getDh512() { return DHparams_dup(mDh512); } DH* getDh1024() { return DHparams_dup(mDh1024); } diff --git a/src/key.h b/src/key.h index 4fe681631..4d9983da1 100644 --- a/src/key.h +++ b/src/key.h @@ -16,7 +16,7 @@ #include #include "SecureAllocator.h" -#include "NewcoinAddress.h" +#include "RippleAddress.h" #include "uint256.h" #include "base58.h" @@ -124,9 +124,9 @@ public: static uint128 PassPhraseToKey(const std::string& passPhrase); static EC_KEY* GenerateRootDeterministicKey(const uint128& passPhrase); static EC_KEY* GenerateRootPubKey(BIGNUM* pubGenerator); - static EC_KEY* GeneratePublicDeterministicKey(const NewcoinAddress& generator, int n); - static EC_KEY* GeneratePrivateDeterministicKey(const NewcoinAddress& family, const BIGNUM* rootPriv, int n); - static EC_KEY* GeneratePrivateDeterministicKey(const NewcoinAddress& family, const uint256& rootPriv, int n); + static EC_KEY* GeneratePublicDeterministicKey(const RippleAddress& generator, int n); + static EC_KEY* GeneratePrivateDeterministicKey(const RippleAddress& family, const BIGNUM* rootPriv, int n); + static EC_KEY* GeneratePrivateDeterministicKey(const RippleAddress& family, const uint256& rootPriv, int n); CKey(const uint128& passPhrase) : fSet(false) { @@ -135,14 +135,14 @@ public: assert(pkey); } - CKey(const NewcoinAddress& generator, int n) : fSet(false) + CKey(const RippleAddress& generator, int n) : fSet(false) { // public deterministic key pkey = GeneratePublicDeterministicKey(generator, n); fSet = true; assert(pkey); } - CKey(const NewcoinAddress& base, const BIGNUM* rootPrivKey, int n) : fSet(false) + CKey(const RippleAddress& base, const BIGNUM* rootPrivKey, int n) : fSet(false) { // private deterministic key pkey = GeneratePrivateDeterministicKey(base, rootPrivKey, n); fSet = true; @@ -156,7 +156,7 @@ public: } #if 0 - CKey(const NewcoinAddress& masterKey, int keyNum, bool isPublic) : pkey(NULL), fSet(false) + CKey(const RippleAddress& masterKey, int keyNum, bool isPublic) : pkey(NULL), fSet(false) { if (isPublic) SetPubSeq(masterKey, keyNum); diff --git a/src/main.cpp b/src/main.cpp index d06f8838c..360c07857 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -84,7 +84,6 @@ int main(int argc, char* argv[]) { int iResult = 0; po::variables_map vm; // Map of options. - bool bTest = false; // // Set up option parsing. @@ -117,14 +116,11 @@ int main(int argc, char* argv[]) iResult = 2; } + if (iResult) { nothing(); } - else if (argc >= 2 && !strcmp(argv[1], "--test")) { - bTest = true; - Log::setMinSeverity(lsTRACE); - } else { // Parse options, if no error. @@ -142,9 +138,16 @@ int main(int argc, char* argv[]) } } + if (vm.count("verbose")) + Log::setMinSeverity(lsTRACE, true); + else + Log::setMinSeverity(lsWARNING, true); + + if (vm.count("test")) { - Log::setMinSeverity(lsTRACE); + unit_test_main(init_unit_test, argc, argv); + return 0; } if (!iResult) @@ -169,15 +172,6 @@ int main(int argc, char* argv[]) { iResult = 1; } - else if (vm.count("test")) - { - std::cerr << "--test must be first parameter." << std::endl; - iResult = 1; - } - else if (bTest) - { - iResult = unit_test_main(init_unit_test, argc, argv); - } else if (!vm.count("parameters")) { // No arguments. Run server. diff --git a/src/ripple.proto b/src/ripple.proto index a6ed14e55..64cd1b71f 100644 --- a/src/ripple.proto +++ b/src/ripple.proto @@ -245,15 +245,20 @@ message TMGetLedger { optional uint32 requestCookie = 6; } +enum TMReplyError { + reNO_LEDGER = 1; // We don't have the ledger you are asking about + reNO_NODE = 2; // We don't have any of the nodes you are asking for +} + message TMLedgerData { required bytes ledgerHash = 1; required uint32 ledgerSeq = 2; required TMLedgerInfoType type = 3; repeated TMLedgerNode nodes = 4; optional uint32 requestCookie = 5; + optional TMReplyError error = 6; } - message TMPing { enum pingType { PING = 0; // we want a reply diff --git a/test/amount-test.js b/test/amount-test.js index 3ddd40074..10f0fac8d 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -1,32 +1,95 @@ var buster = require("buster"); +var jsbn = require('../js/jsbn.js'); +var BigInteger = jsbn.BigInteger; +var nbi = jsbn.nbi; + var amount = require("../js/amount.js"); +var Amount = require("../js/amount.js").Amount; +var UInt160 = require("../js/amount.js").UInt160; + +var config = require('./config.js'); buster.testCase("Amount", { "UInt160" : { "Parse 0" : function () { - buster.assert.equals(0, amount.UInt160.from_json("0").value); + buster.assert.equals(nbi(), UInt160.from_json("0").value); }, "Parse 0 export" : function () { - buster.assert.equals(amount.consts.hex_xns, amount.UInt160.from_json("0").to_json()); + buster.assert.equals(amount.consts.address_xns, UInt160.from_json("0").to_json()); }, - "Parse native 123" : function () { - buster.assert.equals("123/XNS", amount.Amount.from_json("123").to_text_full()); + "Parse 1" : function () { + buster.assert.equals(new BigInteger([1]), UInt160.from_json("1").value); + }, + "Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export" : function () { + buster.assert.equals(amount.consts.address_xns, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json()); + }, + "Parse rrrrrrrrrrrrrrrrrrrrBZbvji export" : function () { + buster.assert.equals(amount.consts.address_one, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json()); + }, + "Parse mtgox export" : function () { + buster.assert.equals(config.accounts["mtgox"].account, UInt160.from_json("mtgox").to_json()); + }, + }, + "Amount parsing" : { + "Parse 800/USD/mtgox" : function () { + buster.assert.equals("800/USD/"+config.accounts["mtgox"].account, Amount.from_json("800/USD/mtgox").to_text_full()); + }, + "Parse native 0" : function () { + buster.assert.equals("0/XNS", Amount.from_json("0").to_text_full()); + }, + "Parse native 0.0" : function () { + buster.assert.equals("0/XNS", Amount.from_json("0.0").to_text_full()); + }, + "Parse native -0" : function () { + buster.assert.equals("0/XNS", Amount.from_json("-0").to_text_full()); + }, + "Parse native -0.0" : function () { + buster.assert.equals("0/XNS", Amount.from_json("-0.0").to_text_full()); + }, + "Parse native 1000" : function () { + buster.assert.equals("1000/XNS", Amount.from_json("1000").to_text_full()); }, "Parse native 12.3" : function () { - buster.assert.equals("12300000/XNS", amount.Amount.from_json("12.3").to_text_full()); + buster.assert.equals("12300000/XNS", Amount.from_json("12.3").to_text_full()); + }, + "Parse native -12.3" : function () { + buster.assert.equals("-12300000/XNS", Amount.from_json("-12.3").to_text_full()); }, "Parse 123./USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { - buster.assert.equals("123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("123./USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + buster.assert.equals("123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("123./USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); }, "Parse 12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { - buster.assert.equals("12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + buster.assert.equals("12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("12300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); }, "Parse 12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { - buster.assert.equals("12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + buster.assert.equals("12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("12.3/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); }, "Parse 1.2300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { - buster.assert.equals("1.23/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", amount.Amount.from_json("1.2300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + buster.assert.equals("1.23/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("1.2300/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + "Parse -0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + "Parse -0.0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" : function () { + buster.assert.equals("0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-0.0/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").to_text_full()); + }, + }, + "Amount operations" : { + "Negate native 123" : function () { + buster.assert.equals("-123/XNS", Amount.from_json("123").negate().to_text_full()); + }, + "Negate native -123" : function () { + buster.assert.equals("123/XNS", Amount.from_json("-123").negate().to_text_full()); + }, + "Negate non-native 123" : function () { + buster.assert.equals("-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").negate().to_text_full()); + }, + "Negate non-native -123" : function () { + buster.assert.equals("123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").negate().to_text_full()); + }, + "Clone non-native -123" : function () { + buster.assert.equals("-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", Amount.from_json("-123/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh").clone().to_text_full()); }, } }); diff --git a/test/config.js b/test/config.js index dcafa641f..d9e6eb584 100644 --- a/test/config.js +++ b/test/config.js @@ -7,10 +7,12 @@ var path = require("path"); // Where to find the binary. exports.rippled = path.join(process.cwd(), "rippled"); +exports.server_default = "alpha"; + // Configuration for servers. exports.servers = { // A local test server. - 'alpha' : { + "alpha" : { 'trusted' : true, // "peer_ip" : "0.0.0.0", // "peer_port" : 51235, @@ -18,47 +20,47 @@ exports.servers = { 'rpc_port' : 5005, 'websocket_ip' : "127.0.0.1", 'websocket_port' : 6005, - 'validation_seed' : "shhDFVsmS2GSu5vUyZSPXYfj1r79h", - 'validators' : "n9L8LZZCwsdXzKUN9zoVxs4YznYXZ9hEhsQZY7aVpxtFaSceiyDZ beta" + // 'validation_seed' : "shhDFVsmS2GSu5vUyZSPXYfj1r79h", + // 'validators' : "n9L8LZZCwsdXzKUN9zoVxs4YznYXZ9hEhsQZY7aVpxtFaSceiyDZ beta" } }; // Configuration for test accounts. exports.accounts = { // Users - 'alice' : { - 'account' : 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - 'secret' : 'alice', + "alice" : { + 'account' : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn", + 'secret' : "alice", }, - 'bob' : { - 'account' : 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK', - 'secret' : 'bob', + "bob" : { + 'account' : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK", + 'secret' : "bob", }, - 'carol' : { - 'account' : 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW', - 'secret' : 'carol', + "carol" : { + 'account' : "rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW", + 'secret' : "carol", }, // Nexuses - 'bitstamp' : { - 'account' : 'r4jKmc2nQb5yEU6eycefiNKGHTU5NQJASx', - 'secret' : 'bitstamp', + "bitstamp" : { + 'account' : "r4jKmc2nQb5yEU6eycefiNKGHTU5NQJASx", + 'secret' : "bitstamp", }, - 'mtgox' : { - 'account' : 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - 'secret' : 'mtgox', + "mtgox" : { + 'account' : "rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v", + 'secret' : "mtgox", }, // Merchants - 'amazon' : { - 'account' : 'rhheXqX7bDnXePJeMHhubDDvw2uUTtenPd', - 'secret' : 'amazon', + "amazon" : { + 'account' : "rhheXqX7bDnXePJeMHhubDDvw2uUTtenPd", + 'secret' : "amazon", }, // Master account - 'root' : { - 'account' : 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', - 'secret' : 'masterpassphrase', + "root" : { + 'account' : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", + 'secret' : "masterpassphrase", }, }; diff --git a/test/offer-test.js b/test/offer-test.js new file mode 100644 index 000000000..0efe31651 --- /dev/null +++ b/test/offer-test.js @@ -0,0 +1,334 @@ + +var async = require("async"); +var buster = require("buster"); + +var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; + +var testutils = require("./testutils.js"); + +buster.testRunner.timeout = 5000; + +buster.testCase("Offer tests", { + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), + + "offer create then cancel in one ledger" : + function (done) { + var self = this; + var final_create; + + async.waterfall([ + function (callback) { + self.remote.transaction() + .offer_create("root", "500", "100/USD/root") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS', m); + }) + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + + final_create = m; + }) + .submit(); + }, + function (m, callback) { + self.remote.transaction() + .offer_cancel("root", m.transaction.Sequence) + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS', m); + }) + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + buster.assert(final_create); + done(); + }) + .submit(); + }, + function (m, callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + }) + .ledger_accept(); + } + ], function (error) { + // console.log("result: error=%s", error); + buster.refute(error); + + if (error) done(); + }); + }, + + "offer_create then ledger_accept then offer_cancel then ledger_accept." : + function (done) { + var self = this; + var final_create; + var offer_seq; + + async.waterfall([ + function (callback) { + self.remote.transaction() + .offer_create("root", "500", "100/USD/root") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + + offer_seq = m.transaction.Sequence; + + callback(m.result != 'tesSUCCESS'); + }) + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + + final_create = m; + + callback(); + }) + .submit(); + }, + function (callback) { + if (!final_create) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + + }) + .ledger_accept(); + } + else { + callback(); + } + }, + function (callback) { + // console.log("CANCEL: offer_cancel: %d", offer_seq); + + self.remote.transaction() + .offer_cancel("root", offer_seq) + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + buster.assert(final_create); + + done(); + }) + .submit(); + }, + // See if ledger_accept will crash. + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + ], function (error) { + // console.log("result: error=%s", error); + buster.refute(error); + + if (error) done(); + }); + }, + + + "new user offer_create then ledger_accept then offer_cancel then ledger_accept." : + function (done) { + var self = this; + var final_create; + var offer_seq; + + async.waterfall([ + function (callback) { + self.remote.transaction() + .payment('root', 'alice', "1000") + .set_flags('CreateAccount') + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + buster.assert.equals(m.result, 'tesSUCCESS'); + callback(); + }) + .submit() + }, + function (callback) { + self.remote.transaction() + .offer_create("alice", "500", "100/USD/alice") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + + offer_seq = m.transaction.Sequence; + + callback(m.result != 'tesSUCCESS'); + }) + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + + final_create = m; + + callback(); + }) + .submit(); + }, + function (callback) { + if (!final_create) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + + }) + .ledger_accept(); + } + else { + callback(); + } + }, + function (callback) { + // console.log("CANCEL: offer_cancel: %d", offer_seq); + + self.remote.transaction() + .offer_cancel("alice", offer_seq) + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + buster.assert(final_create); + + done(); + }) + .submit(); + }, + // See if ledger_accept will crash. + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + ], function (error) { + // console.log("result: error=%s", error); + buster.refute(error); + if (error) done(); + }); + }, + + "offer cancel past and future sequence" : + function (done) { + var self = this; + var final_create; + + async.waterfall([ + function (callback) { + self.remote.transaction() + .payment('root', 'alice', Amount.from_json("10000")) + .set_flags('CreateAccount') + .on('proposed', function (m) { + // console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS', m); + }) + .on('error', function(m) { + // console.log("error: %s", m); + + buster.assert(false); + callback(m); + }) + .submit(); + }, + // Past sequence but wrong + function (m, callback) { + self.remote.transaction() + .offer_cancel("root", m.transaction.Sequence) + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS', m); + }) + .submit(); + }, + // Same sequence + function (m, callback) { + self.remote.transaction() + .offer_cancel("root", m.transaction.Sequence+1) + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m)); + callback(m.result != 'temBAD_SEQUENCE', m); + }) + .submit(); + }, + // Future sequence + function (m, callback) { + // After a malformed transaction, need to recover correct sequence. + self.remote.set_account_seq("root", self.remote.account_seq("root")-1); + + self.remote.transaction() + .offer_cancel("root", m.transaction.Sequence+2) + .on('proposed', function (m) { + // console.log("ERROR: offer_cancel future: %s", JSON.stringify(m)); + callback(m.result != 'temBAD_SEQUENCE'); + }) + .submit(); + }, + // See if ledger_accept will crash. + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + callback(); + } + ], function (error) { + // console.log("result: error=%s", error); + buster.refute(error); + + done(); + }); + }, +}); +// vim:sw=2:sts=2:ts=8 diff --git a/test/remote-test.js b/test/remote-test.js index b45f1a62a..eec201357 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -1,66 +1,32 @@ var buster = require("buster"); -var config = require("./config.js"); -var server = require("./server.js"); -var amount = require("../js/amount.js"); -var remote = require("../js/remote.js"); +var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; -var Amount = amount.Amount; +var testutils = require("./testutils.js"); var fastTearDown = true; // How long to wait for server to start. -var serverDelay = 1500; +var serverDelay = 1500; // XXX Not implemented. buster.testRunner.timeout = 5000; buster.testCase("Remote functions", { - 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - - alpha = remote.remoteConfig(config, "alpha"); - - alpha.connect(function (stat) { - buster.assert(1 == stat); // OPEN - done(); - }, serverDelay); - }); - }, - - 'tearDown' : - function (done) { - if (fastTearDown) { - // Fast tearDown - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - } - else { - alpha.disconnect(function (stat) { - buster.assert(3 == stat); // CLOSED - - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }); - } - }, + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), 'request_ledger_current' : function (done) { - alpha.request_ledger_current().on('success', function (m) { - console.log(m); + this.remote.request_ledger_current().on('success', function (m) { + // console.log(m); buster.assert.equals(m.ledger_current_index, 3); done(); }) .on('error', function(m) { - console.log(m); + // console.log(m); buster.assert(false); }) @@ -69,14 +35,14 @@ buster.testCase("Remote functions", { 'request_ledger_closed' : function (done) { - alpha.request_ledger_closed().on('success', function (m) { - console.log("result: %s", JSON.stringify(m)); + this.remote.request_ledger_closed().on('success', function (m) { + // console.log("result: %s", JSON.stringify(m)); buster.assert.equals(m.ledger_closed_index, 2); done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -85,12 +51,14 @@ buster.testCase("Remote functions", { 'manual account_root success' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { + var self = this; + + this.remote.request_ledger_closed().on('success', function (r) { // console.log("result: %s", JSON.stringify(r)); - alpha + self.remote .request_ledger_entry('account_root') - .ledger(r.ledger_closed) + .ledger_closed(r.ledger_closed) .account_root("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -99,28 +67,31 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .request(); }, + // XXX This should be detected locally. 'account_root remote malformedAddress' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('account_root') - .ledger(r.ledger_closed) + .ledger_closed(r.ledger_closed) .account_root("zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -128,7 +99,7 @@ buster.testCase("Remote functions", { buster.assert(false); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert.equals(m.error, 'remoteError'); buster.assert.equals(m.remote.error, 'malformedAddress'); @@ -137,7 +108,7 @@ buster.testCase("Remote functions", { .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -146,20 +117,22 @@ buster.testCase("Remote functions", { 'account_root entryNotFound' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('account_root') - .ledger(r.ledger_closed) - .account_root(config.accounts.alice.account) + .ledger_closed(r.ledger_closed) + .account_root("alice") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); buster.assert(false); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert.equals(m.error, 'remoteError'); buster.assert.equals(m.remote.error, 'entryNotFound'); @@ -168,7 +141,7 @@ buster.testCase("Remote functions", { .request(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }).request(); @@ -176,13 +149,15 @@ buster.testCase("Remote functions", { 'ledger_entry index' : function (done) { - alpha.request_ledger_closed().on('success', function (r) { - console.log("result: %s", JSON.stringify(r)); + var self = this; - alpha + this.remote.request_ledger_closed().on('success', function (r) { + // console.log("result: %s", JSON.stringify(r)); + + self.remote .request_ledger_entry('index') - .ledger(r.ledger_closed) - .account_root(config.accounts.alice.account) + .ledger_closed(r.ledger_closed) + .account_root("alice") .index("2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -191,14 +166,14 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }). request(); }) .on('error', function(m) { - console.log(m); + // console.log(m); buster.assert(false); }) @@ -207,9 +182,9 @@ buster.testCase("Remote functions", { 'create account' : function (done) { - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') + .set_flags('CreateAccount') .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -217,12 +192,54 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .submit(); }, + + "create account final" : + function (done) { + var self = this; + + var got_proposed; + var got_success; + + this.remote.transaction() + .payment('root', 'alice', Amount.from_json("10000")) + .set_flags('CreateAccount') + .on('success', function (r) { + // console.log("create_account: %s", JSON.stringify(r)); + + got_success = true; + }) + .on('error', function (m) { + // console.log("error: %s", m); + + buster.assert(false); + }) + .on('final', function (m) { + // console.log("final: %s", JSON.stringify(m)); + + buster.assert(got_success && got_proposed); + done(); + }) + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + // buster.assert.equals(m.result, 'terNO_DST'); + buster.assert.equals(m.result, 'tesSUCCESS'); + + got_proposed = true; + + self.remote.ledger_accept(); + }) + .on('status', function (s) { + // console.log("status: %s", JSON.stringify(s)); + }) + .submit(); + }, }); // vim:sw=2:sts=2:ts=8 diff --git a/test/send-test.js b/test/send-test.js new file mode 100644 index 000000000..898132209 --- /dev/null +++ b/test/send-test.js @@ -0,0 +1,511 @@ +var async = require("async"); +var buster = require("buster"); + +var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; + +var testutils = require("./testutils.js"); + +// How long to wait for server to start. +var serverDelay = 1500; + +buster.testRunner.timeout = 3000; + +buster.testCase("// Sending", { + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), + + "send XNS to non-existant account without create." : + function (done) { + var self = this; + var ledgers = 20; + var got_proposed; + + this.remote.transaction() + .payment('root', 'alice', "10000") + .on('success', function (r) { + // Transaction sent. + + // console.log("success: %s", JSON.stringify(r)); + }) + .on('pending', function() { + // Moving ledgers along. + // console.log("missing: %d", ledgers); + + ledgers -= 1; + if (ledgers) { + self.remote.ledger_accept(); + } + else { + buster.assert(false, "Final never received."); + done(); + } + }) + .on('lost', function () { + // Transaction did not make it in. + // console.log("lost"); + + buster.assert(true); + done(); + }) + .on('proposed', function (m) { + // Transaction got an error. + // console.log("proposed: %s", JSON.stringify(m)); + + buster.assert.equals(m.result, 'terNO_DST'); + + got_proposed = true; + + self.remote.ledger_accept(); // Move it along. + }) + .on('final', function (m) { + // console.log("final: %s", JSON.stringify(m)); + + buster.assert(false, "Should not have got a final."); + done(); + }) + .on('error', function(m) { + // console.log("error: %s", m); + + buster.assert(false); + }) + .submit(); + }, + + // Also test transaction becomes lost after terNO_DST. + "credit_limit to non-existant account = terNO_DST" : + function (done) { + this.remote.transaction() + .ripple_line_set("root", "100/USD/alice") + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + buster.assert.equals(m.result, 'terNO_DST'); + + done(); + }) + .submit(); + }, + + "credit_limit" : + function (done) { + var self = this; + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback); + }, + function (callback) { + self.what = "Check a non-existant credit limit."; + + self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT') + .on('ripple_state', function (m) { + callback(true); + }) + .on('error', function(m) { + // console.log("error: %s", JSON.stringify(m)); + + buster.assert.equals('remoteError', m.error); + buster.assert.equals('entryNotFound', m.remote.error); + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Create a credit limit."; + + testutils.credit_limit(self.remote, "alice", "800/USD/mtgox", callback); + }, + function (callback) { + self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT') + .on('ripple_state', function (m) { +// console.log("BALANCE: %s", JSON.stringify(m)); +// console.log("account_balance: %s", m.account_balance.to_text_full()); +// console.log("account_limit: %s", m.account_limit.to_text_full()); +// console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); +// console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); + buster.assert(m.account_balance.equals("0/USD/alice")); + buster.assert(m.account_limit.equals("800/USD/alice")); + buster.assert(m.issuer_balance.equals("0/USD/mtgox")); + buster.assert(m.issuer_limit.equals("0/USD/mtgox")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Modify a credit limit."; + + testutils.credit_limit(self.remote, "alice", "700/USD/mtgox", callback); + }, + function (callback) { + self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT') + .on('ripple_state', function (m) { + buster.assert(m.account_balance.equals("0/USD/alice")); + buster.assert(m.account_limit.equals("700/USD/alice")); + buster.assert(m.issuer_balance.equals("0/USD/mtgox")); + buster.assert(m.issuer_limit.equals("0/USD/mtgox")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Zero a credit limit."; + + testutils.credit_limit(self.remote, "alice", "0/USD/mtgox", callback); + }, + function (callback) { + self.what = "Make sure still exists."; + + self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT') + .on('ripple_state', function (m) { + buster.assert(m.account_balance.equals("0/USD/alice")); + buster.assert(m.account_limit.equals("0/USD/alice")); + buster.assert(m.issuer_balance.equals("0/USD/mtgox")); + buster.assert(m.issuer_limit.equals("0/USD/mtgox")); + + callback(); + }) + .request(); + }, + // Set negative limit. + function (callback) { + self.remote.transaction() + .ripple_line_set("alice", "-1/USD/mtgox") + .on('proposed', function (m) { + buster.assert.equals('temBAD_AMOUNT', m.result); + + // After a malformed transaction, need to recover correct sequence. + self.remote.set_account_seq("alice", self.remote.account_seq("alice")-1); + callback('temBAD_AMOUNT' !== m.result); + }) + .submit(); + }, + // TODO Check in both owner books. + function (callback) { + self.what = "Set another limit."; + + testutils.credit_limit(self.remote, "alice", "600/USD/bob", callback); + }, + function (callback) { + self.what = "Set limit on other side."; + + testutils.credit_limit(self.remote, "bob", "500/USD/alice", callback); + }, + function (callback) { + self.what = "Check ripple_line's state from alice's pov."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .on('ripple_state', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + buster.assert(m.account_balance.equals("0/USD/alice")); + buster.assert(m.account_limit.equals("600/USD/alice")); + buster.assert(m.issuer_balance.equals("0/USD/bob")); + buster.assert(m.issuer_limit.equals("500/USD/bob")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Check ripple_line's state from bob's pov."; + + self.remote.request_ripple_balance("bob", "alice", "USD", 'CURRENT') + .on('ripple_state', function (m) { + buster.assert(m.account_balance.equals("0/USD/bob")); + buster.assert(m.account_limit.equals("500/USD/bob")); + buster.assert(m.issuer_balance.equals("0/USD/alice")); + buster.assert(m.issuer_limit.equals("600/USD/alice")); + + callback(); + }) + .request(); + }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, +}); + +// XXX In the future add ledger_accept after partial retry is implemented in the server. +buster.testCase("// Sending future", { + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), + + "direct ripple" : + function (done) { + var self = this; + + self.remote.set_trace(); + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob"], callback); + }, + function (callback) { + self.what = "Set alice's limit."; + + testutils.credit_limit(self.remote, "alice", "600/USD/bob", callback); + }, + function (callback) { + self.what = "Set bob's limit."; + + testutils.credit_limit(self.remote, "bob", "700/USD/alice", callback); + }, + function (callback) { + self.what = "Set alice send bob partial with alice as issuer."; + + self.remote.transaction() + .payment('alice', 'bob', "24/USD/alice") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .once('final', function (m) { + buster.assert(m.result != 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .once('ripple_state', function (m) { + console.log("BALANCE: %s", JSON.stringify(m)); + console.log("account_balance: %s", m.account_balance.to_text_full()); + console.log("account_limit: %s", m.account_limit.to_text_full()); + console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); + console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); + + buster.assert(m.account_balance.equals("-24/USD/alice")); + buster.assert(m.issuer_balance.equals("24/USD/bob")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Set alice send bob more with bob as issuer."; + + self.remote.transaction() + .payment('alice', 'bob', "33/USD/bob") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .once('final', function (m) { + buster.assert(m.result != 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance from bob's pov."; + + self.remote.request_ripple_balance("bob", "alice", "USD", 'CURRENT') + .once('ripple_state', function (m) { + buster.assert(m.account_balance.equals("57/USD/bob")); + buster.assert(m.issuer_balance.equals("-57/USD/alice")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Bob send back more than sent."; + + self.remote.transaction() + .payment('bob', 'alice', "90/USD/bob") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .once('final', function (m) { + buster.assert(m.result != 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance from alice's pov."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .once('ripple_state', function (m) { + buster.assert(m.account_balance.equals("33/USD/alice")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Alice send to limit."; + + self.remote.transaction() + .payment('alice', 'bob', "733/USD/bob") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .once('final', function (m) { + buster.assert(m.result != 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance from alice's pov."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .once('ripple_state', function (m) { + buster.assert(m.account_balance.equals("-700/USD/alice")); + + callback(); + }) + .request(); + }, + function (callback) { + self.what = "Bob send to limit."; + + self.remote.transaction() + .payment('bob', 'alice', "1300/USD/bob") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS'); + }) + .once('final', function (m) { + buster.assert(m.result != 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance from alice's pov."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .once('ripple_state', function (m) { + buster.assert(m.account_balance.equals("600/USD/alice")); + + callback(); + }) + .request(); + }, + function (callback) { + // If this gets applied out of order, it could stop the big payment. + self.what = "Bob send past limit."; + + self.remote.transaction() + .payment('bob', 'alice', "1/USD/bob") + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result != 'tepPATH_PARTIAL'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balance from alice's pov."; + + self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') + .once('ripple_state', function (m) { + buster.assert(m.account_balance.equals("600/USD/alice")); + + callback(); + }) + .request(); + }, +// function (callback) { +// // Make sure all is good after canonical ordering. +// self.what = "Close the ledger and check balance."; +// +// self.remote +// .once('ledger_closed', function (ledger_closed, ledger_closed_index) { +// // console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); +// callback(); +// }) +// .ledger_accept(); +// }, +// function (callback) { +// self.what = "Verify balance from alice's pov."; +// +// self.remote.request_ripple_balance("alice", "bob", "USD", 'CURRENT') +// .once('ripple_state', function (m) { +// console.log("account_balance: %s", m.account_balance.to_text_full()); +// console.log("account_limit: %s", m.account_limit.to_text_full()); +// console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); +// console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); +// +// buster.assert(m.account_balance.equals("600/USD/alice")); +// +// callback(); +// }) +// .request(); +// }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, + + // Ripple without credit path. + // Ripple with one-way credit path. +}); + +buster.testCase("Indirect ripple", { + 'setUp' : testutils.build_setup({ verbose: true, no_server: false }), + 'tearDown' : testutils.test_teardown, + + "indirect ripple" : + function (done) { + var self = this; + + self.remote.set_trace(); + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback); + }, + function (callback) { + self.what = "Set alice's limit."; + + testutils.credit_limit(self.remote, "alice", "600/USD/mtgox", callback); + }, + function (callback) { + self.what = "Set bob's limit."; + + testutils.credit_limit(self.remote, "bob", "700/USD/mtgox", callback); + }, + function (callback) { + self.what = "Give alice some mtgox."; + + testutils.payment(self.remote, "mtgox", "alice", "70/USD/mtgox", callback); + }, + function (callback) { + self.what = "Give bob some mtgox."; + + testutils.payment(self.remote, "mtgox", "bob", "50/USD/mtgox", callback); + }, + function (callback) { + self.what = "Verify alice balance with mtgox."; + + testutils.verify_balance(self.remote, "alice", "70/USD/mtgox", callback); + }, + function (callback) { + self.what = "Verify bob balance with mtgox."; + + testutils.verify_balance(self.remote, "bob", "50/USD/mtgox", callback); + }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, + + // Ripple without credit path. + // Ripple with one-way credit path. +}); +// vim:sw=2:sts=2:ts=8 diff --git a/test/server-test.js b/test/server-test.js index 817b5f9fd..24496537e 100644 --- a/test/server-test.js +++ b/test/server-test.js @@ -1,22 +1,27 @@ var buster = require("buster"); -var server = require("./server.js"); +var Server = require("./server.js").Server; // How long to wait for server to start. -var serverDelay = 1500; +// var serverDelay = 1500; -buster.testRunner.timeout = 5000; +var alpha; 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(); - }); - }); + alpha = Server.from_config("alpha"); + + alpha + .on('started', function () { + alpha + .on('stopped', function () { + buster.assert(true); + + done(); + }) + .stop(); + }) + .start(); } }); diff --git a/test/server.js b/test/server.js index be6d8e7be..aca9b3984 100644 --- a/test/server.js +++ b/test/server.js @@ -1,6 +1,13 @@ -// Manage test servers +// Create and stop test servers. // -// YYY Would be nice to be able to hide server output. +// Usage: +// s = new Server(name, config) +// s.verbose() : optional +// .start() +// 'started' +// +// s.stop() : stops server is started. +// 'stopped' // // Provide servers @@ -8,28 +15,40 @@ // Servers are created in tmp/server/$server // -var config = require("./config.js"); -var nodeutils = require("../js/nodeutils.js"); +var buster = require("buster"); +var child = require("child_process"); +var fs = require("fs"); +var path = require("path"); +var util = require("util"); +var EventEmitter = require('events').EventEmitter; -var fs = require("fs"); -var path = require("path"); -var util = require("util"); -var child = require("child_process"); - -var servers = {}; +var config = require("./config.js"); +var nodeutils = require("../js/nodeutils.js"); // Create a server object -var Server = function (name) { - this.name = name; +var Server = function (name, config, verbose) { + this.name = name; + this.config = config; + this.started = false; + this.quiet = !verbose; }; -// Return a server's rippled.cfg as string. -Server.prototype.configContent = function() { - var cfg = config.servers[this.name]; +Server.prototype = new EventEmitter; - return Object.keys(cfg).map(function(o) { - return util.format("[%s]\n%s\n", o, cfg[o]); - }).join(""); +Server.from_config = function (name, verbose) { + return new Server(name, config.servers[name], verbose); +}; + +Server.prototype.on = function (e, c) { + EventEmitter.prototype.on.call(this, e, c); + + return this; +}; + +Server.prototype.once = function (e, c) { + EventEmitter.prototype.once.call(this, e, c); + + return this; }; Server.prototype.serverPath = function() { @@ -41,38 +60,51 @@ Server.prototype.configPath = function() { }; // Write a server's rippled.cfg. -Server.prototype.writeConfig = function(done) { - fs.writeFile(this.configPath(), this.configContent(), 'utf8', done); +Server.prototype._writeConfig = function(done) { + var self = this; + + fs.writeFile( + this.configPath(), + Object.keys(this.config).map(function(o) { + return util.format("[%s]\n%s\n", o, self.config[o]); + }).join(""), + 'utf8', done); }; // Spawn the server. -Server.prototype.serverSpawnSync = function() { - // Spawn in standalone mode for now. - this.child = child.spawn( - config.rippled, - [ +Server.prototype._serverSpawnSync = function() { + var self = this; + + var args = [ "-a", "-v", "--conf=rippled.cfg" - ], + ]; + + // Spawn in standalone mode for now. + this.child = child.spawn( + config.rippled, + args, { cwd: this.serverPath(), env: process.env, - stdio: 'inherit' + stdio: this.quiet ? 'ignore' : 'inherit' }); - console.log("server: start %s: %s -a --conf=%s", this.child.pid, config.rippled, this.configPath()); + if (!this.quiet) + console.log("server: start %s: %s --conf=%s", + this.child.pid, config.rippled, args.join(" "), this.configPath()); // By default, just log exits. this.child.on('exit', function(code, signal) { // If could not exec: code=127, signal=null // If regular exit: code=0, signal=null - console.log("server: spawn: server exited code=%s: signal=%s", code, signal); + if (!self.quiet) console.log("server: spawn: server exited code=%s: signal=%s", code, signal); }); }; // Prepare server's working directory. -Server.prototype.makeBase = function (done) { +Server.prototype._makeBase = function (done) { var path = this.serverPath(); var self = this; @@ -82,71 +114,59 @@ Server.prototype.makeBase = function (done) { throw e; } else { - self.writeConfig(done); + self._writeConfig(done); } }); }; +Server.prototype.verbose = function () { + this.quiet = false; + + return this; +}; + // Create a standalone server. // Prepare the working directory and spawn the server. -Server.prototype.start = function (done) { +Server.prototype.start = function () { var self = this; - this.makeBase(function (e) { + if (!this.quiet) console.log("server: start: %s: %s", this.name, JSON.stringify(this.config)); + + this._makeBase(function (e) { if (e) { throw e; } else { - self.serverSpawnSync(); - done(); + self._serverSpawnSync(); + self.emit('started'); } }); + + return this; }; // Stop a standalone server. -Server.prototype.stop = function (done) { +Server.prototype.stop = function () { + var self = this; + if (this.child) { // Update the on exit to invoke done. this.child.on('exit', function (code, signal) { - console.log("server: stop: server exited"); - done(); + + if (!self.quiet) console.log("server: stop: server exited"); + + self.emit('stopped'); + delete this.child; }); + this.child.kill(); } else { - console.log("server: stop: no such server"); - done('noSuchServer'); + buster.log("server: stop: can't stop"); } -}; -// Start the named server. -exports.start = function (name, done) { - if (servers[name]) - { - console.log("server: start: server already started."); - } - else - { - var server = new Server(name); - - servers[name] = server; - - console.log("server: start: %s", JSON.stringify(server)); - - server.start(done); - } -}; - -// Delete the named server. -exports.stop = function (name, done) { - console.log("server: stop: %s of %s", name, Object.keys(servers).toString()); - - var server = servers[name]; - if (server) { - server.stop(done); - delete servers[name]; - } + return this; }; exports.Server = Server; diff --git a/test/testutils.js b/test/testutils.js new file mode 100644 index 000000000..d573111c8 --- /dev/null +++ b/test/testutils.js @@ -0,0 +1,173 @@ +var async = require("async"); +// var buster = require("buster"); + +var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; + +var config = require("./config.js"); + +/** + * Helper called by test cases to generate a setUp routine. + * + * By default you would call this without options, but it is useful to + * be able to plug options in during development for quick and easy + * debugging. + * + * @example + * buster.testCase("Foobar", { + * setUp: testutils.build_setup({verbose: true}), + * // ... + * }); + * + * @param opts {Object} These options allow quick-and-dirty test-specific + * customizations of your test environment. + * @param opts.verbose {Bool} Enable all debug output (then cover your ears + * and run) + * @param opts.verbose_ws {Bool} Enable tracing in the Remote class. Prints + * websocket traffic. + * @param opts.verbose_server {Bool} Set the -v option when running rippled. + * @param opts.no_server {Bool} Don't auto-run rippled. + * @param host {String} Identifier for the host configuration to be used. + */ +var build_setup = function (opts, host) { + opts = opts || {}; + + // Normalize options + if (opts.verbose) { + opts.verbose_ws = true; + opts.verbose_server = true; + }; + + return function (done) { + var self = this; + + host = host || config.server_default; + + this.store = this.store || {}; + + var data = this.store[host] = this.store[host] || {}; + + data.opts = opts; + + async.series([ + function runServerStep(callback) { + if (opts.no_server) return callback(); + + data.server = Server.from_config(host, !!opts.verbose_server).on('started', callback).start(); + }, + function connectWebsocketStep(callback) { + self.remote = data.remote = Remote.from_config(host, !!opts.verbose_ws).once('ledger_closed', callback).connect(); + } + ], done); + }; +}; + +/** + * Generate tearDown routine. + * + * @param host {String} Identifier for the host configuration to be used. + */ +var build_teardown = function (host) { + return function (done) { + host = host || config.server_default; + + var data = this.store[host]; + var opts = data.opts; + + async.series([ + function disconnectWebsocketStep(callback) { + data.remote + .on('disconnected', callback) + .connect(false); + }, + function stopServerStep(callback) { + if (opts.no_server) return callback(); + + data.server.on('stopped', callback).stop(); + } + ], done); + }; +}; + +var create_accounts = function (remote, src, amount, accounts, callback) { + assert(5 === arguments.length); + + async.forEachSeries(accounts, function (account, callback) { + remote.transaction() + .payment(src, account, amount) + .set_flags('CreateAccount') + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result != 'tesSUCCESS'); + }) + .on('error', function (m) { + // console.log("error: %s", JSON.stringify(m)); + + callback(m); + }) + .submit(); + }, callback); +}; + +var credit_limit = function (remote, src, amount, callback) { + assert(4 === arguments.length); + + remote.transaction() + .ripple_line_set(src, amount) + .on('proposed', function (m) { + console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result != 'tesSUCCESS'); + }) + .on('error', function (m) { + // console.log("error: %s", JSON.stringify(m)); + + callback(m); + }) + .submit(); +}; + +var payment = function (remote, src, dst, amount, callback) { + assert(5 === arguments.length); + + remote.transaction() + .payment(src, dst, amount) + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result != 'tesSUCCESS'); + }) + .on('error', function (m) { + // console.log("error: %s", JSON.stringify(m)); + + callback(m); + }) + .submit(); +}; + +var verify_balance = function (remote, src, amount_json, callback) { + assert(4 === arguments.length); + var amount = Amount.from_json(amount_json); + + remote.request_ripple_balance(src, amount.issuer.to_json(), amount.currency.to_json(), 'CURRENT') + .once('ripple_state', function (m) { + console.log("BALANCE: %s", JSON.stringify(m)); + console.log("account_balance: %s", m.account_balance.to_text_full()); + console.log("account_limit: %s", m.account_limit.to_text_full()); + console.log("issuer_balance: %s", m.issuer_balance.to_text_full()); + console.log("issuer_limit: %s", m.issuer_limit.to_text_full()); + + callback(!m.account_balance.equals(amount)); + }) + .request(); +}; +exports.build_setup = build_setup; +exports.create_accounts = create_accounts; +exports.credit_limit = credit_limit; +exports.payment = payment; +exports.build_teardown = build_teardown; +exports.verify_balance = verify_balance; + +// vim:sw=2:sts=2:ts=8 diff --git a/test/websocket-test.js b/test/websocket-test.js index 9e6e87261..dcfc25d5b 100644 --- a/test/websocket-test.js +++ b/test/websocket-test.js @@ -1,8 +1,7 @@ var buster = require("buster"); -var config = require("./config.js"); -var server = require("./server.js"); -var remote = require("../js/remote.js"); +var Server = require("./server.js").Server; +var Remote = require("../js/remote.js").Remote; // How long to wait for server to start. var serverDelay = 1500; @@ -11,35 +10,29 @@ buster.testRunner.timeout = 5000; buster.testCase("WebSocket connection", { 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - done(); - } - ); - }, + function (done) { server = Server.from_config("alpha").on('started', done).start(); }, 'tearDown' : - function (done) { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }, + function (done) { server.on('stopped', done).stop(); }, "websocket connect and disconnect" : function (done) { - var alpha = remote.remoteConfig(config, "alpha", 'TRACE'); + var alpha = Remote.from_config("alpha"); - alpha.connect(function (stat) { - buster.assert.equals(stat, 1); // OPEN + alpha + .on('connected', function () { + // OPEN + buster.assert(true); - alpha.disconnect(function (stat) { - buster.assert.equals(stat, 3); // CLOSED - done(); - }); - }, serverDelay); + alpha + .on('disconnected', function () { + // CLOSED + buster.assert(true); + done(); + }) + .connect(false); + }) + .connect(); }, }); diff --git a/validators.txt b/validators.txt index 335d5d5ad..cde2a6711 100644 --- a/validators.txt +++ b/validators.txt @@ -20,7 +20,6 @@ # [validators] -n9LQC4xFSWXNv1SU1sKtjrW6TZpBZSwp1nRWej8saGs155x42YFZ first -n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second -n9KXAZxiHkWuVGxDEE8boW7WmcycpZNmWei4vxVaywLZ391Nbuqx third -n94365hzFKikgCULeJwczs3kwzpir3KVHkfhUWGT4MjmbEbC5xBy +n9KPnVLn7ewVzHvn218DcEYsnWLzKerTDwhpofhk4Ym1RUq4TeGw first +n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second +n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P third