From 7664065cf61066dc59a7cdac5a3aaf3ab2baf730 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 18:48:06 -0700 Subject: [PATCH 01/24] WS: Make ledger_entry more strict. --- src/WSDoor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index f3726ee53..71deb7d0a 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -637,7 +637,8 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq { NewcoinAddress naAccount; - if (!naAccount.setAccountID(jvRequest["account_root"].asString())) + if (!naAccount.setAccountID(jvRequest["account_root"].asString()) + || !naAccount.getAccountID()) { jvResult["error"] = "malformedAddress"; } From 4be22da6d3d39e5c5a001d9b9ff04d4544ee4e49 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 18:49:55 -0700 Subject: [PATCH 02/24] Return terNO_LINE instead of tefEXCEPTION where approriate. --- src/RippleCalc.cpp | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/RippleCalc.cpp b/src/RippleCalc.cpp index c660e4dd8..d87758a4a 100644 --- a/src/RippleCalc.cpp +++ b/src/RippleCalc.cpp @@ -1400,30 +1400,30 @@ TER PathState::pushImply( // Currency is different, need to convert via an offer. terResult = pushNode( - STPathElement::typeCurrency // Offer. - | STPathElement::typeIssuer, - ACCOUNT_ONE, // Placeholder for offers. - uCurrencyID, // The offer's output is what is now wanted. - uIssuerID); + STPathElement::typeCurrency // Offer. + | STPathElement::typeIssuer, + ACCOUNT_ONE, // Placeholder for offers. + uCurrencyID, // The offer's output is what is now wanted. + uIssuerID); } // For ripple, non-stamps, ensure the issuer is on at least one side of the transaction. if (tesSUCCESS == terResult - && !!uCurrencyID // Not stamps. - && (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs. - && uAccountID != uIssuerID)) // Current is not receiving own IOUs. + && !!uCurrencyID // Not stamps. + && (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs. + && uAccountID != uIssuerID)) // Current is not receiving own IOUs. { // Need to ripple through uIssuerID's account. terResult = pushNode( - STPathElement::typeAccount, - uIssuerID, // Intermediate account is the needed issuer. - uCurrencyID, - uIssuerID); + STPathElement::typeAccount, + uIssuerID, // Intermediate account is the needed issuer. + uCurrencyID, + uIssuerID); } - cLog(lsINFO) << "pushImply< " << terResult; + cLog(lsDEBUG) << boost::str(boost::format("pushImply< : %s") % transToken(terResult)); return terResult; } @@ -1544,11 +1544,12 @@ TER PathState::pushNode( vpnNodes.push_back(pnCur); } } - cLog(lsINFO) << "pushNode< " << terResult; + cLog(lsDEBUG) << boost::str(boost::format("pushNode< : %s") % transToken(terResult)); return terResult; } +// terStatus = tesSUCCESS, temBAD_PATH, terNO_LINE, or temBAD_PATH_LOOP PathState::PathState( const int iIndex, const LedgerEntrySet& lesSource, @@ -1886,7 +1887,7 @@ TER RippleCalc::rippleCalc( if (pspDirect) { // Return if malformed. - if (pspDirect->terStatus >= temMALFORMED && pspDirect->terStatus < tefFAILURE) + if (isTemMalformed(pspDirect->terStatus)) return pspDirect->terStatus; if (tesSUCCESS == pspDirect->terStatus) @@ -1917,14 +1918,11 @@ TER RippleCalc::rippleCalc( if (pspExpanded) { // Return if malformed. - if (pspExpanded->terStatus >= temMALFORMED && pspExpanded->terStatus < tefFAILURE) + if (isTemMalformed(pspExpanded->terStatus)) return pspExpanded->terStatus; if (tesSUCCESS == pspExpanded->terStatus) - { - // Had a success. - terResult = tesSUCCESS; - } + terResult = tesSUCCESS; // Had a success. vpsPaths.push_back(pspExpanded); } @@ -1932,7 +1930,8 @@ TER RippleCalc::rippleCalc( if (vpsPaths.empty()) { - return tefEXCEPTION; + // No paths. Missing credit lines. + return terNO_LINE; } else if (tesSUCCESS != terResult) { From 43f7b2654df93b9df968de25c1be61d0aadc2b39 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 18:50:35 -0700 Subject: [PATCH 03/24] JS: Make Amount more flexible. --- js/amount.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/js/amount.js b/js/amount.js index c3dfa3905..6e4d99f86 100644 --- a/js/amount.js +++ b/js/amount.js @@ -6,6 +6,12 @@ var jsbn = require('./jsbn.js'); var BigInteger = jsbn.BigInteger; +var accounts = {}; + +var setAccounts = function (accounts_new) { + accounts = accounts_new; +}; + var UInt160 = function () { // Internal form: // 0, 1, 'iXXXXX', 20 byte string, or NaN. @@ -13,8 +19,11 @@ var UInt160 = function () { this.value = NaN; }; +// Return a new UInt160 from j. UInt160.from_json = function (j) { - return (new UInt160()).parse_json(j in accounts ? accounts[j].account : j); + return 'string' === typeof j + ? (new UInt160()).parse_json(j in accounts ? accounts[j].account : j) + : j.clone(); }; UInt160.prototype.clone = function() { @@ -140,12 +149,6 @@ Currency.prototype.to_human = function() { return this.value ? this.value : "XNS"; }; -var accounts = {}; - -var setAccounts = function (accounts_new) { - accounts = accounts_new; -}; - var Amount = function () { // Json format: // integer : XNS @@ -371,6 +374,9 @@ Amount.prototype.parse_value = function(j) { this.canonicalize(); } + else if (j.constructor == BigInteger) { + this.value = j.clone(); + } else { this.value = NaN; } @@ -396,10 +402,10 @@ Amount.prototype.parse_json = function(j) { } } else if ('object' === typeof j && j.currency) { - // Never XNS. + // Parse the passed value to sanitize and copy it. this.parse_value(j.value); - this.currency.parse_json(j.currency); + this.currency.parse_json(j.currency); // Never XNS. this.issuer.parse_json(j.issuer); } else { From 52d45d0b0482bfa38c9a05944f3d5a257bf18173 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 18:51:07 -0700 Subject: [PATCH 04/24] JS: Make remote.js more flexible. --- js/remote.js | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/js/remote.js b/js/remote.js index 5fed5c549..8c85b79f6 100644 --- a/js/remote.js +++ b/js/remote.js @@ -4,9 +4,6 @@ // http://www.w3.org/TR/websockets/#the-websocket-interface // // YYY Will later provide a network access which use multiple instances of this. -// YYY A better model might be to allow requesting a target state: keep connected or not. -// XXX Make subscribe target state. -// XXX Auto subscribe on connect. // // Node @@ -93,7 +90,7 @@ Request.prototype.ledger_index = function (ledger_index) { }; Request.prototype.account_root = function (account) { - this.message.account_root = account; + this.message.account_root = UInt160.from_json(account).to_json(); return this; }; @@ -119,7 +116,10 @@ Request.prototype.transaction = function (t) { Request.prototype.ripple_state = function (account, issuer, currency) { this.message.ripple_state = { - 'accounts' : [ account, issuer ], + 'accounts' : [ + UInt160.from_json(account).to_json(), + UInt160.from_json(issuer).to_json() + ], 'currency' : currency }; @@ -700,26 +700,25 @@ Remote.prototype.dirty_account_root = function (account) { // --> currency: String // --> current: bool : true = current ledger Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { - var src = this.remote.config.accounts[account] ? this.remote.config.accounts[account].account : account; - var dst = this.remote.config.accounts[issuer] ? this.remote.config.accounts[issuer].account : issuer; + var account_u = UInt160.from_json(account); return (this.request_ledger_entry('ripple_state')) // YYY Could be cached per ledger. - .ripple_state(src, dst, currency) + .ripple_state(account, issuer, currency) .ledger_choose(current) .on('success', function (message) { var node = message.node; - var flip = UInt160.from_json(src) == node.HighLimit.issuer; + var flip = account_u == node.HighLimit.issuer; var issuerLimit = flip ? node.LowLimit : node.HighLimit; var accountLimit = flip ? node.HighLimit : node.LowLimit; - var issuerBalance = (flip ? node.Balance.clone().negate() : node.Balance.clone()).parse_issuer(dst); - var accountBalance = issuerBalance.clone().parse_issuer(dst); + var issuerBalance = (flip ? node.Balance.clone().negate() : node.Balance.clone()).parse_issuer(issuer); + var accountBalance = issuerBalance.clone().parse_issuer(issuer); // If the caller also waits for 'success', they might run before this. request.emit('ripple_state', { - 'issuer_balance' : issuerBalance, // Balance with dst as issuer. - 'account_balance' : accountBalance, // Balance with src as issuer. - 'issuer_limit' : issuerLimit.clone().parse_issuer(src), // Limit set by issuer with src as issuer. - 'account_limit' : accountLimit.clone().parse_issuer(dst) // Limit set by account with dst as issuer. + 'issuer_balance' : issuerBalance, // Balance with dst as issuer. + 'account_balance' : accountBalance, // Balance with account as issuer. + 'issuer_limit' : issuerLimit.clone().parse_issuer(account), // Limit set by issuer with src as issuer. + 'account_limit' : accountLimit.clone().parse_issuer(issuer) // Limit set by account with dst as issuer. }); }); } @@ -1000,7 +999,7 @@ Transaction.prototype.account_secret = function (account) { Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { this.secret = this.account_secret(src); this.transaction.TransactionType = 'OfferCreate'; - this.transaction.Account = this.account_default(src); + this.transaction.Account = UInt160.from_json(src).to_json(); this.transaction.Fee = fees.offer.to_json(); this.transaction.TakerPays = taker_pays.to_json(); this.transaction.TakerGets = taker_gets.to_json(); @@ -1015,12 +1014,15 @@ Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expi // // When a transaction is submitted: // - If the connection is reliable and the server is not merely forwarding and is not malicious, +// --> src : UInt160 or String +// --> dst : UInt160 or String +// --> deliver_amount : Amount or String. Transaction.prototype.payment = function (src, dst, deliver_amount) { this.secret = this.account_secret(src); this.transaction.TransactionType = 'Payment'; - this.transaction.Account = this.account_default(src); - this.transaction.Amount = deliver_amount.to_json(); - this.transaction.Destination = this.account_default(dst); + this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.Amount = Amount.from_json(deliver_amount).to_json(); + this.transaction.Destination = UInt160.from_json(dst).to_json(); return this; } @@ -1028,7 +1030,7 @@ Transaction.prototype.payment = function (src, dst, deliver_amount) { Transaction.prototype.ripple_line_set = function (src, limit, quality_in, quality_out) { this.secret = this.account_secret(src); this.transaction.TransactionType = 'CreditSet'; - this.transaction.Account = this.account_default(src); + this.transaction.Account = UInt160.from_json(src).to_json(); // Allow limit of 0 through. if (undefined !== limit) From 02a361c41a30e6576f89cce606dc7390356b5032 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 19:59:22 -0700 Subject: [PATCH 05/24] WS: Fix ledger_entry for ripple_state. --- src/WSDoor.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/WSDoor.cpp b/src/WSDoor.cpp index 71deb7d0a..da00ea400 100644 --- a/src/WSDoor.cpp +++ b/src/WSDoor.cpp @@ -750,18 +750,27 @@ void WSConnection::doLedgerEntry(Json::Value& jvResult, const Json::Value& jvReq NewcoinAddress naA; NewcoinAddress naB; uint160 uCurrency; + Json::Value jvRippleState = jvRequest["ripple_state"]; + + if (!jvRippleState.isMember("accounts") + || !jvRippleState.isMember("currency") + || !jvRippleState["accounts"].isArray() + || 2 != jvRippleState["accounts"].size()) { + + cLog(lsINFO) + << boost::str(boost::format("ledger_entry: ripple_state: accounts: %d currency: %d array=%d, size=%d") + % jvRippleState.isMember("accounts") + % jvRippleState.isMember("currency") + % jvRippleState["accounts"].isArray() + % jvRippleState["accounts"].size()); - if (!jvRequest.isMember("accounts") - || !jvRequest.isMember("currency") - || !jvRequest["accounts"].isArray() - || 2 != jvRequest["accounts"].size()) { jvResult["error"] = "malformedRequest"; } - else if (!naA.setAccountID(jvRequest["accounts"][0u].asString()) - || !naB.setAccountID(jvRequest["accounts"][1u].asString())) { + else if (!naA.setAccountID(jvRippleState["accounts"][0u].asString()) + || !naB.setAccountID(jvRippleState["accounts"][1u].asString())) { jvResult["error"] = "malformedAddress"; } - else if (!STAmount::currencyFromString(uCurrency, jvRequest["currency"].asString())) { + else if (!STAmount::currencyFromString(uCurrency, jvRippleState["currency"].asString())) { jvResult["error"] = "malformedCurrency"; } else From 81cbbc26425229e163333250367e0c533ae323a0 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 20:00:02 -0700 Subject: [PATCH 06/24] JS: Lots of fix for Amount parsing. --- js/amount.js | 68 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/js/amount.js b/js/amount.js index 6e4d99f86..faac4e3de 100644 --- a/js/amount.js +++ b/js/amount.js @@ -37,7 +37,7 @@ UInt160.prototype.copyTo = function(d) { return d; }; -// value === NaN on error. +// value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate @@ -126,7 +126,7 @@ Currency.prototype.copyTo = function(d) { return d; }; -// this.value === NaN on error. +// this.value = NaN on error. Currency.prototype.parse_json = function(j) { if ("" === j || "0" === j || "XNS" === j) { this.value = 0; @@ -194,12 +194,12 @@ Amount.prototype.copyTo = function(d) { // YYY Might also provide is_valid_json. Amount.prototype.is_valid = function() { - return NaN !== this.value; + return !isNaN(this.value); }; // Convert only value to JSON wire format. Amount.prototype.to_text = function(allow_nan) { - if (NaN === this.value) { + if (isNaN(this.value)) { // Never should happen. return allow_nan ? NaN : "0"; } @@ -240,7 +240,7 @@ Amount.prototype.to_text = function(allow_nan) { }; Amount.prototype.canonicalize = function() { - if (NaN === this.value || !this.currency) { + if (isNaN(this.value) || !this.currency) { // nothing } else if (this.value.equals(BigInteger.ZERO)) { @@ -285,7 +285,7 @@ Amount.prototype.to_json = function() { }; Amount.prototype.to_text_full = function() { - return this.value === NaN + return isNaN(this.value) ? NaN : this.is_native ? this.to_text() + "/XNS" @@ -335,44 +335,55 @@ Amount.prototype.parse_native = function(j) { // Parse a non-native value. Amount.prototype.parse_value = function(j) { + this.is_native = false; + if ('number' === typeof j) { + this.is_negative = j < 0; + if (this.is_negative) j = -j; this.value = new BigInteger(j); this.offset = 0; - this.is_native = false; - this.is_negative = j < 0; this.canonicalize(); } else if ('string' === typeof j) { - var e = j.match(/^(-?\d+)e(\d+)/); - var d = j.match(/^(-?\d+)\.(\d+)/); + var i = j.match(/^(-?)(\d+)$/); + var d = !i && j.match(/^(-?)(\d+)\.(\d*)$/); + var e = !e && j.match(/^(-?)(\d+)e(\d+)$/); if (e) { // e notation - this.value = new BigInteger(e[1]); - this.offset = parseInt(e[2]); + this.value = new BigInteger(e[2]); + this.offset = parseInt(e[3]); + this.is_negative = !!e[1]; + + this.canonicalize(); } else if (d) { // float notation - var integer = new BigInteger(d[1]); - var fraction = new BigInteger(d[2]); - this.value = integer.multiply(exports.consts.bi_10.clone().pow(d[2].length)).add(fraction); - this.offset = -d[2].length; + var integer = new BigInteger(d[2]); + var fraction = new BigInteger(d[3]); + var precision = d[3].length; + + this.value = integer.multiply(exports.consts.bi_10.clone().pow(precision)).add(fraction); + this.offset = -precision; + this.is_negative = !!d[1]; + + this.canonicalize(); } - else - { + else if (i) { // integer notation - this.value = new BigInteger(j); - this.offset = 0; + this.value = new BigInteger(i[2]); + this.offset = 0; + this.is_negative = !!i[1]; + + this.canonicalize(); + } + else { + this.value = NaN; } - - this.is_native = false; - this.is_negative = undefined; - - this.canonicalize(); } else if (j.constructor == BigInteger) { this.value = j.clone(); @@ -401,7 +412,10 @@ Amount.prototype.parse_json = function(j) { this.issuer = new UInt160(); } } - else if ('object' === typeof j && j.currency) { + else if ('object' === typeof j && j.constructor == Amount) { + j.copyTo(this); + } + else if ('object' === typeof j && 'value' in j) { // Parse the passed value to sanitize and copy it. this.parse_value(j.value); @@ -409,7 +423,7 @@ Amount.prototype.parse_json = function(j) { this.issuer.parse_json(j.issuer); } else { - this.value = NaN; + this.value = NaN; } return this; From fd1122c6730c81a63ad9558f242e2a3a79ad9583 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 18 Oct 2012 20:00:36 -0700 Subject: [PATCH 07/24] JS: Fix request_ripple_balance. --- js/remote.js | 15 +++++++++------ test/remote-test.js | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/js/remote.js b/js/remote.js index 8c85b79f6..3f1f85517 100644 --- a/js/remote.js +++ b/js/remote.js @@ -701,19 +701,22 @@ Remote.prototype.dirty_account_root = function (account) { // --> current: bool : true = current ledger Remote.prototype.request_ripple_balance = function (account, issuer, currency, current) { var account_u = UInt160.from_json(account); + var request = this.request_ledger_entry('ripple_state'); // YYY Could be cached per ledger. - return (this.request_ledger_entry('ripple_state')) // YYY Could be cached per ledger. + return request .ripple_state(account, issuer, currency) .ledger_choose(current) .on('success', function (message) { var node = message.node; - var flip = account_u == node.HighLimit.issuer; - var issuerLimit = flip ? node.LowLimit : node.HighLimit; - var accountLimit = flip ? node.HighLimit : node.LowLimit; - var issuerBalance = (flip ? node.Balance.clone().negate() : node.Balance.clone()).parse_issuer(issuer); + var lowLimit = Amount.from_json(node.LowLimit); + var highLimit = Amount.from_json(node.HighLimit); + var balance = Amount.from_json(node.Balance); + var flip = account_u == highLimit.issuer; + var issuerLimit = flip ? lowLimit : highLimit; + var accountLimit = flip ? highLimit : lowLimit; + var issuerBalance = (flip ? balance.negate() : balance).parse_issuer(issuer); var accountBalance = issuerBalance.clone().parse_issuer(issuer); - // If the caller also waits for 'success', they might run before this. request.emit('ripple_state', { 'issuer_balance' : issuerBalance, // Balance with dst as issuer. 'account_balance' : accountBalance, // Balance with account as issuer. diff --git a/test/remote-test.js b/test/remote-test.js index 96740236a..733b29dc2 100644 --- a/test/remote-test.js +++ b/test/remote-test.js @@ -103,6 +103,7 @@ buster.testCase("Remote functions", { .request(); }, + // XXX This should be detected locally. 'account_root remote malformedAddress' : function (done) { alpha.request_ledger_closed().on('success', function (r) { From 19efa3bf9b34d05466bf2d1053a8dd8ecd32ee51 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 11:51:44 -0700 Subject: [PATCH 08/24] Make sure we properly handle receiving our own proposals and validations. --- src/LedgerConsensus.cpp | 5 ++++- src/LedgerConsensus.h | 4 +++- src/NetworkOPs.cpp | 6 ++++++ src/Peer.cpp | 10 ++++------ src/SerializedValidation.cpp | 5 +++-- src/SerializedValidation.h | 3 ++- 6 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index f241934ed..09b91aee7 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -239,6 +239,7 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou cLog(lsINFO) << "Entering consensus process, validating"; mValidating = true; mProposing = theApp->getOPs().getOperatingMode() == NetworkOPs::omFULL; + mValPublic = NewcoinAddress::createNodePublic(mValSeed); } else { @@ -1139,9 +1140,11 @@ void LedgerConsensus::accept(SHAMap::ref set) statusChange(ripple::neACCEPTED_LEDGER, *newLCL); if (mValidating) { + uint256 signingHash; SerializedValidation::pointer v = boost::make_shared - (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing); + (newLCLHash, theApp->getOPs().getValidationTimeNC(), mValSeed, mProposing, boost::ref(signingHash)); v->setTrusted(); + theApp->isNew(signingHash); // suppress it if we receive it theApp->getValidations().addValidation(v); std::vector validation = v->getSigned(); ripple::TMValidation val; diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index a88aee079..4ab27866b 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -87,7 +87,7 @@ protected: Ledger::pointer mPreviousLedger; LedgerAcquire::pointer mAcquiringLedger; LedgerProposal::pointer mOurPosition; - NewcoinAddress mValSeed; + NewcoinAddress mValSeed, mValPublic; bool mProposing, mValidating, mHaveCorrectLCL; int mCurrentMSeconds, mClosePercent, mCloseResolution; @@ -183,6 +183,8 @@ public: bool peerGaveNodes(Peer::ref peer, const uint256& setHash, const std::list& nodeIDs, const std::list< std::vector >& nodeData); + bool isOurPubKey(const NewcoinAddress &k) { return k == mValPublic; } + // test/debug void simulate(); }; diff --git a/src/NetworkOPs.cpp b/src/NetworkOPs.cpp index c5059ee6a..bdf97d4d9 100644 --- a/src/NetworkOPs.cpp +++ b/src/NetworkOPs.cpp @@ -712,6 +712,12 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, cons return mMode != omFULL; } + if (mConsensus->isOurPubKey(naPeerPublic)) + { + cLog(lsTRACE) << "Received our own validation"; + return false; + } + // Is this node on our UNL? if (!theApp->getUNL().nodeInUNL(naPeerPublic)) { diff --git a/src/Peer.cpp b/src/Peer.cpp index 99558dd84..506aad80a 100644 --- a/src/Peer.cpp +++ b/src/Peer.cpp @@ -758,11 +758,9 @@ void Peer::recvValidation(ripple::TMValidation& packet) return; } -// The four #ifndef/#endif's are commented out temporarily to avoid -// an update hassle. They can be removed once all nodes are running this code -//#ifndef TRUST_NETWORK +#ifndef TRUST_NETWORK try -//#endif +#endif { Serializer s(packet.validation()); SerializerIterator sit(s); @@ -788,13 +786,13 @@ void Peer::recvValidation(ripple::TMValidation& packet) theApp->getConnectionPool().relayMessage(this, message); } } -//#ifndef TRUST_NETWORK +#ifndef TRUST_NETWORK catch (...) { cLog(lsWARNING) << "Exception processing validation"; punishPeer(PP_UNKNOWN_REQUEST); } -//#endif +#endif } void Peer::recvGetValidation(ripple::TMGetValidations& packet) diff --git a/src/SerializedValidation.cpp b/src/SerializedValidation.cpp index f95866a51..85f4048d0 100644 --- a/src/SerializedValidation.cpp +++ b/src/SerializedValidation.cpp @@ -37,7 +37,7 @@ SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSi } SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 signTime, - const NewcoinAddress& naSeed, bool isFull) + const NewcoinAddress& naSeed, bool isFull, uint256& signingHash) : STObject(sValidationFormat, sfValidation), mTrusted(false) { setFieldH256(sfLedgerHash, ledgerHash); @@ -52,8 +52,9 @@ SerializedValidation::SerializedValidation(const uint256& ledgerHash, uint32 sig if (!isFull) setFlag(sFullFlag); + signingHash = getSigningHash(); std::vector signature; - NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), signature); + NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(signingHash, signature); setFieldVL(sfSignature, signature); // XXX Check if this can fail. // if (!NewcoinAddress::createNodePrivate(naSeed).signNodePrivate(getSigningHash(), mSignature.peekValue())) diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index 9ce8fb6a2..139850edf 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -21,7 +21,8 @@ public: // These throw if the object is not valid SerializedValidation(SerializerIterator& sit, bool checkSignature = true); - SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull); + SerializedValidation(const uint256& ledgerHash, uint32 signTime, const NewcoinAddress& naSeed, bool isFull, + uint256& signingHash); uint256 getLedgerHash() const; uint32 getSignTime() const; From ec0376533f6bd3b79e75a81b1f21db648703edf7 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 12:28:12 -0700 Subject: [PATCH 09/24] Change "logseverity" command to "log_level". --- src/RPCServer.cpp | 7 ++----- src/RPCServer.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 046a8946d..35c74bbdc 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -2647,7 +2647,7 @@ Json::Value RPCServer::doLogin(const Json::Value& params) } } -Json::Value RPCServer::doLogSeverity(const Json::Value& params) +Json::Value RPCServer::doLogLevel(const Json::Value& params) { if (params.size() == 0) { // get log severities @@ -2666,10 +2666,7 @@ Json::Value RPCServer::doLogSeverity(const Json::Value& params) { // set base log severity LogSeverity sv = Log::stringToSeverity(params[0u].asString()); if (sv == lsINVALID) - { - Log(lsWARNING) << "Unable to parse severity: " << params[0u].asString(); return RPCError(rpcINVALID_PARAMS); - } Log::setMinSeverity(sv); return RPCError(rpcSUCCESS); } @@ -2721,8 +2718,8 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { "data_fetch", &RPCServer::doDataFetch, 1, 1, true }, { "data_store", &RPCServer::doDataStore, 2, 2, true }, { "ledger", &RPCServer::doLedger, 0, 2, false, optNetwork }, + { "log_level", &RPCServer::doLogLevel, 0, 2, true }, { "logrotate", &RPCServer::doLogRotate, 0, 0, true }, - { "logseverity", &RPCServer::doLogSeverity, 0, 2, true }, { "nickname_info", &RPCServer::doNicknameInfo, 1, 1, false, optCurrent }, { "nickname_set", &RPCServer::doNicknameSet, 2, 3, false, optCurrent }, { "offer_create", &RPCServer::doOfferCreate, 9, 10, false, optCurrent }, diff --git a/src/RPCServer.h b/src/RPCServer.h index 7d7695599..af8c6d754 100644 --- a/src/RPCServer.h +++ b/src/RPCServer.h @@ -155,7 +155,7 @@ private: Json::Value doServerInfo(const Json::Value& params); Json::Value doSessionClose(const Json::Value& params); Json::Value doSessionOpen(const Json::Value& params); - Json::Value doLogSeverity(const Json::Value& params); + Json::Value doLogLevel(const Json::Value& params); Json::Value doStop(const Json::Value& params); Json::Value doTransitSet(const Json::Value& params); Json::Value doTx(const Json::Value& params); From bd2b4daae41b947c70c0c66a3e108de8175c4858 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 12:33:11 -0700 Subject: [PATCH 10/24] If a debug logfile is set, don't suppress INFO/DEBUG logging. --- src/Application.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Application.cpp b/src/Application.cpp index ec56e6b91..0d777f518 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -70,7 +70,10 @@ void Application::run() { assert(mTxnDB == NULL); if (!theConfig.DEBUG_LOGFILE.empty()) + { Log::setLogFile(theConfig.DEBUG_LOGFILE); + LogPartition::setSeverity(lsDEBUG); + } boost::thread auxThread(boost::bind(&boost::asio::io_service::run, &mAuxService)); auxThread.detach(); From 887a87081f6cb3c0cd7181b818389c1db18b7501 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 12:51:06 -0700 Subject: [PATCH 11/24] Move to new logging. --- src/ConnectionPool.cpp | 57 ++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/ConnectionPool.cpp b/src/ConnectionPool.cpp index 6285d4555..e592d000e 100644 --- a/src/ConnectionPool.cpp +++ b/src/ConnectionPool.cpp @@ -14,6 +14,7 @@ #include "utils.h" #include "Log.h" +SETUP_LOG(); // How often to enforce policies. #define POLICY_INTERVAL_SECONDS 5 @@ -164,7 +165,7 @@ void ConnectionPool::policyLowWater() if (mConnectedMap.size() > theConfig.PEER_CONNECT_LOW_WATER) { // Above low water mark, don't need more connections. - Log(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER; + cLog(lsTRACE) << "Pool: Low water: sufficient connections: " << mConnectedMap.size() << "/" << theConfig.PEER_CONNECT_LOW_WATER; nothing(); } @@ -178,7 +179,7 @@ void ConnectionPool::policyLowWater() else if (!peerAvailable(strIp, iPort)) { // No more connections available to start. - Log(lsTRACE) << "Pool: Low water: no peers available."; + cLog(lsTRACE) << "Pool: Low water: no peers available."; // XXX Might ask peers for more ips. nothing(); @@ -186,10 +187,12 @@ void ConnectionPool::policyLowWater() else { // Try to start connection. - Log(lsTRACE) << "Pool: Low water: start connection."; + cLog(lsTRACE) << "Pool: Low water: start connection."; if (!peerConnect(strIp, iPort)) - Log(lsINFO) << "Pool: Low water: already connected."; + { + cLog(lsINFO) << "Pool: Low water: already connected."; + } // Check if we need more. policyLowWater(); @@ -303,11 +306,11 @@ Peer::pointer ConnectionPool::peerConnect(const std::string& strIp, int iPort) if (ppResult) { - //Log(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Connecting: " << ADDRESS_SHARED(ppResult) << ": " << strIp << " " << iPort; } else { - //Log(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Already connected: " << strIp << " " << iPort; } return ppResult; @@ -355,7 +358,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, if (naPeer == theApp->getWallet().getNodePublic()) { - Log(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; + cLog(lsINFO) << "Pool: Connected: self: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; } else { @@ -365,7 +368,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, if (itCm == mConnectedMap.end()) { // New connection. - //Log(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: new: " << ADDRESS_SHARED(peer) << ": " << naPeer.humanNodePublic() << " " << strIP << " " << iPort; mConnectedMap[naPeer] = peer; bNew = true; @@ -378,7 +381,7 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, if (itCm->second->getIP().empty()) { // Old peer did not know it's IP. - //Log(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: outbound: " << ADDRESS_SHARED(peer) << " discovered: " << ADDRESS_SHARED(itCm->second) << ": " << strIP << " " << iPort; itCm->second->setIpPort(strIP, iPort); @@ -388,14 +391,14 @@ bool ConnectionPool::peerConnected(Peer::ref peer, const NewcoinAddress& naPeer, else { // Old peer knew its IP. Do nothing. - //Log(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: outbound: rediscovered: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; nothing(); } } else { - //Log(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; + //cLog(lsINFO) << "Pool: Connected: redundant: inbound: " << ADDRESS_SHARED(peer) << " " << strIP << " " << iPort; nothing(); } @@ -418,12 +421,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe if (itCm == mConnectedMap.end()) { // Did not find it. Not already connecting or connected. - Log(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent."; + cLog(lsWARNING) << "Pool: disconnected: Internal Error: mConnectedMap was inconsistent."; // XXX Maybe bad error, considering we have racing connections, may not so bad. } else if (itCm->second != peer) { - Log(lsWARNING) << "Pool: disconected: non canonical entry"; + cLog(lsWARNING) << "Pool: disconected: non canonical entry"; nothing(); } @@ -432,12 +435,12 @@ void ConnectionPool::peerDisconnected(Peer::ref peer, const NewcoinAddress& naPe // Found it. Delete it. mConnectedMap.erase(itCm); - //Log(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort(); + //cLog(lsINFO) << "Pool: disconnected: " << naPeer.humanNodePublic() << " " << peer->getIP() << " " << peer->getPort(); } } else { - //Log(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort(); + //cLog(lsINFO) << "Pool: disconnected: anonymous: " << peer->getIP() << " " << peer->getPort(); } } @@ -463,7 +466,7 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort) boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time(); boost::posix_time::ptime tpNext = tpNow + boost::posix_time::seconds(iInterval); - //Log(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: schedule create: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); db->executeSQL(str(boost::format("UPDATE PeerIps SET ScanNext=%d,ScanInterval=%d WHERE IpPort=%s;") @@ -479,13 +482,13 @@ bool ConnectionPool::peerScanSet(const std::string& strIp, int iPort) // boost::posix_time::ptime tpNow = boost::posix_time::second_clock::universal_time(); // boost::posix_time::ptime tpNext = ptFromSeconds(db->getInt("ScanNext")); - //Log(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: schedule exists: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); } } else { - //Log(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort; + //cLog(lsWARNING) << "Pool: Scan: peer wasn't in PeerIps: " << strIp << " " << iPort; } return bScanDirty; @@ -500,7 +503,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP // If the connection was our scan, we are no longer scanning. if (mScanning && mScanning == peer) { - //Log(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Scan: scan fail: " << strIp << " " << iPort; mScanning = Peer::pointer(); // No longer scanning. bScanRefresh = true; // Look for more to scan. @@ -517,13 +520,13 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP if (itIp == mIpMap.end()) { // Did not find it. Not already connecting or connected. - Log(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + cLog(lsWARNING) << "Pool: Closed: UNEXPECTED: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; // XXX Internal error. } else if (mIpMap[ipPeer] == peer) { // We were the identified connection. - //Log(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Closed: identified: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; // Delete our entry. mIpMap.erase(itIp); @@ -533,7 +536,7 @@ void ConnectionPool::peerClosed(Peer::ref peer, const std::string& strIp, int iP else { // Found it. But, we were redundant. - //Log(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; + //cLog(lsINFO) << "Pool: Closed: redundant: " << ADDRESS_SHARED(peer) << ": " << strIp << " " << iPort; } } @@ -557,7 +560,7 @@ void ConnectionPool::peerVerified(Peer::ref peer) std::string strIpPort = str(boost::format("%s %d") % strIp % iPort); - //Log(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort); + //cLog(lsINFO) << str(boost::format("Pool: Scan: connected: %s %s %s (scanned)") % ADDRESS_SHARED(peer) % strIp % iPort); if (peer->getNodePublic() == theApp->getWallet().getNodePublic()) { @@ -604,7 +607,7 @@ void ConnectionPool::scanRefresh() if (mScanning) { // Currently scanning, will scan again after completion. - Log(lsTRACE) << "Pool: Scan: already scanning"; + cLog(lsTRACE) << "Pool: Scan: already scanning"; nothing(); } @@ -641,7 +644,7 @@ void ConnectionPool::scanRefresh() if (tpNow.is_not_a_date_time()) { - //Log(lsINFO) << "Pool: Scan: stop."; + //cLog(lsINFO) << "Pool: Scan: stop."; (void) mScanTimer.cancel(); } @@ -656,7 +659,7 @@ void ConnectionPool::scanRefresh() tpNext = tpNow + boost::posix_time::seconds(iInterval); - //Log(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: Now: %s %s (next %s, delay=%d)") // % mScanIp % mScanPort % tpNext % (tpNext-tpNow).total_seconds()); iInterval *= 2; @@ -681,7 +684,7 @@ void ConnectionPool::scanRefresh() } else { - //Log(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)") + //cLog(lsINFO) << str(boost::format("Pool: Scan: Next: %s (next %s, delay=%d)") // % strIpPort % tpNext % (tpNext-tpNow).total_seconds()); mScanTimer.expires_at(tpNext); From d3e91345c9d85f4c5090d188620856af70ee550b Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 14:24:32 -0700 Subject: [PATCH 12/24] Begin cleaning up the handling of ledger changes during the consensus window. --- src/LedgerConsensus.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 09b91aee7..fe61a0a83 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -247,11 +247,16 @@ LedgerConsensus::LedgerConsensus(const uint256& prevLCLHash, Ledger::ref previou mProposing = mValidating = false; } - handleLCL(prevLCLHash); + mHaveCorrectLCL = (mPreviousLedger->getHash() == mPrevLedgerHash); if (!mHaveCorrectLCL) { - cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash(); - cLog(lsINFO) << "Correct LCL is: " << prevLCLHash; + handleLCL(mPrevLedgerHash); + if (!mHaveCorrectLCL) + { + mProposing = mValidating = false; + cLog(lsINFO) << "Entering consensus with: " << previousLedger->getHash(); + cLog(lsINFO) << "Correct LCL is: " << prevLCLHash; + } } } @@ -357,7 +362,7 @@ void LedgerConsensus::handleLCL(const uint256& lclHash) } } - cLog(lsINFO) << "Acquired the consensus ledger " << mPrevLedgerHash; + cLog(lsINFO) << "Have the consensus ledger " << mPrevLedgerHash; mHaveCorrectLCL = true; mAcquiringLedger = LedgerAcquire::pointer(); theApp->getOPs().clearNeedNetworkLedger(); From 7ee9a20f40e946edac0729cccf32f0e1b9dc1db6 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 14:30:09 -0700 Subject: [PATCH 13/24] Some logging cleanups. --- src/LedgerConsensus.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index fe61a0a83..4c3c3d8e6 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -155,14 +155,14 @@ void LCTransaction::setVote(const uint160& peer, bool votesYes) } else if (votesYes && !res.first->second) { // changes vote to yes - cLog(lsTRACE) << "Peer " << peer << " now votes YES on " << mTransactionID; + cLog(lsDEBUG) << "Peer " << peer << " now votes YES on " << mTransactionID; --mNays; ++mYays; res.first->second = true; } else if (!votesYes && res.first->second) { // changes vote to no - cLog(lsTRACE) << "Peer " << peer << " now votes NO on " << mTransactionID; + cLog(lsDEBUG) << "Peer " << peer << " now votes NO on " << mTransactionID; ++mNays; --mYays; res.first->second = false; @@ -216,7 +216,7 @@ bool LCTransaction::updateVote(int percentTime, bool proposing) return false; } mOurVote = newPosition; - cLog(lsTRACE) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; + cLog(lsDEBUG) << "We now vote " << (mOurVote ? "YES" : "NO") << " on " << mTransactionID; return true; } @@ -818,7 +818,8 @@ void LedgerConsensus::startAcquiring(const TransactionAcquire::pointer& acquire) void LedgerConsensus::propose() { - cLog(lsTRACE) << "We propose: " << mOurPosition->getCurrentHash(); + cLog(lsTRACE) << "We propose: " << + (mOurPosition->isBowOut() ? std::string("bowOut") : mOurPosition->getCurrentHash().GetHex()); ripple::TMProposeSet prop; prop.set_currenttxhash(mOurPosition->getCurrentHash().begin(), 256 / 8); @@ -836,7 +837,7 @@ void LedgerConsensus::propose() void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vector& tx) { - cLog(lsTRACE) << "Transaction " << txID << " is disputed"; + cLog(lsDEBUG) << "Transaction " << txID << " is disputed"; boost::unordered_map::iterator it = mDisputes.find(txID); if (it != mDisputes.end()) return; @@ -916,7 +917,7 @@ bool LedgerConsensus::peerPosition(const LedgerProposal::pointer& newPosition) it.second->setVote(peerID, set->hasItem(it.first)); } else - cLog(lsTRACE) << "Don't have that tx set"; + cLog(lsDEBUG) << "Don't have that tx set"; return true; } From 81170d78119003c7d73f030f01fa7cd83d32e5ba Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 16:37:39 -0700 Subject: [PATCH 14/24] Logging improvements. --- src/LedgerConsensus.cpp | 8 ++++---- src/LedgerConsensus.h | 2 +- src/LedgerTiming.cpp | 23 +++++++++++++---------- src/LedgerTiming.h | 3 ++- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 4c3c3d8e6..df3764830 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -562,9 +562,9 @@ void LedgerConsensus::stateEstablish() updateOurPositions(); if (!mHaveCloseTimeConsensus) { - tLog(haveConsensus(), lsINFO) << "We have TX consensus but not CT consensus"; + tLog(haveConsensus(false), lsINFO) << "We have TX consensus but not CT consensus"; } - else if (haveConsensus()) + else if (haveConsensus(true)) { cLog(lsINFO) << "Converge cutoff (" << mPeerPositions.size() << " participants)"; mState = lcsFINISHED; @@ -721,7 +721,7 @@ void LedgerConsensus::updateOurPositions() } } -bool LedgerConsensus::haveConsensus() +bool LedgerConsensus::haveConsensus(bool forReal) { // FIXME: Should check for a supermajority on each disputed transaction // counting unacquired TX sets as disagreeing int agree = 0, disagree = 0; @@ -743,7 +743,7 @@ bool LedgerConsensus::haveConsensus() #endif return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations, - mPreviousMSeconds, mCurrentMSeconds); + mPreviousMSeconds, mCurrentMSeconds, forReal); } SHAMap::pointer LedgerConsensus::getTransactionTree(const uint256& hash, bool doAcquire) diff --git a/src/LedgerConsensus.h b/src/LedgerConsensus.h index 4ab27866b..23582e071 100644 --- a/src/LedgerConsensus.h +++ b/src/LedgerConsensus.h @@ -174,7 +174,7 @@ public: void stateFinished(); void stateAccepted(); - bool haveConsensus(); + bool haveConsensus(bool forReal); bool peerPosition(const LedgerProposal::pointer&); diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp index 9ce760fc4..10ef38b62 100644 --- a/src/LedgerTiming.cpp +++ b/src/LedgerTiming.cpp @@ -6,6 +6,7 @@ #include #include "Log.h" +SETUP_LOG(); // NOTE: First and last times must be repeated int ContinuousLedgerTiming::LedgerTimeResolution[] = { 10, 10, 20, 30, 60, 90, 120, 120 }; @@ -23,7 +24,7 @@ bool ContinuousLedgerTiming::shouldClose( if ((previousMSeconds < -1000) || (previousMSeconds > 600000) || (currentMSeconds < -1000) || (currentMSeconds > 600000)) { - Log(lsWARNING) << + cLog(lsWARNING) << boost::str(boost::format("CLC::shouldClose range Trans=%s, Prop: %d/%d, Secs: %d (last:%d)") % (anyTransactions ? "yes" : "no") % previousProposers % proposersClosed % currentMSeconds % previousMSeconds); @@ -34,14 +35,14 @@ bool ContinuousLedgerTiming::shouldClose( { // no transactions so far this interval if (proposersClosed > (previousProposers / 4)) // did we miss a transaction? { - Log(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, " + cLog(lsTRACE) << "no transactions, many proposers: now (" << proposersClosed << " closed, " << previousProposers << " before)"; return true; } #if 0 // This false triggers on the genesis ledger if (previousMSeconds > (1000 * (LEDGER_IDLE_INTERVAL + 2))) // the last ledger was very slow to close { - Log(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")"; + cLog(lsTRACE) << "was slow to converge (p=" << (previousMSeconds) << ")"; if (previousMSeconds < 2000) return previousMSeconds; return previousMSeconds - 1000; @@ -61,10 +62,12 @@ bool ContinuousLedgerTiming::haveConsensus( int currentAgree, // proposers who agree with us int currentClosed, // proposers who have currently closed their ledgers int previousAgreeTime, // how long it took to agree on the last ledger - int currentAgreeTime) // how long we've been trying to agree + int currentAgreeTime, // how long we've been trying to agree + bool forReal) // deciding whether to stop consensus process { - Log(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d") % - currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime); + cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d%s") % + currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime % + (forReal ? "" : "X")); if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) return false; @@ -73,7 +76,7 @@ bool ContinuousLedgerTiming::haveConsensus( { // Less than 3/4 of the last ledger's proposers are present, we may need more time if (currentAgreeTime < (previousAgreeTime + LEDGER_MIN_CONSENSUS)) { - Log(lsTRACE) << "too fast, not enough proposers"; + tLog(forReal, lsTRACE) << "too fast, not enough proposers"; return false; } } @@ -81,19 +84,19 @@ bool ContinuousLedgerTiming::haveConsensus( // If 80% of current proposers (plus us) agree on a set, we have consensus if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) { - Log(lsTRACE) << "normal consensus"; + tLog(forReal, lsTRACE) << "normal consensus"; return true; } // If 50% of the nodes on your UNL (minus us) have closed, you should close if (((currentClosed * 100 - 100) / (currentProposers + 1)) > 50) { - Log(lsTRACE) << "many closers"; + tLog(forReal, lsTRACE) << "many closers"; return true; } // no consensus yet - Log(lsTRACE) << "no consensus"; + tLog(forReal, lsTRACE) << "no consensus"; return false; } diff --git a/src/LedgerTiming.h b/src/LedgerTiming.h index 551c9bfce..6cf535c40 100644 --- a/src/LedgerTiming.h +++ b/src/LedgerTiming.h @@ -65,7 +65,8 @@ public: static bool haveConsensus( int previousProposers, int currentProposers, int currentAgree, int currentClosed, - int previousAgreeTime, int currentAgreeTime); + int previousAgreeTime, int currentAgreeTime, + bool forReal); static int getNextLedgerTimeResolution(int previousResolution, bool previousAgree, int ledgerSeq); }; From aa4a1b599492539b847f532200d699fcc046c435 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 16:49:47 -0700 Subject: [PATCH 15/24] Simplifications. --- src/ValidationCollection.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 5f5fc17a5..65e861e8d 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -9,6 +9,8 @@ SETUP_LOG(); +typedef std::pair u160_val_pair; + bool ValidationCollection::addValidation(const SerializedValidation::pointer& val) { NewcoinAddress signer = val->getSignerPublic(); @@ -121,10 +123,9 @@ int ValidationCollection::getNodesAfter(const uint256& ledger) { // Number of trusted nodes that have moved past this ledger int count = 0; boost::mutex::scoped_lock sl(mValidationLock); - for (boost::unordered_map::iterator it = mCurrentValidations.begin(), - end = mCurrentValidations.end(); it != end; ++it) + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second->isTrusted() && it->second->isPreviousHash(ledger)) + if (it.second->isTrusted() && it.second->isPreviousHash(ledger)) ++count; } return count; @@ -136,12 +137,11 @@ int ValidationCollection::getLoadRatio(bool overLoaded) int badNodes = overLoaded ? 0 : 1; { boost::mutex::scoped_lock sl(mValidationLock); - for (boost::unordered_map::iterator it = mCurrentValidations.begin(), - end = mCurrentValidations.end(); it != end; ++it) + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second->isTrusted()) + if (it.second->isTrusted()) { - if (it->second->isFull()) + if (it.second->isFull()) ++goodNodes; else ++badNodes; @@ -190,14 +190,13 @@ ValidationCollection::getCurrentValidations(uint256 currentLedger) void ValidationCollection::flush() { - boost::mutex::scoped_lock sl(mValidationLock); - boost::unordered_map::iterator it = mCurrentValidations.begin(); bool anyNew = false; - while (it != mCurrentValidations.end()) + + boost::mutex::scoped_lock sl(mValidationLock); + BOOST_FOREACH(u160_val_pair& it, mCurrentValidations) { - if (it->second) - mStaleValidations.push_back(it->second); - ++it; + if (it.second) + mStaleValidations.push_back(it.second); anyNew = true; } mCurrentValidations.clear(); @@ -243,7 +242,6 @@ void ValidationCollection::doWrite() % db->escape(strCopy(it->getSignature())))); db->executeSQL("END TRANSACTION;"); } - sl.lock(); } mWriting = false; From a98aa82f3a5ebb458c297893e9fc6e6cdb997aed Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 16:49:56 -0700 Subject: [PATCH 16/24] Fix some broken logic in the way we detect when other nodes have declared consensus. --- src/LedgerTiming.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LedgerTiming.cpp b/src/LedgerTiming.cpp index 10ef38b62..6119123a4 100644 --- a/src/LedgerTiming.cpp +++ b/src/LedgerTiming.cpp @@ -60,13 +60,13 @@ bool ContinuousLedgerTiming::haveConsensus( int previousProposers, // proposers in the last closing (not including us) int currentProposers, // proposers in this closing so far (not including us) int currentAgree, // proposers who agree with us - int currentClosed, // proposers who have currently closed their ledgers + int currentFinished, // proposers who have validated a ledger after this one int previousAgreeTime, // how long it took to agree on the last ledger int currentAgreeTime, // how long we've been trying to agree bool forReal) // deciding whether to stop consensus process { - cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d closed=%d time=%d/%d%s") % - currentProposers % previousProposers % currentAgree % currentClosed % currentAgreeTime % previousAgreeTime % + cLog(lsTRACE) << boost::str(boost::format("CLC::haveConsensus: prop=%d/%d agree=%d validated=%d time=%d/%d%s") % + currentProposers % previousProposers % currentAgree % currentFinished % currentAgreeTime % previousAgreeTime % (forReal ? "" : "X")); if (currentAgreeTime <= LEDGER_MIN_CONSENSUS) @@ -84,14 +84,14 @@ bool ContinuousLedgerTiming::haveConsensus( // If 80% of current proposers (plus us) agree on a set, we have consensus if (((currentAgree * 100 + 100) / (currentProposers + 1)) > 80) { - tLog(forReal, lsTRACE) << "normal consensus"; + tLog(forReal, lsINFO) << "normal consensus"; return true; } - // If 50% of the nodes on your UNL (minus us) have closed, you should close - if (((currentClosed * 100 - 100) / (currentProposers + 1)) > 50) + // If 50% of the nodes on your UNL have moved on, you should declare consensus + if (((currentFinished * 100) / (currentProposers + 1)) > 50) { - tLog(forReal, lsTRACE) << "many closers"; + tLog(forReal, lsWARNING) << "We see no consensus, but 50% of nodes have moved on"; return true; } From 90d061529b1dc2305e56f0f6d01f523419dfc8d5 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 16:50:14 -0700 Subject: [PATCH 17/24] Tiny fix. --- src/LedgerConsensus.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index df3764830..2bd8e5744 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -738,9 +738,7 @@ bool LedgerConsensus::haveConsensus(bool forReal) } int currentValidations = theApp->getValidations().getNodesAfter(mPrevLedgerHash); -#ifdef LC_DEBUG - cLog(lsINFO) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree; -#endif + cLog(lsDEBUG) << "Checking for TX consensus: agree=" << agree << ", disagree=" << disagree; return ContinuousLedgerTiming::haveConsensus(mPreviousProposers, agree + disagree, agree, currentValidations, mPreviousMSeconds, mCurrentMSeconds, forReal); From 17437784d6bbe95c56fe11472f2f24f8f7978abe Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Fri, 19 Oct 2012 16:53:32 -0700 Subject: [PATCH 18/24] If these are going to be in the repo, they should be correct. --- validators.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/validators.txt b/validators.txt index 335d5d5ad..cde2a6711 100644 --- a/validators.txt +++ b/validators.txt @@ -20,7 +20,6 @@ # [validators] -n9LQC4xFSWXNv1SU1sKtjrW6TZpBZSwp1nRWej8saGs155x42YFZ first -n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second -n9KXAZxiHkWuVGxDEE8boW7WmcycpZNmWei4vxVaywLZ391Nbuqx third -n94365hzFKikgCULeJwczs3kwzpir3KVHkfhUWGT4MjmbEbC5xBy +n9KPnVLn7ewVzHvn218DcEYsnWLzKerTDwhpofhk4Ym1RUq4TeGw first +n9LFzWuhKNvXStHAuemfRKFVECLApowncMAM5chSCL9R5ECHGN4V second +n94rSdgTyBNGvYg8pZXGuNt59Y5bGAZGxbxyvjDaqD9ceRAgD85P third From 2ada16e2223fa8f1ff9c13550ea5c4eec5306fdc Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:14:23 -0700 Subject: [PATCH 19/24] Change OfferCancel to succeed if offer not found. --- src/TransactionAction.cpp | 36 ++++++++++++++++++++++++------------ src/TransactionErr.cpp | 2 +- src/TransactionErr.h | 2 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/TransactionAction.cpp b/src/TransactionAction.cpp index 81b9fe85d..c4aa817c9 100644 --- a/src/TransactionAction.cpp +++ b/src/TransactionAction.cpp @@ -1136,24 +1136,37 @@ Log(lsINFO) << boost::str(boost::format("doOfferCreate: saTakerPays=%s saTakerGe TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn) { TER terResult; - const uint32 uSequence = txn.getFieldU32(sfOfferSequence); - const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uSequence); - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); + const uint32 uOfferSequence = txn.getFieldU32(sfOfferSequence); + const uint32 uAccountSequenceNext = mTxnAccount->getFieldU32(sfSequence); - if (sleOffer) + Log(lsDEBUG) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; + + if (!uOfferSequence || uAccountSequenceNext-1 <= uOfferSequence) { - Log(lsWARNING) << "doOfferCancel: uSequence=" << uSequence; + Log(lsINFO) << "doOfferCancel: uAccountSequenceNext=" << uAccountSequenceNext << " uOfferSequence=" << uOfferSequence; - terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); + terResult = temBAD_SEQUENCE; } else { - Log(lsWARNING) << "doOfferCancel: offer not found: " - << NewcoinAddress::createHumanAccountID(mTxnAccountID) - << " : " << uSequence - << " : " << uOfferIndex.ToString(); + const uint256 uOfferIndex = Ledger::getOfferIndex(mTxnAccountID, uOfferSequence); + SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - terResult = terOFFER_NOT_FOUND; + if (sleOffer) + { + Log(lsWARNING) << "doOfferCancel: uOfferSequence=" << uOfferSequence; + + terResult = mNodes.offerDelete(sleOffer, uOfferIndex, mTxnAccountID); + } + else + { + Log(lsWARNING) << "doOfferCancel: offer not found: " + << NewcoinAddress::createHumanAccountID(mTxnAccountID) + << " : " << uOfferSequence + << " : " << uOfferIndex.ToString(); + + terResult = tesSUCCESS; + } } return terResult; @@ -1179,7 +1192,6 @@ TER TransactionEngine::doContractAdd(const SerializedTransaction& txn) // place contract in ledger // run create code - if (mLedger->getParentCloseTimeNC() >= expiration) { diff --git a/src/TransactionErr.cpp b/src/TransactionErr.cpp index f85f0cda4..9f50b4d3a 100644 --- a/src/TransactionErr.cpp +++ b/src/TransactionErr.cpp @@ -33,6 +33,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { temBAD_PUBLISH, "temBAD_PUBLISH", "Malformed: Bad publish." }, { temBAD_TRANSFER_RATE, "temBAD_TRANSFER_RATE", "Malformed: Transfer rate must be >= 1.0" }, { temBAD_SET_ID, "temBAD_SET_ID", "Malformed." }, + { temBAD_SEQUENCE, "temBAD_SEQUENCE", "Malformed: Sequence in not in the past." }, { temCREATEXNS, "temCREATEXNS", "Can not specify non XNS for Create." }, { temDST_IS_SRC, "temDST_IS_SRC", "Destination may not be source." }, { temDST_NEEDED, "temDST_NEEDED", "Destination not specified." }, @@ -53,7 +54,6 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { terNO_DST, "terNO_DST", "The destination does not exist." }, { terNO_LINE, "terNO_LINE", "No such line." }, { terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." }, - { terOFFER_NOT_FOUND, "terOFFER_NOT_FOUND", "Can not cancel offer." }, { terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." }, { terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." }, { terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." }, diff --git a/src/TransactionErr.h b/src/TransactionErr.h index e2aa0e443..f672caf7f 100644 --- a/src/TransactionErr.h +++ b/src/TransactionErr.h @@ -34,6 +34,7 @@ enum TER // aka TransactionEngineResult temBAD_PATH_LOOP, temBAD_PUBLISH, temBAD_TRANSFER_RATE, + temBAD_SEQUENCE, temBAD_SET_ID, temCREATEXNS, temDST_IS_SRC, @@ -83,7 +84,6 @@ enum TER // aka TransactionEngineResult terNO_DST, terNO_LINE, terNO_LINE_NO_ZERO, - terOFFER_NOT_FOUND, // XXX If we check sequence first this could be hard failure. terPRE_SEQ, terSET_MISSING_DST, terUNFUNDED, From 5d9fdae5cf3c37415eca8b2b4e1c279bbd256a79 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:15:22 -0700 Subject: [PATCH 20/24] JS: Fix UInt160 to use config. --- js/amount.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/js/amount.js b/js/amount.js index faac4e3de..36d57889b 100644 --- a/js/amount.js +++ b/js/amount.js @@ -19,10 +19,14 @@ var UInt160 = function () { this.value = NaN; }; +UInt160.json_rewrite = function (j) { + return UInt160.from_json(j).to_json(); +}; + // Return a new UInt160 from j. UInt160.from_json = function (j) { return 'string' === typeof j - ? (new UInt160()).parse_json(j in accounts ? accounts[j].account : j) + ? (new UInt160()).parse_json(j) : j.clone(); }; @@ -40,6 +44,8 @@ UInt160.prototype.copyTo = function(d) { // value = NaN on error. UInt160.prototype.parse_json = function (j) { // Canonicalize and validate + if (j in accounts) + j = accounts[j].account; switch (j) { case undefined: @@ -163,6 +169,10 @@ var Amount = function () { this.issuer = new UInt160(); }; +Amount.json_rewrite = function(j) { + return Amount.from_json(j).to_json(); +}; + Amount.from_json = function(j) { return (new Amount()).parse_json(j); }; From 083e78e124602a0b47eaf0bdfbda5f40007a6811 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:16:09 -0700 Subject: [PATCH 21/24] JS: Add offer_cancel. --- js/remote.js | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/js/remote.js b/js/remote.js index 3f1f85517..4f57c5c3a 100644 --- a/js/remote.js +++ b/js/remote.js @@ -90,7 +90,7 @@ Request.prototype.ledger_index = function (ledger_index) { }; Request.prototype.account_root = function (account) { - this.message.account_root = UInt160.from_json(account).to_json(); + this.message.account_root = UInt160.json_rewrite(account); return this; }; @@ -168,7 +168,9 @@ var Remote = function (trusted, websocket_ip, websocket_port, config, trace) { // Cache for various ledgers. // XXX Clear when ledger advances. this.ledgers = { - 'current' : {} + 'current' : { + 'account_root' : {} + } }; }; @@ -626,6 +628,8 @@ Remote.prototype._server_subscribe = function () { // Ask the remote to accept the current ledger. // - To be notified when the ledger is accepted, server_subscribe() then listen to 'ledger_closed' events. +// A good way to be notified of the result of this is: +// remote.once('ledger_closed', function (ledger_closed, ledger_closed_index) { ... } ); Remote.prototype.ledger_accept = function () { if (this.stand_alone || undefined === this.stand_alone) { @@ -655,12 +659,13 @@ Remote.prototype.request_account_balance = function (account, current) { // Return the next account sequence if possible. // <-- undefined or Sequence Remote.prototype.account_seq = function (account, advance) { - var account_info = this.accounts[account]; + var account = UInt160.json_rewrite(account); + var account_info = this.accounts[account]; var seq; if (account_info && account_info.seq) { - var seq = account_info.seq; + seq = account_info.seq; if (advance) account_info.seq += 1; } @@ -668,6 +673,14 @@ Remote.prototype.account_seq = function (account, advance) { return seq; } +Remote.prototype.set_account_seq = function (account, seq) { + var account = UInt160.json_rewrite(account); + + if (!this.accounts[account]) this.accounts[account] = {}; + + this.accounts[account].seq = seq; +} + // Return a request to refresh accounts[account].seq. Remote.prototype.account_seq_cache = function (account, current) { var self = this; @@ -690,7 +703,7 @@ Remote.prototype.account_seq_cache = function (account, current) { // Mark an account's root node as dirty. Remote.prototype.dirty_account_root = function (account) { - delete this.ledgers.current.account_root[account]; + delete this.ledgers.current.account_root[UInt160.json_rewrite(account)]; }; // Return a request to get a ripple balance. @@ -802,6 +815,7 @@ var Transaction = function (remote) { self.set_state('client_proposed'); self.emit('proposed', { + 'transaction' : message.transaction, 'result' : message.engine_result, 'result_code' : message.engine_result_code, 'result_message' : message.engine_result_message, @@ -999,13 +1013,23 @@ Transaction.prototype.account_secret = function (account) { return this.remote.config.accounts[account] ? this.remote.config.accounts[account].secret : undefined; }; +Transaction.prototype.offer_cancel = function (src, sequence) { + this.secret = this.account_secret(src); + this.transaction.TransactionType = 'OfferCancel'; + this.transaction.Account = UInt160.from_json(src).to_json(); + this.transaction.OfferSequence = Number(sequence); + + return this; +}; + +// XXX Expiration should use a time. Transaction.prototype.offer_create = function (src, taker_pays, taker_gets, expiration) { this.secret = this.account_secret(src); this.transaction.TransactionType = 'OfferCreate'; this.transaction.Account = UInt160.from_json(src).to_json(); this.transaction.Fee = fees.offer.to_json(); - this.transaction.TakerPays = taker_pays.to_json(); - this.transaction.TakerGets = taker_gets.to_json(); + this.transaction.TakerPays = Amount.json_rewrite(taker_pays); + this.transaction.TakerGets = Amount.json_rewrite(taker_gets); if (expiration) this.transaction.Expiration = expiration; @@ -1024,7 +1048,7 @@ Transaction.prototype.payment = function (src, dst, deliver_amount) { this.secret = this.account_secret(src); this.transaction.TransactionType = 'Payment'; this.transaction.Account = UInt160.from_json(src).to_json(); - this.transaction.Amount = Amount.from_json(deliver_amount).to_json(); + this.transaction.Amount = Amount.json_rewrite(deliver_amount); this.transaction.Destination = UInt160.from_json(dst).to_json(); return this; From c3ebbf1f777dbd26018759c0b86d1f222b2167b3 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:17:03 -0700 Subject: [PATCH 22/24] Cosmetic. --- src/SerializeProto.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SerializeProto.h b/src/SerializeProto.h index f7d46a50f..1a7b67641 100644 --- a/src/SerializeProto.h +++ b/src/SerializeProto.h @@ -151,3 +151,5 @@ FIELD(Template, ARRAY, 5) FIELD(Necessary, ARRAY, 6) FIELD(Sufficient, ARRAY, 7) + +// vim:ts=4 From 3691def75e209c7c3202bce9d5834a8f23533cdc Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:32:01 -0700 Subject: [PATCH 23/24] UT: Add offer-test.js --- test/offer-test.js | 306 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 test/offer-test.js diff --git a/test/offer-test.js b/test/offer-test.js new file mode 100644 index 000000000..a47468ba4 --- /dev/null +++ b/test/offer-test.js @@ -0,0 +1,306 @@ + +var async = require("async"); +var buster = require("buster"); +var fs = require("fs"); + +var server = require("./server.js"); +var remote = require("../js/remote.js"); +var config = require("./config.js"); + +var Amount = require("../js/amount.js").Amount; + +require("../js/amount.js").setAccounts(config.accounts); + +buster.testRunner.timeout = 5000; + +var alpha; + +// success: callback(), error: callback(err) +var createAccounts = function (remote, src, amount, accounts, callback) { + async.forEachSeries(accounts, function (account, callback) { + remote.transaction() + .payment(src, account, Amount.from_json(amount)) + .flags('CreateAccount') + .on('proposed', function (m) { + console.log("proposed: %s", JSON.stringify(m)); + + buster.assert.equals(m.result, 'tesSUCCESS'); + + callback(); + }) + .on('error', function (m) { + console.log("error: %s", JSON.stringify(m)); + + callback('error'); + }) + .submit(); + }, callback); +}; + +// success: callback(), error: callback(err) +var creditLimit = function (remote, src, amount, callback) { + remote.transaction() + .ripple_line_set(src, Amount.from_json(amount)) + .on('proposed', function (m) { + console.log("proposed: %s", JSON.stringify(m)); + + buster.assert.equals(m.result, 'tesSUCCESS'); + + callback(); + }) + .on('error', function (m) { + console.log("error: %s", JSON.stringify(m)); + + callback('error'); + }) + .submit(); +}; + +buster.testCase("Work in progress", { + 'setUp' : + function (done) { + server.start("alpha", + function (e) { + buster.refute(e); + + alpha = remote.remoteConfig(config, "alpha", 'TRACE'); + + alpha + .once('ledger_closed', done) + .connect(); + } +// , 'MOCK' + ); + }, + + 'tearDown' : + function (done) { + alpha + .on('disconnected', function () { + server.stop("alpha", function (e) { + buster.refute(e); + done(); + }); + }) + .connect(false); + }, + + "offer create then cancel in one ledger" : + function (done) { + var final_create; + + async.waterfall([ + function (callback) { + alpha.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); + + final_create = m; + }) + .submit(); + }, + function (m, callback) { + alpha.transaction() + .offer_cancel("root", m.transaction.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)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + buster.assert(final_create); + done(); + }) + .submit(); + }, + function (m, callback) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + }) + .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 final_create; + var offer_seq; + + async.waterfall([ + function (callback) { + alpha.transaction() + .offer_create("root", "500", "100/USD/root") + .on("proposed", function (m) { + console.log("PROPOSED: offer_create: %s", JSON.stringify(m)); + + offer_seq = m.transaction.Sequence; + + callback(m.result != 'tesSUCCESS'); + }) + .on("final", function (m) { + console.log("FINAL: offer_create: %s", JSON.stringify(m)); + + buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult); + + final_create = m; + + callback(); + }) + .submit(); + }, + function (callback) { + if (!final_create) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: %d: %s", ledger_closed_index, ledger_closed); + + }) + .ledger_accept(); + } + else { + callback(); + } + }, + function (callback) { + console.log("CANCEL: offer_cancel: %d", offer_seq); + + alpha.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) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + 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 final_create; + + async.waterfall([ + function (callback) { + alpha.transaction() + .payment('root', 'alice', Amount.from_json("10000")) + .flags('CreateAccount') + .on("proposed", function (m) { + console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m)); + callback(m.result != 'tesSUCCESS', m); + }) + .on('error', function(m) { + console.log("error: %s", m); + + buster.assert(false); + callback(m); + }) + .submit(); + }, + // Past sequence but wrong + function (m, callback) { + alpha.transaction() + .offer_cancel("root", m.transaction.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) { + alpha.transaction() + .offer_cancel("root", m.transaction.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. + alpha.set_account_seq("root", alpha.account_seq("root")-1); + + alpha.transaction() + .offer_cancel("root", m.transaction.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) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: A: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + alpha + .once("ledger_closed", function (ledger_closed, ledger_closed_index) { + console.log("LEDGER_CLOSED: B: %d: %s", ledger_closed_index, ledger_closed); + callback(); + }) + .ledger_accept(); + }, + function (callback) { + callback(); + } + ], function (error) { + console.log("result: error=%s", error); + buster.refute(error); + + done(); + }); + }, +}); +// vim:sw=2:sts=2:ts=8 From c6cb4836312c52ae31b02a8421d55e6e357d60e4 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 19 Oct 2012 21:36:05 -0700 Subject: [PATCH 24/24] UT: offer-test.js remove extra functions --- test/offer-test.js | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/test/offer-test.js b/test/offer-test.js index a47468ba4..cb421fdef 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -15,47 +15,6 @@ buster.testRunner.timeout = 5000; var alpha; -// success: callback(), error: callback(err) -var createAccounts = function (remote, src, amount, accounts, callback) { - async.forEachSeries(accounts, function (account, callback) { - remote.transaction() - .payment(src, account, Amount.from_json(amount)) - .flags('CreateAccount') - .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); - - buster.assert.equals(m.result, 'tesSUCCESS'); - - callback(); - }) - .on('error', function (m) { - console.log("error: %s", JSON.stringify(m)); - - callback('error'); - }) - .submit(); - }, callback); -}; - -// success: callback(), error: callback(err) -var creditLimit = function (remote, src, amount, callback) { - remote.transaction() - .ripple_line_set(src, Amount.from_json(amount)) - .on('proposed', function (m) { - console.log("proposed: %s", JSON.stringify(m)); - - buster.assert.equals(m.result, 'tesSUCCESS'); - - callback(); - }) - .on('error', function (m) { - console.log("error: %s", JSON.stringify(m)); - - callback('error'); - }) - .submit(); -}; - buster.testCase("Work in progress", { 'setUp' : function (done) {