diff --git a/package.json b/package.json index 891503098d..a1ab132707 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "test": "test" }, "dependencies": { - "assert-diff": "0.0.4", "async": "~0.2.9", "deep-equal": "0.0.0", "extend": "~1.2.0", @@ -16,6 +15,7 @@ "simple-jsonrpc": "~0.0.2" }, "devDependencies": { + "assert-diff": "^1.0.1", "coffee-script": "^1.8.0", "mocha": "^2.1.0" }, diff --git a/src/ripple/app/ledger/LedgerEntrySet.cpp b/src/ripple/app/ledger/LedgerEntrySet.cpp index 64b78d534c..aa361e767a 100644 --- a/src/ripple/app/ledger/LedgerEntrySet.cpp +++ b/src/ripple/app/ledger/LedgerEntrySet.cpp @@ -1309,6 +1309,12 @@ TER LedgerEntrySet::trustCreate ( const bool bSetDst = saLimit.getIssuer () == uDstAccountID; const bool bSetHigh = bSrcHigh ^ bSetDst; + assert (sleAccount->getFieldAccount160 (sfAccount) == + (bSetHigh ? uHighAccountID : uLowAccountID)); + SLE::pointer slePeer = entryCache (ltACCOUNT_ROOT, + getAccountRootIndex (bSetHigh ? uLowAccountID : uHighAccountID)); + assert (slePeer); + // Remember deletion hints. sleRippleState->setFieldU64 (sfLowNode, uLowNode); sleRippleState->setFieldU64 (sfHighNode, uHighNode); @@ -1343,6 +1349,12 @@ TER LedgerEntrySet::trustCreate ( uFlags |= (!bSetHigh ? lsfLowFreeze : lsfHighFreeze); } + if ((slePeer->getFlags() & lsfDefaultRipple) == 0) + { + // The other side's default is no rippling + uFlags |= (bSetHigh ? lsfLowNoRipple : lsfHighNoRipple); + } + sleRippleState->setFieldU32 (sfFlags, uFlags); incrementOwnerCount (sleAccount); @@ -1474,7 +1486,9 @@ TER LedgerEntrySet::rippleCredit ( // Sender is zero or negative. && (uFlags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) // Sender reserve is set. - && !(uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) + && static_cast (uFlags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast (entryCache (ltACCOUNT_ROOT, + getAccountRootIndex (uSenderID))->getFlags() & lsfDefaultRipple) && !(uFlags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && !sleRippleState->getFieldAmount ( !bSenderHigh ? sfLowLimit : sfHighLimit) diff --git a/src/ripple/app/transactors/SetAccount.cpp b/src/ripple/app/transactors/SetAccount.cpp index 58c3f5c2fc..6ed1eb201c 100644 --- a/src/ripple/app/transactors/SetAccount.cpp +++ b/src/ripple/app/transactors/SetAccount.cpp @@ -174,6 +174,15 @@ public: uFlagsOut &= ~lsfDisableMaster; } + if (uSetFlag == asfDefaultRipple) + { + uFlagsOut |= lsfDefaultRipple; + } + else if (uClearFlag == asfDefaultRipple) + { + uFlagsOut &= ~lsfDefaultRipple; + } + if (uSetFlag == asfNoFreeze) { if (!mSigMaster && !(uFlagsIn & lsfDisableMaster)) diff --git a/src/ripple/app/transactors/SetTrust.cpp b/src/ripple/app/transactors/SetTrust.cpp index bb751082bf..e042472f94 100644 --- a/src/ripple/app/transactors/SetTrust.cpp +++ b/src/ripple/app/transactors/SetTrust.cpp @@ -288,15 +288,17 @@ public: if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; + bool const bLowDefRipple = sleLowAccount->getFlags() & lsfDefaultRipple; + bool const bHighDefRipple = sleHighAccount->getFlags() & lsfDefaultRipple; bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || - (uFlagsOut & lsfLowNoRipple) || + ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || (uFlagsOut & lsfLowFreeze) || saLowLimit || saLowBalance > zero; bool const bLowReserveClear = !bLowReserveSet; bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || - (uFlagsOut & lsfHighNoRipple) || + ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || (uFlagsOut & lsfHighFreeze) || saHighLimit || saHighBalance > zero; bool const bHighReserveClear = !bHighReserveSet; diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index 286d800575..d3398bb99b 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -111,6 +111,7 @@ enum LedgerSpecificFlags lsfDisableMaster = 0x00100000, // True, force regular key lsfNoFreeze = 0x00200000, // True, cannot freeze ripple states lsfGlobalFreeze = 0x00400000, // True, all assets frozen + lsfDefaultRipple = 0x00800000, // True, trust lines allow rippling by default // ltOFFER lsfPassive = 0x00010000, diff --git a/src/ripple/protocol/TxFlags.h b/src/ripple/protocol/TxFlags.h index d343878d84..0161ca99a6 100644 --- a/src/ripple/protocol/TxFlags.h +++ b/src/ripple/protocol/TxFlags.h @@ -65,6 +65,7 @@ const std::uint32_t asfDisableMaster = 4; const std::uint32_t asfAccountTxnID = 5; const std::uint32_t asfNoFreeze = 6; const std::uint32_t asfGlobalFreeze = 7; +const std::uint32_t asfDefaultRipple = 8; // OfferCreate flags: const std::uint32_t tfPassive = 0x00010000; diff --git a/test/freeze-test.coffee b/test/freeze-test.coffee index 333b466165..8d3e235384 100644 --- a/test/freeze-test.coffee +++ b/test/freeze-test.coffee @@ -848,4 +848,4 @@ execute_if_enabled (suite, enforced) -> remote.request_account_offers args, (err, res) -> assert res.offers.length == 0 - done() \ No newline at end of file + done() diff --git a/test/ledger-state.coffee b/test/ledger-state.coffee index 30645634cb..5ab434528c 100644 --- a/test/ledger-state.coffee +++ b/test/ledger-state.coffee @@ -491,8 +491,9 @@ exports.LedgerState = class LedgerState add_transaction_fees: -> extra_fees = {} + account_sets = ([k] for k,ac of @accounts) fee = Amount.from_json(@remote.fee_cushion * 10) - for list in [@trusts, @iou_payments, @offers] + for list in [@trusts, @iou_payments, @offers, account_sets] for [src, args...] in list extra = extra_fees[src] extra = if extra? then extra.add(fee) else fee @@ -526,6 +527,13 @@ exports.LedgerState = class LedgerState LOG("Account `#{src}` creating account `#{dest}` by "+ "making payment of #{amt.to_text_full()}") ), cb) + (cb) -> + reqs.transactor( + Transaction::account_set, + accounts_apply_arguments, + ((account, tx) -> + tx.tx_json.SetFlag = 8 + ), cb) (cb) -> reqs.transactor( Transaction::ripple_line_set, diff --git a/test/no-ripple-test.js b/test/no-ripple-test.js index ea116f635f..32146c65db 100644 --- a/test/no-ripple-test.js +++ b/test/no-ripple-test.js @@ -326,3 +326,92 @@ suite('NoRipple', function() { }); }); }); + +suite('Default ripple', function() { + var $ = { }; + + setup(function(done) { + testutils.build_setup().call($, done); + }); + + teardown(function(done) { + testutils.build_teardown().call($, done); + }); + + test('Set default ripple on account, check new trustline', function(done) { + var steps = [ + function (callback) { + testutils.create_accounts( + $.remote, + 'root', + '10000.0', + [ 'alice', 'bob' ], + { default_rippling: false }, + callback); + }, + function (callback) { + var tx = $.remote.createTransaction('AccountSet', { + account: 'bob', + set_flag: 8 + }); + testutils.submit_transaction(tx, callback); + }, + function (callback) { + var tx = $.remote.createTransaction('TrustSet', { + account: 'root', + limit: '100/USD/alice' + }); + testutils.submit_transaction(tx, callback); + }, + function (callback) { + var tx = $.remote.createTransaction('TrustSet', { + account: 'root', + limit: '100/USD/bob' + }); + testutils.submit_transaction(tx, callback); + }, + function (callback) { + $.remote.requestAccountLines({ account: 'root', peer: 'alice' }, function(err, m) { + assert.ifError(err); + assert(Array.isArray(m.lines)); + assert(m.lines[0].no_ripple_peer, + 'Trustline should have no_ripple_peer set'); + callback(); + }); + }, + function (callback) { + $.remote.requestAccountLines({ account: 'alice', peer: 'root' }, function(err, m) { + assert.ifError(err); + assert(Array.isArray(m.lines)); + assert(m.lines[0].no_ripple, + 'Trustline should have no_ripple set'); + callback(); + }); + }, + function (callback) { + $.remote.requestAccountLines({ account: 'root', peer: 'bob' }, function(err, m) { + assert.ifError(err); + assert(Array.isArray(m.lines)); + assert(!m.lines[0].no_ripple, + 'Trustline should not have no_ripple set'); + callback(); + }); + }, + function (callback) { + $.remote.requestAccountLines({ account: 'bob', peer: 'root' }, function(err, m) { + assert.ifError(err); + assert(Array.isArray(m.lines)); + assert(!m.lines[0].no_ripple_peer, + 'Trustline should not have no_ripple_peer set'); + callback(); + }); + } + ] + + async.series(steps, function(error) { + assert(!error, error); + done(); + }); + }); + +}); diff --git a/test/offer-test.js b/test/offer-test.js index 97a5f99c06..aaec3c1848 100644 --- a/test/offer-test.js +++ b/test/offer-test.js @@ -1909,7 +1909,7 @@ suite("Client Issue #535", function() { var starting_xrp = $.amount_for({ ledger_entries: 1, default_transactions: 2, - extra: '100.0' + extra: '100.1' }); testutils.create_accounts($.remote, "root", starting_xrp, ["alice", "bob", "mtgox"], callback); @@ -1938,12 +1938,16 @@ suite("Client Issue #535", function() { $.remote.transaction() .offer_create("alice", "100/XTS/mtgox", "100/XXX/mtgox") - .on('submitted', function (m) { - // console.log("proposed: offer_create: %s", json.stringify(m)); - callback(m.engine_result !== 'tesSUCCESS'); + .on('submitted', function(m) { + if (m.engine_result === 'tesSUCCESS') { + callback(); + } else { + // console.log("proposed: %s", JSON.stringify(m, undefined, 2)); + callback(m); + } - seq_carol = m.tx_json.sequence; - }) + seq_carol = m.tx_json.sequence; + }) .submit(); }, function (callback) { @@ -1982,13 +1986,12 @@ suite("Client Issue #535", function() { }, callback); }, - ], function (error) { - if (error) - //console.log("result: %s: error=%s", self.what, error); - assert(!error, self.what); - - done(); - }); + ], function (error) { + if (error) + //console.log("result: %s: error=%s", self.what, error); + assert(!error, self.what); + done(); + }); }); }); // vim:sw=2:sts=2:ts=8:et diff --git a/test/orderbook-test.js b/test/orderbook-test.js index 13cb634ed5..45dcb94a2a 100644 --- a/test/orderbook-test.js +++ b/test/orderbook-test.js @@ -1,11 +1,13 @@ var async = require('async'); -var assert = require('assert-diff')({strict:true}); +var assert = require('assert-diff'); var Account = require('ripple-lib').UInt160; var Remote = require('ripple-lib').Remote; var Transaction = require('ripple-lib').Transaction; var testutils = require('./testutils'); var config = testutils.init_config(); +assert.options.strict = true; + suite('Order Book', function() { var $ = { }; @@ -22,15 +24,15 @@ suite('Order Book', function() { var steps = [ function(callback) { - self.what = 'Create accounts'; + self.what = 'Create accounts'; - testutils.create_accounts( - $.remote, - 'root', - '20000.0', - [ 'mtgox', 'alice', 'bob' ], - callback - ); + testutils.create_accounts( + $.remote, + 'root', + '20000.0', + [ 'mtgox', 'alice', 'bob' ], + callback + ); }, function waitLedgers(callback) { @@ -49,7 +51,7 @@ suite('Order Book', function() { testutils.verify_balance( $.remote, [ 'mtgox', 'alice', 'bob' ], - '20000000000', + '19999999988', callback ); }, @@ -123,26 +125,27 @@ suite('Order Book', function() { ob.getOffers(function(err, offers) { assert.ifError(err); - // console.log('OFFERS', offers); + + //console.log('OFFERS', offers); var expected = [ - { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', - BookNode: '0000000000000000', - Flags: 0, - LedgerEntryType: 'Offer', - OwnerNode: '0000000000000000', - Sequence: 2, - TakerGets: { currency: 'USD', - issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - value: '10' - }, - TakerPays: '4000', - index: 'CD6AE78EE0A5438978501A0404D9093597F57B705D566B5070D58BD48F98468C', - owner_funds: '100', - is_fully_funded: true, - taker_gets_funded: '10', - taker_pays_funded: '4000' } + { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', + BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', + BookNode: '0000000000000000', + Flags: 0, + LedgerEntryType: 'Offer', + OwnerNode: '0000000000000000', + Sequence: 3, + TakerGets: { currency: 'USD', + issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', + value: '10' + }, + TakerPays: '4000', + index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61', + owner_funds: '100', + is_fully_funded: true, + taker_gets_funded: '10', + taker_pays_funded: '4000' } ] assert.deepEqual(offers, expected); @@ -178,19 +181,19 @@ suite('Order Book', function() { //console.log('OFFERS', offers); var expected = [ - { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', - BookNode: '0000000000000000', - Flags: 0, - LedgerEntryType: 'Offer', - OwnerNode: '0000000000000000', - Sequence: 2, - TakerGets: - { currency: 'USD', - issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - value: '5' }, + { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', + BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', + BookNode: '0000000000000000', + Flags: 0, + LedgerEntryType: 'Offer', + OwnerNode: '0000000000000000', + Sequence: 3, + TakerGets: + { currency: 'USD', + issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', + value: '5' }, TakerPays: '2000', - index: 'CD6AE78EE0A5438978501A0404D9093597F57B705D566B5070D58BD48F98468C', + index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61', owner_funds: '94.5', is_fully_funded: true, taker_gets_funded: '5', diff --git a/test/robust-transaction-test.js b/test/robust-transaction-test.js index 4bcd8becac..a5824dbee1 100644 --- a/test/robust-transaction-test.js +++ b/test/robust-transaction-test.js @@ -120,7 +120,7 @@ make_suite('Robust transaction submission', function() { function verifyBalance(callback) { self.what = 'Verify balance'; - testutils.verify_balance($.remote, 'bob', '20001000000', callback); + testutils.verify_balance($.remote, 'bob', '20000999988', callback); } ] @@ -205,7 +205,7 @@ make_suite('Robust transaction submission', function() { function verifyBalance(callback) { self.what = 'Verify balance'; - testutils.verify_balance($.remote, 'alice', '20001000000', callback); + testutils.verify_balance($.remote, 'alice', '20000999988', callback); } ] @@ -236,7 +236,7 @@ make_suite('Robust transaction submission', function() { function verifyBalance(callback) { self.what = 'Verify balance'; - testutils.verify_balance($.remote, 'alice', '20000000000', callback); + testutils.verify_balance($.remote, 'alice', '19999999988', callback); }, function submitTransaction(callback) { @@ -304,7 +304,7 @@ make_suite('Robust transaction submission', function() { function verifyBalance(callback) { self.what = 'Verify balance'; - testutils.verify_balance($.remote, 'alice', '20001000000', callback); + testutils.verify_balance($.remote, 'alice', '20000999988', callback); } ] @@ -368,7 +368,7 @@ make_suite('Robust transaction submission', function() { function verifyBalance(callback) { self.what = 'Verify balance'; - testutils.verify_balance($.remote, 'alice', '20001000000', callback); + testutils.verify_balance($.remote, 'alice', '20000999988', callback); } ] diff --git a/test/testutils.js b/test/testutils.js index c6b2a8da3e..292b094211 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -269,6 +269,18 @@ function account_dump(remote, account, callback) { // construct a json result }; +function set_account_flag(remote, account, options, callback) { + if (typeof options === 'number') { + options = { set_flag: options }; + } + + var tx = remote.createTransaction('AccountSet', extend({ + account: account + }, options)); + + submit_transaction(tx, callback); +} + exports.fund_account = fund_account = function(remote, src, account, amount, callback) { @@ -282,7 +294,7 @@ function(remote, src, account, amount, callback) { tx.once('proposed', function (result) { //console.log('proposed: %s', JSON.stringify(result)); - callback(result.engine_result === 'tesSUCCESS' ? null : new Error()); + callback(result.engine_result === 'tesSUCCESS' ? null : result); }); tx.once('error', function (result) { @@ -295,7 +307,14 @@ function(remote, src, account, amount, callback) { exports.create_account = create_account = -function(remote, src, account, amount, callback) { +function(remote, src, account, amount, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + options = extend({default_rippling: true}, options); + // Before creating the account, check if it exists in the ledger. // If it does, regardless of the balance, fail the test, because // the ledger is not in the expected state. @@ -307,25 +326,39 @@ function(remote, src, account, amount, callback) { }); info.once('error', function(result) { - if (result.error === "remoteError" && result.remote.error === "actNotFound") { - // rippled indicated the account does not exist. Create it by funding it. - fund_account(remote, src, account, amount, callback); - } else { - // Some other error occurred. Pass it up to the callback. - callback(result); + var isNotFoundError = result.error === 'remoteError' + && result.remote.error === 'actNotFound'; + + if (!isNotFoundError) { + return callback(result); } + + // rippled indicated the account does not exist. Create it by funding it. + fund_account(remote, src, account, amount, function(err) { + if (err) { + callback(err); + } else if (!options.default_rippling) { + callback(); + } else { + // Set default rippling on trustlines for account + set_account_flag(remote, account, 8, callback); + } + }); }); info.request(); } -function create_accounts(remote, src, amount, accounts, callback) { - assert.strictEqual(arguments.length, 5); +function create_accounts(remote, src, amount, accounts, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } remote.set_account_seq(src, 1); async.forEach(accounts, function (account, callback) { - create_account(remote, src, account, amount, callback); + create_account(remote, src, account, amount, options, callback); }, callback); }; @@ -629,6 +662,11 @@ function ledger_wait(remote, tx) { })(); }; +function submit_transaction(tx, callback) { + tx.submit(callback); + ledger_wait(tx.remote, tx); +} + exports.account_dump = account_dump; exports.build_setup = build_setup; exports.build_teardown = build_teardown; @@ -649,6 +687,7 @@ exports.verify_offer_not_found = verify_offer_not_found; exports.verify_owner_count = verify_owner_count; exports.verify_owner_counts = verify_owner_counts; exports.ledger_wait = ledger_wait; +exports.submit_transaction = submit_transaction; // Close up all unclosed servers on exit. process.on('exit', function() {