From 75d935e0df84d2c1537154fe0d5d1a2c26e1692f Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 13:04:07 -0800 Subject: [PATCH 1/7] Merge tepPARTIAL into tecCLAIMED. --- src/cpp/ripple/RippleCalc.cpp | 8 +++----- src/cpp/ripple/TransactionEngine.cpp | 4 ---- src/cpp/ripple/TransactionErr.cpp | 5 ++--- src/cpp/ripple/TransactionErr.h | 17 +++-------------- src/js/remote.js | 8 ++++---- test/offer-test.js | 2 +- test/send-test.js | 4 ++-- 7 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/cpp/ripple/RippleCalc.cpp b/src/cpp/ripple/RippleCalc.cpp index 783ec7be2..2d85bea14 100644 --- a/src/cpp/ripple/RippleCalc.cpp +++ b/src/cpp/ripple/RippleCalc.cpp @@ -2567,8 +2567,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s qual { // Have sent maximum allowed. Partial payment not allowed. - terResult = tepPATH_PARTIAL; - lesActive = lesBase; // Revert to just fees charged. + terResult = tecPATH_PARTIAL; } else { @@ -2581,14 +2580,13 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s qual else if (!bPartialPayment) { // Partial payment not allowed. - terResult = tepPATH_PARTIAL; - lesActive = lesBase; // Revert to just fees charged. + terResult = tecPATH_PARTIAL; } // Partial payment ok. else if (!saDstAmountAct) { // No payment at all. - terResult = tecPATH_DRY; // Revert to just fees charged is built into tec. + terResult = tecPATH_DRY; } else { diff --git a/src/cpp/ripple/TransactionEngine.cpp b/src/cpp/ripple/TransactionEngine.cpp index 7a3af2d4c..2f8641b70 100644 --- a/src/cpp/ripple/TransactionEngine.cpp +++ b/src/cpp/ripple/TransactionEngine.cpp @@ -116,10 +116,6 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Transa { didApply = true; } - else if (isTepPartial(terResult) && !isSetBit(params, tapRETRY)) - { - didApply = true; - } else if (isTecClaim(terResult) && !isSetBit(params, tapRETRY)) { // only claim the transaction fee cLog(lsINFO) << "Reprocessing to only claim fee"; diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index df2d62cef..131e03ad3 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -17,6 +17,8 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { tecNO_LINE_INSUF_RESERVE, "tecNO_LINE_INSUF_RESERVE", "No such line. Too little reserve to create it." }, { tecNO_LINE_REDUNDANT, "tecNO_LINE_REDUNDANT", "Can't set non-existant line to default." }, { tecPATH_DRY, "tecPATH_DRY", "Path could not send partial amount." }, + { tecPATH_PARTIAL, "tecPATH_PARTIAL", "Path could not send full amount." }, + { tecUNFUNDED, "tecUNFUNDED", "Source account had insufficient balance for transaction." }, { tefFAILURE, "tefFAILURE", "Failed to apply." }, @@ -58,9 +60,6 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { temUNCERTAIN, "temUNCERTAIN", "In process of determining result. Never returned." }, { temUNKNOWN, "temUNKNOWN", "The transactions requires logic not implemented yet." }, - { tepPARTIAL, "tepPARTIAL", "Partial success." }, - { tepPATH_PARTIAL, "tepPATH_PARTIAL", "Path could not send full amount." }, - { terRETRY, "terRETRY", "Retry transaction." }, { terFUNDS_SPENT, "terFUNDS_SPENT", "Can't set password, password set funds already spent." }, { terINSUF_FEE_B, "terINSUF_FEE_B", "Account balance can't pay fee." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 238ef7ddf..85f822607 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -92,20 +92,9 @@ enum TER // aka TransactionEngineResult // - Forwarded tesSUCCESS = 0, - // 100 .. 119 P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account) + // 100 .. 129 C Claim fee only (ripple transaction with no good paths, pay to non-existent account, no path) // Causes: // - Success, but does not achieve optimal result. - // Implications: - // - Applied - // - Forwarded - // Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY. - // - // DO NOT CHANGE THESE NUMBERS: They appear in ledger meta data. - tepPARTIAL = 100, - tepPATH_PARTIAL = 101, - - // 120 .. C Claim fee only (CO) (no path) - // Causes: // - Invalid transaction or no effect, but claim fee to use the sequence number. // Implications: // - Applied @@ -113,7 +102,8 @@ enum TER // aka TransactionEngineResult // Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY. // // DO NOT CHANGE THESE NUMBERS: They appear in ledger meta data. - tecCLAIM = 120, + tecCLAIM = 100, + tecPATH_PARTIAL = 101, tecDIR_FULL = 121, tecINSUF_RESERVE_LINE = 122, tecINSUF_RESERVE_OFFER = 123, @@ -130,7 +120,6 @@ enum TER // aka TransactionEngineResult #define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY) #define isTerRetry(x) ((x) >= terRETRY && (x) < tesSUCCESS) #define isTepSuccess(x) ((x) >= tesSUCCESS) -#define isTepPartial(x) ((x) >= tepPATH_PARTIAL && (x) < tecCLAIM) #define isTecClaim(x) ((x) >= tecCLAIM) bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman); diff --git a/src/js/remote.js b/src/js/remote.js index 9e46470b4..76b27f718 100644 --- a/src/js/remote.js +++ b/src/js/remote.js @@ -1162,7 +1162,7 @@ Remote.prototype.transaction = function () { // |/ // |- 'tesSUCCESS' - Transaction in ledger as expected. // |- 'ter...' - Transaction failed. -// \- 'tep...' - Transaction partially succeeded. +// \- 'tec...' - Transaction claimed fee only. // // Notes: // - All transactions including those with local and malformed errors may be @@ -1225,7 +1225,7 @@ Transaction.prototype.consts = { 'tefFAILURE' : -199, 'terRETRY' : -99, 'tesSUCCESS' : 0, - 'tepPARTIAL' : 100, + 'tecCLAIMED' : 100, }; Transaction.prototype.isTelLocal = function (ter) { @@ -1248,8 +1248,8 @@ Transaction.prototype.isTepSuccess = function (ter) { return ter >= this.consts.tesSUCCESS; }; -Transaction.prototype.isTepPartial = function (ter) { - return ter >= this.consts.tepPATH_PARTIAL; +Transaction.prototype.isTecClaimed = function (ter) { + return ter >= this.consts.tecCLAIMED; }; Transaction.prototype.isRejected = function (ter) { diff --git a/test/offer-test.js b/test/offer-test.js index ec629c66d..2c20dafb1 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -602,7 +602,7 @@ buster.testCase("Offer tests", { .on('proposed', function (m) { // console.log("proposed: %s", JSON.stringify(m)); - callback(m.result !== 'tepPATH_PARTIAL'); + callback(m.result !== 'tecPATH_PARTIAL'); }) .submit(); }, diff --git a/test/send-test.js b/test/send-test.js index 2322da8a9..c47b054f1 100644 --- a/test/send-test.js +++ b/test/send-test.js @@ -549,7 +549,7 @@ buster.testCase("Indirect ripple", { .on('proposed', function (m) { // console.log("proposed: %s", JSON.stringify(m)); - callback(m.result !== 'tepPATH_PARTIAL'); + callback(m.result !== 'tecPATH_PARTIAL'); }) .submit(); }, @@ -561,7 +561,7 @@ buster.testCase("Indirect ripple", { .on('proposed', function (m) { // console.log("proposed: %s", JSON.stringify(m)); - callback(m.result !== 'tepPATH_PARTIAL'); + callback(m.result !== 'tecPATH_PARTIAL'); }) .submit(); }, From 75466bc4b6566834fe6773e912dda2f42b8980fe Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 13:04:48 -0800 Subject: [PATCH 2/7] UT: Add reserve tests. --- test/reserve-test.js | 920 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 920 insertions(+) create mode 100644 test/reserve-test.js diff --git a/test/reserve-test.js b/test/reserve-test.js new file mode 100644 index 000000000..50c5d4e74 --- /dev/null +++ b/test/reserve-test.js @@ -0,0 +1,920 @@ + +var async = require("async"); +var buster = require("buster"); + +var Amount = require("../src/js/amount.js").Amount; +var Remote = require("../src/js/remote.js").Remote; +var Server = require("./server.js").Server; + +var testutils = require("./testutils.js"); + +require("../src/js/amount.js").config = require("./config.js"); +require("../src/js/remote.js").config = require("./config.js"); + +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*(Remote.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'); + + 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*(Remote.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*(Remote.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 949679dd1a5d5321f40250b8405a41895fea98af Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 13:06:38 -0800 Subject: [PATCH 3/7] Add disabled support for DestinationTag & lsfRequireDestTag. --- src/cpp/ripple/LedgerFormats.h | 5 +++++ src/cpp/ripple/PaymentTransactor.cpp | 8 ++++++++ src/cpp/ripple/SerializeProto.h | 5 +++++ src/cpp/ripple/Transaction.h | 2 +- src/cpp/ripple/TransactionErr.cpp | 3 +++ src/cpp/ripple/TransactionErr.h | 3 +++ src/cpp/ripple/TransactionFormats.cpp | 3 +++ 7 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/cpp/ripple/LedgerFormats.h b/src/cpp/ripple/LedgerFormats.h index e39e7a894..4c2ba9c54 100644 --- a/src/cpp/ripple/LedgerFormats.h +++ b/src/cpp/ripple/LedgerFormats.h @@ -3,6 +3,8 @@ #include "SerializedObject.h" +#define ENABLE_REQUIRE_DEST_TAG 0 + // Used as the type of a transaction or the type of a ledger entry. enum LedgerEntryType { @@ -40,6 +42,9 @@ enum LedgerSpecificFlags { // ltACCOUNT_ROOT lsfPasswordSpent = 0x00010000, // True, if password set fee is spent. +#if ENABLE_REQUIRE_DEST_TAG + lsfRequireDestTag = 0x00020000, // True, to require a DestinationTag for payments. +#endif // ltOFFER lsfPassive = 0x00010000, diff --git a/src/cpp/ripple/PaymentTransactor.cpp b/src/cpp/ripple/PaymentTransactor.cpp index e5e27a057..9cc97309e 100644 --- a/src/cpp/ripple/PaymentTransactor.cpp +++ b/src/cpp/ripple/PaymentTransactor.cpp @@ -99,6 +99,14 @@ TER PaymentTransactor::doApply() sleDst->setFieldAccount(sfAccount, uDstAccountID); sleDst->setFieldU32(sfSequence, 1); } +#if ENABLE_REQUIRE_DEST_TAG + else if ((sleDst->getFlags() & lsfRequireDestTag) && !mTxn.isFieldPresent(sfDestinationTag)) + { + cLog(lsINFO) << "doPayment: Malformed transaction: DestinationTag required."; + + return temINVALID; + } +#endif else { mEngine->entryModify(sleDst); diff --git a/src/cpp/ripple/SerializeProto.h b/src/cpp/ripple/SerializeProto.h index 57ef55200..fc6ee038a 100644 --- a/src/cpp/ripple/SerializeProto.h +++ b/src/cpp/ripple/SerializeProto.h @@ -1,6 +1,8 @@ // This is not really a header file, but it can be used as one with // appropriate #define statements. +#define ENABLE_DESTINATION_TAG 0 + // types (common) TYPE(Int16, UINT16, 1) TYPE(Int32, UINT32, 2) @@ -44,6 +46,9 @@ FIELD(TransferRate, UINT32, 11) FIELD(WalletSize, UINT32, 12) FIELD(OwnerCount, UINT32, 13) +#if ENABLE_DESTINATION_TAG + FIELD(DestinationTag, UINT32, 14) +#endif // 32-bit integers (uncommon) FIELD(HighQualityIn, UINT32, 16) diff --git a/src/cpp/ripple/Transaction.h b/src/cpp/ripple/Transaction.h index 35d33b91a..29505f285 100644 --- a/src/cpp/ripple/Transaction.h +++ b/src/cpp/ripple/Transaction.h @@ -84,7 +84,7 @@ public: STAmount getAmount() const { return mTransaction->getFieldU64(sfAmount); } STAmount getFee() const { return mTransaction->getTransactionFee(); } uint32 getFromAccountSeq() const { return mTransaction->getSequence(); } - uint32 getIdent() const { return mTransaction->getFieldU32(sfSourceTag); } + uint32 getSourceTag() const { return mTransaction->getFieldU32(sfSourceTag); } std::vector getSignature() const { return mTransaction->getSignature(); } uint32 getLedger() const { return mInLedger; } TransStatus getStatus() const { return mStatus; } diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index 131e03ad3..b02b2c50c 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -53,6 +53,9 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence is not in the past." }, { temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." }, { temDST_NEEDED, "temDST_NEEDED", "Destination not specified." }, +#if ENABLE_REQUIRE_DEST_TAG + { temDST_TAG_NEEDED, "temDST_TAG_NEEDED", "Destination tag required." }, +#endif { temINVALID, "temINVALID", "The transaction is ill-formed." }, { temINVALID_FLAG, "temINVALID_FLAG", "The transaction has an invalid flag." }, { temREDUNDANT, "temREDUNDANT", "Sends same currency to self." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 85f822607..676a84142 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -39,6 +39,9 @@ enum TER // aka TransactionEngineResult temBAD_SEQUENCE, temDST_IS_SRC, temDST_NEEDED, +#if ENABLE_REQUIRE_DEST_TAG + temDST_TAG_NEEDED, +#endif temINVALID, temINVALID_FLAG, temREDUNDANT, diff --git a/src/cpp/ripple/TransactionFormats.cpp b/src/cpp/ripple/TransactionFormats.cpp index b428ed764..8e77e585c 100644 --- a/src/cpp/ripple/TransactionFormats.cpp +++ b/src/cpp/ripple/TransactionFormats.cpp @@ -55,6 +55,9 @@ static bool TFInit() << SOElement(sfSendMax, SOE_OPTIONAL) << SOElement(sfPaths, SOE_DEFAULT) << SOElement(sfInvoiceID, SOE_OPTIONAL) +#if ENABLE_DESTINATION_TAG + << SOElement(sfDestinationTag, SOE_OPTIONAL) +#endif ; DECLARE_TF(Contract, ttCONTRACT) From 7dfbe2aea1e43758a0331abf0f94031bb54bae81 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 14:07:41 -0800 Subject: [PATCH 4/7] Return temINVALID_FLAG for transaction with no flags. --- src/cpp/ripple/AccountSetTransactor.cpp | 35 +++++++---- src/cpp/ripple/OfferCancelTransactor.cpp | 19 ++++-- src/cpp/ripple/OfferCreateTransactor.cpp | 72 +++++++++++----------- src/cpp/ripple/PaymentTransactor.cpp | 24 ++++---- src/cpp/ripple/RegularKeySetTransactor.cpp | 24 +++++--- src/cpp/ripple/TrustSetTransactor.cpp | 9 +++ src/cpp/ripple/WalletAddTransactor.cpp | 13 +++- 7 files changed, 123 insertions(+), 73 deletions(-) diff --git a/src/cpp/ripple/AccountSetTransactor.cpp b/src/cpp/ripple/AccountSetTransactor.cpp index 0d1499e5a..08f2bedaf 100644 --- a/src/cpp/ripple/AccountSetTransactor.cpp +++ b/src/cpp/ripple/AccountSetTransactor.cpp @@ -4,7 +4,16 @@ SETUP_LOG(); TER AccountSetTransactor::doApply() { - cLog(lsINFO) << "doAccountSet>"; + cLog(lsINFO) << "AccountSet>"; + + const uint32 uTxFlags = mTxn.getFlags(); + + if (uTxFlags) + { + cLog(lsINFO) << "AccountSet: Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } // // EmailHash @@ -16,13 +25,13 @@ TER AccountSetTransactor::doApply() if (!uHash) { - cLog(lsINFO) << "doAccountSet: unset email hash"; + cLog(lsINFO) << "AccountSet: unset email hash"; mTxnAccount->makeFieldAbsent(sfEmailHash); } else { - cLog(lsINFO) << "doAccountSet: set email hash"; + cLog(lsINFO) << "AccountSet: set email hash"; mTxnAccount->setFieldH128(sfEmailHash, uHash); } @@ -38,13 +47,13 @@ TER AccountSetTransactor::doApply() if (!uHash) { - cLog(lsINFO) << "doAccountSet: unset wallet locator"; + cLog(lsINFO) << "AccountSet: unset wallet locator"; mTxnAccount->makeFieldAbsent(sfEmailHash); } else { - cLog(lsINFO) << "doAccountSet: set wallet locator"; + cLog(lsINFO) << "AccountSet: set wallet locator"; mTxnAccount->setFieldH256(sfWalletLocator, uHash); } @@ -60,7 +69,7 @@ TER AccountSetTransactor::doApply() } else { - cLog(lsINFO) << "doAccountSet: set message key"; + cLog(lsINFO) << "AccountSet: set message key"; mTxnAccount->setFieldVL(sfMessageKey, mTxn.getFieldVL(sfMessageKey)); } @@ -75,13 +84,13 @@ TER AccountSetTransactor::doApply() if (vucDomain.empty()) { - cLog(lsINFO) << "doAccountSet: unset domain"; + cLog(lsINFO) << "AccountSet: unset domain"; mTxnAccount->makeFieldAbsent(sfDomain); } else { - cLog(lsINFO) << "doAccountSet: set domain"; + cLog(lsINFO) << "AccountSet: set domain"; mTxnAccount->setFieldVL(sfDomain, vucDomain); } @@ -97,25 +106,27 @@ TER AccountSetTransactor::doApply() if (!uRate || uRate == QUALITY_ONE) { - cLog(lsINFO) << "doAccountSet: unset transfer rate"; + cLog(lsINFO) << "AccountSet: unset transfer rate"; mTxnAccount->makeFieldAbsent(sfTransferRate); } else if (uRate > QUALITY_ONE) { - cLog(lsINFO) << "doAccountSet: set transfer rate"; + cLog(lsINFO) << "AccountSet: set transfer rate"; mTxnAccount->setFieldU32(sfTransferRate, uRate); } else { - cLog(lsINFO) << "doAccountSet: bad transfer rate"; + cLog(lsINFO) << "AccountSet: bad transfer rate"; return temBAD_TRANSFER_RATE; } } - cLog(lsINFO) << "doAccountSet<"; + cLog(lsINFO) << "AccountSet<"; return tesSUCCESS; } + +// vim:ts=4 diff --git a/src/cpp/ripple/OfferCancelTransactor.cpp b/src/cpp/ripple/OfferCancelTransactor.cpp index 0eaff9daa..e17e4c9cf 100644 --- a/src/cpp/ripple/OfferCancelTransactor.cpp +++ b/src/cpp/ripple/OfferCancelTransactor.cpp @@ -1,17 +1,28 @@ #include "OfferCancelTransactor.h" #include "Log.h" +SETUP_LOG(); + TER OfferCancelTransactor::doApply() { TER terResult; const uint32 uOfferSequence = mTxn.getFieldU32(sfOfferSequence); const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32(sfSequence); - Log(lsDEBUG) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; + cLog(lsDEBUG) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; + + const uint32 uTxFlags = mTxn.getFlags(); + + if (uTxFlags) + { + cLog(lsINFO) << "OfferCancel: Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } if (!uOfferSequence || uAccountSequenceNext-1 <= uOfferSequence) { - Log(lsINFO) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; + cLog(lsINFO) << "OfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; terResult = temBAD_SEQUENCE; } @@ -22,13 +33,13 @@ TER OfferCancelTransactor::doApply() if (sleOffer) { - Log(lsWARNING) << "doOfferCancel: uOfferSequence=" << uOfferSequence; + cLog(lsWARNING) << "OfferCancel: uOfferSequence=" << uOfferSequence; terResult = mEngine->getNodes().offerDelete(sleOffer, uOfferIndex, mTxnAccountID); } else { - Log(lsWARNING) << "doOfferCancel: offer not found: " + cLog(lsWARNING) << "OfferCancel: offer not found: " << RippleAddress::createHumanAccountID(mTxnAccountID) << " : " << uOfferSequence << " : " << uOfferIndex.ToString(); diff --git a/src/cpp/ripple/OfferCreateTransactor.cpp b/src/cpp/ripple/OfferCreateTransactor.cpp index ec6b9192b..aa7929c41 100644 --- a/src/cpp/ripple/OfferCreateTransactor.cpp +++ b/src/cpp/ripple/OfferCreateTransactor.cpp @@ -256,13 +256,13 @@ TER OfferCreateTransactor::takeOffers( TER OfferCreateTransactor::doApply() { - cLog(lsWARNING) << "doOfferCreate> " << mTxn.getJson(0); + cLog(lsWARNING) << "OfferCreate> " << mTxn.getJson(0); const uint32 uTxFlags = mTxn.getFlags(); const bool bPassive = isSetBit(uTxFlags, tfPassive); STAmount saTakerPays = mTxn.getFieldAmount(sfTakerPays); STAmount saTakerGets = mTxn.getFieldAmount(sfTakerGets); - cLog(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGets=%s") + cLog(lsINFO) << boost::str(boost::format("OfferCreate: saTakerPays=%s saTakerGets=%s") % saTakerPays.getFullText() % saTakerGets.getFullText()); @@ -274,7 +274,7 @@ TER OfferCreateTransactor::doApply() const uint256 uLedgerIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); - cLog(lsINFO) << "doOfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence; + cLog(lsINFO) << "OfferCreate: Creating offer node: " << uLedgerIndex.ToString() << " uSequence=" << uSequence; const uint160 uPaysCurrency = saTakerPays.getCurrency(); const uint160 uGetsCurrency = saTakerGets.getCurrency(); @@ -287,49 +287,49 @@ TER OfferCreateTransactor::doApply() if (uTxFlags & tfOfferCreateMask) { - cLog(lsINFO) << "doOfferCreate: Malformed transaction: Invalid flags set."; + cLog(lsINFO) << "OfferCreate: Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } else if (bHaveExpiration && !uExpiration) { - cLog(lsWARNING) << "doOfferCreate: Malformed offer: bad expiration"; + cLog(lsWARNING) << "OfferCreate: Malformed offer: bad expiration"; terResult = temBAD_EXPIRATION; } else if (bHaveExpiration && mEngine->getLedger()->getParentCloseTimeNC() >= uExpiration) { - cLog(lsWARNING) << "doOfferCreate: Expired transaction: offer expired"; + cLog(lsWARNING) << "OfferCreate: Expired transaction: offer expired"; terResult = tesSUCCESS; // Only charged fee. } else if (saTakerPays.isNative() && saTakerGets.isNative()) { - cLog(lsWARNING) << "doOfferCreate: Malformed offer: XRP for XRP"; + cLog(lsWARNING) << "OfferCreate: Malformed offer: XRP for XRP"; terResult = temBAD_OFFER; } else if (!saTakerPays.isPositive() || !saTakerGets.isPositive()) { - cLog(lsWARNING) << "doOfferCreate: Malformed offer: bad amount"; + cLog(lsWARNING) << "OfferCreate: Malformed offer: bad amount"; terResult = temBAD_OFFER; } else if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID) { - cLog(lsWARNING) << "doOfferCreate: Malformed offer: redundant offer"; + cLog(lsWARNING) << "OfferCreate: Malformed offer: redundant offer"; terResult = temREDUNDANT; } else if (saTakerPays.isNative() != !uPaysIssuerID || saTakerGets.isNative() != !uGetsIssuerID) { - cLog(lsWARNING) << "doOfferCreate: Malformed offer: bad issuer"; + cLog(lsWARNING) << "OfferCreate: Malformed offer: bad issuer"; terResult = temBAD_ISSUER; } else if (!mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive()) { - cLog(lsWARNING) << "doOfferCreate: delay: Offers must be at least partially funded."; + cLog(lsWARNING) << "OfferCreate: delay: Offers must be at least partially funded."; terResult = tecUNFUNDED; } @@ -340,7 +340,7 @@ TER OfferCreateTransactor::doApply() if (!sleTakerPays) { - cLog(lsWARNING) << "doOfferCreate: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID(uPaysIssuerID); + cLog(lsWARNING) << "OfferCreate: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID(uPaysIssuerID); terResult = terNO_ACCOUNT; } @@ -353,13 +353,13 @@ TER OfferCreateTransactor::doApply() { const uint256 uTakeBookBase = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); - cLog(lsINFO) << boost::str(boost::format("doOfferCreate: take against book: %s for %s -> %s") + cLog(lsINFO) << boost::str(boost::format("OfferCreate: take against book: %s for %s -> %s") % uTakeBookBase.ToString() % saTakerGets.getFullText() % saTakerPays.getFullText()); // Take using the parameters of the offer. - cLog(lsWARNING) << "doOfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText(); terResult = takeOffers( bPassive, uTakeBookBase, @@ -371,11 +371,11 @@ TER OfferCreateTransactor::doApply() saOfferGot // How much was got. ); - cLog(lsWARNING) << "doOfferCreate: takeOffers=" << terResult; - cLog(lsWARNING) << "doOfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText(); - cLog(lsWARNING) << "doOfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText(); - cLog(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); - cLog(lsWARNING) << "doOfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText(); + 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(); if (tesSUCCESS == terResult) { @@ -384,13 +384,13 @@ TER OfferCreateTransactor::doApply() } } - cLog(lsWARNING) << "doOfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); - cLog(lsWARNING) << "doOfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); - cLog(lsWARNING) << "doOfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID); - cLog(lsWARNING) << "doOfferCreate: takeOffers: FUNDS=" << mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerGets=" << saTakerGets.getFullText(); + cLog(lsWARNING) << "OfferCreate: takeOffers: mTxnAccountID=" << RippleAddress::createHumanAccountID(mTxnAccountID); + cLog(lsWARNING) << "OfferCreate: takeOffers: FUNDS=" << mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).getFullText(); - // cLog(lsWARNING) << "doOfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); - // cLog(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); + // cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); + // cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); if (tesSUCCESS != terResult || !saTakerPays // Wants nothing more. @@ -424,7 +424,7 @@ TER OfferCreateTransactor::doApply() else { // We need to place the remainder of the offer into its order book. - cLog(lsINFO) << boost::str(boost::format("doOfferCreate: offer not fully consumed: saTakerPays=%s saTakerGets=%s") + cLog(lsINFO) << boost::str(boost::format("OfferCreate: offer not fully consumed: saTakerPays=%s saTakerGets=%s") % saTakerPays.getFullText() % saTakerGets.getFullText()); @@ -440,7 +440,7 @@ TER OfferCreateTransactor::doApply() uint256 uBookBase = Ledger::getBookBase(uPaysCurrency, uPaysIssuerID, uGetsCurrency, uGetsIssuerID); - cLog(lsINFO) << boost::str(boost::format("doOfferCreate: adding to book: %s : %s/%s -> %s/%s") + cLog(lsINFO) << boost::str(boost::format("OfferCreate: adding to book: %s : %s/%s -> %s/%s") % uBookBase.ToString() % saTakerPays.getHumanCurrency() % RippleAddress::createHumanAccountID(saTakerPays.getIssuer()) @@ -457,13 +457,13 @@ TER OfferCreateTransactor::doApply() if (tesSUCCESS == terResult) { - cLog(lsWARNING) << "doOfferCreate: sfAccount=" << RippleAddress::createHumanAccountID(mTxnAccountID); - cLog(lsWARNING) << "doOfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); - cLog(lsWARNING) << "doOfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); - cLog(lsWARNING) << "doOfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative(); - cLog(lsWARNING) << "doOfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative(); - cLog(lsWARNING) << "doOfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency(); - cLog(lsWARNING) << "doOfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency(); + cLog(lsWARNING) << "OfferCreate: sfAccount=" << RippleAddress::createHumanAccountID(mTxnAccountID); + cLog(lsWARNING) << "OfferCreate: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID); + cLog(lsWARNING) << "OfferCreate: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID); + cLog(lsWARNING) << "OfferCreate: saTakerPays.isNative()=" << saTakerPays.isNative(); + cLog(lsWARNING) << "OfferCreate: saTakerGets.isNative()=" << saTakerGets.isNative(); + cLog(lsWARNING) << "OfferCreate: uPaysCurrency=" << saTakerPays.getHumanCurrency(); + cLog(lsWARNING) << "OfferCreate: uGetsCurrency=" << saTakerGets.getHumanCurrency(); SLE::pointer sleOffer = mEngine->entryCreate(ltOFFER, uLedgerIndex); @@ -481,13 +481,13 @@ TER OfferCreateTransactor::doApply() if (bPassive) sleOffer->setFlag(lsfPassive); - cLog(lsINFO) << boost::str(boost::format("doOfferCreate: final terResult=%s sleOffer=%s") + cLog(lsINFO) << boost::str(boost::format("OfferCreate: final terResult=%s sleOffer=%s") % transToken(terResult) % sleOffer->getJson(0)); } } - tLog(tesSUCCESS != terResult, lsINFO) << boost::str(boost::format("doOfferCreate: final terResult=%s") % transToken(terResult)); + tLog(tesSUCCESS != terResult, lsINFO) << boost::str(boost::format("OfferCreate: final terResult=%s") % transToken(terResult)); return terResult; } diff --git a/src/cpp/ripple/PaymentTransactor.cpp b/src/cpp/ripple/PaymentTransactor.cpp index 9cc97309e..51dd34649 100644 --- a/src/cpp/ripple/PaymentTransactor.cpp +++ b/src/cpp/ripple/PaymentTransactor.cpp @@ -26,37 +26,37 @@ TER PaymentTransactor::doApply() const uint160 uSrcCurrency = saMaxAmount.getCurrency(); const uint160 uDstCurrency = saDstAmount.getCurrency(); - cLog(lsINFO) << boost::str(boost::format("doPayment> saMaxAmount=%s saDstAmount=%s") + cLog(lsINFO) << boost::str(boost::format("Payment> saMaxAmount=%s saDstAmount=%s") % saMaxAmount.getFullText() % saDstAmount.getFullText()); if (uTxFlags & tfPaymentMask) { - cLog(lsINFO) << "doPayment: Malformed transaction: Invalid flags set."; + cLog(lsINFO) << "Payment: Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } else if (!uDstAccountID) { - cLog(lsINFO) << "doPayment: Malformed transaction: Payment destination account not specified."; + cLog(lsINFO) << "Payment: Malformed transaction: Payment destination account not specified."; return temDST_NEEDED; } else if (bMax && !saMaxAmount.isPositive()) { - cLog(lsINFO) << "doPayment: Malformed transaction: bad max amount: " << saMaxAmount.getFullText(); + cLog(lsINFO) << "Payment: Malformed transaction: bad max amount: " << saMaxAmount.getFullText(); return temBAD_AMOUNT; } else if (!saDstAmount.isPositive()) { - cLog(lsINFO) << "doPayment: Malformed transaction: bad dst amount: " << saDstAmount.getFullText(); + cLog(lsINFO) << "Payment: Malformed transaction: bad dst amount: " << saDstAmount.getFullText(); return temBAD_AMOUNT; } else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) { - cLog(lsINFO) << boost::str(boost::format("doPayment: Malformed transaction: Redundant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") + cLog(lsINFO) << boost::str(boost::format("Payment: Malformed transaction: Redundant transaction: src=%s, dst=%s, src_cur=%s, dst_cur=%s") % mTxnAccountID.ToString() % uDstAccountID.ToString() % uSrcCurrency.ToString() @@ -68,7 +68,7 @@ TER PaymentTransactor::doApply() && ((saMaxAmount == saDstAmount && saMaxAmount.getCurrency() == saDstAmount.getCurrency()) || (saDstAmount.isNative() && saMaxAmount.isNative()))) { - cLog(lsINFO) << "doPayment: Malformed transaction: bad SendMax."; + cLog(lsINFO) << "Payment: Malformed transaction: bad SendMax."; return temINVALID; } @@ -80,14 +80,14 @@ TER PaymentTransactor::doApply() if (!saDstAmount.isNative()) { - cLog(lsINFO) << "doPayment: Delay transaction: Destination account does not exist."; + cLog(lsINFO) << "Payment: Delay transaction: Destination account does not exist."; // Another transaction could create the account and then this transaction would succeed. return tecNO_DST; } else if (saDstAmount.getNValue() < mEngine->getLedger()->getReserve(0)) // Reserve is not scaled by load. { - cLog(lsINFO) << "doPayment: Delay transaction: Destination account does not exist. Insufficent payment to create account."; + cLog(lsINFO) << "Payment: Delay transaction: Destination account does not exist. Insufficent payment to create account."; // Another transaction could create the account and then this transaction would succeed. return tecNO_DST_INSUF_XRP; @@ -102,7 +102,7 @@ TER PaymentTransactor::doApply() #if ENABLE_REQUIRE_DEST_TAG else if ((sleDst->getFlags() & lsfRequireDestTag) && !mTxn.isFieldPresent(sfDestinationTag)) { - cLog(lsINFO) << "doPayment: Malformed transaction: DestinationTag required."; + cLog(lsINFO) << "Payment: Malformed transaction: DestinationTag required."; return temINVALID; } @@ -156,7 +156,7 @@ TER PaymentTransactor::doApply() { // Vote no. However, transaction might succeed, if applied in a different order. cLog(lsINFO) << ""; - cLog(lsINFO) << boost::str(boost::format("doPayment: Delay transaction: Insufficient funds: %s / %s (%d)") + cLog(lsINFO) << boost::str(boost::format("Payment: Delay transaction: Insufficient funds: %s / %s (%d)") % saSrcXRPBalance.getText() % (saDstAmount + uReserve).getText() % uReserve); terResult = tecUNFUNDED; @@ -179,7 +179,7 @@ TER PaymentTransactor::doApply() if (transResultInfo(terResult, strToken, strHuman)) { - cLog(lsINFO) << boost::str(boost::format("doPayment: %s: %s") % strToken % strHuman); + cLog(lsINFO) << boost::str(boost::format("Payment: %s: %s") % strToken % strHuman); } else { diff --git a/src/cpp/ripple/RegularKeySetTransactor.cpp b/src/cpp/ripple/RegularKeySetTransactor.cpp index b2361c873..0b88bc478 100644 --- a/src/cpp/ripple/RegularKeySetTransactor.cpp +++ b/src/cpp/ripple/RegularKeySetTransactor.cpp @@ -1,14 +1,12 @@ #include "RegularKeySetTransactor.h" #include "Log.h" - SETUP_LOG(); - uint64_t RegularKeySetTransactor::calculateBaseFee() { - if ( !(mTxnAccount->getFlags() & lsfPasswordSpent) && - (mSigningPubKey.getAccountID() == mTxnAccountID)) + if ( !(mTxnAccount->getFlags() & lsfPasswordSpent) + && (mSigningPubKey.getAccountID() == mTxnAccountID)) { // flag is armed and they signed with the right account return 0; } @@ -18,9 +16,18 @@ uint64_t RegularKeySetTransactor::calculateBaseFee() TER RegularKeySetTransactor::doApply() { - std::cerr << "doRegularKeySet>" << std::endl; + std::cerr << "RegularKeySet>" << std::endl; - if(mFeeDue.isZero()) + const uint32 uTxFlags = mTxn.getFlags(); + + if (uTxFlags) + { + cLog(lsINFO) << "RegularKeySet: Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } + + if (mFeeDue.isZero()) { mTxnAccount->setFlag(lsfPasswordSpent); } @@ -28,8 +35,9 @@ TER RegularKeySetTransactor::doApply() uint160 uAuthKeyID=mTxn.getFieldAccount160(sfRegularKey); mTxnAccount->setFieldAccount(sfRegularKey, uAuthKeyID); - - std::cerr << "doRegularKeySet<" << std::endl; + std::cerr << "RegularKeySet<" << std::endl; return tesSUCCESS; } + +// vim:ts=4 diff --git a/src/cpp/ripple/TrustSetTransactor.cpp b/src/cpp/ripple/TrustSetTransactor.cpp index 8262eb063..ca852b82f 100644 --- a/src/cpp/ripple/TrustSetTransactor.cpp +++ b/src/cpp/ripple/TrustSetTransactor.cpp @@ -25,6 +25,15 @@ TER TrustSetTransactor::doApply() if (bQualityOut && QUALITY_ONE == uQualityOut) uQualityOut = 0; + const uint32 uTxFlags = mTxn.getFlags(); + + if (uTxFlags) + { + cLog(lsINFO) << "doTrustSet: Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } + // Check if destination makes sense. if (saLimitAmount.isNegative()) diff --git a/src/cpp/ripple/WalletAddTransactor.cpp b/src/cpp/ripple/WalletAddTransactor.cpp index 2567d8f1b..05a06dc1c 100644 --- a/src/cpp/ripple/WalletAddTransactor.cpp +++ b/src/cpp/ripple/WalletAddTransactor.cpp @@ -1,5 +1,7 @@ #include "WalletAddTransactor.h" +SETUP_LOG(); + TER WalletAddTransactor::doApply() { std::cerr << "WalletAdd>" << std::endl; @@ -7,9 +9,18 @@ TER WalletAddTransactor::doApply() const std::vector vucPubKey = mTxn.getFieldVL(sfPublicKey); const std::vector vucSignature = mTxn.getFieldVL(sfSignature); const uint160 uAuthKeyID = mTxn.getFieldAccount160(sfRegularKey); - const RippleAddress naMasterPubKey = RippleAddress::createAccountPublic(vucPubKey); + const RippleAddress naMasterPubKey = RippleAddress::createAccountPublic(vucPubKey); const uint160 uDstAccountID = naMasterPubKey.getAccountID(); + const uint32 uTxFlags = mTxn.getFlags(); + + if (uTxFlags) + { + cLog(lsINFO) << "WalletAdd: Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } + // FIXME: This should be moved to the transaction's signature check logic and cached if (!naMasterPubKey.accountPublicVerify(Serializer::getSHA512Half(uAuthKeyID.begin(), uAuthKeyID.size()), vucSignature)) { From 039568616c0908fa62563623cc8ae7457f3c7654 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 14:25:32 -0800 Subject: [PATCH 5/7] Cosmetic. --- src/cpp/ripple/SerializedObject.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/SerializedObject.h b/src/cpp/ripple/SerializedObject.h index aeab2e351..545ce91fb 100644 --- a/src/cpp/ripple/SerializedObject.h +++ b/src/cpp/ripple/SerializedObject.h @@ -77,8 +77,8 @@ public: int giveObject(std::auto_ptr t) { mData.push_back(t); return mData.size() - 1; } int giveObject(SerializedType* t) { mData.push_back(t); return mData.size() - 1; } const boost::ptr_vector& peekData() const { return mData; } - boost::ptr_vector& peekData() { return mData; } - SerializedType& front() { return mData.front(); } + boost::ptr_vector& peekData() { return mData; } + SerializedType& front() { return mData.front(); } const SerializedType& front() const { return mData.front(); } SerializedType& back() { return mData.back(); } const SerializedType& back() const { return mData.back(); } @@ -248,7 +248,7 @@ public: bool operator==(const STArray &s) { return value == s.value; } bool operator!=(const STArray &s) { return value != s.value; } - virtual SerializedTypeID getSType() const { return STI_ARRAY; } + virtual SerializedTypeID getSType() const { return STI_ARRAY; } virtual bool isEquivalent(const SerializedType& t) const; virtual bool isDefault() const { return value.empty(); } }; From 21940ee9cf45a177789b408f88cddc4d774458b3 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 14:25:57 -0800 Subject: [PATCH 6/7] Add disabled support for setting RequireDestTag. --- src/cpp/ripple/AccountSetTransactor.cpp | 37 +++++++++++++++++++++++++ src/cpp/ripple/TransactionFormats.h | 8 ++++++ 2 files changed, 45 insertions(+) diff --git a/src/cpp/ripple/AccountSetTransactor.cpp b/src/cpp/ripple/AccountSetTransactor.cpp index 08f2bedaf..7808dcdb6 100644 --- a/src/cpp/ripple/AccountSetTransactor.cpp +++ b/src/cpp/ripple/AccountSetTransactor.cpp @@ -8,13 +8,50 @@ TER AccountSetTransactor::doApply() const uint32 uTxFlags = mTxn.getFlags(); +#if ENABLE_REQUIRE_DEST_TAG + const uint32 uFlagsIn = mTxnAccount->getFieldU32(sfFlags); + uint32 uFlagsOut = uFlagsIn; + + if (uTxFlags & tfAccountSetMask) +#else if (uTxFlags) +#endif { cLog(lsINFO) << "AccountSet: Malformed transaction: Invalid flags set."; return temINVALID_FLAG; } +#if ENABLE_REQUIRE_DEST_TAG + // + // RequireDestTag + // + + if ((tfRequireDestTag|tfOptionalDestTag) == (uTxFlags & (tfRequireDestTag|tfOptionalDestTag))) + { + cLog(lsINFO) << "AccountSet: Malformed transaction: Contradictory flags set."; + + return temINVALID_FLAG; + } + + if (uTxFlags & tfRequireDestTag) + { + cLog(lsINFO) << "AccountSet: Set RequireDestTag."; + + uFlagsOut |= lsfRequireDestTag; + } + + if (uTxFlags & tfOptionalDestTag) + { + cLog(lsINFO) << "AccountSet: Clear RequireDestTag."; + + uFlagsOut &= ~lsfRequireDestTag; + } + + if (uFlagsIn != uFlagsOut) + mTxnAccount->setFieldU32(sfFlags, uFlagsOut); +#endif + // // EmailHash // diff --git a/src/cpp/ripple/TransactionFormats.h b/src/cpp/ripple/TransactionFormats.h index 6af1a74d9..333ede8a9 100644 --- a/src/cpp/ripple/TransactionFormats.h +++ b/src/cpp/ripple/TransactionFormats.h @@ -2,6 +2,7 @@ #define __TRANSACTIONFORMATS__ #include "SerializedObject.h" +#include "LedgerFormats.h" enum TransactionType { @@ -58,6 +59,13 @@ const int TransactionMaxLen = 1048576; // Transaction flags. // +#if ENABLE_REQUIRE_DEST_TAG +// AccountSet flags: +const uint32 tfRequireDestTag = 0x00010000; +const uint32 tfOptionalDestTag = 0x00020000; +const uint32 tfAccountSetMask = ~(tfRequireDestTag|tfOptionalDestTag); +#endif + // OfferCreate flags: const uint32 tfPassive = 0x00010000; const uint32 tfOfferCreateMask = ~(tfPassive); From a043b7084f6741b26c7327efd3bb4bbf25049ef2 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 3 Jan 2013 14:28:45 -0800 Subject: [PATCH 7/7] Cosmetic. --- src/cpp/ripple/TransactionFormats.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cpp/ripple/TransactionFormats.h b/src/cpp/ripple/TransactionFormats.h index 333ede8a9..ad804ccb9 100644 --- a/src/cpp/ripple/TransactionFormats.h +++ b/src/cpp/ripple/TransactionFormats.h @@ -20,10 +20,10 @@ enum TransactionType ttCONTRACT = 9, ttCONTRACT_REMOVE = 10, // can we use the same msg as offer cancel - ttTRUST_SET = 20, + ttTRUST_SET = 20, - ttFEATURE = 100, - ttFEE = 101, + ttFEATURE = 100, + ttFEE = 101, }; class TransactionFormat