#!/usr/bin/node // // ledger?l=L // transaction?h=H // ledger_entry?l=L&h=H // account?l=L&a=A // directory?l=L&dir_root=H&i=I // directory?l=L&o=A&i=I // owner directory // offer?l=L&offer=H // offer?l=L&account=A&i=I // ripple_state=l=L&a=A&b=A&c=C // account_lines?l=L&a=A // // A=address // C=currency 3 letter code // H=hash // I=index // L=current | closed | validated | index | hash // var async = require("async"); var extend = require("extend"); var http = require("http"); var url = require("url"); var Remote = require("ripple-lib").Remote; var program = process.argv[1]; var httpd_response = function (res, opts) { var self=this; res.statusCode = opts.statusCode; res.end( "" + "Title" + "" + "State:" + self.state + "" + (opts.body || '') + '
'
      + (opts.url || '')
      + '
' + "" + "" ); }; var html_link = function (generic) { return '' + generic + ''; }; // Build a link to a type. var build_uri = function (params, opts) { var c; if (params.type === 'account') { c = { pathname: 'account', query: { l: params.ledger, a: params.account, }, }; } else if (params.type === 'ledger') { c = { pathname: 'ledger', query: { l: params.ledger, }, }; } else if (params.type === 'transaction') { c = { pathname: 'transaction', query: { h: params.hash, }, }; } else { c = {}; } opts = opts || {}; c.protocol = "http"; c.hostname = opts.hostname || self.base.hostname; c.port = opts.port || self.base.port; return url.format(c); }; var build_link = function (item, link) { console.log(link); return "" + item + ""; }; var rewrite_field = function (type, obj, field, opts) { if (field in obj) { obj[field] = rewrite_type(type, obj[field], opts); } }; var rewrite_type = function (type, obj, opts) { if ('amount' === type) { if ('string' === typeof obj) { // XRP. return '' + obj + ''; } else { rewrite_field('address', obj, 'issuer', opts); return obj; } return build_link( obj, build_uri({ type: 'account', account: obj }, opts) ); } if ('address' === type) { return build_link( obj, build_uri({ type: 'account', account: obj }, opts) ); } else if ('ledger' === type) { return build_link( obj, build_uri({ type: 'ledger', ledger: obj, }, opts) ); } else if ('node' === type) { // A node if ('PreviousTxnID' in obj) obj.PreviousTxnID = rewrite_type('transaction', obj.PreviousTxnID, opts); if ('Offer' === obj.LedgerEntryType) { if ('NewFields' in obj) { if ('TakerGets' in obj.NewFields) obj.NewFields.TakerGets = rewrite_type('amount', obj.NewFields.TakerGets, opts); if ('TakerPays' in obj.NewFields) obj.NewFields.TakerPays = rewrite_type('amount', obj.NewFields.TakerPays, opts); } } obj.LedgerEntryType = '' + obj.LedgerEntryType + ''; return obj; } else if ('transaction' === type) { // Reference to a transaction. return build_link( obj, build_uri({ type: 'transaction', hash: obj }, opts) ); } return 'ERROR: ' + type; }; var rewrite_object = function (obj, opts) { var out = extend({}, obj); rewrite_field('address', out, 'Account', opts); rewrite_field('ledger', out, 'parent_hash', opts); rewrite_field('ledger', out, 'ledger_index', opts); rewrite_field('ledger', out, 'ledger_current_index', opts); rewrite_field('ledger', out, 'ledger_hash', opts); if ('ledger' in obj) { // It's a ledger header. out.ledger = rewrite_object(out.ledger, opts); if ('ledger_hash' in out.ledger) out.ledger.ledger_hash = '' + out.ledger.ledger_hash + ''; delete out.ledger.hash; delete out.ledger.totalCoins; } if ('TransactionType' in obj) { // It's a transaction. out.TransactionType = '' + obj.TransactionType + ''; rewrite_field('amount', out, 'TakerGets', opts); rewrite_field('amount', out, 'TakerPays', opts); rewrite_field('ledger', out, 'inLedger', opts); out.meta.AffectedNodes = out.meta.AffectedNodes.map(function (node) { var kind = 'CreatedNode' in node ? 'CreatedNode' : 'ModifiedNode' in node ? 'ModifiedNode' : 'DeletedNode' in node ? 'DeletedNode' : undefined; if (kind) { node[kind] = rewrite_type('node', node[kind], opts); } return node; }); } else if ('node' in obj && 'LedgerEntryType' in obj.node) { // Its a ledger entry. if (obj.node.LedgerEntryType === 'AccountRoot') { rewrite_field('address', out.node, 'Account', opts); rewrite_field('transaction', out.node, 'PreviousTxnID', opts); rewrite_field('ledger', out.node, 'PreviousTxnLgrSeq', opts); } out.node.LedgerEntryType = '' + out.node.LedgerEntryType + ''; } return out; }; var augment_object = function (obj, opts, done) { if (obj.node.LedgerEntryType == 'AccountRoot') { var tx_hash = obj.node.PreviousTxnID; var tx_ledger = obj.node.PreviousTxnLgrSeq; obj.history = []; async.whilst( function () { return tx_hash; }, function (callback) { // console.log("augment_object: request: %s %s", tx_hash, tx_ledger); opts.remote.request_tx(tx_hash) .on('success', function (m) { tx_hash = undefined; tx_ledger = undefined; //console.log("augment_object: ", JSON.stringify(m)); m.meta.AffectedNodes.filter(function(n) { // console.log("augment_object: ", JSON.stringify(n)); // if (n.ModifiedNode) // console.log("augment_object: %s %s %s %s %s %s/%s", 'ModifiedNode' in n, n.ModifiedNode && (n.ModifiedNode.LedgerEntryType === 'AccountRoot'), n.ModifiedNode && n.ModifiedNode.FinalFields && (n.ModifiedNode.FinalFields.Account === obj.node.Account), Object.keys(n)[0], n.ModifiedNode && (n.ModifiedNode.LedgerEntryType), obj.node.Account, n.ModifiedNode && n.ModifiedNode.FinalFields && n.ModifiedNode.FinalFields.Account); // if ('ModifiedNode' in n && n.ModifiedNode.LedgerEntryType === 'AccountRoot') // { // console.log("***: ", JSON.stringify(m)); // console.log("***: ", JSON.stringify(n)); // } return 'ModifiedNode' in n && n.ModifiedNode.LedgerEntryType === 'AccountRoot' && n.ModifiedNode.FinalFields && n.ModifiedNode.FinalFields.Account === obj.node.Account; }) .forEach(function (n) { tx_hash = n.ModifiedNode.PreviousTxnID; tx_ledger = n.ModifiedNode.PreviousTxnLgrSeq; obj.history.push({ tx_hash: tx_hash, tx_ledger: tx_ledger }); console.log("augment_object: next: %s %s", tx_hash, tx_ledger); }); callback(); }) .on('error', function (m) { callback(m); }) .request(); }, function (err) { if (err) { done(); } else { async.forEach(obj.history, function (o, callback) { opts.remote.request_account_info(obj.node.Account) .ledger_index(o.tx_ledger) .on('success', function (m) { //console.log("augment_object: ", JSON.stringify(m)); o.Balance = m.account_data.Balance; // o.account_data = m.account_data; callback(); }) .on('error', function (m) { o.error = m; callback(); }) .request(); }, function (err) { done(err); }); } }); } else { done(); } }; if (process.argv.length < 4 || process.argv.length > 7) { console.log("Usage: %s ws_ip ws_port [ [ []]]", program); } else { var ws_ip = process.argv[2]; var ws_port = process.argv[3]; var ip = process.argv.length > 4 ? process.argv[4] : "127.0.0.1"; var port = process.argv.length > 5 ? process.argv[5] : "8080"; // console.log("START"); var self = this; var remote = (new Remote({ websocket_ip: ws_ip, websocket_port: ws_port, trace: false })) .on('state', function (m) { console.log("STATE: %s", m); self.state = m; }) // .once('ledger_closed', callback) .connect() ; self.base = { hostname: ip, port: port, remote: remote, }; // console.log("SERVE"); var server = http.createServer(function (req, res) { var input = ""; req.setEncoding(); req.on('data', function (buffer) { // console.log("DATA: %s", buffer); input = input + buffer; }); req.on('end', function () { // console.log("URL: %s", req.url); // console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2)); var _parsed = url.parse(req.url, true); var _url = JSON.stringify(_parsed, undefined, 2); // console.log("HEADERS: %s", JSON.stringify(_parsed, undefined, 2)); if (_parsed.pathname === "/account") { var request = remote .request_ledger_entry('account_root') .ledger_index(-1) .account_root(_parsed.query.a) .on('success', function (m) { // console.log("account_root: %s", JSON.stringify(m, undefined, 2)); augment_object(m, self.base, function() { httpd_response(res, { statusCode: 200, url: _url, body: "
"
                              + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
                              + "
" }); }); }) .request(); } else if (_parsed.pathname === "/ledger") { var request = remote .request_ledger(undefined, { expand: true, transactions: true }) .on('success', function (m) { // console.log("Ledger: %s", JSON.stringify(m, undefined, 2)); httpd_response(res, { statusCode: 200, url: _url, body: "
"
                          + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
                          +"
" }); }) if (_parsed.query.l && _parsed.query.l.length === 64) { request.ledger_hash(_parsed.query.l); } else if (_parsed.query.l) { request.ledger_index(Number(_parsed.query.l)); } else { request.ledger_index(-1); } request.request(); } else if (_parsed.pathname === "/transaction") { var request = remote .request_tx(_parsed.query.h) // .request_transaction_entry(_parsed.query.h) // .ledger_select(_parsed.query.l) .on('success', function (m) { // console.log("transaction: %s", JSON.stringify(m, undefined, 2)); httpd_response(res, { statusCode: 200, url: _url, body: "
"
                            + JSON.stringify(rewrite_object(m, self.base), undefined, 2)
                            +"
" }); }) .on('error', function (m) { httpd_response(res, { statusCode: 200, url: _url, body: "
"
                            + 'ERROR: ' + JSON.stringify(m, undefined, 2)
                            +"
" }); }) .request(); } else { var test = build_uri({ type: 'account', ledger: 'closed', account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', }, self.base); httpd_response(res, { statusCode: req.url === "/" ? 200 : 404, url: _url, }); } }); }); server.listen(port, ip, undefined, function () { console.log("Listening at: http://%s:%s", ip, port); }); } // vim:sw=2:sts=2:ts=8:et