diff --git a/test/ledger-state.coffee b/test/ledger-state.coffee index 5442022893..2257170e72 100644 --- a/test/ledger-state.coffee +++ b/test/ledger-state.coffee @@ -2,16 +2,18 @@ # This gives coffee-script proper file/lines in the exceptions -async = require("async") +async = require 'async' assert = require 'assert' -Amount = require("ripple-lib").Amount -Remote = require("ripple-lib").Remote -Seed = require("ripple-lib").Seed -Base = require("ripple-lib").Base -Transaction = require("ripple-lib").Transaction -sjcl = require("ripple-lib").sjcl -Server = require("./server").Server -testutils = require("./testutils") +{ + Amount + Remote + Seed + Base + Transaction + sjcl +} = require 'ripple-lib' +{Server} = require './server' +testutils = require './testutils' #################################### HELPERS ################################### @@ -21,14 +23,18 @@ exports.TestAccount = class TestAccount SHA256_RIPEMD160: (bits) -> sjcl.hash.ripemd160.hash sjcl.hash.sha256.hash(bits) - get_address: (passphrase) -> - key_pair = Seed.from_json(passphrase).get_key() + derive_pair: (passphrase) -> + seed = Seed.from_json(passphrase) + master_seed = seed.to_json() + key_pair = seed.get_key() pubKey = sjcl.codec.hex.toBits key_pair.to_hex_pub() - Base.encode_check(0,sjcl.codec.bytes.fromBits(@SHA256_RIPEMD160 pubKey)) + address = Base.encode_check(0, + sjcl.codec.bytes.fromBits(@SHA256_RIPEMD160 pubKey)) + [address, master_seed, key_pair] constructor: (passphrase) -> - @passphrase = passphrase - @address = @get_address(passphrase) + @passphrase = passphrase + [@address, @master_seed, @key_pair] = @derive_pair(passphrase) ############################# LEDGER STATE COMPILER ############################ @@ -46,25 +52,17 @@ exports.LedgerState = class LedgerState @realias_issuer = (str) -> str.replace(realias, (match) ->lookup[match]) parse_amount: (amt_val) -> - try - amt = Amount.from_json(amt_val) - if not amt.is_valid() - throw new Error() - catch e - try + amt = Amount.from_json(amt_val) + if not amt.is_valid() amt = Amount.from_human(amt_val) if not amt.is_valid() - throw new Error() - catch e - amt = null + amt = null amt amount_key: (amt) -> currency = amt.currency().to_json() issuer = @realias_issuer amt.issuer().to_json() - key = "#{currency}/#{issuer}" - key.issuer = issuer - key + "#{currency}/#{issuer}" apply: (context)-> @create_accounts_by_issuing_xrp_from_root(context) @@ -112,6 +110,8 @@ exports.LedgerState = class LedgerState "Trust amount #{amt_val} specified for #{account_id} is not valid" @record_trust(account_id, amt, false) + undefined + compile_accounts_balances_and_implicit_trusts: -> for account_id, account of @declaration.accounts xrp_balance = null @@ -133,6 +133,8 @@ exports.LedgerState = class LedgerState @assert xrp_balance, "No XRP balanced declared for #{account_id}" + undefined + compile_offers: -> for account_id, account of @declaration.accounts if not account.offers? @@ -147,28 +149,31 @@ exports.LedgerState = class LedgerState @assert pays_amt != null, "For account #{account_id} taker_pays amount #{pays} is invalid" - a_offers = @ensure(account_id, @offers_by_ci) - a_offers = @ensure(account_id, @offers_by_ci) - offers_all = @ensure('offers', a_offers, []) + offers = @ensure(account_id, @offers_by_ci) + offers = @ensure(account_id, @offers_by_ci) + offers_all = @ensure('offers', offers, []) if gets_amt.is_native() - total = a_offers.xrp_total ?= new Amount.from_json('0') + total = offers.xrp_total ?= new Amount.from_json('0') new_total = total.add(gets_amt) @assert @accounts[account_id].compareTo(new_total) != - 1, "Account #{account_id}s doesn't have enough xrp to place #{offer}" else key = @amount_key gets_amt - key_offers = @ensure(key, a_offers, {}) + + if key.split('/')[1] != account_id + key_offers = @ensure(key, offers, {}) - total = key_offers.total ?= Amount.from_json("0/#{key}") - new_total = total.add(gets_amt) - a_ious = @ensure(account_id, @ious) - @assert a_ious[key]?, - "Account #{account_id} doesn't hold any #{key}" - @assert a_ious[key].compareTo(new_total) != - 1, - "Account #{account_id} doesn't have enough #{key} to place #{offer}" + total = key_offers.total ?= Amount.from_json("0/#{key}") + new_total = total.add(gets_amt) + a_ious = @ensure(account_id, @ious) + @assert a_ious[key]?, + "Account #{account_id} doesn't hold any #{key}" + @assert a_ious[key].compareTo(new_total) != - 1, + "Account #{account_id} doesn't have enough #{key}" + " to place #{offer}" - key_offers.total = new_total + key_offers.total = new_total offers_all.push [pays_amt, gets_amt, splat...] @@ -180,11 +185,13 @@ exports.LedgerState = class LedgerState @offers.push sliced # @offers[account_id] = obj.offers + @offers + base_reserve: -> - @declaration.reserve?.base ? "50.0" + @declaration.reserve?.base ? "200.0" incr_reserve: -> - @declaration.reserve?.base ? "12.5" + @declaration.reserve?.base ? "50.0" check_reserves: -> base_reserve_amt = @base_reserve() @@ -203,11 +210,11 @@ exports.LedgerState = class LedgerState total_needed = base_reserve.clone() owner_count = 0 - a_offers = @offers_by_ci[account_id] - if a_offers? - if a_offers.xrp_total? - total_needed = total_needed.add a_offers.xrp_total - if a_offers.offers? + offers = @offers_by_ci[account_id] + if offers? + if offers.xrp_total? + total_needed = total_needed.add offers.xrp_total + if offers.offers? owner_count += @offers_by_ci[account_id].offers.length if @trusts_by_ci[account_id]? @@ -232,12 +239,16 @@ exports.LedgerState = class LedgerState src = @realias_issuer amt.issuer().to_json() dst = account_id @iou_payments.push [src, dst, amt] + + undefined format_trusts: -> for account_id, trusts of @trusts_by_ci for curr_issuer, amt of trusts @trusts.push [account_id, amt] + undefined + transactor: (fn, args_list, on_each, callback) -> if args_list.length == 0 return callback() @@ -256,9 +267,10 @@ exports.LedgerState = class LedgerState async.concatSeries(args_list, ((args, callback) => tx = @remote.transaction() - on_each?(args..., tx) - fn.apply(tx, args).on("proposed", (m) => - @assert m.engine_result is "tesSUCCESS", "Transactor failure: #{pretty_json m}" + fn.apply(tx, args) + on_each?(args..., tx) # after payment() offer_create() etc so set_flags works + tx.on("proposed", (m) => + @assert m.engine_result is "tesSUCCESS", "Transactor failure: #{@pretty_json m}" callback() ).on('final', (m) => finalized.one() @@ -270,7 +282,7 @@ exports.LedgerState = class LedgerState => testutils.ledger_close @remote, -> ) - requester: (fn, args_list, on_each, callback) -> + requester: (fn, args_list, on_each, callback, on_results) -> if not callback? callback = on_each on_each = null @@ -282,12 +294,16 @@ exports.LedgerState = class LedgerState on_each?(args..., req) req.on("success", (m) => if m.status? - @assert m.status is "success", "requester failure: #{pretty_json m}" - callback() + @assert m.status is "success", "requester failure: #{@pretty_json m}" + callback(null, m) ).on("error", (m) => - @assert false, pretty_json m + @assert false, @pretty_json m ).request() - ), -> callback()) + ), + (error, results_list) -> + on_results?(results_list); + callback() + ) ensure_config_has_test_accounts: -> for account of @declaration.accounts @@ -295,7 +311,7 @@ exports.LedgerState = class LedgerState acc = @config.accounts[account] = {} user = new TestAccount(account) acc.account = user.address - acc.secret = user.passphrase + acc.secret = user.master_seed # Index by nickname ... @remote.set_secret account, acc.secret # ... and by account ID @@ -333,7 +349,7 @@ exports.LedgerState = class LedgerState accounts = (k for k,ac of @accounts).sort() @remote.set_account_seq(seq, 1) for seq in accounts.concat 'root' # <-- - accounts_apply_arguments = ([ac] for ac in @accounts) + accounts_apply_arguments = ([ac] for ac, _ of @accounts) self = this async.waterfall [ @@ -367,9 +383,10 @@ exports.LedgerState = class LedgerState self.transactor( Transaction::offer_create, self.offers, - ((src, pays, gets) -> - LOG("Account `#{src}` is selling #{gets.to_text_full()} - for #{pays.to_text_full()}")), + ((src, pays, gets, tx) -> + tx.set_flags('Passive') + LOG("Account `#{src}` is selling #{gets.to_text_full()} + for #{pays.to_text_full()}")), cb) (cb) -> testutils.ledger_close self.remote, cb @@ -378,6 +395,31 @@ exports.LedgerState = class LedgerState ((acc) -> LOG("Checking account_lines for #{acc}")), cb) + (cb) -> + self.requester(Remote::request_account_offers, accounts_apply_arguments, + ((acc) -> + LOG("Checking account_offers for #{acc}")), + cb, (results) -> + + for [ac], ix in accounts_apply_arguments + account = self.declaration.accounts[ac] + offers_declared = (account.offers ? []).length + actual = results[ix].offers + offers_made = actual.length + if offers_made != offers_declared + shortened = [] + for offer in actual + keys = ['taker_pays', 'taker_gets'] + pair = (Amount.from_json(offer[k]).to_text_full() for k in keys) + shortened.push pair + + shortened_text = self.pretty_json shortened + self.assert offers_made == offers_declared, + "Account #{ac} has failed offer\n"+ + "Declared: #{pretty_json account.offers}\n"+ + "Actual: #{shortened_text}" + + ) (cb) -> self.requester(Remote::request_account_info, accounts_apply_arguments, ((acc) -> diff --git a/test/new-path-test.coffee b/test/new-path-test.coffee index 9a0806a730..25183c0d57 100644 --- a/test/new-path-test.coffee +++ b/test/new-path-test.coffee @@ -74,6 +74,7 @@ The tests are written in a declarative style: #################################### HELPERS ################################### assert = simple_assert + refute = (cond, msg) -> assert(!cond, msg) prettyj = pretty_json = (v) -> JSON.stringify(v, undefined, 2) @@ -131,7 +132,7 @@ expand_alternative = (alt) -> prev_issuer = hop.issuer else if hop.account? prev_issuer = hop.account - + return alt create_shorthand = (alternatives) -> @@ -268,7 +269,7 @@ test_alternatives_factory = (realias_pp, realias_text) -> [t_amt_txt, a_amt_txt] = amounts_text(t_amt, a_amt) # console.log typeof t_amt - + assert t_amt.equals(a_amt), "Expecting alternative[#{ti}].amount: "+ "#{t_amt_txt} == #{a_amt_txt}" @@ -300,8 +301,6 @@ test_alternatives_factory = (realias_pp, realias_text) -> create_path_test = (pth) -> return (done) -> - propagates = propagater done - self = this WHAT = self.log_what ledger = self.ledger @@ -319,7 +318,11 @@ create_path_test = (pth) -> one_message (m) -> sent = m error_info = (m, more) -> - info = path_expected: pth, path_find_request: sent, path_find_updates: m + info = + path_expected: pth, + path_find_request: sent, + path_find_updates: messages + extend(info, more) if more? ledger.pretty_json(info) @@ -337,6 +340,8 @@ create_path_test = (pth) -> max_seen = 0 messages = {} + propagates = propagater done + pf.on "error", propagates (m) -> # <-- assert false, "fail (error): #{error_info(m)}" done() @@ -402,17 +407,16 @@ skip_or_only = (title, test_or_suite) -> else test_or_suite +definer_factory = (group, title, path) -> + path.title = "#{[group, title].join('.')}" + test_func = skip_or_only path.title, test + -> + test_func(path.title, create_path_test(path) ) + gather_path_definers = (path_expected) -> tests = [] - for group, subgroup of path_expected for title, path of subgroup - definer_factory = (group, title, path) -> - path.title = "#{[group, title].join('.')}" - test_func = skip_or_only path.title, test - -> - test_func(path.title, create_path_test(path) ) - tests.push definer_factory(group, title, path) tests @@ -425,9 +429,9 @@ suite_factory = (declaration) -> @log_what = -> testutils.build_setup().call @, -> - context.ledger = new LedgerState(declaration.ledger, - assert, - context.remote, + context.ledger = new LedgerState(declaration.ledger, + assert, + context.remote, config) context.ledger.setup(context.log_what, done) @@ -456,6 +460,142 @@ path_finding_cases = JSON.parse path_finding_cases_string # gateway and holds its currency, and a destination that trusts the other. extend path_finding_cases, + "CNY test": + paths_expected: + BS: + P101: src: "SRC", dst: "GATEWAY_DST", send: "10.1/CNY/GATEWAY_DST", via: "XRP", n_alternatives: 1 + + ledger: + accounts: + SRC: + balance: ["4999.999898"] + trusts: [] + offers: [] + + GATEWAY_DST: + balance: ["10846.168060"] + trusts: [] + offers: [] + + MONEY_MAKER_1: + balance: ["4291.430036"] + trusts: [] + offers: [] + + MONEY_MAKER_2: + balance: [ + "106839375770" + "0.0000000003599/CNY/MONEY_MAKER_1" + "137.6852546843001/CNY/GATEWAY_DST" + ] + trusts: [ + "1001/CNY/MONEY_MAKER_1" + "1001/CNY/GATEWAY_DST" + ] + offers: [ + [ + "1000000" + "1/CNY/GATEWAY_DST" + # [] + ] + [ + "1/CNY/GATEWAY_DST" + "1000000" + # [] + ] + [ + "318000/CNY/GATEWAY_DST" + "53000000000" + # ["Sell"] + ] + [ + "209000000" + "4.18/CNY/MONEY_MAKER_2" + # [] + ] + [ + "990000/CNY/MONEY_MAKER_1" + "10000000000" + # ["Sell"] + ] + [ + "9990000/CNY/MONEY_MAKER_1" + "10000000000" + # ["Sell"] + ] + [ + "8870000/CNY/GATEWAY_DST" + "10000000000" + # ["Sell"] + ] + [ + "232000000" + "5.568/CNY/MONEY_MAKER_2" + # [] + ] + ] + + A1: + balance: [ + # "240.997150" + "1240.997150" + "0.0000000119761/CNY/MONEY_MAKER_1" + "33.047994/CNY/GATEWAY_DST" + ] + trusts: [ + "1000000/CNY/MONEY_MAKER_1" + "100000/USD/MONEY_MAKER_1" + "10000/BTC/MONEY_MAKER_1" + "1000/USD/GATEWAY_DST" + "1000/CNY/GATEWAY_DST" + ] + offers: [] + + A2: + balance: [ + "14115.046893" + "209.3081873019994/CNY/MONEY_MAKER_1" + "694.6251706504019/CNY/GATEWAY_DST" + ] + trusts: [ + "3000/CNY/MONEY_MAKER_1" + "3000/CNY/GATEWAY_DST" + ] + offers: [ + [ + "2000000000" + "66.8/CNY/MONEY_MAKER_1" + # [] + ] + [ + "1200000000" + "42/CNY/GATEWAY_DST" + # [] + ] + [ + "43.2/CNY/MONEY_MAKER_1" + "900000000" + # ["Sell"] + ] + ] + + A3: + balance: [ + "512087.883181" + "23.617050013581/CNY/MONEY_MAKER_1" + "70.999614649799/CNY/GATEWAY_DST" + ] + trusts: [ + "10000/CNY/MONEY_MAKER_1" + "10000/CNY/GATEWAY_DST" + ] + offers: [[ + "2240/CNY/MONEY_MAKER_1" + "50000000000" + # ["Sell"] + ]] + + "Path Tests (Bitstamp + SnapSwap account holders | liquidity provider with no offers)": ledger: accounts: