From a44767fd5bc14f0e6b537f705b741c6195d831c8 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 19 Nov 2012 12:53:37 -0800 Subject: [PATCH 1/4] JS: Add a way to convert an amount to a number. --- src/js/amount.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/js/amount.js b/src/js/amount.js index e75f248378..5f5f58e37c 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -353,6 +353,12 @@ Amount.prototype.issuer = function() { return this._issuer; }; +Amount.prototype.to_number = function(allow_nan) { + var s = this.to_text(allow_nan); + + return ('string' === typeof s) ? Number(s) : s; +} + // Convert only value to JSON wire format. Amount.prototype.to_text = function(allow_nan) { if (isNaN(this._value)) { From ad17519dc0e42ac645f120d91e372cf26f49ee11 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 19 Nov 2012 12:54:08 -0800 Subject: [PATCH 2/4] JS: Return account_balance as an amoutn instead of json. --- src/js/remote.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/js/remote.js b/src/js/remote.js index 0f990fe950..c7040e2396 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -806,13 +806,15 @@ Remote.prototype.ledger_accept = function () { // Return a request to refresh the account balance. Remote.prototype.request_account_balance = function (account, current) { - return (this.request_ledger_entry('account_root')) + var request = this.request_ledger_entry('account_root'); + + return request .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); - }); + // If the caller also waits for 'success', they might run before this. + request.emit('account_balance', Amount.from_json(message.node.Balance)); + }); }; // Return the next account sequence if possible. From 4615f47148cf998dcb1220fed1d998fe82dcc42e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 19 Nov 2012 12:55:11 -0800 Subject: [PATCH 3/4] UT: Add support to verify XRP balances. --- test/testutils.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/test/testutils.js b/test/testutils.js index 8a840bd5f0..bea65eb8a1 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -265,14 +265,22 @@ var transfer_rate = function (remote, src, billionths, callback) { var verify_balance = function (remote, src, amount_json, callback) { assert(4 === arguments.length); - var amount = Amount.from_json(amount_json); + var amount_req = Amount.from_json(amount_json); - if (amount.is_native()) { - // XXX Not implemented. - callback(); + if (amount_req.is_native()) { + remote.request_account_balance(src, 'CURRENT') + .once('account_balance', function (amount_act) { + if (!amount_act.equals(amount_req)) + console.log("verify_balance: failed: %s / %s", + amount_act.to_text_full(), + amount_req.to_text_full()); + + callback(!amount_act.equals(amount_req)); + }) + .request(); } else { - remote.request_ripple_balance(src, amount.issuer().to_json(), amount.currency().to_json(), 'CURRENT') + remote.request_ripple_balance(src, amount_req.issuer().to_json(), amount_req.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()); @@ -282,11 +290,11 @@ var verify_balance = function (remote, src, amount_json, callback) { var account_balance = Amount.from_json(m.account_balance); - if (!account_balance.equals(amount)) { - console.log("verify_balance: failed: %s vs %s is %s: %s", src, account_balance.to_text_full(), amount.to_text_full(), account_balance.not_equals_why(amount)); + if (!account_balance.equals(amount_req)) { + console.log("verify_balance: failed: %s vs %s is %s: %s", src, account_balance.to_text_full(), amount_req.to_text_full(), account_balance.not_equals_why(amount_req)); } - callback(!account_balance.equals(amount)); + callback(!account_balance.equals(amount_req)); }) .request(); } From 87b5d0ca60bd715354522028259a4e890a295ff4 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 19 Nov 2012 12:55:50 -0800 Subject: [PATCH 4/4] UT: Add test for cross currency payment ending in XRP. --- test/offer-test.js | 200 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 196 insertions(+), 4 deletions(-) diff --git a/test/offer-test.js b/test/offer-test.js index fc42aac2e1..ef83dd5901 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -400,7 +400,7 @@ buster.testCase("Offer tests", { testutils.verify_balances(self.remote, { - "alice" : [ "0/USD/mtgox", "500" ], + "alice" : [ "0/USD/mtgox", String(10000+500-2*(Remote.fees['default'].to_number())) ], "bob" : "100/USD/mtgox", }, callback); @@ -482,7 +482,7 @@ buster.testCase("Offer tests", { testutils.verify_balances(self.remote, { - "alice" : [ "160/USD/mtgox", "200" ], // XXX Verfiying XRP balance needs to account for fees. + "alice" : [ "160/USD/mtgox", String(10000+200-2*(Remote.fees['default'].to_number())) ], "bob" : "40/USD/mtgox", }, callback); @@ -524,7 +524,7 @@ buster.testCase("Offer tests", { testutils.verify_balances(self.remote, { - "alice" : [ "100/USD/mtgox", "500" ], // XXX Verfiying XRP balance needs to account for fees. + "alice" : [ "100/USD/mtgox", String(10000+200+300-4*(Remote.fees['default'].to_number())) ], "bob" : "100/USD/mtgox", }, callback); @@ -534,8 +534,13 @@ buster.testCase("Offer tests", { done(); }); }, +}); - "ripple cross currency payment" : +buster.testCase("Offer cross currency", { + 'setUp' : testutils.build_setup(), + 'tearDown' : testutils.build_teardown(), + + "ripple cross currency payment - start with XRP" : // alice --> [XRP --> carol --> USD/mtgox] --> bob function (done) { @@ -616,5 +621,192 @@ buster.testCase("Offer tests", { done(); }); }, + + "ripple cross currency payment - end with XRP" : + // alice --> [USD/mtgox --> carol --> XRP] --> bob + + function (done) { + var self = this; + var seq; + + // self.remote.set_trace(); + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "mtgox"], callback); + }, + function (callback) { + self.what = "Set limits."; + + testutils.credit_limits(self.remote, + { + "alice" : "1000/USD/mtgox", + "carol" : "2000/USD/mtgox" + }, + callback); + }, + function (callback) { + self.what = "Distribute funds."; + + testutils.payments(self.remote, + { + "mtgox" : "500/USD/alice" + }, + callback); + }, + function (callback) { + self.what = "Create offer."; + + self.remote.transaction() + .offer_create("carol", "50/USD/mtgox", "500") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + callback(m.result !== 'tesSUCCESS'); + + seq = m.tx_json.Sequence; + }) + .submit(); + }, + function (callback) { + self.what = "Alice send XRP to bob converting from USD/mtgox."; + + self.remote.transaction() + .payment("alice", "bob", "250") + .send_max("333/USD/mtgox") + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result !== 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Verify balances."; + + testutils.verify_balances(self.remote, + { + "alice" : "475/USD/mtgox", + "bob" : "10250", + "carol" : "25/USD/mtgox", + }, + callback); + }, + function (callback) { + self.what = "Verify offer partially consumed."; + + testutils.verify_offer(self.remote, "carol", seq, "250", "25/USD/mtgox", callback); + }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, + + "// ripple cross currency bridged payment" : + // alice --> [USD/mtgox --> carol --> XRP] --> [XRP --> dan --> EUR/bitstamp] --> bob + + function (done) { + var self = this; + var seq_carol; + var seq_dan; + + // self.remote.set_trace(); + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "carol", "dan", "bitstamp", "mtgox"], callback); + }, + function (callback) { + self.what = "Set limits."; + + testutils.credit_limits(self.remote, + { + "alice" : "1000/USD/mtgox", + "bob" : "1000/EUR/bitstamp", + "carol" : "1000/USD/mtgox", + "dan" : "1000/EUR/bitstamp" + }, + callback); + }, + function (callback) { + self.what = "Distribute funds."; + + testutils.payments(self.remote, + { + "bitstamp" : "400/EUR/dan", + "mtgox" : "500/USD/alice", + }, + callback); + }, + function (callback) { + self.what = "Create offer carol."; + + self.remote.transaction() + .offer_create("carol", "50/USD/mtgox", "500") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + callback(m.result !== 'tesSUCCESS'); + + seq_carol = m.tx_json.Sequence; + }) + .submit(); + }, + function (callback) { + self.what = "Create offer dan."; + + self.remote.transaction() + .offer_create("dan", "500", "50/EUR/bitstamp") + .on('proposed', function (m) { + // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + callback(m.result !== 'tesSUCCESS'); + + seq_dan = m.tx_json.Sequence; + }) + .submit(); + }, + function (callback) { + self.what = "Alice send EUR/bitstamp to bob converting from USD/mtgox."; + + self.remote.transaction() + .payment("alice", "bob", "30/EUR/bitstamp") + .send_max("333/USD/mtgox") + .on('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + + callback(m.result !== 'tesSUCCESS'); + }) + .submit(); + }, +// function (callback) { +// self.what = "Verify balances."; +// +// testutils.verify_balances(self.remote, +// { +// "alice" : "470/USD/mtgox", +// "bob" : "30/EUR/bitstamp", +// "carol" : "30/USD/mtgox", +// "dan" : "370/EUR/bitstamp", +// }, +// callback); +// }, +// function (callback) { +// self.what = "Verify carol offer partially consumed."; +// +// testutils.verify_offer(self.remote, "carol", seq_carol, "250", "25/USD/mtgox", callback); +// }, +// function (callback) { +// self.what = "Verify dan offer partially consumed."; +// +// testutils.verify_offer(self.remote, "dan", seq_dan, "250", "25/USD/mtgox", callback); +// }, + ], function (error) { + buster.refute(error, self.what); + done(); + }); + }, }); // vim:sw=2:sts=2:ts=8