From e9381ddeb202fd815f97e1a0dd2344503e728c06 Mon Sep 17 00:00:00 2001 From: David Schwartz Date: Mon, 9 Mar 2015 13:43:58 -0700 Subject: [PATCH] Add "Default Ripple" account flag and associated logic: AccountSet set/clear, asfDefaultRipple = 8 AccountRoot flag, lsfDefaultRipple = 0x00800000 In trustCreate, set no ripple flag if appropriate. If an account does not have the default ripple flag set, new ripple lines created as a result of its offers being taken or people creating trust lines to it have no ripple set by that account's side automatically Trust lines can be deleted if the no ripple flag matches its default setting based on the account's default ripple setting. Fix default no-rippling in integration tests. --- package.json | 7 +- src/ripple/app/ledger/LedgerEntrySet.cpp | 16 ++- src/ripple/app/transactors/SetAccount.cpp | 9 ++ src/ripple/app/transactors/SetTrust.cpp | 6 +- src/ripple/protocol/LedgerFormats.h | 1 + src/ripple/protocol/TxFlags.h | 1 + test/freeze-test.coffee | 2 +- test/ledger-state.coffee | 10 +- test/no-ripple-test.js | 89 ++++++++++++ test/offer-test.js | 29 ++-- test/orderbook-test.js | 168 +++++++++++----------- test/robust-transaction-test.js | 10 +- test/testutils.js | 63 ++++++-- 13 files changed, 286 insertions(+), 125 deletions(-) diff --git a/package.json b/package.json index 329eb8713a..b644c415a1 100644 --- a/package.json +++ b/package.json @@ -2,13 +2,10 @@ "name": "rippled", "version": "0.0.1", "description": "Rippled Server", - "private": true, - "directories": { "test": "test" }, - "dependencies": { "ripple-lib": "0.8.2", "async": "~0.2.9", @@ -17,18 +14,16 @@ "deep-equal": "0.0.0" }, "devDependencies": { + "assert-diff": "^1.0.1", "coffee-script": "~1.6.3", "mocha": "~1.13.0" }, - "scripts": { "test": "mocha test/websocket-test.js test/server-test.js test/*-test.{js,coffee}" }, - "repository": { "type": "git", "url": "git://github.com/ripple/rippled.git" }, - "readmeFilename": "README.md" } diff --git a/src/ripple/app/ledger/LedgerEntrySet.cpp b/src/ripple/app/ledger/LedgerEntrySet.cpp index 61ac908636..a93b92d113 100644 --- a/src/ripple/app/ledger/LedgerEntrySet.cpp +++ b/src/ripple/app/ledger/LedgerEntrySet.cpp @@ -1342,6 +1342,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); @@ -1376,6 +1382,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); @@ -1507,7 +1519,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 85337a85ee..926f4b93f9 100644 --- a/src/ripple/app/transactors/SetAccount.cpp +++ b/src/ripple/app/transactors/SetAccount.cpp @@ -168,6 +168,15 @@ public: uFlagsOut &= ~lsfDisableMaster; } + if (uSetFlag == asfDefaultRipple) + { + uFlagsOut |= lsfDefaultRipple; + } + else if (uClearFlag == asfDefaultRipple) + { + uFlagsOut &= ~lsfDefaultRipple; + } + if (uSetFlag == asfNoFreeze) { m_journal.trace << "Set NoFreeze flag"; 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 1b8824f30e..a2d9235bbb 100644 --- a/test/freeze-test.coffee +++ b/test/freeze-test.coffee @@ -847,4 +847,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 b57f6f9d88..30c3eb57c1 100644 --- a/test/ledger-state.coffee +++ b/test/ledger-state.coffee @@ -497,8 +497,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 @@ -532,6 +533,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 4e62eadcc9..cd0086da8d 100644 --- a/test/orderbook-test.js +++ b/test/orderbook-test.js @@ -1,115 +1,115 @@ var async = require('async'); -var assert = require('assert'); +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(); - + suite('Order Book', function() { var $ = { }; - + setup(function(done) { testutils.build_setup().call($, done); }); - + teardown(function(done) { testutils.build_teardown().call($, done); }); - + test('Track offers', function (done) { var self = this; - + 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) { self.what = 'Wait ledger'; $.remote.once('ledger_closed', function() { callback(); }); - + $.remote.ledger_accept(); }, - + function verifyBalance(callback) { self.what = 'Verify balance'; testutils.verify_balance( $.remote, [ 'mtgox', 'alice', 'bob' ], - '20000000000', + '19999999988', callback ); }, - + function (callback) { self.what = 'Set transfer rate'; - + var tx = $.remote.transaction('AccountSet', { account: 'mtgox' }); - + tx.transferRate(1.1 * 1e9); - + tx.submit(function(err, m) { assert.ifError(err); assert.strictEqual(m.engine_result, 'tesSUCCESS'); callback(); }); - + testutils.ledger_wait($.remote, tx); }, - + function (callback) { self.what = 'Set limits'; - + testutils.credit_limits($.remote, { 'alice' : '1000/USD/mtgox', 'bob' : '1000/USD/mtgox' }, callback); }, - + function (callback) { self.what = 'Distribute funds'; - + testutils.payments($.remote, { 'mtgox' : [ '100/USD/alice', '50/USD/bob' ] }, callback); }, - + function (callback) { self.what = 'Create offer'; - + // get 4000/XRP pay 10/USD : offer pays 10 USD for 4000 XRP var tx = $.remote.transaction('OfferCreate', { account: 'alice', taker_pays: '4000', taker_gets: '10/USD/mtgox' }); - + tx.submit(function(err, m) { assert.ifError(err); assert.strictEqual(m.engine_result, 'tesSUCCESS'); callback(); }); - + testutils.ledger_wait($.remote, tx); }, - + function (callback) { self.what = 'Create order book'; @@ -118,60 +118,60 @@ suite('Order Book', function() { issuer_gets: Account.json_rewrite('mtgox'), currency_gets: 'USD' }); - + ob.on('model', function(){}); ob.getOffers(function(err, offers) { assert.ifError(err); //console.log('OFFERS', offers); - + var expected = [ - { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', - BookNode: '0000000000000000', - Flags: 0, - LedgerEntryType: 'Offer', - OwnerNode: '0000000000000000', - PreviousTxnID: offers[0].PreviousTxnID, - PreviousTxnLgrSeq: offers[0].PreviousTxnLgrSeq, - Sequence: 2, - TakerGets: { currency: 'USD', - issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - value: '10' - }, - TakerPays: '4000', - index: 'CD6AE78EE0A5438978501A0404D9093597F57B705D566B5070D58BD48F98468C', - owner_funds: '100', - quality: '400', - is_fully_funded: true, - taker_gets_funded: '10', - taker_pays_funded: '4000' } + { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', + BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', + BookNode: '0000000000000000', + Flags: 0, + LedgerEntryType: 'Offer', + OwnerNode: '0000000000000000', + PreviousTxnID: offers[0].PreviousTxnID, + PreviousTxnLgrSeq: offers[0].PreviousTxnLgrSeq, + Sequence: 3, + TakerGets: { currency: 'USD', + issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', + value: '10' + }, + TakerPays: '4000', + index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61', + owner_funds: '100', + quality: '400', + is_fully_funded: true, + taker_gets_funded: '10', + taker_pays_funded: '4000' } ] assert.deepEqual(offers, expected); - + callback(null, ob); }); }, function (ob, callback) { self.what = 'Create offer'; - + // get 5/USD pay 2000/XRP: offer pays 2000 XRP for 5 USD var tx = $.remote.transaction('OfferCreate', { account: 'bob', taker_pays: '5/USD/mtgox', taker_gets: '2000', }); - - tx.submit(function(err, m) { - assert.ifError(err); - assert.strictEqual(m.engine_result, 'tesSUCCESS'); - callback(null, ob); - }); - - testutils.ledger_wait($.remote, tx); + + tx.submit(function(err, m) { + assert.ifError(err); + assert.strictEqual(m.engine_result, 'tesSUCCESS'); + callback(null, ob); + }); + + testutils.ledger_wait($.remote, tx); }, - + function (ob, callback) { self.what = 'Check order book tracking'; @@ -180,21 +180,21 @@ suite('Order Book', function() { //console.log('OFFERS', offers); var expected = [ - { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', - BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', - BookNode: '0000000000000000', - Flags: 0, - LedgerEntryType: 'Offer', - OwnerNode: '0000000000000000', - PreviousTxnID: offers[0].PreviousTxnID, - PreviousTxnLgrSeq: offers[0].PreviousTxnLgrSeq, - Sequence: 2, - TakerGets: - { currency: 'USD', - issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', - value: '5' }, + { Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn', + BookDirectory: 'AE0A97F385FFE42E3096BA3F98A0173090FE66A3C2482FE0570E35FA931A0000', + BookNode: '0000000000000000', + Flags: 0, + LedgerEntryType: 'Offer', + OwnerNode: '0000000000000000', + PreviousTxnID: offers[0].PreviousTxnID, + PreviousTxnLgrSeq: offers[0].PreviousTxnLgrSeq, + Sequence: 3, + TakerGets: + { currency: 'USD', + issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v', + value: '5' }, TakerPays: '2000', - index: 'CD6AE78EE0A5438978501A0404D9093597F57B705D566B5070D58BD48F98468C', + index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61', owner_funds: '94.5', quality: '400', is_fully_funded: true, @@ -207,10 +207,10 @@ suite('Order Book', function() { }); }, ]; - - async.waterfall(steps, function (error) { - assert(!error, self.what + ': ' + error); - done(); - }); + + async.waterfall(steps, function (error) { + assert(!error, self.what + ': ' + error); + done(); + }); }); }); diff --git a/test/robust-transaction-test.js b/test/robust-transaction-test.js index f5814d99df..383eaaf7e4 100644 --- a/test/robust-transaction-test.js +++ b/test/robust-transaction-test.js @@ -133,7 +133,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); } ] @@ -218,7 +218,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); } ] @@ -249,7 +249,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) { @@ -317,7 +317,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); } ] @@ -381,7 +381,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 671ace017c..04931bbd3f 100644 --- a/test/testutils.js +++ b/test/testutils.js @@ -215,6 +215,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) { @@ -228,7 +240,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) { @@ -241,7 +253,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. @@ -253,25 +272,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); }; @@ -568,13 +601,18 @@ function ledger_wait(remote, tx) { ;(function nextLedger() { remote.once('ledger_closed', function() { if (!tx.finalized) { - setTimeout(nextLedger, isTravis ? 400 : 100); + setTimeout(nextLedger, isTravis ? 200 : 50); } }); remote.ledger_accept(); })(); }; +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; @@ -595,6 +633,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; process.on('uncaughtException', function() { Object.keys(server).forEach(function(host) {