From 0bcf3db027dc7eb9ceea88e97e71462861c934a8 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 11:29:08 -0800 Subject: [PATCH 1/7] UT: Remove reserve tests which were't. --- test/reserve-test.js | 921 ------------------------------------------- 1 file changed, 921 deletions(-) delete mode 100644 test/reserve-test.js diff --git a/test/reserve-test.js b/test/reserve-test.js deleted file mode 100644 index 1abd4fe7d5..0000000000 --- a/test/reserve-test.js +++ /dev/null @@ -1,921 +0,0 @@ - -var async = require("async"); -var buster = require("buster"); - -var Amount = require("../src/js/amount").Amount; -var Remote = require("../src/js/remote").Remote; -var Transaction = require("../src/js/transaction").Transaction; -var Server = require("./server").Server; - -var testutils = require("./testutils"); - -require("../src/js/amount").config = require("./config"); -require("../src/js/remote").config = require("./config"); - -buster.testRunner.timeout = 5000; - -buster.testCase("Reserve", { - 'setUp' : testutils.build_setup(), - // 'setUp' : testutils.build_setup({ verbose: true, standalone: false }), - '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); - - buster.assert(final_create); - }) - .submit(); - }, - function (m, callback) { - self.remote.transaction() - .offer_cancel("root", m.tx_json.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, undefined, 2)); - - buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); - buster.assert(final_create); - done(); - }) - .submit(); - }, - function (m, callback) { - self.remote - .once('ledger_closed', function (message) { - // console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash); - final_create = message; - }) - .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.tx_json.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 (mesage) { - // console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash); - - }) - .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 (mesage) { - // console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash); - callback(); - }) - .ledger_accept(); - }, - function (callback) { - self.remote - .once('ledger_closed', function (mesage) { - // console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash); - 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") - .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.tx_json.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 (mesage) { - // console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash); - - }) - .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 (mesage) { - // console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash); - callback(); - }) - .ledger_accept(); - }, - function (callback) { - self.remote - .once('ledger_closed', function (mesage) { - // console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash); - 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.0")) - .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.tx_json.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.tx_json.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.tx_json.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 (mesage) { - // console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash); - callback(); - }) - .ledger_accept(); - }, - function (callback) { - self.remote - .once('ledger_closed', function (mesage) { - // console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash); - callback(); - }) - .ledger_accept(); - }, - function (callback) { - callback(); - } - ], function (error) { - // console.log("result: error=%s", error); - buster.refute(error); - - done(); - }); - }, - - "ripple currency conversion : entire offer" : - // mtgox in, XRP out - function (done) { - var self = this; - var seq; - - async.waterfall([ - function (callback) { - self.what = "Create accounts."; - - testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback); - }, - function (callback) { - self.what = "Owner count 0."; - - testutils.verify_owner_count(self.remote, "bob", 0, callback); - }, - function (callback) { - self.what = "Set limits."; - - testutils.credit_limits(self.remote, - { - "alice" : "100/USD/mtgox", - "bob" : "1000/USD/mtgox" - }, - callback); - }, - function (callback) { - self.what = "Owner counts after trust."; - - testutils.verify_owner_counts(self.remote, - { - "alice" : 1, - "bob" : 1, - }, - callback); - }, - function (callback) { - self.what = "Distribute funds."; - - testutils.payments(self.remote, - { - "mtgox" : "100/USD/alice" - }, - callback); - }, - function (callback) { - self.what = "Create offer."; - - self.remote.transaction() - .offer_create("bob", "100/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 = "Owner counts after offer create."; - - testutils.verify_owner_counts(self.remote, - { - "alice" : 1, - "bob" : 2, - }, - callback); - }, - function (callback) { - self.what = "Verify offer balance."; - - testutils.verify_offer(self.remote, "bob", seq, "100/USD/mtgox", "500", callback); - }, - function (callback) { - self.what = "Alice converts USD to XRP."; - - self.remote.transaction() - .payment("alice", "alice", "500") - .send_max("100/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" : [ "0/USD/mtgox", String(10000000000+500-2*(Transaction.fees['default'].to_number())) ], - "bob" : "100/USD/mtgox", - }, - callback); - }, - function (callback) { - self.what = "Verify offer consumed."; - - testutils.verify_offer_not_found(self.remote, "bob", seq, callback); - }, - function (callback) { - self.what = "Owner counts after consumed."; - - testutils.verify_owner_counts(self.remote, - { - "alice" : 1, - "bob" : 1, - }, - callback); - }, - ], function (error) { - buster.refute(error, self.what); - done(); - }); - }, - - "ripple currency conversion : offerer into debt" : - // alice in, carol out - function (done) { - var self = this; - var seq; - - async.waterfall([ - function (callback) { - self.what = "Create accounts."; - - testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol"], callback); - }, - function (callback) { - self.what = "Set limits."; - - testutils.credit_limits(self.remote, - { - "alice" : "2000/EUR/carol", - "bob" : "100/USD/alice", - "carol" : "1000/EUR/bob" - }, - callback); - }, - function (callback) { - self.what = "Create offer to exchange."; - - self.remote.transaction() - .offer_create("bob", "50/USD/alice", "200/EUR/carol") - .on('proposed', function (m) { - // console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); - callback(m.result !== 'tecUNFUNDED_OFFER'); - - seq = m.tx_json.Sequence; - }) - .submit(); - }, -// function (callback) { -// self.what = "Alice converts USD to EUR via offer."; -// -// self.remote.transaction() -// .offer_create("alice", "200/EUR/carol", "50/USD/alice") -// .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 = "Verify balances."; -// -// testutils.verify_balances(self.remote, -// { -// "alice" : [ "-50/USD/bob", "200/EUR/carol" ], -// "bob" : [ "50/USD/alice", "-200/EUR/carol" ], -// "carol" : [ "-200/EUR/alice", "200/EUR/bob" ], -// }, -// callback); -// }, -// function (callback) { -// self.what = "Verify offer consumed."; -// -// testutils.verify_offer_not_found(self.remote, "bob", seq, callback); -// }, - ], function (error) { - buster.refute(error, self.what); - done(); - }); - }, - - "ripple currency conversion : in parts" : - function (done) { - var self = this; - var seq; - - async.waterfall([ - function (callback) { - self.what = "Create accounts."; - - testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback); - }, - function (callback) { - self.what = "Set limits."; - - testutils.credit_limits(self.remote, - { - "alice" : "200/USD/mtgox", - "bob" : "1000/USD/mtgox" - }, - callback); - }, - function (callback) { - self.what = "Distribute funds."; - - testutils.payments(self.remote, - { - "mtgox" : "200/USD/alice" - }, - callback); - }, - function (callback) { - self.what = "Create offer."; - - self.remote.transaction() - .offer_create("bob", "100/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 converts USD to XRP."; - - self.remote.transaction() - .payment("alice", "alice", "200") - .send_max("100/USD/mtgox") - .on('proposed', function (m) { - // console.log("proposed: %s", JSON.stringify(m)); - - callback(m.result !== 'tesSUCCESS'); - }) - .submit(); - }, - function (callback) { - self.what = "Verify offer balance."; - - testutils.verify_offer(self.remote, "bob", seq, "60/USD/mtgox", "300", callback); - }, - function (callback) { - self.what = "Verify balances."; - - testutils.verify_balances(self.remote, - { - "alice" : [ "160/USD/mtgox", String(10000000000+200-2*(Transaction.fees['default'].to_number())) ], - "bob" : "40/USD/mtgox", - }, - callback); - }, - function (callback) { - self.what = "Alice converts USD to XRP should fail due to PartialPayment."; - - self.remote.transaction() - .payment("alice", "alice", "600") - .send_max("100/USD/mtgox") - .on('proposed', function (m) { - // console.log("proposed: %s", JSON.stringify(m)); - - callback(m.result !== 'tecPATH_PARTIAL'); - }) - .submit(); - }, - function (callback) { - self.what = "Alice converts USD to XRP."; - - self.remote.transaction() - .payment("alice", "alice", "600") - .send_max("100/USD/mtgox") - .set_flags('PartialPayment') - .on('proposed', function (m) { - // console.log("proposed: %s", JSON.stringify(m)); - - callback(m.result !== 'tesSUCCESS'); - }) - .submit(); - }, - function (callback) { - self.what = "Verify offer consumed."; - - testutils.verify_offer_not_found(self.remote, "bob", seq, callback); - }, - function (callback) { - self.what = "Verify balances."; - - testutils.verify_balances(self.remote, - { - "alice" : [ "100/USD/mtgox", String(10000000000+200+300-4*(Transaction.fees['default'].to_number())) ], - "bob" : "100/USD/mtgox", - }, - callback); - }, - ], function (error) { - buster.refute(error, self.what); - done(); - }); - }, -}); - -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) { - var self = this; - var seq; - - // self.remote.set_trace(); - - async.waterfall([ - function (callback) { - self.what = "Create accounts."; - - testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "mtgox"], callback); - }, - function (callback) { - self.what = "Set limits."; - - testutils.credit_limits(self.remote, - { - "carol" : "1000/USD/mtgox", - "bob" : "2000/USD/mtgox" - }, - callback); - }, - function (callback) { - self.what = "Distribute funds."; - - testutils.payments(self.remote, - { - "mtgox" : "500/USD/carol" - }, - callback); - }, - function (callback) { - self.what = "Create offer."; - - self.remote.transaction() - .offer_create("carol", "500", "50/USD/mtgox") - .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 USD/mtgox converting from XRP."; - - self.remote.transaction() - .payment("alice", "bob", "25/USD/mtgox") - .send_max("333") - .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" : [ "500" ], - "bob" : "25/USD/mtgox", - "carol" : "475/USD/mtgox", - }, - callback); - }, - function (callback) { - self.what = "Verify offer consumed."; - - testutils.verify_offer_not_found(self.remote, "bob", seq, callback); - }, - ], function (error) { - buster.refute(error, self.what); - 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.0", ["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" : "10000000250", - "carol" : "25/USD/mtgox", - }, - callback); - }, - function (callback) { - self.what = "Verify offer partially consumed."; - - testutils.verify_offer(self.remote, "carol", seq, "25/USD/mtgox", "250", 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.0", ["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") - .path_add( [ { currency: "XRP" } ]) - .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, "20/USD/mtgox", "200", callback); - }, - function (callback) { - self.what = "Verify dan offer partially consumed."; - - testutils.verify_offer(self.remote, "dan", seq_dan, "200", "20/EUR/mtgox", callback); - }, - ], function (error) { - buster.refute(error, self.what); - done(); - }); - }, -}); - -// vim:sw=2:sts=2:ts=8:et From d31d83c125eeb17b9e712795e32c9ae0f153a6f8 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 11:49:26 -0800 Subject: [PATCH 2/7] JS: Don't require leading zeros on float styles. --- src/js/amount.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/amount.js b/src/js/amount.js index 222b912284..b24ff0397b 100644 --- a/src/js/amount.js +++ b/src/js/amount.js @@ -1009,7 +1009,7 @@ Amount.prototype.parse_native = function (j) { var m; if ('string' === typeof j) - m = j.match(/^(-?)(\d+)(\.\d{0,6})?$/); + m = j.match(/^(-?)(\d*)(\.\d{0,6})?$/); if (m) { if (undefined === m[3]) { @@ -1056,8 +1056,8 @@ Amount.prototype.parse_value = function (j) { } else if ('string' === typeof j) { var i = j.match(/^(-?)(\d+)$/); - var d = !i && j.match(/^(-?)(\d+)\.(\d*)$/); - var e = !e && j.match(/^(-?)(\d+)e(-?\d+)$/); + var d = !i && j.match(/^(-?)(\d*)\.(\d*)$/); + var e = !e && j.match(/^(-?)(\d*)e(-?\d+)$/); if (e) { // e notation From 02b9a0b623c9d4d53c299ce144cff238e71bc752 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 12:40:01 -0800 Subject: [PATCH 3/7] Fix applyOffer() calculations and doc. --- src/cpp/ripple/Amount.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index a8e86f4c7b..0f45d01eca 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -1023,7 +1023,7 @@ STAmount STAmount::setRate(uint64 rate) // --> uOfferPaysRate: >= QUALITY_ONE | TransferRate for third party IOUs paid by offer owner. // --> saOfferRate: Original saOfferGets/saOfferPays, when offer was made. // --> saOfferFunds: Limit for saOfferPays : How much can pay including fees. -// --> saTakerFunds: Limit for saOfferGets : How much taker really wants. : Driver +// --> saTakerFunds: Limit for saOfferGets : How much can pay including fees. // --> saOfferPays: Request : this should be reduced as the offer is fullfilled. // --> saOfferGets: Request : this should be reduced as the offer is fullfilled. // --> saTakerPays: Limit for taker to Pays. @@ -1050,7 +1050,7 @@ bool STAmount::applyOffer( // Limit offerer funds available, by transfer fees. STAmount saOfferFundsAvailable = QUALITY_ONE == uOfferPaysRate ? saOfferFunds - : STAmount::divide(saOfferFunds, STAmount(CURRENCY_ONE, uOfferPaysRate, -9)); + : STAmount::divide(saOfferFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9)); cLog(lsINFO) << "applyOffer: uOfferPaysRate=" << uOfferPaysRate; cLog(lsINFO) << "applyOffer: saOfferFundsAvailable=" << saOfferFundsAvailable.getFullText(); @@ -1058,8 +1058,9 @@ bool STAmount::applyOffer( // Limit taker funds available, by transfer fees. STAmount saTakerFundsAvailable = QUALITY_ONE == uTakerPaysRate ? saTakerFunds - : STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, uTakerPaysRate, -9)); + : STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9)); + cLog(lsINFO) << "applyOffer: TAKER_FEES=" << STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9).getFullText(); cLog(lsINFO) << "applyOffer: uTakerPaysRate=" << uTakerPaysRate; cLog(lsINFO) << "applyOffer: saTakerFundsAvailable=" << saTakerFundsAvailable.getFullText(); @@ -1085,10 +1086,14 @@ bool STAmount::applyOffer( cLog(lsINFO) << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText(); STAmount saTakerPaysAvailable = std::min(saTakerPays, saTakerFundsAvailable); + cLog(lsINFO) << "applyOffer: saTakerPaysAvailable=" << saTakerPaysAvailable.getFullText(); STAmount saTakerPaysMax = std::min(saTakerPaysAvailable, saOfferGetsAvailable); + cLog(lsINFO) << "applyOffer: saTakerPaysMax=" << saTakerPaysMax.getFullText(); STAmount saTakerGetsMax = saTakerPaysMax >= saOfferGetsAvailable ? saOfferPaysAvailable // Potentially take entire offer. Avoid math shenanigans. - : std::min(saOfferPaysAvailable, multiply(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer. + : std::min(saOfferPaysAvailable, divide(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer. + cLog(lsINFO) << "applyOffer: saOfferRate=" << saOfferRate.getFullText(); + cLog(lsINFO) << "applyOffer: saTakerGetsMax=" << saTakerGetsMax.getFullText(); saTakerGot = std::min(saTakerGets, saTakerGetsMax); // Limit by wanted. saTakerPaid = saTakerGot == saOfferPaysAvailable From faa344c7d0145e8a4f1807fc75e7996705bc7714 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Mon, 28 Jan 2013 14:32:52 -0800 Subject: [PATCH 4/7] Dispatch write ahead logging through our job queue. SQL logging improvements. --- src/cpp/database/SqliteDatabase.cpp | 42 +++++++++++++++++++---------- src/cpp/database/SqliteDatabase.h | 3 ++- src/cpp/database/database.h | 5 ++-- src/cpp/ripple/Application.cpp | 8 +++--- src/cpp/ripple/JobQueue.cpp | 2 ++ src/cpp/ripple/JobQueue.h | 13 ++++----- src/cpp/ripple/Log.h | 1 + 7 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/cpp/database/SqliteDatabase.cpp b/src/cpp/database/SqliteDatabase.cpp index addf399b68..4575a9e8b4 100644 --- a/src/cpp/database/SqliteDatabase.cpp +++ b/src/cpp/database/SqliteDatabase.cpp @@ -9,9 +9,14 @@ #include #include +#include "../ripple/JobQueue.h" +#include "../ripple/Log.h" + +SETUP_NLOG("DataBase"); + using namespace std; -SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"",""), walRunning(false) +SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"",""), mWalQ(NULL), walRunning(false) { mConnection = NULL; mCurrentStmt = NULL; @@ -22,7 +27,7 @@ void SqliteDatabase::connect() int rc = sqlite3_open(mHost.c_str(), &mConnection); if (rc) { - cout << "Can't open database: " << mHost << " " << rc << endl; + cLog(lsFATAL) << "Can't open database: " << mHost << " " << rc; sqlite3_close(mConnection); assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED)); } @@ -46,9 +51,9 @@ bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok) if (!fail_ok) { #ifdef DEBUG - cout << "SQL Perror:" << rc << endl; - cout << "Statement: " << sql << endl; - cout << "Error: " << sqlite3_errmsg(mConnection) << endl; + cLog(lsWARNING) << "SQL Perror:" << rc; + cLog(lsWARNING) << "Statement: " << sql; + cLog(lsWARNING) << "Error: " << sqlite3_errmsg(mConnection); #endif } return false; @@ -70,9 +75,9 @@ bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok) if (!fail_ok) { #ifdef DEBUG - cout << "SQL Serror:" << rc << endl; - cout << "Statement: " << sql << endl; - cout << "Error: " << sqlite3_errmsg(mConnection) << endl; + cLog(lsWARNING) << "SQL Serror:" << rc; + cLog(lsWARNING) << "Statement: " << sql; + cLog(lsWARNING) << "Error: " << sqlite3_errmsg(mConnection); #endif } return false; @@ -130,7 +135,7 @@ bool SqliteDatabase::getNextRow() else { assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED)); - cout << "SQL Rerror:" << rc << endl; + cLog(lsWARNING) << "SQL Rerror:" << rc; return(false); } } @@ -194,27 +199,32 @@ static int SqliteWALHook(void *s, sqlite3* dbCon, const char *dbName, int walSiz return SQLITE_OK; } -bool SqliteDatabase::setupCheckpointing() +bool SqliteDatabase::setupCheckpointing(JobQueue *q) { + mWalQ = q; sqlite3_wal_hook(mConnection, SqliteWALHook, this); return true; } void SqliteDatabase::doHook(const char *db, int pages) { - if (pages < 256) + if (pages < 512) return; boost::mutex::scoped_lock sl(walMutex); if (walDBs.insert(db).second && !walRunning) { walRunning = true; - boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach(); + if (mWalQ) + mWalQ->addJob(jtWAL, boost::bind(&SqliteDatabase::runWal, this)); + else + boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach(); } } void SqliteDatabase::runWal() { std::set walSet; + std::string name = sqlite3_db_filename(mConnection, "main"); while (1) { @@ -231,10 +241,14 @@ void SqliteDatabase::runWal() BOOST_FOREACH(const std::string& db, walSet) { int log, ckpt; - sqlite3_wal_checkpoint_v2(mConnection, db.c_str(), SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt); + int ret = sqlite3_wal_checkpoint_v2(mConnection, db.c_str(), SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt); + if (ret != SQLITE_OK) + { + cLog((ret == SQLITE_LOCKED) ? lsDEBUG : lsWARNING) << "WAL " + << sqlite3_db_filename(mConnection, "main") << " / " << db << " errror " << ret; + } } walSet.clear(); - } } diff --git a/src/cpp/database/SqliteDatabase.h b/src/cpp/database/SqliteDatabase.h index ad0c0eab66..5b4cfa048d 100644 --- a/src/cpp/database/SqliteDatabase.h +++ b/src/cpp/database/SqliteDatabase.h @@ -16,6 +16,7 @@ class SqliteDatabase : public Database bool mMoreRows; boost::mutex walMutex; + JobQueue* mWalQ; std::set walDBs; bool walRunning; @@ -51,7 +52,7 @@ public: uint64 getBigInt(int colIndex); sqlite3* peekConnection() { return mConnection; } - virtual bool setupCheckpointing(); + virtual bool setupCheckpointing(JobQueue*); virtual SqliteDatabase* getSqliteDB() { return this; } void runWal(); diff --git a/src/cpp/database/database.h b/src/cpp/database/database.h index aaad8ac488..05f9e87700 100644 --- a/src/cpp/database/database.h +++ b/src/cpp/database/database.h @@ -18,6 +18,7 @@ */ class SqliteDatabase; +class JobQueue; class Database { @@ -86,8 +87,8 @@ public: // float getSingleDBValueFloat(const char* sql); // char* getSingleDBValueStr(const char* sql, std::string& retStr); - virtual bool setupCheckpointing() { return false; } - virtual SqliteDatabase* getSqliteDB() { return NULL; } + virtual bool setupCheckpointing(JobQueue*) { return false; } + virtual SqliteDatabase* getSqliteDB() { return NULL; } }; #endif diff --git a/src/cpp/ripple/Application.cpp b/src/cpp/ripple/Application.cpp index 09e37a2e4f..a2d608279c 100644 --- a/src/cpp/ripple/Application.cpp +++ b/src/cpp/ripple/Application.cpp @@ -62,10 +62,10 @@ void Application::stop() { cLog(lsINFO) << "Received shutdown request"; mIOService.stop(); - mJobQueue.shutdown(); mHashedObjectStore.bulkWrite(); mValidations.flush(); mAuxService.stop(); + mJobQueue.shutdown(); cLog(lsINFO) << "Stopped: " << mIOService.stopped(); Instance::shutdown(); @@ -122,9 +122,9 @@ void Application::setup() boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount)); boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount)); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join(); - mTxnDB->getDB()->setupCheckpointing(); - mLedgerDB->getDB()->setupCheckpointing(); - mHashNodeDB->getDB()->setupCheckpointing(); + mTxnDB->getDB()->setupCheckpointing(&mJobQueue); + mLedgerDB->getDB()->setupCheckpointing(&mJobQueue); + mHashNodeDB->getDB()->setupCheckpointing(&mJobQueue); if (theConfig.START_UP == Config::FRESH) { diff --git a/src/cpp/ripple/JobQueue.cpp b/src/cpp/ripple/JobQueue.cpp index fe1dcd829a..5d0106f381 100644 --- a/src/cpp/ripple/JobQueue.cpp +++ b/src/cpp/ripple/JobQueue.cpp @@ -16,6 +16,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false) mJobLoads[jtTRANSACTION].setTargetLatency(250, 1000); mJobLoads[jtPROPOSAL_ut].setTargetLatency(500, 1250); mJobLoads[jtPUBLEDGER].setTargetLatency(1000, 2500); + mJobLoads[jtWAL].setTargetLatency(1000, 2500); mJobLoads[jtVALIDATION_t].setTargetLatency(500, 1500); mJobLoads[jtWRITE].setTargetLatency(750, 1500); mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500); @@ -41,6 +42,7 @@ const char* Job::toString(JobType t) case jtTRANSACTION: return "transaction"; case jtPUBLEDGER: return "publishLedger"; case jtVALIDATION_t: return "trustedValidation"; + case jtWAL: return "writeAhead"; case jtWRITE: return "writeObjects"; case jtTRANSACTION_l: return "localTransaction"; case jtPROPOSAL_t: return "trustedProposal"; diff --git a/src/cpp/ripple/JobQueue.h b/src/cpp/ripple/JobQueue.h index dfdcd1039e..1df31aca7e 100644 --- a/src/cpp/ripple/JobQueue.h +++ b/src/cpp/ripple/JobQueue.h @@ -27,12 +27,13 @@ enum JobType jtCLIENT = 4, // A websocket command from the client jtTRANSACTION = 5, // A transaction received from the network jtPUBLEDGER = 6, // Publish a fully-accepted ledger - jtVALIDATION_t = 7, // A validation from a trusted source - jtWRITE = 8, // Write out hashed objects - jtTRANSACTION_l = 9, // A local transaction - jtPROPOSAL_t = 10, // A proposal from a trusted source - jtADMIN = 11, // An administrative operation - jtDEATH = 12, // job of death, used internally + jtWAL = 7, // Write-ahead logging + jtVALIDATION_t = 8, // A validation from a trusted source + jtWRITE = 9, // Write out hashed objects + jtTRANSACTION_l = 10, // A local transaction + jtPROPOSAL_t = 11, // A proposal from a trusted source + jtADMIN = 12, // An administrative operation + jtDEATH = 13, // job of death, used internally // special types not dispatched by the job pool jtPEER = 17, diff --git a/src/cpp/ripple/Log.h b/src/cpp/ripple/Log.h index 9f72af8176..f9ec0b8f81 100644 --- a/src/cpp/ripple/Log.h +++ b/src/cpp/ripple/Log.h @@ -15,6 +15,7 @@ // Put at the beginning of a C++ file that needs its own log partition #define SETUP_LOG() static LogPartition logPartition(__FILE__) +#define SETUP_NLOG(x) static LogPartition logPartition(x) // Standard conditional log #define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x, logPartition) From 823e04b148dd4229c37029ed840a5799911ef9a8 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 21:13:19 -0800 Subject: [PATCH 5/7] Fixes for rippling funds. --- src/cpp/ripple/Amount.cpp | 7 ++--- src/cpp/ripple/LedgerEntrySet.cpp | 44 +++++++++++++++++-------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/cpp/ripple/Amount.cpp b/src/cpp/ripple/Amount.cpp index 0f45d01eca..2edc7ad32d 100644 --- a/src/cpp/ripple/Amount.cpp +++ b/src/cpp/ripple/Amount.cpp @@ -1111,11 +1111,12 @@ bool STAmount::applyOffer( { // Compute fees in a rounding safe way. - // TakerCost includes transfer fees. - STAmount saTakerCost = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9)); - STAmount saTransferRate = STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9); cLog(lsINFO) << "applyOffer: saTransferRate=" << saTransferRate.getFullText(); + + // TakerCost includes transfer fees. + STAmount saTakerCost = STAmount::multiply(saTakerPaid, saTransferRate); + cLog(lsINFO) << "applyOffer: saTakerCost=" << saTakerCost.getFullText(); cLog(lsINFO) << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText(); saTakerIssuerFee = saTakerCost > saTakerFunds diff --git a/src/cpp/ripple/LedgerEntrySet.cpp b/src/cpp/ripple/LedgerEntrySet.cpp index 7f22632134..d23e0dc433 100644 --- a/src/cpp/ripple/LedgerEntrySet.cpp +++ b/src/cpp/ripple/LedgerEntrySet.cpp @@ -1129,21 +1129,25 @@ STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount& // Calculate transit fee. STAmount LedgerEntrySet::rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount) { - STAmount saTransitFee; - if (uSenderID != uIssuerID && uReceiverID != uIssuerID) { uint32 uTransitRate = rippleTransferRate(uIssuerID); if (QUALITY_ONE != uTransitRate) { - STAmount saTransitRate(CURRENCY_ONE, uTransitRate, -9); + STAmount saTransitRate(CURRENCY_ONE, ACCOUNT_ONE, uTransitRate, -9); - saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer()); + STAmount saTransferTotal = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer()); + STAmount saTransferFee = saTransferTotal-saAmount; + + cLog(lsINFO) << boost::str(boost::format("rippleTransferFee: saTransferFee=%s") + % saTransferFee.getFullText()); + + return saTransferFee; } } - return saTransitFee; + return STAmount(saAmount.getCurrency(), saAmount.getIssuer()); } TER LedgerEntrySet::trustCreate( @@ -1239,7 +1243,7 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei saBalance.setIssuer(ACCOUNT_ONE); - cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s (0) -> %s : %s") + cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s --> %s : %s") % RippleAddress::createHumanAccountID(uSenderID) % RippleAddress::createHumanAccountID(uReceiverID) % saAmount.getFullText()); @@ -1258,21 +1262,21 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei { STAmount saBalance = sleRippleState->getFieldAmount(sfBalance); - if (!bSenderHigh) - saBalance.negate(); // Put balance in low terms. + if (bSenderHigh) + saBalance.negate(); // Put balance in sender terms. STAmount saBefore = saBalance; - saBalance += saAmount; + saBalance -= saAmount; - cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: %s -- (%s > %s) -> %s : %s") + cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: %s --> %s : before=%s amount=%s after=%s") % RippleAddress::createHumanAccountID(uSenderID) - % saBefore.getFullText() - % saBalance.getFullText() % RippleAddress::createHumanAccountID(uReceiverID) - % saAmount.getFullText()); + % saBefore.getFullText() + % saAmount.getFullText() + % saBalance.getFullText()); - if (!bSenderHigh) + if (bSenderHigh) saBalance.negate(); sleRippleState->setFieldAmount(sfBalance, saBalance); @@ -1286,8 +1290,8 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei } // Send regardless of limits. -// --> saAmount: Amount/currency/issuer for receiver to get. -// <-- saActual: Amount actually sent. Sender pay's fees. +// --> saAmount: Amount/currency/issuer to deliver to reciever. +// <-- saActual: Amount actually cost. Sender pay's fees. TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, STAmount& saActual) { const uint160 uIssuerID = saAmount.getIssuer(); @@ -1314,12 +1318,12 @@ TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceive saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above. - cLog(lsINFO) << boost::str(boost::format("rippleSend> %s -- %s--> %s (%s) : %s") + cLog(lsINFO) << boost::str(boost::format("rippleSend> %s -- > %s : deliver=%s fee=%s cost=%s") % RippleAddress::createHumanAccountID(uSenderID) - % saTransitFee.getFullText() % RippleAddress::createHumanAccountID(uReceiverID) - % saActual.getFullText() - % saAmount.getFullText()); + % saAmount.getFullText() + % saTransitFee.getFullText() + % saActual.getFullText()); terResult = rippleCredit(uIssuerID, uReceiverID, saAmount); From 28d4a71e7664c7090ac2cd578ac041e294ef7923 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 21:13:48 -0800 Subject: [PATCH 6/7] Fixes for OfferCreate. --- src/cpp/ripple/OfferCreateTransactor.cpp | 87 +++++++++++++++++------- 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index 7e313f93b0..6a5cbe9f0b 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -26,6 +26,13 @@ TER OfferCreateTransactor::takeOffers( STAmount& saTakerGot, bool& bUnfunded) { + // The book has the most elements. Take the perspective of the book. + // Book is ordered for taker: taker pays / taker gets, < is better + + // The order is for the other books currencys for get and pays are opposites. + // We want the same ratio for the respective currencies. + // So we swap paid and gets for determing take quality. + assert(saTakerPays && saTakerGets); cLog(lsINFO) << "takeOffers: against book: " << uBookBase.ToString(); @@ -58,10 +65,13 @@ TER OfferCreateTransactor::takeOffers( sleOfferDir = mEngine->entryCache(ltDIR_NODE, mEngine->getLedger()->getNextLedgerIndex(uTipIndex, uBookEnd)); if (sleOfferDir) { - cLog(lsINFO) << "takeOffers: possible counter offer found"; - uTipIndex = sleOfferDir->getIndex(); uTipQuality = Ledger::getQuality(uTipIndex); + + cLog(lsINFO) << boost::str(boost::format("takeOffers: possible counter offer found: uTipQuality=%d uTipQuality=%s") + % uTipQuality + % uTipIndex.ToString()); + } else { @@ -77,7 +87,25 @@ TER OfferCreateTransactor::takeOffers( || (bPassive && uTakeQuality == uTipQuality)) { // Done. - cLog(lsINFO) << "takeOffers: done"; + STAmount saTipRate = sleOfferDir ? STAmount::setRate(uTipQuality) : saTakerRate; + + cLog(lsINFO) << boost::str(boost::format("takeOffers: done: dir=%d uTakeQuality=%d %c uTipQuality=%d saTakerRate=%s %c saTipRate=%s bPassive=%d") + % !!sleOfferDir + % uTakeQuality + % (uTakeQuality == uTipQuality + ? '=' + : uTakeQuality < uTipQuality + ? '<' + : '>') + % uTipQuality + % saTakerRate + % (saTakerRate == saTipRate + ? '=' + : saTakerRate < saTipRate + ? '<' + : '>') + % saTipRate + % bPassive); terResult = tesSUCCESS; } @@ -143,9 +171,6 @@ TER OfferCreateTransactor::takeOffers( } else { - STAmount saPay = saTakerPays - saTakerPaid; - if (saTakerFunds < saPay) - saPay = saTakerFunds; STAmount saSubTakerPaid; STAmount saSubTakerGot; STAmount saTakerIssuerFee; @@ -156,7 +181,6 @@ TER OfferCreateTransactor::takeOffers( cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText(); - cLog(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText(); cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText(); @@ -168,7 +192,7 @@ TER OfferCreateTransactor::takeOffers( mEngine->getNodes().rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID), saOfferRate, saOfferFunds, - saPay, + saTakerFunds, saOfferPays, saOfferGets, saTakerPays, @@ -221,17 +245,28 @@ TER OfferCreateTransactor::takeOffers( { terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker. - if (tesSUCCESS == terResult) - terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee); // Offer owner pays issuer transfer fee. +// if (tesSUCCESS == terResult) +// terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee); // Offer owner pays issuer transfer fee. if (tesSUCCESS == terResult) terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner. - if (tesSUCCESS == terResult) - terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee); // Taker pays issuer transfer fee. +// if (tesSUCCESS == terResult) +// terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee); // Taker pays issuer transfer fee. // Reduce amount considered paid by taker's rate (not actual cost). - saTakerPaid += std::min(saPay, STAmount::multiply(saSubTakerGot, saTakerRate, saPay)); + STAmount saPay = saTakerPays - saTakerPaid; + if (saTakerFunds < saPay) + saPay = saTakerFunds; + + STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays); + + cLog(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText(); + cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText(); + + saTakerPaid += std::min(saPay, saTakerUsed); saTakerGot += saSubTakerGot; if (tesSUCCESS == terResult) @@ -368,8 +403,8 @@ TER OfferCreateTransactor::doApply() } } - STAmount saOfferPaid; - STAmount saOfferGot; + STAmount saPaid; + STAmount saGot; bool bUnfunded = false; if (tesSUCCESS == terResult) @@ -383,27 +418,29 @@ TER OfferCreateTransactor::doApply() // Take using the parameters of the offer. cLog(lsWARNING) << "OfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText(); + terResult = takeOffers( bPassive, uTakeBookBase, mTxnAccountID, mTxnAccount, - saTakerGets, + saTakerGets, // Reverse as we are the taker for taking. saTakerPays, - saOfferPaid, // How much would have spent at full price. - saOfferGot, // How much was got. + saPaid, // How much would have spent at full price. + saGot, // How much was got. bUnfunded); cLog(lsWARNING) << "OfferCreate: takeOffers=" << terResult; - cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText(); - cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText(); - cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); - cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: saPaid=" << saPaid.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: saGot=" << saGot.getFullText(); if (tesSUCCESS == terResult && !bUnfunded) { - saTakerPays -= saOfferGot; // Reduce payin from takers by what offer just got. - saTakerGets -= saOfferPaid; // Reduce payout to takers by what srcAccount just paid. + saTakerPays -= saGot; // Reduce pay in from takers by what offer just got. + saTakerGets -= saPaid; // Reduce pay out to takers by what srcAccount just paid. + + cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerPays=" << saTakerPays.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText(); } } @@ -431,7 +468,7 @@ TER OfferCreateTransactor::doApply() // Hope for more reserve to come in or more offers to consume. terResult = tecINSUF_RESERVE_OFFER; } - else if (!saOfferPaid && !saOfferGot) + else if (!saPaid && !saGot) { // Ledger is final, insufficent reserve to create offer, processed nothing. From ee65b779f95006851b107218f2aae8625347418a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 28 Jan 2013 21:14:14 -0800 Subject: [PATCH 7/7] UT: New offer test to test for problems seend in the field. --- test/offer-test.js | 114 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/test/offer-test.js b/test/offer-test.js index 56083b2e7f..de87c7d103 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -1349,4 +1349,118 @@ buster.testCase("Offer cross currency", { }, }); +buster.testCase("Offer tests 3", { + 'setUp' : testutils.build_setup(), + // 'setUp' : testutils.build_setup({ verbose: true }), + // 'setUp' : testutils.build_setup({ verbose: true, standalone: true }), + 'tearDown' : testutils.build_teardown(), + + "offer create then cross offer" : + function (done) { + var self = this; + var final_create; + + async.waterfall([ + function (callback) { + self.what = "Create accounts."; + + testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback); + }, + function (callback) { + self.what = "Set transfer rate."; + + self.remote.transaction() + .account_set("mtgox") + .transfer_rate(1005000000) + .once('proposed', function (m) { + // console.log("proposed: %s", JSON.stringify(m)); + callback(m.result !== 'tesSUCCESS'); + }) + .submit(); + }, + function (callback) { + self.what = "Set limits."; + + testutils.credit_limits(self.remote, + { + "alice" : "1000/USD/mtgox", + "bob" : "1000/USD/mtgox", + "mtgox" : "50/USD/alice", + }, + callback); + }, + function (callback) { + self.what = "Distribute funds."; + + testutils.payments(self.remote, + { + "mtgox" : [ "1/USD/bob" ], + "alice" : [ "50/USD/mtgox" ] + }, + callback); + }, + function (callback) { + self.what = "Set limits 2."; + + testutils.credit_limits(self.remote, + { + "mtgox" : "0/USD/alice", + }, + callback); + }, + function (callback) { + self.what = "Create offer alice."; + + self.remote.transaction() + .offer_create("alice", "50/USD/mtgox", "150000.0") + .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 bob."; + + self.remote.transaction() + .offer_create("bob", "100.0", ".1/USD/mtgox") + .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 = "Display ledger"; +// +// self.remote.request_ledger('current', true) +// .on('success', function (m) { +// console.log("Ledger: %s", JSON.stringify(m, undefined, 2)); +// +// callback(); +// }) +// .request(); +// }, + function (callback) { + self.what = "Verify balances."; + + testutils.verify_balances(self.remote, + { + "alice" : "-49.96666666666667/USD/mtgox", + "bob" : "0.9665/USD/mtgox", + }, + callback); + }, + ], function (error) { + // console.log("result: error=%s", error); + buster.refute(error); + + done(); + }); + }, +}); // vim:sw=2:sts=2:ts=8:et