diff --git a/js/amount.js b/js/amount.js index 36d57889b..b0ac9c0ed 100644 --- a/js/amount.js +++ b/js/amount.js @@ -4,14 +4,11 @@ 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 accounts = {}; - -var setAccounts = function (accounts_new) { - accounts = accounts_new; -}; - var UInt160 = function () { // Internal form: // 0, 1, 'iXXXXX', 20 byte string, or NaN. @@ -41,11 +38,15 @@ UInt160.prototype.copyTo = function(d) { return d; }; +UInt160.prototype.equals = function(d) { + return this.value === d.value; +}; + // value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate - if (j in accounts) - j = accounts[j].account; + if (config.accounts && j in config.accounts) + j = config.accounts[j].account; switch (j) { case undefined: @@ -169,6 +170,12 @@ 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(); }; @@ -177,15 +184,18 @@ 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 { @@ -194,7 +204,7 @@ 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 : !this.is_negative; this.currency.copyTo(d.currency); this.issuer.copyTo(d.issuer); @@ -269,15 +279,13 @@ Amount.prototype.canonicalize = function() { this.offset += 1; } } + + return this; }; +// Return a new value. Amount.prototype.negate = function () { - if (this.is_native) { - this.value.negate(); - } - else { - this.is_negative = !this.is_negative; - } + return this.clone('NEGATE'); }; Amount.prototype.to_json = function() { @@ -310,23 +318,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; @@ -445,7 +456,20 @@ Amount.prototype.parse_issuer = function (issuer) { return this; }; -exports.setAccounts = setAccounts; +// 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; diff --git a/js/remote.js b/js/remote.js index 4f57c5c3a..554411fc6 100644 --- a/js/remote.js +++ b/js/remote.js @@ -3,7 +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. +// 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,6 +24,9 @@ var WebSocket = require('ws'); 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. // 'error' : Request failed. @@ -117,8 +128,8 @@ Request.prototype.transaction = function (t) { Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { 'accounts' : [ - UInt160.from_json(account).to_json(), - UInt160.from_json(issuer).to_json() + UInt160.json_rewrite(account), + UInt160.json_rewrite(issuer) ], 'currency' : currency }; @@ -140,12 +151,11 @@ Request.prototype.ripple_state = function (account, issuer, currency) { // // --> 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; @@ -155,6 +165,7 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { this.state = 'offline'; // 'online', 'offline' this.retry_timer = undefined; this.retry = undefined; + this.config = config || { 'accounts' : {}}; // Cache information for accounts. this.accounts = { @@ -176,10 +187,10 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { 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) { @@ -190,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, }, @@ -204,7 +215,7 @@ 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"), @@ -233,6 +244,12 @@ Remote.prototype._set_state = function (state) { } }; +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; @@ -703,7 +720,9 @@ Remote.prototype.account_seq_cache = function (account, current) { // Mark an account's root node as dirty. Remote.prototype.dirty_account_root = function (account) { - delete this.ledgers.current.account_root[UInt160.json_rewrite(account)]; + var account = UInt160.json_rewrite(account); + + delete this.ledgers.current.account_root[account]; }; // Return a request to get a ripple balance. @@ -712,8 +731,9 @@ Remote.prototype.dirty_account_root = function (account) { // --> 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 account_u = UInt160.from_json(account); var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. return request @@ -721,20 +741,25 @@ Remote.prototype.request_ripple_balance = function (account, issuer, currency, c .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 account holds of issuer (after negation if needed). var balance = Amount.from_json(node.Balance); - var flip = account_u == highLimit.issuer; - var issuerLimit = flip ? lowLimit : highLimit; - var accountLimit = flip ? highLimit : lowLimit; - var issuerBalance = (flip ? balance.negate() : balance).parse_issuer(issuer); - var accountBalance = issuerBalance.clone().parse_issuer(issuer); + // 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 issuer. + var issuerLimit = (accountHigh ? lowLimit : highLimit).parse_issuer(issuer); + // The limit set by account. + var accountLimit = (accountHigh ? highLimit : lowLimit).parse_issuer(account); + var issuerBalance = (accountHigh ? balance.negate() : balance).parse_issuer(issuer); + var accountBalance = issuerBalance.clone().negate().parse_issuer(account); request.emit('ripple_state', { - 'issuer_balance' : issuerBalance, // Balance with dst as issuer. - 'account_balance' : accountBalance, // Balance with account as issuer. - 'issuer_limit' : issuerLimit.clone().parse_issuer(account), // Limit set by issuer with src as issuer. - 'account_limit' : accountLimit.clone().parse_issuer(issuer) // Limit set by account with dst as issuer. + '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. }); }); } @@ -749,14 +774,14 @@ Remote.prototype.transaction = function () { // Construction: // remote.transaction() // Build a transaction object. // .offer_create(...) // Set major parameters. -// .flags() // Set optional 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. @@ -797,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. @@ -893,12 +916,12 @@ 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(); } } @@ -971,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; @@ -992,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; @@ -1003,40 +1026,78 @@ Transaction.prototype.flags = function (flags) { // Transactions // -// 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; }; +// .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.secret = this._account_secret(src); this.transaction.TransactionType = 'OfferCancel'; - this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.Account = UInt160.json_rewrite(src); this.transaction.OfferSequence = Number(sequence); return this; }; -// XXX Expiration should use a time. +// --> expiration : Date or Number Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'OfferCreate'; - this.transaction.Account = UInt160.from_json(src).to_json(); - this.transaction.Fee = fees.offer.to_json(); + 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 = 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: @@ -1045,23 +1106,23 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi // --> 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 = UInt160.from_json(src).to_json(); + this.transaction.Account = UInt160.json_rewrite(src); this.transaction.Amount = Amount.json_rewrite(deliver_amount); - this.transaction.Destination = UInt160.from_json(dst).to_json(); + this.transaction.Destination = UInt160.json_rewrite(dst); return this; } Transaction.prototype.ripple_line_set = function (src, limit, quality_in, quality_out) { - this.secret = this.account_secret(src); + this.secret = this._account_secret(src); this.transaction.TransactionType = 'CreditSet'; - this.transaction.Account = UInt160.from_json(src).to_json(); + 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 (quality_in) this.transaction.QualityIn = quality_in; @@ -1074,9 +1135,17 @@ Transaction.prototype.ripple_line_set = function (src, limit, quality_in, qualit 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/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..ed00ce114 100644 --- a/newcoin.vcxproj +++ b/newcoin.vcxproj @@ -108,6 +108,7 @@ + @@ -134,7 +135,6 @@ - diff --git a/newcoin.vcxproj.filters b/newcoin.vcxproj.filters index 7941c68ec..60cb2ecf0 100644 --- a/newcoin.vcxproj.filters +++ b/newcoin.vcxproj.filters @@ -120,9 +120,6 @@ Source Files - - Source Files - Source Files @@ -303,6 +300,9 @@ Source Files + + Source Files + diff --git a/src/HTTPRequest.h b/src/HTTPRequest.h index 9ecea2443..596ce8924 100644 --- a/src/HTTPRequest.h +++ b/src/HTTPRequest.h @@ -58,4 +58,4 @@ public: int getDataSize() { return iDataSize; } }; -#endif +#endif \ No newline at end of file diff --git a/src/Peer.cpp b/src/Peer.cpp index 506aad80a..e44608049 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -875,7 +875,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); diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 35c74bbdc..46a59852e 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1394,7 +1394,7 @@ Json::Value RPCServer::doProfile(const Json::Value ¶ms) boost::posix_time::ptime ptStart(boost::posix_time::microsec_clock::local_time()); - for(int n=0; ngetTxnDB()->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 RPCServer::doTx(const Json::Value& params) { // tx @@ -2518,7 +2551,7 @@ Json::Value RPCServer::doWalletPropose(const Json::Value& params) Json::Value obj(Json::objectValue); obj["master_seed"] = naSeed.humanSeed(); - obj["master_key"] = naSeed.humanSeed1751(); + //obj["master_key"] = naSeed.humanSeed1751(); obj["account_id"] = naAccount.humanAccountID(); return obj; @@ -2736,6 +2769,7 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { "server_info", &RPCServer::doServerInfo, 0, 0, true }, { "stop", &RPCServer::doStop, 0, 0, true }, { "tx", &RPCServer::doTx, 1, 1, true }, + { "tx_history", &RPCServer::doTxHistory, 1, 1, false, }, { "unl_add", &RPCServer::doUnlAdd, 1, 2, true }, { "unl_delete", &RPCServer::doUnlDelete, 1, 1, true }, diff --git a/src/RPCServer.h b/src/RPCServer.h index af8c6d754..0513091de 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -159,6 +159,7 @@ private: 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 doUnlAdd(const Json::Value& params); Json::Value doUnlDelete(const Json::Value& params); diff --git a/src/RippleLines.cpp b/src/RippleLines.cpp index 46b88d75f..a9808780b 100644 --- a/src/RippleLines.cpp +++ b/src/RippleLines.cpp @@ -12,11 +12,12 @@ RippleLines::RippleLines(const uint160& accountID, Ledger::pointer ledger) fillLines(accountID,ledger); } -void RippleLines::printRippleLines() { - for (int i =0; i < mLines.size(); i++) { - std::cout << i << ": " << mLines[i]->getAccountID().humanAccountID() << std::endl; - } - std::cout << std::endl; +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 ) diff --git a/src/Transaction.cpp b/src/Transaction.cpp index 6ef87abaf..8eea1dd76 100644 --- a/src/Transaction.cpp +++ b/src/Transaction.cpp @@ -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; diff --git a/src/Transaction.h b/src/Transaction.h index 7cd94aedd..c78a617bb 100644 --- a/src/Transaction.h +++ b/src/Transaction.h @@ -17,6 +17,8 @@ #include "SerializedTransaction.h" #include "TransactionErr.h" +class Database; + enum TransStatus { NEW = 0, // just received / generated @@ -130,6 +132,7 @@ public: Transaction(const SerializedTransaction::pointer& st, bool bValidate); static Transaction::pointer sharedTransaction(const std::vector&vucTransaction, bool bValidate); + static Transaction::pointer transactionFromSQL(Database* db, bool bValidate); Transaction( TransactionType ttKind, diff --git a/test/amount-test.js b/test/amount-test.js index 3ddd40074..97654d2a7 100644 --- a/test/amount-test.js +++ b/test/amount-test.js @@ -1,32 +1,71 @@ var buster = require("buster"); var amount = require("../js/amount.js"); +var Amount = require("../js/amount.js").Amount; +var UInt160 = require("../js/amount.js").UInt160; buster.testCase("Amount", { "UInt160" : { "Parse 0" : function () { - buster.assert.equals(0, amount.UInt160.from_json("0").value); + buster.assert.equals(0, 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.hex_xns, UInt160.from_json("0").to_json()); }, - "Parse native 123" : function () { - buster.assert.equals("123/XNS", amount.Amount.from_json("123").to_text_full()); + }, + "Amount parsing" : { + "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()); }, } }); diff --git a/test/config.js b/test/config.js index 6d448ae17..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, @@ -26,39 +28,39 @@ exports.servers = { // 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 index bae3b947f..590d3f262 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -1,63 +1,34 @@ var async = require("async"); var buster = require("buster"); -var fs = require("fs"); - -var server = require("./server.js"); -var remote = require("../js/remote.js"); -var config = require("./config.js"); var Amount = require("../js/amount.js").Amount; +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; -require("../js/amount.js").setAccounts(config.accounts); +var testutils = require("./testutils.js"); buster.testRunner.timeout = 5000; -var alpha; - -buster.testCase("Work in progress", { - 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - - alpha = remote.remoteConfig(config, "alpha", 'TRACE'); - - alpha - .once('ledger_closed', done) - .connect(); - } -// , 'MOCK' - ); - }, - - 'tearDown' : - function (done) { - alpha - .on('disconnected', function () { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }) - .connect(false); - }, +buster.testCase("Offer tests", { + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_teardown, "offer create then cancel in one ledger" : function (done) { + var self = this; var final_create; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .offer_create("root", "500", "100/USD/root") - .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -66,14 +37,14 @@ buster.testCase("Work in progress", { .submit(); }, function (m, callback) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence) - .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -82,14 +53,14 @@ buster.testCase("Work in progress", { .submit(); }, function (m, callback) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + 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); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); @@ -98,22 +69,23 @@ buster.testCase("Work in progress", { "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) { - alpha.transaction() + self.remote.transaction() .offer_create("root", "500", "100/USD/root") - .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -125,9 +97,9 @@ buster.testCase("Work in progress", { }, function (callback) { if (!final_create) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); }) .ledger_accept(); @@ -137,16 +109,16 @@ buster.testCase("Work in progress", { } }, function (callback) { - console.log("CANCEL: offer_cancel: %d", offer_seq); + // console.log("CANCEL: offer_cancel: %d", offer_seq); - alpha.transaction() + self.remote.transaction() .offer_cancel("root", offer_seq) - .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -157,23 +129,23 @@ buster.testCase("Work in progress", { }, // See if ledger_accept will crash. function (callback) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + 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) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + 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); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); @@ -183,33 +155,34 @@ buster.testCase("Work in progress", { "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) { - alpha.transaction() + self.remote.transaction() .payment('root', 'alice', "1000") - .flags('CreateAccount') + .set_flags('CreateAccount') .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); buster.assert.equals(m.result, 'tesSUCCESS'); callback(); }) .submit() }, function (callback) { - alpha.transaction() + self.remote.transaction() .offer_create("alice", "500", "100/USD/alice") - .on("proposed", function (m) { - console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_create: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); @@ -221,9 +194,9 @@ buster.testCase("Work in progress", { }, function (callback) { if (!final_create) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + self.remote + .once('ledger_closed', function (ledger_closed, ledger_closed_index) { + // console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); }) .ledger_accept(); @@ -233,16 +206,16 @@ buster.testCase("Work in progress", { } }, function (callback) { - console.log("CANCEL: offer_cancel: %d", offer_seq); + // console.log("CANCEL: offer_cancel: %d", offer_seq); - alpha.transaction() + self.remote.transaction() .offer_cancel("alice", offer_seq) - .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m)); + .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)); + .on('final', function (m) { + // console.log("FINAL: offer_cancel: %s", JSON.stringify(m)); buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); buster.assert(final_create); @@ -253,23 +226,23 @@ buster.testCase("Work in progress", { }, // See if ledger_accept will crash. function (callback) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + 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) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + 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); + // console.log("result: error=%s", error); buster.refute(error); if (error) done(); }); @@ -277,19 +250,20 @@ buster.testCase("Work in progress", { "offer cancel past and future sequence" : function (done) { + var self = this; var final_create; async.waterfall([ function (callback) { - alpha.transaction() + self.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') - .on("proposed", function (m) { - console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m)); + .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); + // console.log("error: %s", m); buster.assert(false); callback(m); @@ -298,20 +272,20 @@ buster.testCase("Work in progress", { }, // Past sequence but wrong function (m, callback) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence) - .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m)); + .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) { - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence+1) - .on("proposed", function (m) { - console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m)); + .on('proposed', function (m) { + // console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m)); callback(m.result != 'temBAD_SEQUENCE', m); }) .submit(); @@ -319,29 +293,29 @@ buster.testCase("Work in progress", { // Future sequence function (m, callback) { // After a malformed transaction, need to recover correct sequence. - alpha.set_account_seq("root", alpha.account_seq("root")-1); + self.remote.set_account_seq("root", self.remote.account_seq("root")-1); - alpha.transaction() + self.remote.transaction() .offer_cancel("root", m.transaction.Sequence+2) - .on("proposed", function (m) { - console.log("ERROR: offer_cancel future: %s", JSON.stringify(m)); + .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) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + 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) { - alpha - .once("ledger_closed", function (ledger_closed, ledger_closed_index) { - console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + 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(); @@ -350,7 +324,7 @@ buster.testCase("Work in progress", { callback(); } ], function (error) { - console.log("result: error=%s", error); + // console.log("result: error=%s", error); buster.refute(error); done(); diff --git a/test/remote-test.js b/test/remote-test.js index 86f31554e..a22230af0 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -1,12 +1,10 @@ var buster = require("buster"); -var config = require("./config.js"); -var server = require("./server.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; -require("../js/amount.js").setAccounts(config.accounts); +var testutils = require("./testutils.js"); var fastTearDown = true; @@ -16,42 +14,19 @@ 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 - .once('ledger_closed', done) - .connect(); - }); - }, - - 'tearDown' : - function (done) { - alpha - .on('disconnected', function () { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }) - .connect(false); - }, + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_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); }) @@ -60,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); }) @@ -76,10 +51,12 @@ 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_closed(r.ledger_closed) .account_root("rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") @@ -90,14 +67,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("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -107,10 +84,12 @@ buster.testCase("Remote functions", { // 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_closed(r.ledger_closed) .account_root("zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh") @@ -120,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'); @@ -129,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); }) @@ -138,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_closed(r.ledger_closed) - .account_root(config.accounts.alice.account) + .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'); @@ -160,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(); @@ -168,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_closed(r.ledger_closed) - .account_root(config.accounts.alice.account) + .account_root("alice") .index("2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8") .on('success', function (r) { // console.log("account_root: %s", JSON.stringify(r)); @@ -183,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); }) @@ -199,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)); @@ -209,7 +192,7 @@ buster.testCase("Remote functions", { done(); }) .on('error', function(m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) @@ -218,40 +201,42 @@ buster.testCase("Remote functions", { "create account final" : function (done) { + var self = this; + var got_proposed; var got_success; - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) - .flags('CreateAccount') + .set_flags('CreateAccount') .on('success', function (r) { - console.log("create_account: %s", JSON.stringify(r)); + // console.log("create_account: %s", JSON.stringify(r)); got_success = true; }) .on('error', function (m) { - console.log("error: %s", m); + // console.log("error: %s", m); buster.assert(false); }) .on('final', function (m) { - console.log("final: %s", JSON.stringify(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)); + // console.log("proposed: %s", JSON.stringify(m)); // buster.assert.equals(m.result, 'terNO_DST'); buster.assert.equals(m.result, 'tesSUCCESS'); got_proposed = true; - alpha.ledger_accept(); + self.remote.ledger_accept(); }) .on('status', function (s) { - console.log("status: %s", JSON.stringify(s)); + // console.log("status: %s", JSON.stringify(s)); }) .submit(); }, diff --git a/test/send-test.js b/test/send-test.js index 145ce3410..9d66ad030 100644 --- a/test/send-test.js +++ b/test/send-test.js @@ -1,63 +1,41 @@ +var async = require("async"); 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"); // How long to wait for server to start. var serverDelay = 1500; -buster.testRunner.timeout = 5000; +buster.testRunner.timeout = 2000; buster.testCase("Sending", { - 'setUp' : - function (done) { - server.start("alpha", - function (e) { - buster.refute(e); - - alpha = remote.remoteConfig(config, "alpha"); - - alpha - .once('ledger_closed', done) - .connect(); - }); - }, - - 'tearDown' : - function (done) { - alpha - .on('disconnected', function () { - server.stop("alpha", function (e) { - buster.refute(e); - done(); - }); - }) - .connect(false); - }, - - "send to non-existant account without create." : + 'setUp' : testutils.test_setup, + 'tearDown' : testutils.test_teardown, + + "send XNS to non-existant account without create." : function (done) { + var self = this; + var ledgers = 20; var got_proposed; - var ledgers = 20; - alpha.transaction() + this.remote.transaction() .payment('root', 'alice', Amount.from_json("10000")) .on('success', function (r) { // Transaction sent. - console.log("success: %s", JSON.stringify(r)); + // console.log("success: %s", JSON.stringify(r)); }) .on('pending', function() { // Moving ledgers along. - console.log("missing: %d", ledgers); + // console.log("missing: %d", ledgers); ledgers -= 1; if (ledgers) { - alpha.ledger_accept(); + self.remote.ledger_accept(); } else { buster.assert(false, "Final never received."); @@ -66,34 +44,146 @@ buster.testCase("Sending", { }) .on('lost', function () { // Transaction did not make it in. - console.log("lost"); + // console.log("lost"); buster.assert(true); done(); }) .on('proposed', function (m) { // Transaction got an error. - console.log("proposed: %s", JSON.stringify(m)); + // console.log("proposed: %s", JSON.stringify(m)); buster.assert.equals(m.result, 'terNO_DST'); got_proposed = true; - alpha.ledger_accept(); // Move it along. + self.remote.ledger_accept(); // Move it along. }) .on('final', function (m) { - console.log("final: %s", JSON.stringify(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); + // 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; + //this.remote.set_trace(); + + async.waterfall([ + function (callback) { + this.what = "Create account."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback); + }, + function (callback) { + this.what = "Check a non-existant credit limit."; + self.remote.request_ripple_balance("alice", "mtgox", "USD", 'CURRENT') + .on('ripple_state', function (m) { + buster.assert(false); + + callback(); + }) + .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) { + this.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) { + this.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) { + this.what = "Zero a credit limit."; + + testutils.credit_limit(self.remote, "alice", "0/USD/mtgox", callback); + }, + function (callback) { + this.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(); + }, + // Check in both owner books. + // Set limit on other side. + // Set negative limit. + //function (callback) { + // testutils.credit_limit(self.remote, "alice", "-1/USD/mtgox", callback); + //}, + ], function (error) { + buster.refute(error, this.what); + done(); + }); + } }); // 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 85efe695b..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,29 +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, mock) { - this.name = name; - this.mock = mock; +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() { @@ -42,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; @@ -83,80 +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; - if (this.mock) { - done(); - } - else { - this.makeBase(function (e) { - if (e) { - throw e; - } - else { - self.serverSpawnSync(); - done(); - } - }); - } + 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(); + self.emit('started'); + } + }); + + return this; }; // Stop a standalone server. -Server.prototype.stop = function (done) { - if (this.mock) { - console.log("server: stop: mock"); - done(); - } - else if (this.child) { +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, mock) { - if (servers[name]) - { - console.log("server: start: server already started."); - } - else - { - var server = new Server(name, mock); - - 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..605f8e27d --- /dev/null +++ b/test/testutils.js @@ -0,0 +1,80 @@ +var async = require("async"); +// var buster = require("buster"); + +var Remote = require("../js/remote.js").Remote; +var Server = require("./server.js").Server; + +var config = require("./config.js"); + +var test_setup = function (done, host) { + var self = this; + var host = host || config.server_default; + + this.store = this.store || {}; + + var data = this.store[host] = this.store[host] || {}; + + data.server = Server.from_config(host).on('started', function () { + self.remote = data.remote = Remote.from_config(host).once('ledger_closed', done).connect(); + }).start(); +}; + +var test_teardown = function (done, host) { + var host = host || config.server_default; + + var data = this.store[host]; + + data.remote + .on('disconnected', function () { + data.server.on('stopped', done).stop(); + }) + .connect(false); +}; + +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)); + + // buster.assert.equals(m.result, 'tesSUCCESS'); + + callback(m.result != 'tesSUCCESS'); + }) + .on('error', function (m) { + // console.log("error: %s", JSON.stringify(m)); + + callback(m); + }) + .submit(); +}; + +exports.create_accounts = create_accounts; +exports.credit_limit = credit_limit; +exports.test_setup = test_setup; +exports.test_teardown = test_teardown; + +// vim:sw=2:sts=2:ts=8 diff --git a/test/websocket-test.js b/test/websocket-test.js index 055a0aab2..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,26 +10,14 @@ 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 .on('connected', function () {