Eliminate npm tests (RIPD-1369)

Remove mention of npm tests in developer docs. Eliminate `npm test` from
automation and ci scripts.
This commit is contained in:
Mike Ellery
2016-12-21 15:02:52 -08:00
committed by Nik Bougalis
parent 698ea58b39
commit e3ff30657c
64 changed files with 16 additions and 24219 deletions

View File

@@ -9,7 +9,6 @@ url="https://github.com/ripple/rippled"
license=('custom:ISC')
depends=('protobuf' 'openssl' 'boost-libs')
makedepends=('git' 'scons' 'boost')
checkdepends=('nodejs')
backup=("etc/$pkgname/rippled.cfg")
source=("git://github.com/ripple/rippled.git#branch=master")
sha512sums=('SKIP')
@@ -26,8 +25,6 @@ build() {
check() {
cd "$srcdir/$pkgname"
npm install
npm test
build/rippled --unittest
}

View File

@@ -117,12 +117,6 @@ parser.add_argument(
help='Add a prefix for unit tests',
)
parser.add_argument(
'--nonpm', '-n',
action='store_true',
help='Do not run npm tests',
)
parser.add_argument(
'--clean', '-c',
action='store_true',
@@ -203,15 +197,6 @@ def run_tests(args):
if not ARGS.keep_going:
break
if not ARGS.nonpm:
print('npm tests for', target)
resultcode, lines = shell('npm', ('test', '--rippled=' + executable,))
if resultcode:
if not ARGS.verbose:
print('ERROR:\n', *lines, sep='')
failed.append([target, 'npm'])
if not ARGS.keep_going:
break
return failed

View File

@@ -25,7 +25,7 @@ if [ ${ubuntu_release} == "12.04" ]; then
add-apt-repository ppa:ubuntu-toolchain-r/test
apt-get update
apt-get -y upgrade
apt-get -y install curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties boost1.57-all-dev nodejs g++-5 g++-4.9
apt-get -y install curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties boost1.57-all-dev g++-5 g++-4.9
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 99 --slave /usr/bin/g++ g++ /usr/bin/g++-5
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 99 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9
exit 0
@@ -38,7 +38,7 @@ if [ ${ubuntu_release} == "14.04" ] || [ ${ubuntu_release} == "15.04" ]; then
add-apt-repository ppa:ubuntu-toolchain-r/test
apt-get update
apt-get -y upgrade
apt-get -y install curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties boost-all-dev nodejs g++-5 g++-4.9
apt-get -y install curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties boost-all-dev g++-5 g++-4.9
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 99 --slave /usr/bin/g++ g++ /usr/bin/g++-5
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 99 --slave /usr/bin/g++ g++ /usr/bin/g++-4.9
exit 0
@@ -47,7 +47,7 @@ fi
if [ ${ubuntu_release} == "16.04" ] || [ ${ubuntu_release} == "15.10" ]; then
apt-get update
apt-get -y upgrade
apt-get -y install python-software-properties curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties libboost-all-dev nodejs
apt-get -y install python-software-properties curl git scons ctags pkg-config protobuf-compiler libprotobuf-dev libssl-dev python-software-properties libboost-all-dev
exit 0
fi

View File

@@ -18,7 +18,6 @@ software components:
* (Optional) [Python and Scons](README.md#optional-install-python-and-scons)
* [OpenSSL Library](README.md#install-openssl)
* [Boost library](README.md#build-boost)
* [Node.js](README.md#install-nodejs)
## Install Software
@@ -242,9 +241,7 @@ and then choose the **Build->Build Solution** menu item.
# Unit Tests (Recommended)
## Internal
The internal rippled unit tests are written in C++ and are part
The rippled unit tests are written in C++ and are part
of the rippled executable.
From a Windows console, run the unit tests:
@@ -255,108 +252,4 @@ From a Windows console, run the unit tests:
Substitute the correct path to the executable to test different builds.
## External
The external rippled unit tests are written in Javascript using Node.js,
and utilize the mocha unit test framework. To run the unit tests, it
will be necessary to perform the following steps:
### Install Node.js
[Install Node.js](http://nodejs.org/download/). We recommend the Windows
installer (**.msi** file) as it takes care of updating the *PATH* environment
variable so that scripts can find the command. On Windows systems,
**Node.js** comes with **npm**. A separate installation of **npm**
is not necessary.
### Create node_modules
Open a windows console. From the root of your local rippled repository
directory, invoke **npm** to bring in the necessary components:
```
npm install
```
If you get an error that looks like
```
Error: ENOENT, stat 'C:\Users\username\AppData\Roaming\npm'
```
simply create the indicated folder and try again.
### Create a test config.js
From a *bash* shell (installed with Git for Windows), copy the
example configuration file into the appropriate location:
```
cp test/config-example.js test/config.js
```
Edit your version of test/config.js to reflect the correct path to the rippled executable:
```
exports.default_server_config = {
// Where to find the binary.
rippled_path: path.resolve(__dirname, "../build/msvc.debug/rippled.exe")
};
```
Also in **test/config.js**, change any occurrences of the
IP address *0.0.0.0* to *127.0.0.1*.
### Run Tests
From a windows console, run the unit tests:
```
npm test
```
Alternatively, run an individual test using mocha:
```
sh
node_modules/mocha/bin/mocha test/account_tx-test.js
```
* NOTE: The version of ripple-lib provided by the npm install
facility is usually slightly behind the develop branch of the
authoritative ripple-lib repository. Therefore, some tests might fail.
### Development ripple-lib
To use the latest branch of **ripple-lib** during the unit tests,
first clone the repository in a new location outside of your rippled
repository. Then update the submodules. After, run **npm install**
to set up the **node_modules** directory. Finally, install the
**grunt** command line tools required to run **grunt** and
build **ripple-lib**.
```
git clone git@github.com:ripple/ripple-lib.git
cd ripple-lib
git submodule update --init
npm install
npm install -g grunt-cli
grunt
```
Now link this version of **ripple-lib** into the global packages:
```
sudo npm link
```
To make rippled use the newly linked global **ripple-lib** package
instead of the one installed under **node_modules**, change
directories to the local rippled repository and delete the old
**ripple-lib** then link to the new one:
```
sh
rm -rf node_modules/ripple-lib
npm link ripple-lib
```

View File

@@ -18,7 +18,6 @@ these software components:
* [Homebrew](http://brew.sh/)
* [Git](http://git-scm.com/)
* [Scons](http://www.scons.org/)
* [Node.js](http://nodejs.org/download/)
## Install Software
@@ -175,68 +174,4 @@ rippled builds a set of unit tests into the server executable. To run these unit
tests after building, pass the `--unittest` option to the compiled `rippled`
executable. The executable will exit after running the unit tests.
## System Tests (Recommended)
The external rippled system tests are written in Javascript using Node.js, and
utilize the buster system test framework. To run the system tests, it will be
necessary to perform the following steps:
### Install Node.js
Install [Node.js](http://nodejs.org/download/). We recommend the macos
installer (`.pkg` file) since it takes care of updating the `PATH`
environment variable so that scripts can find the command. On macos systems,
`Node.js` comes with `npm`. A separate installation of `npm` is not
necessary.
### Create node_modules
From the root of your local rippled repository, invoke `npm` to
bring in the necessary components:
```
npm install
```
### Run Tests
```
npm test
```
### Development ripple-lib
If you want to use the latest branch of `ripple-lib` during the system tests:
1. clone the repository in a new location outside of your rippled repository.
2. update the submodules in that repo.
3. run `npm install` to set up the `node_modules` directory.
4. install the `grunt` command line tools required to run `grunt` and build `ripple-lib`.
i.e.:
```
git clone https://github.com/ripple/rippled.git
cd ripple-lib
git submodule update --init
npm install
npm install -g grunt-cli
grunt
```
Now link this version of `ripple-lib` into the global packages:
```
sudo npm link
```
To make rippled use the newly linked global `ripple-lib` package instead of
the one installed under `node_modules`, change directories to the local
rippled repository and delete the old `ripple-lib` then link to the new
one:
```
rm -rf node_modules/ripple-lib
npm link ripple-lib
```

View File

@@ -67,8 +67,6 @@ ISC license. See the LICENSE file for more details.
| ./Builds| Platform or IDE-specific project files. |
| ./doc | Documentation and example configuration files. |
| ./src | Source code. |
| ./test | Javascript / Mocha tests. |
Some of the directories under `src` are external repositories inlined via
git-subtree. See the corresponding README for more details.

View File

@@ -69,6 +69,7 @@ install:
echo "Download from $env:RIPPLED_DEPS_URL"
Start-FileDownload "$env:RIPPLED_DEPS_URL"
7z x "$($env:RIPPLED_DEPS_PATH).zip" -oC:\ -y > $null
if ($LastExitCode -ne 0) { throw "7z failed" }
}
# Newer DEPS include a versions file.
@@ -92,6 +93,7 @@ build_script:
if ($env:build -eq "scons") {
# Build with scons
scons $env:target -j%NUMBER_OF_PROCESSORS%
if ($LastExitCode -ne 0) { throw "scons build failed" }
}
else
{
@@ -102,14 +104,15 @@ build_script:
New-Item -ItemType Directory -Force -Path "build/$cmake_target"
Push-Location "build/$cmake_target"
cmake -G"Visual Studio 14 2015 Win64" -Dtarget="$cmake_target" ../..
if ($LastExitCode -ne 0) { throw "CMake failed" }
cmake --build . --config $env:buildconfig -- -m
if ($LastExitCode -ne 0) { throw "CMake build failed" }
Pop-Location
}
after_build:
- ps: |
if ($env:build -eq "scons") {
# Put our executable in a place where npm test can find it.
cp build/$($env:target)/rippled.exe build
ls build
$exe="build/rippled"
@@ -122,11 +125,10 @@ after_build:
test_script:
- ps: |
& {
# Run the rippled unit tests
& $exe --unittest
# Run the rippled integration tests
& npm install --progress=false
& npm test --rippled="$exe"
# https://connect.microsoft.com/PowerShell/feedback/details/751703/option-to-stop-script-if-command-line-exe-fails
if ($LastExitCode -ne 0) { throw "Unit tests failed" }
}

View File

@@ -89,8 +89,4 @@ if [[ $TARGET == "coverage" ]]; then
codecov -X gcov # don't even try and look for .gcov files ;)
fi
if [[ ${APP} == "rippled" ]]; then
# Run NPM tests
npm install --progress=false
npm test --rippled=$APP_PATH
fi

View File

@@ -53,11 +53,6 @@ if [ -x $HOME/bin/g++ ]; then
$HOME/bin/g++ -v
fi
# Avoid `spurious errors` caused by ~/.npm permission issues
# Does it already exist? Who owns? What permissions?
ls -lah ~/.npm || mkdir ~/.npm
# Make sure we own it
chown -Rc $USER ~/.npm
pip install --user https://github.com/codecov/codecov-python/archive/master.zip
bash bin/sh/install-boost.sh

View File

@@ -22,7 +22,3 @@ test:
override:
# Execute unit tests under gdb
- gdb -return-child-result -quiet -batch -ex "set env MALLOC_CHECK_=3" -ex "set print thread-events off" -ex run -ex "thread apply all backtrace full" -ex "quit" --args build/clang.debug/rippled --unittest
- npm install --progress=false
- |
echo "exports.default_server_config = {\"rippled_path\" : \"$HOME/rippled/build/clang.debug/rippled\"};" > test/config.js
- npm test

View File

@@ -1,31 +0,0 @@
{
"name": "rippled",
"version": "0.0.1",
"description": "Rippled Server",
"private": true,
"directories": {
"test": "test"
},
"dependencies": {
"assert-diff": "^1.0.1",
"async": "~0.2.9",
"babel": "^5.8.21",
"coffee-script": "^1.8.0",
"deep-equal": "0.0.0",
"extend": "~1.2.0",
"lodash": "^3.5.0",
"mocha": "^2.1.0",
"request": "^2.47.0",
"ripple-lib": "0.13.0-rc6.0",
"simple-jsonrpc": "~0.0.2"
},
"scripts": {
"pretest": "node test/pretest.js",
"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"
}

View File

@@ -1,92 +0,0 @@
# Integration tests
## Basic usage.
Documentation for installation of dependencies and running these
tests can be found with the
[_Rippled build instructions_][unit_testing].
(Also for [_Windows_][windows_unit_testing],
[_OS X_][osx_unit_testing],
or [_Ubuntu_][ubuntu_unit_testing].)
## Advanced usage.
These instructions assume familiarity with the instructions linked above.
### Debugging rippled
By default, each test will start and stop an independent instance of `rippled`.
This ensures that each test is run against the known
[_genesis ledger_][genesis_ledger].
To use a running `rippled`, particularly one running in a debugger, follow
these steps:
#### Setup
##### Using configuration files
1. Make a copy of the example configuration file: `cp -i test/config-example.js test/config.js`
2. Edit `test/config.js` to select the "debug" server configuration.
* Change the existing default server to: `exports.server_default = "debug";`
(near the top of the file).
3. Create a `rippled.cfg` file for the tests.
1. Run `npm test`. The tests will fail. **This failure is expected.**
2. Copy and/or rename the `tmp/server/debug/rippled.cfg` file to somewhere
convenient.
##### Using the command line
1. Create a `rippled.cfg` file for the tests.
1. Run `npm test --noserver`. The tests will fail. **This failure is expected.**
2. Copy and/or rename the `tmp/server/alpha/rippled.cfg` file to somewhere
convenient.
#### Running the tests.
1. Start `rippled` (in a debugger) with command line options
`-av --conf <rippled-created-above.cfg>`.
2. Set any desired breakpoints in the `rippled` source.
3. Running one test per [_genesis ledger_][genesis_ledger] is highly recommended.
If the relevant `.js` file contains more than one test, change `test(` to
`test.only(` for the single desired test.
* To run multiple tests, change `test(` to `test.skip(` for any undesired tests
in the .js file.
4. Start test(s) in the [_node-inspector_][node_inspector] debugger.
(The tests can be run without the debugger, but there will probably
be problems with timeouts or reused ledgers).
1. `node_modules/node-inspector/bin/inspector.js &`
2. `node node_modules/.bin/mocha --debug --debug-brk test/<testfile.js>`
3. Browse to http://127.0.0.1:8080/debug?port=5858 in a browser supported
by [_node-inspector_][node_inspector] (i.e. Chrome or Safari).
5. To run multiple tests (not recommended), put a breakpoint in the following function:
* File `testutils.js` -> function `build_teardown()` -> nested function
`teardown()` -> nested series function `stop_server()`.
* When this breakpoint is hit, stop and restart `rippled`.
6. Use the [_node-inspector UI_][node_inspector_ui] to step through and run
the test(s) until control is handed off to `rippled`. When the request is
finished control will be handed back to node-inspector, which may or may not
stop depending on which breakpoints are set.
### After debugging using configuration files.
With the command line `--noserver` flag, this step is unnecessary.
1. To return to the default behavior, edit `test/config.js` and change the
default server back to its original value: `exports.server_default = "alpha";`.
* Alternately, delete `test/config.js`.
[unit_testing]: https://wiki.ripple.com/Rippled_build_instructions#Unit_testing
[windows_unit_testing]: https://wiki.ripple.com/Visual_Studio_2013_Build_Instructions#Unit_Tests_.28Recommended.29
[osx_unit_testing]: https://wiki.ripple.com/OSX_Build_Instructions#System_Tests_.28Recommended.29
[ubuntu_unit_testing]: https://wiki.ripple.com/Ubuntu_build_instructions#System_Tests_.28Recommended.29
[genesis_ledger]: https://wiki.ripple.com/Genesis_ledger
[node_inspector]: https://wiki.ripple.com/Rippled_build_instructions#node-inspector
[node_inspector_ui]: https://github.com/node-inspector/node-inspector/blob/master/README.md

View File

@@ -1,289 +0,0 @@
/* -------------------------------- REQUIRES -------------------------------- */
var async = require('async');
var assert = require('assert-diff');
var lodash = require('lodash');
var Remote = require('ripple-lib').Remote;
var Request = require('ripple-lib').Request;
var Account = require('ripple-lib').UInt160;
var testutils = require('./testutils');
var LedgerState = require('./ledger-state').LedgerState;
var config = testutils.init_config();
// We just use equal instead of strictEqual everywhere.
assert.options.strict = true;
/* --------------------------------- HELPERS -------------------------------- */
function noop() {}
function account_objects(remote, account, params, callback) {
if (lodash.isFunction(params)) {
callback = params;
params = null;
}
var request = new Request(remote, 'account_objects');
request.message.account = Account.json_rewrite(account);
lodash.forOwn(params || {}, function(v, k) { request.message[k] = v; });
request.callback(callback);
}
function filter_threading_fields(entries) {
return entries.map(function(entry) {
return lodash.omit(entry, ['PreviousTxnID', 'PreviousTxnLgrSeq']);
});
}
/* ---------------------------------- TEST ---------------------------------- */
suite('account_objects', function() {
// A declarative description of the ledger
var ledger_state = {
accounts: {
// Gateways
G1 : {balance: ["1000.0"]},
G2 : {balance: ["1000.0"]},
// Bob has two RippleState and two Offer account objects.
bob : {
balance: ["1000.0", "1000/USD/G1",
"1000/USD/G2"],
// these offers will be in `Sequence`
offers: [["100.0", "1/USD/bob"],
["100.0", "1/USD/G1"]]
}
}
};
// build_(setup|teardown) utils functions set state on this context var.
var context = {};
// After setup we bind the remote to `account_objects` helper above.
var request_account_objects;
var bob;
var G1;
var G2;
// This runs only once
suiteSetup(function(done) {
testutils.build_setup().call(context, function() {
request_account_objects = account_objects.bind(null, context.remote);
var ledger = new LedgerState(ledger_state,
assert, context.remote,
config);
// Get references to the account objects for usage later.
bob = Account.json_rewrite('bob');
G1 = Account.json_rewrite('G1');
G2 = Account.json_rewrite('G2');
// Run the ledger setup util. This compiles the declarative description
// into a series of transactions and executes them.
ledger.setup(noop /*logger*/, function(){
done();
})
});
});
suiteTeardown(function(done) {
testutils.build_teardown().call(context, done);
});
// With PreviousTxnID, PreviousTxnLgrSeq omitted.
var bobs_objects = [
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1000"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value": "1000"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value": "0"
},
"LowNode": "0000000000000000",
"index":
"D89BC239086183EB9458C396E643795C1134963E6550E682A190A5F021766D43"
},
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-1000"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value": "1000"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "r9cZvwKU3zzuZK9JFovGg1JC5n7QiqNL8L",
"value": "0"
},
"LowNode": "0000000000000000",
"index":
"D13183BCFFC9AAC9F96AEBB5F66E4A652AD1F5D10273AEB615478302BEBFD4A4"
},
{
"Account": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory":
"50AD0A9E54D2B381288D535EB724E4275FFBF41580D28A925D038D7EA4C68000",
"BookNode": "0000000000000000",
"Flags": 65536,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"Sequence": 4,
"TakerGets": {
"currency": "USD",
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value": "1"
},
"TakerPays": "100000000",
"index":
"A984D036A0E562433A8377CA57D1A1E056E58C0D04818F8DFD3A1AA3F217DD82"
},
{
"Account": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"BookDirectory":
"B025997A323F5C3E03DDF1334471F5984ABDE31C59D463525D038D7EA4C68000",
"BookNode": "0000000000000000",
"Flags": 65536,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"Sequence": 5,
"TakerGets": {
"currency": "USD",
"issuer": "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value": "1"
},
"TakerPays": "100000000",
"index":
"CAFE32332D752387B01083B60CC63069BA4A969C9730836929F841450F6A718E"
}
]
test('stepped 1 at a time using marker/limit', function(done) {
// We step through bob's account objects one at a time by using `limit` and
// `marker` and for each object we see, we `push` them onto this array so we
// can later check it against an un`limit`ed request.
var objects_stepped = [];
var steps = [
function first_ripple_state(next) {
request_account_objects('bob', {limit: 1}, function(e, m) {
assert.ifError(e);
var objects = m.account_objects;
var ripple_state = m.account_objects[0];
assert.equal(m.limit, 1);
assert.equal(objects.length, 1);
assert.equal(ripple_state.LedgerEntryType, 'RippleState');
assert.equal(ripple_state.HighLimit.issuer, bob);
assert.equal(ripple_state.LowLimit.issuer, G1);
objects_stepped.push(ripple_state);
next(null, m.marker);
});
},
function second_ripple_state(resume_marker, next) {
request_account_objects('bob', {limit: 1, marker: resume_marker},
function(e, m) {
assert.ifError(e);
var objects = m.account_objects;
var ripple_state = m.account_objects[0];
assert.equal(m.limit, 1);
assert.equal(objects.length, 1);
assert.equal(ripple_state.LedgerEntryType, 'RippleState');
assert.equal(ripple_state.HighLimit.issuer, bob);
assert.equal(ripple_state.LowLimit.issuer, G2);
objects_stepped.push(ripple_state);
next(null, m.marker);
});
},
function first_offer(resume_marker, next) {
request_account_objects('bob', {limit: 1, marker: resume_marker},
function(e, m) {
assert.ifError(e);
var objects = m.account_objects;
var offer = m.account_objects[0];
assert.equal(m.limit, 1);
assert.equal(objects.length, 1);
assert.equal(offer.LedgerEntryType, 'Offer');
assert.equal(offer.TakerGets.issuer, bob);
assert.equal(offer.Account, bob);
objects_stepped.push(offer);
next(null, m.marker);
});
},
function second_offer(resume_marker, next) {
request_account_objects('bob', {limit: 1, marker: resume_marker},
function(e, m) {
assert.ifError(e);
var objects = m.account_objects;
var offer = m.account_objects[0];
assert.equal(objects.length, 1);
assert.equal(offer.Account, bob);
assert.equal(offer.LedgerEntryType, 'Offer');
assert.equal(offer.TakerGets.issuer, G1);
assert.equal(m.marker, undefined);
objects_stepped.push(offer);
next();
});
},
];
async.waterfall(steps, function (err, result) {
assert.ifError(err);
var filtered = filter_threading_fields(objects_stepped);
// Compare against a known/inspected exchaustive response.
assert.deepEqual(filtered, bobs_objects);
done();
});
});
test('unstepped', function(done) {
request_account_objects('bob', function(e, m){
var objects = m.account_objects;
assert.equal(m.marker, undefined);
var filtered = filter_threading_fields(objects);
// Compare against a known/inspected exchaustive response.
assert.deepEqual(filtered, bobs_objects);
done();
});
});
});

View File

@@ -1,91 +0,0 @@
/* -------------------------------- REQUIRES -------------------------------- */
var assert = require('assert-diff');
var lodash = require('lodash');
var testutils = require('./testutils');
var LedgerState = require('./ledger-state').LedgerState;
var config = testutils.init_config();
// We just use equal instead of strictEqual everywhere.
assert.options.strict = true;
/* ---------------------------------- TEST ---------------------------------- */
function makeSuite(name, ledger_state, tests) {
suite(name, function() {
// build_(setup|teardown) utils functions set state on this context var.
var context = {};
// This runs only once
suiteSetup(function(done) {
testutils.build_setup().call(context, function() {
var ledger = new LedgerState(ledger_state,
assert, context.remote,
config);
// Run the ledger setup util. This compiles the declarative description
// into a series of transactions and executes them.
ledger.setup(lodash.noop /*logger*/, function(){
done();
})
});
});
suiteTeardown(function(done) {
testutils.build_teardown().call(context, done);
});
lodash.forOwn(tests, function(func, name) {
test(name, function(done) {
func.call(this, context.remote, context, done);
});
});
});
}
makeSuite (
'account_offers',
{
accounts: {
G1 : {balance: ["1000.0"]},
bob : {
balance: ["1000.0", "1000/USD/G1"],
// these offers will be in `Sequence`
offers: [["100.0", "1/USD/bob"],
["100.0", "1/USD/G1"],
["10.0", "2/USD/G1"]]
}
}
},
{
quality: function(remote, _, done) {
remote.requestAccountOffers({account: 'bob'}, function(err, response) {
var expected = [
{"flags": 65536,
"quality": "100000000",
"seq": 3,
"taker_gets": {"currency": "USD",
"issuer": "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
"value": "1"},
"taker_pays": "100000000"},
{"flags": 65536,
"quality": "100000000",
"seq": 4,
"taker_gets": {"currency": "USD",
"issuer": "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value": "1"},
"taker_pays": "100000000"},
{"flags": 65536,
"quality": "5000000",
"seq": 5,
"taker_gets": {"currency": "USD",
"issuer": "r32rQHyesiTtdWFU7UJVtff4nCR5SHCbJW",
"value": "2"},
"taker_pays": "10000000"}];
assert.deepEqual(response.offers, expected);
done();
});
}}
);

View File

@@ -1,275 +0,0 @@
var async = require('async');
var assert = require('assert');
var Remote = require('ripple-lib').Remote;
var testutils = require('./testutils');
var config = testutils.init_config();
suite('Account set', function() {
var $ = { };
setup(function(done) {
testutils.build_setup().call($, function() {
$.remote.local_signing = true;
done();
});
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('null AccountSet', function(done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Send null AccountSet';
var transaction = $.remote.transaction().accountSet('root');
transaction.setFlags(0);
transaction.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
callback();
});
transaction.submit();
},
function (callback) {
self.what = 'Check account flags';
$.remote.requestAccountFlags('root', 'current', function(err, m) {
assert.ifError(err);
assert.strictEqual(m, 0);
done();
});
}
]
async.series(steps, function(err) {
assert(!err, self.what + ': ' + err);
done();
});
});
test('set RequireDestTag', function(done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Set RequireDestTag.';
$.remote.transaction()
.account_set('root')
.set_flags('RequireDestTag')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
if (m.engine_result === 'tesSUCCESS') {
callback(null);
} else {
//console.log(m);
callback(new Error(m.engine_result));
}
})
.submit();
},
function (callback) {
self.what = 'Check RequireDestTag';
$.remote.request_account_flags('root', 'current')
.on('success', function (m) {
var wrong = !(m.node.Flags & Remote.flags.account_root.RequireDestTag);
if (wrong) {
//console.log('Set RequireDestTag: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(wrong) : null);
})
.request();
},
function (callback) {
self.what = 'Clear RequireDestTag.';
$.remote.transaction()
.account_set('root')
.set_flags('OptionalDestTag')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : m.engine_result);
})
.submit();
},
function (callback) {
self.what = 'Check No RequireDestTag';
$.remote.request_account_flags('root', 'current')
.on('success', function (m) {
var wrong = !!(m.node.Flags & Remote.flags.account_root.RequireDestTag);
if (wrong) {
console.log('Clear RequireDestTag: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(m) : null);
})
.request();
}
]
async.waterfall(steps,function (error) {
assert(!error, self.what + ': ' + error);
done();
});
});
test('set RequireAuth', function (done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Set RequireAuth.';
$.remote.transaction()
.account_set('root')
.set_flags('RequireAuth')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error(m));
})
.submit();
},
function (callback) {
self.what = 'Check RequireAuth';
$.remote.request_account_flags('root', 'current')
.on('error', callback)
.on('success', function (m) {
var wrong = !(m.node.Flags & Remote.flags.account_root.RequireAuth);
if (wrong) {
console.log('Set RequireAuth: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(m) : null);
})
.request();
},
function (callback) {
self.what = 'Clear RequireAuth.';
$.remote.transaction()
.account_set('root')
.set_flags('OptionalAuth')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result !== 'tesSUCCESS');
})
.submit();
},
function (callback) {
self.what = 'Check No RequireAuth';
$.remote.request_account_flags('root', 'current')
.on('error', callback)
.on('success', function (m) {
var wrong = !!(m.node.Flags & Remote.flags.account_root.RequireAuth);
if (wrong) {
console.log('Clear RequireAuth: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(m) : null);
})
.request();
}
// XXX Also check fails if something is owned.
]
async.waterfall(steps, function(error) {
assert(!error, self.what + ': ' + error);
done();
});
});
test('set DisallowXRP', function(done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Set DisallowXRP.';
$.remote.transaction()
.account_set('root')
.set_flags('DisallowXRP')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error(m));
})
.submit();
},
function (callback) {
self.what = 'Check DisallowXRP';
$.remote.request_account_flags('root', 'current')
.on('error', callback)
.on('success', function (m) {
var wrong = !(m.node.Flags & Remote.flags.account_root.DisallowXRP);
if (wrong) {
console.log('Set RequireDestTag: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(m) : null);
})
.request();
},
function (callback) {
self.what = 'Clear DisallowXRP.';
$.remote.transaction()
.account_set('root')
.set_flags('AllowXRP')
.on('submitted', function (m) {
//console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error(m));
})
.submit();
},
function (callback) {
self.what = 'Check AllowXRP';
$.remote.request_account_flags('root', 'current')
.on('error', callback)
.on('success', function (m) {
var wrong = !!(m.node.Flags & Remote.flags.account_root.DisallowXRP);
if (wrong) {
console.log('Clear DisallowXRP: failed: %s', JSON.stringify(m));
}
callback(wrong ? new Error(m) : null);
})
.request();
}
]
async.waterfall(steps, function(err) {
assert(!err);
done();
});
});
});

View File

@@ -1,395 +0,0 @@
////var assert = require('assert');
////var async = require("async");
////var extend = require('extend');
////var Amount = require("ripple-lib").Amount;
////var Remote = require("ripple-lib").Remote;
////var Transaction = require("ripple-lib").Transaction;
////var RippleError = require("ripple-lib").RippleError;
////var Server = require("./server").Server;
////var testutils = require("./testutils");
////var config = testutils.init_config();
////
////// Hard-coded limits we'll be testing:
////var BINARY_LIMIT = 500;
////var NONBINARY_LIMIT = 200;
////
////var ACCOUNT = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
////var OFFSET = 180;
////var LIMIT = 170;
////
////var FIRST_BATCH = 199; // Within both limits
////var SECOND_BATCH = 10; // Between NONBINARY_LIMIT and BINARY_LIMIT
////var THIRD_BATCH = 295; // Exceeds both limits
////var VERBOSE = false;
////
////
////suite('Account_tx tests', function() {
//// var $ = { };
////
//// setup(function(done) {
//// testutils.build_setup().call($, done);
//// });
////
//// teardown(function(done) {
//// testutils.build_teardown().call($, done);
//// });
////
//// test('make many transactions and query using account_tx', function(done) {
//// var root_id = $.remote.account('root')._account_id;
//// $.remote.request_subscribe().accounts(root_id).request();
////
//// var self = this;
//// var transactionCounter = 0;
//// var final_create;
////
//// function createOffer(callback) {
//// var tx = $.remote.transaction();
//// tx.offer_create("root", "500", "100/USD/root");
////
//// tx.once('proposed', function(m) {
//// $.remote.ledger_accept();
//// });
////
//// tx.once('error', callback);
////
//// tx.once('final', function(m) {
//// callback();
//// });
////
//// tx.submit();
//// };
////
//// function lotsOfTransactions(count, callback) {
//// ;(function nextOffer(i) {
//// createOffer(function(err) {
//// console.log(i, count);
//// if (err) callback(err);
//// else if (++i === count) callback(null);
//// else nextOffer(i);
//// });
//// })(0);
//// };
////
//// function firstBatch() {
//// lotsOfTransactions(FIRST_BATCH, function(a, b) {
//// runTests(self, FIRST_BATCH, 0, void(0), function() {
//// console.log('2');
//// runTests(self, FIRST_BATCH, OFFSET, void(0), function() {
//// console.log('3');
//// runTests(self, FIRST_BATCH, -1, LIMIT, secondBatch);
//// })
//// })});
//// }
////
//// function secondBatch() {
//// lotsOfTransactions(SECOND_BATCH, function() {
//// runTests(self, FIRST_BATCH+SECOND_BATCH, 0, void(0), function() {
//// runTests(self, FIRST_BATCH+SECOND_BATCH, OFFSET, void(0), thirdBatch);
//// })});
//// }
////
//// function thirdBatch() {
//// lotsOfTransactions(THIRD_BATCH, function() {
//// runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, 0, void(0), function() {
//// runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, OFFSET, void(0), done);
//// })});
//// }
////
//// firstBatch();
////
//// function errorHandler(callback) {
//// return function(r) {
//// if (VERBOSE) console.log("ERROR!");
//// if (VERBOSE) console.log(r);
//// callback(r);
//// }
//// }
////
//// function txOptions(ext) {
//// var defaults = {
//// account: ACCOUNT,
//// ledger_index_min: -1,
//// ledger_index_max: -1
//// }
//// return extend(defaults, ext);
//// };
////
//// function sortByLedger(a, b) {
//// assert(a.tx, 'Transaction missing');
//// assert(b.tx, 'Transaction missing');
//// if (a.tx.inLedger > b.tx.inLedger) {
//// return 1;
//// } else if (a.tx.inLedger < b.tx.inLedger) {
//// return -1;
//// } else {
//// return 0;
//// }
//// };
////
//// function runTests(self, transactionCount, offset, limit, callback) {
//// var steps = [
//// function(callback) {
//// if (VERBOSE) console.log('nonbinary');
////
//// var req = $.remote.request_account_tx(txOptions({ offset: offset, limit: limit }));
////
//// req.callback(function(err, r) {
//// if (err) return callback(err);
//// assert(r && r.transactions);
//// var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit, transactionCount - offset) : transactionCount - offset);
//// assert.strictEqual(r.transactions.length, targetLength, 'Transactions unexpected length');
//// //assert.deepEqual(r.transactions.sort(sortByLedger), r.transactions, 'Transactions out of order');
//// callback();
//// });
//// },
////
//// function(callback) {
//// if (VERBOSE) console.log('binary');
////
//// var req = $.remote.request_account_tx(txOptions({ binary: true, offset: offset, limit: limit }));
////
//// req.callback(function(err, r) {
//// if (err) return callback(err);
//// assert(r && r.transactions);
//// var targetLength = Math.min(BINARY_LIMIT, limit ? Math.min(limit, transactionCount-offset) : transactionCount-offset);
//// assert.strictEqual(r.transactions.length, targetLength, 'Transactions unexpected length');
//// //assert.deepEqual(r.transactions.sort(sortByLedger), r.transactions, 'Transactions out of order');
//// callback();
//// });
//// },
////
//// function(callback) {
//// if (VERBOSE) console.log('nonbinary+offset');
////
//// var req = $.remote.request_account_tx(txOptions({ descending: true, offset: offset, limit: limit }));
////
//// req.callback(function(err, r) {
//// if (err) return callback(err);
//// assert(r && r.transactions);
//// var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit,transactionCount-offset) : transactionCount-offset );
//// assert.strictEqual(r.transactions.length, targetLength, 'Transactions unexpected length');
//// //assert.deepEqual(r.transactions.sort(sortByLedger), r.transactions, 'Transactions out of order');
//// callback();
//// });
//// }
//// ]
////
//// async.series(steps, function(error) {
//// console.log(error);
//// assert.ifError(error);
//// callback();
//// });
//// }
//// });
////});
//=======
//var async = require("async");
//var buster = require("buster");
//
//var Amount = require("ripple-lib").Amount;
//var Remote = require("ripple-lib").Remote;
//var Transaction = require("ripple-lib").Transaction;
//var Server = require("./server").Server;
//
//var testutils = require("./testutils");
//var config = testutils.init_config();
//
//buster.testRunner.timeout = 350000; //This is a very long test!
//
//
//// Hard-coded limits we'll be testing:
//var BINARY_LIMIT = 500;
//var NONBINARY_LIMIT = 200;
//
//var ACCOUNT = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
//var FIRST_BATCH = 199; // Within both limits
//var OFFSET = 180;
//var LIMIT = 170;
//var SECOND_BATCH = 10; // Between NONBINARY_LIMIT and BINARY_LIMIT
//var THIRD_BATCH = 295; // Exceeds both limits
//var VERBOSE = false;
//
//buster.testCase("//Account_tx tests", {
// 'setUp' : testutils.build_setup(),
// 'tearDown' : testutils.build_teardown(),
//
// "make a lot of transactions and query using account_tx" : function (done) {
// var self = this;
// var final_create;
// var transactionCounter = 0;
// var f = 0;
// var functionHolder;
// var createOfferFunction = function (callback) {
// self.remote.transaction()
// .offer_create("root", "500", "100/USD/root")
// .on('proposed', function (m) {
// transactionCounter++;
// if (VERBOSE) console.log('Submitted transaction', transactionCounter);
//
// callback(m.result !== 'tesSUCCESS');
// })
// .on('final', function (m) {
// f++;
// if (VERBOSE) console.log("Finalized transaction", f);
// buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
// buster.assert(final_create);
// if ( f == transactionCounter ) {
// if (VERBOSE) console.log("All transactions have been finalized.");
// functionHolder();
// }
// })
// .submit();
// };
//
// function lotsOfTransactions(number, whenDone) {
// var bunchOfOffers = [];
// for (var i=0; i<number; i++) {
// bunchOfOffers.push(createOfferFunction);
// }
// functionHolder = whenDone; //lolwut
// async.parallel(bunchOfOffers, function (error) {
// if (VERBOSE) console.log("Accepting ledger.");
// buster.refute(error);
// self.remote
// .once('ledger_closed', function (message) {
// final_create = message;
// })
// .ledger_accept();
// });
// }
//
// function firstBatch() {
// lotsOfTransactions(FIRST_BATCH,
// function(){runTests(self, FIRST_BATCH, 0, undefined,
// function(){runTests(self, FIRST_BATCH, OFFSET, undefined,
// function(){runTests(self, FIRST_BATCH, 0, LIMIT, secondBatch)})}
// )});
// }
//
// function secondBatch() {
// lotsOfTransactions(SECOND_BATCH,
// function(){runTests(self, FIRST_BATCH+SECOND_BATCH, 0, undefined,
// function(){runTests(self, FIRST_BATCH+SECOND_BATCH, OFFSET, undefined, thirdBatch)}
// )});
// }
//
// function thirdBatch() {
// lotsOfTransactions(THIRD_BATCH,
// function(){runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, 0, undefined,
// function(){runTests(self, FIRST_BATCH+SECOND_BATCH+THIRD_BATCH, OFFSET, undefined, done)}
// )});
// }
//
// firstBatch();
//
//
// function standardErrorHandler(callback) {
// return function(r) {
// if (VERBOSE) console.log("ERROR!");
// if (VERBOSE) console.log(r);
// callback(r);
// }
// }
//
//
// function runTests(self, actualNumberOfTransactions, offset, limit, finalCallback) {
// if (VERBOSE) console.log("Testing batch with offset and limit:", offset, limit);
// async.series([
// function(callback) {
// if (VERBOSE) console.log('nonbinary');
// self.remote.request_account_tx({
// account:ACCOUNT,
// ledger_index_min:-1,
// ledger_index_max:-1,
// offset:offset,
// limit:limit
// }).on('success', function (r) {
// if (r.transactions) {
// var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset);
// buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
// //Check for proper ordering.
// for (var i=0; i<r.transactions.length-1; i++) {
// var t1 = r.transactions[i].tx;
// var t2 = r.transactions[i+1].tx;
// buster.assert(t1.inLedger<=t2.inLedger,
// "Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.Sequence+" should not have come before "+t2.inLedger+"#"+t2.Sequence);
// }
// } else {
// buster.assert(r.transactions, "No transactions returned: "+offset+" "+limit);
// }
//
// callback(false);
// })
// .on('error', standardErrorHandler(callback))
// .request();
// },
//
// function(callback) {
// if (VERBOSE) console.log('binary');
// self.remote.request_account_tx({
// account:ACCOUNT,
// ledger_index_min:-1,
// ledger_index_max:-1,
// binary:true,
// offset:offset,
// limit:limit
// }).on('success', function (r) {
// if (r.transactions) {
// var targetLength = Math.min(BINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset);
// buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
// } else {
// buster.assert(r.transactions, "No transactions returned: "+offset+" "+limit);
// }
// callback(false);
// })
// .on('error', standardErrorHandler(callback))
// .request();
// },
//
// function(callback) {
// if (VERBOSE) console.log('nonbinary+offset');
// self.remote.request_account_tx({
// account:ACCOUNT,
// ledger_index_min:-1,
// ledger_index_max:-1,
// descending:true,
// offset:offset,
// limit:limit
// }).on('success', function (r) {
// if (r.transactions) {
// var targetLength = Math.min(NONBINARY_LIMIT, limit ? Math.min(limit,actualNumberOfTransactions-offset) : actualNumberOfTransactions-offset );
// buster.assert(r.transactions.length == targetLength, "Got "+r.transactions.length+" transactions; expected "+targetLength );
// //Check for proper ordering.
// for (var i=0; i<r.transactions.length-1; i++) {
// var t1 = r.transactions[i].tx;
// var t2 = r.transactions[i+1].tx;
// //buster.assert(t1.inLedger>t2.inLedger || (t1.inLedger==t2.inLedger && t1.Sequence > t2.Sequence ),
// // "Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.Sequence+" should not have come before "+t2.inLedger+"#"+t2.Sequence);
// buster.assert(t1.inLedger>=t2.inLedger,
// "Transactions were not ordered correctly: "+t1.inLedger+"#"+t1.Sequence+" should not have come before "+t2.inLedger+"#"+t2.Sequence);
// }
// } else {
// buster.assert(r.transactions, "No transactions returned: "+offset+" "+limit);
// }
//
//
// callback(false);
// })
// .on('error', standardErrorHandler(callback))
// .request();
// },
//
//
// ], function (error) {
// buster.refute(error);
// finalCallback();
// }
// );
// }
// }
//});
//
//
//
//// TODO:
//// Test the "count" feature.

View File

@@ -1,177 +0,0 @@
################################### REQUIRES ###################################
extend = require 'extend'
fs = require 'fs'
assert = require 'assert'
{
Amount
UInt160
Transaction
} = require 'ripple-lib'
testutils = require './testutils'
{
LedgerState
LedgerVerifier
TestAccount
} = require './ledger-state'
{
beast_configured
is_focused_test
pretty_json
server_setup_teardown
skip_or_only
str_ends_with
submit_for_final
} = require './batmans-belt'
#################################### CONFIG ####################################
config = testutils.init_config()
#################################### HELPERS ###################################
make_offer = (remote, account, pays, gets, flag_or_flags) ->
tx = remote.transaction()
tx.offer_create(account, pays, gets)
tx.set_flags(flag_or_flags) if flag_or_flags?
tx
dump_rpc_script = (ledger_state, test_decl) ->
lines = ledger_state.compile_to_rpc_commands()
# Realias etc ;)
# TODO
account = test_decl.offer[0]
[pays, gets, flags] = test_decl.offer[1..]
tx = new Transaction({secrets: {}})
tx.offer_create(account, pays, gets)
tx.set_flags(flags)
tx_json = tx.tx_json
# Account: account
# TransactionType: "OfferCreate"
# TakerPays: pays
# TakerGets: gets
lines += "\nbuild/rippled submit #{account} '#{JSON.stringify tx_json}'"
lines += "\nbuild/rippled ledger_accept\n"
fs.writeFileSync(__dirname + '/../manual-offer-test.sh', lines)
dump_aliased_ledger = (pre_or_post, ledger_state, done) ->
# TODO: generify to post/pre
ledger_state.remote.request_ledger 'validated', {full: true}, (e, m) ->
ledger_dump = ledger_state.pretty_json m.ledger.accountState
fn = __dirname + "/../manual-offer-test-#{pre_or_post}-ledger.json"
fs.writeFileSync(fn, ledger_dump)
done()
################################# TEST FACTORY #################################
make_offer_create_test = (get_context, test_name, test_decl) ->
'''
@get_context {Function}
a getter function, which gets the current context with the ripple-lib remote
etc attached
@test_name {String}
This function will create a `test` using @test_name based on @test_decl
@test_decl {Object}
@pre_ledger
@post_ledger
@offer
'''
test_func = skip_or_only test_name, test
focused_test = is_focused_test test_name
test_func test_name, (done) ->
context = get_context()
remote = context.remote
ledger_state = context.ledger
tx = make_offer(remote, test_decl.offer...)
submit_for_final tx, (m) ->
'assert transaction was successful'
assert.equal m.metadata.TransactionResult, 'tesSUCCESS'
context.ledger.verifier(test_decl.post_ledger).do_verify (errors) ->
this_done = ->
assert Object.keys(errors).length == 0,
"post_ledger errors:\n"+ pretty_json errors
done()
if focused_test
dump_aliased_ledger('post', ledger_state, this_done)
else
this_done()
test_func
ledger_state_setup = (get_context, decls) ->
setup (done) ->
[test_name, test_decl] = decls.shift()
context = get_context()
focused_test = is_focused_test test_name
context.ledger =
new LedgerState(test_decl.pre_ledger, assert, context.remote, config)
if focused_test
dump_rpc_script(context.ledger, test_decl)
context.ledger.setup(
# console.log
->, # noop logging function
->
context.ledger.verifier().do_verify (errors) ->
assert Object.keys(errors).length == 0,
"pre_ledger errors:\n"+ pretty_json errors
if focused_test
dump_aliased_ledger('pre', context.ledger, done)
else
done()
)
############################### TEST DECLARATIONS ##############################
try
offer_create_tests = require("./offer-tests-json")
# console.log offer_create_tests
# offer_create_tests = JSON.parse offer_tests_string
extend offer_create_tests, {}
catch e
console.log e
if beast_configured('RIPPLE_ENABLE_AUTOBRIDGING', '1')
suite_func = suite
else
suite_func = suite.skip
suite_func 'Offer Create Tests', ->
try
get_context = server_setup_teardown()
# tests = ([k,v] for k,v of offer_create_tests)
tests = []
only = false
for k,v of offer_create_tests
f = make_offer_create_test(get_context, k, v)
if not only and f == test.only
only = [[k, v]]
if not str_ends_with k, '_skip'
tests.push [k,v]
# f = make_offer_create_test(get_context, k, v) for [k,v] in tests
ledger_state_setup(get_context, if only then only else tests)
catch e
console.log e

View File

@@ -1,109 +0,0 @@
################################### REQUIRES ###################################
fs = require 'fs'
testutils = require './testutils'
################################### REQUIRES ###################################
exports.pretty_json = (v) -> JSON.stringify(v, undefined, 2)
exports.beast_configured = beast_configured = (k, v) ->
'''
A very naive regex search in $repo_root/src/BeastConfig.h
@k the name of the macro
@v the value as a string expected
@return {Boolean} k's configured value, trimmed, is v
'''
beast_configured.buf ?= fs.readFileSync("#{__dirname}/../src/BeastConfig.h")
pattern = "^#define\\s+#{k}\\s+(.*?)$"
res = (new RegExp(pattern, 'm').exec(beast_configured.buf))
return false if res == null
actual = res[1].trim()
return v == actual
exports.server_setup_teardown = (options) ->
{setup_func, teardown_func, post_setup, server_opts} = options ? {}
context = null
setup_func ?= setup
teardown_func ?= teardown
setup_func (done) ->
context = @
testutils.build_setup(server_opts).call @, ->
if post_setup?
post_setup(context, done)
else
done()
teardown_func (done) ->
testutils.build_teardown().call context, done
# We turn a function to access the `context`, if we returned it now, it
# would be null (DUH ;)
-> context
exports.str_ends_with = ends_with = (s, suffix) ->
~s.indexOf(suffix, s.length - suffix.length)
exports.skip_or_only = (title, test_or_suite) ->
if ends_with title, '_only'
test_or_suite.only
else if ends_with title, '_skip'
test_or_suite.skip
else
test_or_suite
exports.is_focused_test = (test_name) -> ends_with test_name, '_only'
class BailError extends Error
constructor: (@message) ->
@message ?= "Failed test due to relying on prior failed tests"
exports.suite_test_bailer = () ->
bailed = false
bail = (e) -> bailed = true
suiteSetup ->
process.on 'uncaughtException', bail
suiteTeardown ->
process.removeListener 'uncaughtException', bail
wrapper = (test_func) ->
test = (title, fn) ->
wrapped = (done) ->
if not bailed
fn(done)
else
# We could do this, but it's just noisy
if process.env.BAIL_PASSES
done()
else
done(new BailError)
test_func title, wrapped
test.only = test_func.only
test.skip = test_func.skip
return test
wrapper(global.test)
exports.submit_for_final = (tx, done) ->
'''
This helper submits a transaction, and once it's proposed sends a ledger
accept so the transaction will finalize.
'''
tx.on 'submitted', (m) ->
ter = (m.engine_result ? '').slice(0, 3)
if ter in ['tes', 'tec']
tx.remote.ledger_accept()
tx.on 'final', (m) -> done(m)
tx.submit()

View File

@@ -1,174 +0,0 @@
//
// Configuration for unit tests: to be locally customized as needed.
//
var extend = require('extend');
var path = require("path");
var extend = require('extend');
var testconfig = require("./testconfig.js");
//
// Helpers
//
function lines () {
return Array.prototype.slice.call(arguments).join('\n');
};
function for_each_item (o, f) {
for (var k in o) {
if (o.hasOwnProperty(k)) {
f(k, o[k], o);
}
}
};
exports.accounts = testconfig.accounts;
exports.server_default = "alpha";
exports.default_server_config = {
// Where to find the binary.
rippled_path: path.resolve(__dirname, "../build/rippled")
};
//
// Configuration for servers.
//
// For testing, you might choose to target a persistent server at alternate ports.
//
exports.servers = {
// A local test server.
"alpha" : {
// ripple-lib.Remote
'local_fee' : true,
'local_sequence' : true,
'local_signing' : false,
'trace' : false,
'trusted' : true,
'servers' : ['ws://127.0.0.1:5006'],
'websocket_ip': "127.0.0.1",
'websocket_port': 5006,
'websocket_ssl': false,
// json rpc test
'rpc_ip' : "127.0.0.1",
'rpc_port' : 5005,
// rippled.cfg
'server' : lines('port_admin_http',
'port_admin_ws'),
'port_admin_http': lines('port = 5005',
'ip = 127.0.0.1',
'admin = 127.0.0.1',
'protocol = http'),
'port_admin_ws': lines('port = 5006',
'ip = 127.0.0.1',
'admin = 127.0.0.1',
'protocol = ws'),
'node_db': lines('type=memory', 'path=integration'),
features: lines('MultiSign')
},
'uniport_tests' : {
// rippled.cfg
'node_db': lines('type=memory', 'path=integration'),
// We let testutils.build_setup connect normally, and use the Remote to
// determine when the server is ready, so we must configure it, even though
// we don't otherwise use it in the test.
'websocket_ip': "127.0.0.1",
'websocket_port': 6432,
'websocket_ssl': false,
'servers' : ['ws://127.0.0.1:6432'],
'trusted' : true,
}
};
exports.servers.debug = extend({
no_server: true,
debug_logfile: "debug.log"
}, exports.servers.alpha);
exports.uniport_test_ports = {
'port_admin_http':
{'admin': '127.0.0.1', 'port': 6434, 'protocol': 'http'},
'port_admin_http_and_https':
{'admin': '127.0.0.1', 'port': 6437, 'protocol': 'http,https'},
'port_admin_https':
{'admin': '127.0.0.1', 'port': 6435, 'protocol': 'https'},
'port_admin_ws':
{'admin': '127.0.0.1', 'port': 6432, 'protocol': 'ws'},
'port_admin_ws_and_wss':
{'admin': '127.0.0.1', 'port': 6436, 'protocol': 'ws,wss'},
'port_admin_wss':
{'admin': '127.0.0.1', 'port': 6433, 'protocol': 'wss'},
'port_http':
{'admin': '', 'port': 6440, 'protocol': 'http'},
'port_http_and_https':
{'admin': '', 'port': 6443, 'protocol': 'http,https'},
'port_https':
{'admin': '', 'port': 6441, 'protocol': 'https'},
'port_https_and_http_and_peer':
{'admin': '', 'port': 6450, 'protocol': 'https,peer,http'},
'port_passworded_admin_http':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6446, 'protocol': 'http'},
'port_passworded_admin_http_and_https':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6449, 'protocol': 'http,https'},
'port_passworded_admin_https':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6447, 'protocol': 'https'},
'port_passworded_admin_ws':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6444, 'protocol': 'ws'},
'port_passworded_admin_ws_and_wss':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6448, 'protocol': 'ws,wss'},
'port_passworded_admin_wss':
{'admin': '127.0.0.1', 'admin_password': 'p', 'admin_user': 'u',
'port': 6445, 'protocol': 'wss'},
'port_ws':
{'admin': '', 'port': 6438, 'protocol': 'ws'},
'port_ws_and_wss':
{'admin': '', 'port': 6442, 'protocol': 'ws,wss'},
'port_wss':
{'admin': '', 'port': 6439, 'protocol': 'wss'}
};
(function() {
var server_config = exports.servers.uniport_tests;
var test_ports = exports.uniport_test_ports;
// [server]
server_config.server = Object.keys(test_ports).join('\n');
// [port_*]
for_each_item(test_ports, function(port_name, options) {
var opt_line = ['ip=127.0.0.1'];
for_each_item(options, function(k, v) {opt_line.push(k+'='+v);});
server_config[port_name] = opt_line.join('\n');
});
}());
exports.http_servers = {
// A local test server
"zed" : {
"ip" : "127.0.0.1",
"port" : 8088,
}
};
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,824 +0,0 @@
################################### REQUIRES ###################################
extend = require 'extend'
fs = require 'fs'
async = require 'async'
deep_eq = require 'deep-equal'
{Amount
Remote
Seed
Base
Transaction
PathFind
sjcl
UInt160} = require 'ripple-lib'
testutils = require './testutils'
{Server} = require './server'
{LedgerState, TestAccount} = require './ledger-state'
{test_accounts} = require './random-test-addresses'
simple_assert = require 'assert'
#################################### README ####################################
"""
The tests are written in a declarative style:
Each case has an entry in the `path_finding_cases` object
The key translates to a `suite(key, {...})`
The `{...}` passed in is compiled into a setup/teardown for the `ledger` and
into a bunch of `test` invocations for the `paths_expected`
- aliases are used throughout for easier reading
- test account addresses will be created `on the fly`
no need to declare in testconfig.js
debugged responses from the server substitute addresses for aliases
- The fixtures are setup just once for each ledger, multiple path finding
tests can be executed
- `paths_expected` top level keys are group names
2nd level keys are path test declarations
test declaration keys can be suffixed meaningfully with
`_skip`
`_only`
test declaration values can set
debug: true
Will dump the path declaration and
translated request and subsequent response
- hops in `alternatives[*][paths][*]` can be written in shorthand
eg.
ABC/G3|G3
get `ABC/G3` through `G3`
ABC/M1|M1
get `ABC/M1` through `M1`
XRP|$
get `XRP` through `$`
$ signifies an order book rather than account
------------------------------------------------------------------------------
Tests can be written in the 'path-tests-json.js' file in same directory # <--
------------------------------------------------------------------------------
"""
#################################### HELPERS ###################################
assert = simple_assert
refute = (cond, msg) -> assert(!cond, msg)
prettyj = pretty_json = (v) -> JSON.stringify(v, undefined, 2)
propagater = (done) ->
(f) ->
->
return if done.aborted
try
f(arguments...)
catch e
done.aborted = true
throw e
assert_match = (o, key_vals, message) ->
"""
assert_match path[i], matcher,
"alternative[#{ai}].paths[#{pi}]"
"""
for k,v of key_vals
assert.equal o[k], v, message
#################################### CONFIG ####################################
config = testutils.init_config()
############################### ALTERNATIVES TEST ##############################
expand_alternative = (alt) ->
"""
Make explicit the currency and issuer in each hop in paths_computed
"""
amt = Amount.from_json(alt.source_amount)
for path in alt.paths_computed
prev_issuer = amt.issuer().to_json()
prev_currency = amt.currency().to_json()
for hop, hop_i in path
if not hop.currency?
hop.currency = prev_currency
if not hop.issuer? and hop.currency != 'XRP'
if hop.account?
hop.issuer = hop.account
else
hop.issuer = prev_issuer
if hop.type & 0x10
prev_currency = hop.currency
if hop.type & 0x20
prev_issuer = hop.issuer
else if hop.account?
prev_issuer = hop.account
return alt
create_shorthand = (alternatives) ->
"""
Convert explicit paths_computed into the format used by `paths_expected`
These can be pasted in as the basis of tests.
"""
shorthand = []
for alt in alternatives
short_alt = {}
shorthand.push short_alt
amt = Amount.from_json alt.source_amount
if amt.is_native()
short_alt.amount = amt.to_human()
if not (~short_alt.amount.search('.'))
short_alt.amount = short_alt.amount + '.0'
else
short_alt.amount = amt.to_text_full()
short_alt.paths = []
for path in alt.paths_computed
short_path = []
short_alt.paths.push short_path
for node in path
hop = node.currency
hop = "#{hop}/#{node.issuer}" if node.issuer?
hop = "#{hop}|#{if node.account? then node.account else "$"}"
short_path.push hop
return shorthand
ensure_list = (v) ->
if Array.isArray(v)
v
else
[v]
test_alternatives_factory = (realias_pp, realias_text) ->
"""
We are using a factory to create `test_alternatives` because it needs the
per ledger `realias_*` functions
"""
hop_matcher = (decl_hop) ->
[ci, f] = decl_hop.split('|')
if not f?
throw new Error("No `|` in #{decl_hop}")
[c, i] = ci.split('/')
is_account = if f == '$' then false else true
matcher = currency: c
matcher.issuer = i if i?
matcher.account = f if is_account
matcher
match_path = (test, path, ai, pi) ->
test = (hop_matcher(hop) for hop in test)
assert.equal path.length, test.length,
"alternative[#{ai}] path[#{pi}] expecting #{test.length} hops"
for matcher, i in test
assert_match path[i], matcher,
"alternative[#{ai}].paths[#{pi}]"
return
simple_match_path = (test, path, ai, pi) ->
"""
@test
A shorthand specified path
@path
A path as returned by the server with `expand_alternative` done
so issuer and currency are always stated.
"""
test = (hop_matcher(hop) for hop in test)
return false if not test.length == path.length
for matcher, i in test
for k, v of matcher
return false if not path[i]?
if path[i][k] != v
return false
true
amounts = ->
(Amount.from_json a for a in arguments)
amounts_text = ->
(realias_text a.to_text_full() for a in arguments)
check_for_no_redundant_paths = (alternatives) ->
for alt, i in alternatives
existing_paths = []
for path in alt.paths_computed
for existing in existing_paths
assert !(deep_eq path, existing),
"Duplicate path in alternatives[#{i}]\n"+
"#{realias_pp alternatives[0]}"
existing_paths.push path
return
test_alternatives = (test, actual, error_context) ->
"""
@test
alternatives in shorthand format
@actual
alternatives as returned in a `path_find` response
@error_context
a function providing a string with extra context to provide to assertion
messages
"""
check_for_no_redundant_paths actual
for t, ti in ensure_list(test)
a = actual[ti]
[t_amt, a_amt] = amounts(t.amount, a.source_amount)
[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}"
t_paths = ensure_list(t.paths)
tn = t_paths.length
an = a.paths_computed.length
assert.equal tn, an, "Different number of paths specified for alternative[#{ti}]"+
", expected: #{prettyj t_paths}, "+
"actual(shorthand): #{prettyj create_shorthand actual}"+
"actual(verbose): #{prettyj a.paths_computed}"+
error_context()
for p, i in t_paths
matched = false
for m in a.paths_computed
if simple_match_path(p, m, ti, i)
matched = true
break
assert matched, "Can't find a match for path[#{i}]: #{prettyj p} "+
"amongst #{prettyj create_shorthand [a]}"+
error_context()
return
################################################################################
create_path_test = (pth) ->
return (done) ->
self = this
WHAT = self.log_what
ledger = self.ledger
test_alternatives = test_alternatives_factory ledger.pretty_json.bind(ledger),
ledger.realias_issuer
WHAT "#{pth.title}: #{pth.src} sending #{pth.dst}, "+
"#{pth.send}, via #{pth.via}"
one_message = (f) ->
self.remote._servers[0].once 'before_send_message_for_non_mutators', f
sent = "TODO: need to patch ripple-lib"
one_message (m) -> sent = m
error_info = (m, more) ->
info =
path_expected: pth,
path_find_request: sent,
path_find_updates: messages
extend(info, more) if more?
ledger.pretty_json(info)
assert Amount.from_json(pth.send).is_valid(),
"#{pth.send} is not valid Amount"
_src = UInt160.json_rewrite(pth.src)
_dst = UInt160.json_rewrite(pth.dst)
_amt = Amount.from_json(pth.send)
# self.server.clear_logs() "TODO: need to patch ripple-lib"
options = {
src_account: _src
dst_account: _dst
dst_amount: _amt,
src_currencies: [{currency: pth.via}]
}
pf = self.remote.path_find(options)
updates = 0
max_seen = 0
messages = {}
propagates = propagater done
pf.on "error", propagates (m) -> # <--
assert false, "fail (error): #{error_info(m)}"
done()
pf.on "update", propagates (m) -> # <--
# TODO:hack:
expand_alternative alt for alt in m.alternatives
messages[if updates then "update-#{updates}" else 'initial-response'] = m
updates++
# console.log updates
assert m.alternatives.length >= max_seen,
"Subsequent path_find update' should never have less " +
"alternatives:\n#{ledger.pretty_json messages}"
max_seen = m.alternatives.length
# if updates == 2
# testutils.ledger_close(self.remote, -> )
if updates == 2
# "TODO: need to patch ripple-lib"
# self.log_pre(self.server.get_logs(), "Server Logs")
if pth.do_send?
do_send( (ledger.pretty_json.bind ledger), WHAT, self.remote, pth,
messages['update-2'], done )
if pth.debug
console.log ledger.pretty_json(messages)
console.log error_info(m)
console.log ledger.pretty_json create_shorthand(m.alternatives)
if pth.alternatives?
# We realias before doing any comparisons
alts = ledger.realias_issuer(JSON.stringify(m.alternatives))
alts = JSON.parse(alts)
test = pth.alternatives
assert test.length == alts.length,
"Number of `alternatives` specified is different: "+
"#{error_info(m)}"
if test.length == alts.length
test_alternatives(pth.alternatives, alts, -> error_info(m))
if pth.n_alternatives?
assert pth.n_alternatives == m.alternatives.length,
"fail (wrong n_alternatives): #{error_info(m)}"
pf.close()
done() if not pth.do_send?
################################ SUITE CREATION ################################
skip_or_only = (title, test_or_suite) ->
endsWith = (s, suffix) ->
~s.indexOf(suffix, s.length - suffix.length)
if endsWith title, '_only'
test_or_suite.only
else if endsWith title, '_skip'
test_or_suite.skip
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
tests.push definer_factory(group, title, path)
tests
suite_factory = (declaration) ->
->
context = null
suiteSetup (done) ->
context = @
@log_what = ->
testutils.build_setup().call @, ->
context.ledger = new LedgerState(declaration.ledger,
assert,
context.remote,
config)
context.ledger.setup(context.log_what, done)
suiteTeardown (done) ->
testutils.build_teardown().call context, done
for definer in gather_path_definers(declaration.paths_expected)
definer()
define_suites = (path_finding_cases) ->
for case_name, declaration of path_finding_cases
suite_func = skip_or_only case_name, suite
suite_func case_name, suite_factory(declaration)
############################## PATH FINDING CASES ##############################
# Later we reference A0, the `unknown account`, directly embedding the full
# address.
A0 = (new TestAccount('A0')).address
assert A0 == 'rBmhuVAvi372AerwzwERGjhLjqkMmAwxX'
try
path_finding_cases = require('./path-tests-json')
catch e
console.log e
# You need two gateways, same currency. A market maker. A source that trusts one
# 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:
G1BS:
balance: ["1000.0"]
G2SW:
balance: ["1000.0"]
A1:
balance: ["1000.0", "1000/HKD/G1BS"]
trusts: ["2000/HKD/G1BS"]
A2:
balance: ["1000.0", "1000/HKD/G2SW"]
trusts: ["2000/HKD/G2SW"]
M1:
# SnapSwap wants to be able to set trust line quality settings so they
# can charge a fee when transactions ripple across. Liquitidy
# provider, via trusting/holding both accounts
balance: ["11000.0",
"1200/HKD/G1BS",
"5000/HKD/G2SW"
]
trusts: ["100000/HKD/G1BS", "100000/HKD/G2SW"]
# We haven't got ANY offers
paths_expected: {
BS:
P1:
debug: false
src: "A1", dst: "A2", send: "10/HKD/A2", via: "HKD"
n_alternatives: 1
P2:
debug: false
src: "A2", dst: "A1", send: "10/HKD/A1", via: "HKD"
n_alternatives: 1
P3:
debug: false
src: "G1BS", dst: "A2", send: "10/HKD/A2", via: "HKD"
alternatives: [
amount: "10/HKD/G1BS",
paths: [["HKD/M1|M1", "HKD/G2SW|G2SW"]]
]
P5:
debug: false
src: "M1",
send: "10/HKD/M1",
dst: "G1BS",
via: "HKD"
P4:
debug: false
src: "G2SW", send: "10/HKD/A1", dst: "A1", via: "HKD"
alternatives: [
amount: "10/HKD/G2SW",
paths: [["HKD/M1|M1", "HKD/G1BS|G1BS"]]
]
}
"Path Tests #4 (non-XRP to non-XRP, same currency)": {
ledger:
accounts:
G1: balance: ["1000.0"]
G2: balance: ["1000.0"]
G3: balance: ["1000.0"]
G4: balance: ["1000.0"]
A1:
balance: ["1000.0", "1000/HKD/G1"]
trusts: ["2000/HKD/G1"]
A2:
balance: ["1000.0", "1000/HKD/G2"]
trusts: ["2000/HKD/G2"]
A3:
balance: ["1000.0", "1000/HKD/G1"]
trusts: ["2000/HKD/G1"]
A4:
balance: ["10000.0"]
M1:
balance: ["11000.0", "1200/HKD/G1", "5000/HKD/G2"]
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
offers: [
["1000/HKD/G1", "1000/HKD/G2"]
]
M2:
balance: ["11000.0", "1200/HKD/G1", "5000/HKD/G2"]
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
offers: [
["10000.0", "1000/HKD/G2"]
["1000/HKD/G1", "10000.0"]
]
paths_expected: {
T4:
"A) Borrow or repay":
comment: 'Source -> Destination (repay source issuer)'
src: "A1", send: "10/HKD/G1", dst: "G1", via: "HKD"
alternatives: [amount: "10/HKD/A1", paths: []]
"A2) Borrow or repay":
comment: 'Source -> Destination (repay destination issuer)'
src: "A1", send: "10/HKD/A1", dst: "G1", via: "HKD"
alternatives: [amount: "10/HKD/A1", paths: []]
"B) Common gateway":
comment: 'Source -> AC -> Destination'
src: "A1", send: "10/HKD/A3", dst: "A3", via: "HKD"
alternatives: [amount: "10/HKD/A1", paths: [["HKD/G1|G1"]]]
"C) Gateway to gateway":
comment: 'Source -> OB -> Destination'
src: "G1", send: "10/HKD/G2", dst: "G2", via: "HKD"
debug: false
alternatives: [
amount: "10/HKD/G1"
paths: [["HKD/M2|M2"],
["HKD/M1|M1"],
["HKD/G2|$"]
["XRP|$", "HKD/G2|$"]
]
]
"D) User to unlinked gateway via order book":
comment: 'Source -> AC -> OB -> Destination'
src: "A1", send: "10/HKD/G2", dst: "G2", via: "HKD"
debug: false
alternatives: [
amount: "10/HKD/A1"
paths: [
["HKD/G1|G1", "HKD/G2|$"], # <--
["HKD/G1|G1", "HKD/M2|M2"],
["HKD/G1|G1", "HKD/M1|M1"],
["HKD/G1|G1", "XRP|$", "HKD/G2|$"]
]
]
"I4) XRP bridge":
comment: 'Source -> AC -> OB to XRP -> OB from XRP -> AC -> Destination'
src: "A1", send: "10/HKD/A2", dst: "A2", via: "HKD"
debug: false
alternatives: [
amount: "10/HKD/A1",
paths: [
# Focus
["HKD/G1|G1", "HKD/G2|$", "HKD/G2|G2" ],
["HKD/G1|G1", "XRP|$", "HKD/G2|$", "HKD/G2|G2"], # <--
# Incidental
["HKD/G1|G1", "HKD/M1|M1", "HKD/G2|G2"],
["HKD/G1|G1", "HKD/M2|M2", "HKD/G2|G2"]
]
]
}
},
"Path Tests #2 (non-XRP to non-XRP, same currency)": {
ledger:
accounts:
G1: balance: ["1000.0"]
G2: balance: ["1000.0"]
A1:
balance: ["1000.0", "1000/HKD/G1"]
trusts: ["2000/HKD/G1"]
A2:
balance: ["1000.0", "1000/HKD/G2"]
trusts: ["2000/HKD/G2"]
A3:
balance: ["1000.0"]
trusts: ["2000/HKD/A2"]
M1:
balance: ["11000.0", "5000/HKD/G1", "5000/HKD/G2"]
trusts: ["100000/HKD/G1", "100000/HKD/G2"]
offers: [
["1000/HKD/G1", "1000/HKD/G2"]
# ["2000/HKD/G2", "2000/HKD/G1"]
# ["2000/HKD/M1", "2000/HKD/G1"]
# ["100.0", "1000/HKD/G1"]
# ["1000/HKD/G1", "100.0"]
]
paths_expected: {
T4:
"E) Gateway to user":
ledger: false
comment: 'Source -> OB -> AC -> Destination'
# comment: 'Gateway -> OB -> Gateway 2 -> User'
src: "G1", send: "10/HKD/A2", dst: "A2", via: "HKD"
debug: false
alternatives: [
amount: "10/HKD/G1"
paths: [
["HKD/G2|$", "HKD/G2|G2"],
["HKD/M1|M1", "HKD/G2|G2"]
]
]
"F) Different gateways, ripple _skip":
comment: 'Source -> AC -> AC -> Destination'
"G) Different users of different gateways, ripple _skip":
comment: 'Source -> AC -> AC -> AC -> Destination'
"H) Different gateways, order book _skip":
comment: 'Source -> AC -> OB -> AC -> Destination'
"I1) XRP bridge _skip":
comment: 'Source -> OB to XRP -> OB from XRP -> Destination'
src: "A4", send: "10/HKD/G2", dst: "G2", via: "XRP"
debug: true
"I2) XRP bridge _skip":
comment: 'Source -> AC -> OB to XRP -> OB from XRP -> Destination'
"I3) XRP bridge _skip":
comment: 'Source -> OB to XRP -> OB from XRP -> AC -> Destination'
}
}
################################# DEFINE SUITES ################################
define_suites(path_finding_cases)

View File

@@ -1,57 +0,0 @@
/* global test, suite, suiteSetup, suiteTeardown */
var lodash = require('lodash');
var testutils = require('./testutils');
var LedgerState = require('./ledger-state').LedgerState;
var assert = require('assert-diff');
var config = testutils.init_config();
// We just use equal instead of strictEqual everywhere.
assert.options.strict = true;
function makeSuite (name, ledger_state, tests) {
suite(name, function () {
// build_(setup|teardown) utils functions set state on this context var.
var context = {};
// This runs only once
suiteSetup(function (done) {
var opts = {};
if (ledger_state.dump) {
opts.ledger_file = ledger_state.dump;
}
testutils.build_setup(opts).call(context, function () {
if (opts.ledger_file) {
done();
} else {
var ledger = context.ledger = new LedgerState(ledger_state,
assert, context.remote,
config);
// Run the ledger setup util. This compiles the declarative description
// into a series of transactions and executes them.
ledger.setup(lodash.noop/* logger */, function () {
done();
});
}
});
});
suiteTeardown(function (done) {
testutils.build_teardown().call(context, done);
});
lodash.forOwn(tests, function (func, name) {
test(name, function (done) {
var args = [context, context.ledger, context.remote, done];
while (args.length > func.length && args.length > 1) {
args.shift();
}
func.apply(this, args);
});
});
});
}
module.exports = {
makeSuite: makeSuite
};

View File

@@ -1,157 +0,0 @@
let assert = require('assert-diff');
let async = require('async');
let Remote = require('ripple-lib').Remote;
let testutils = require('./testutils');
testutils.init_config();
suite('Disabling MasterKey', function() {
let $ = {};
setup(function(done) {
testutils.build_setup().call($, function() {
$.remote.local_signing = true;
done();
});
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('lsfPasswordSpent', function(done) {
let password_spent_flag = Remote.flags.account_root.PasswordSpent;
let regular_key = 'rGLnRYhy5fQK5pxZuMxtsJKrbu5onBpRst';
async.series(
[
function(callback) {
testutils.create_accounts($.remote, 'root', '1000.0', ['alice'], callback);
},
function(callback) {
$.remote.requestAccountInfo({account: 'alice'}, function(err, info) {
assert.ifError(err);
assert(!(info.account_data.Flags & password_spent_flag),
'PasswordSpent flag set unexpectedly');
callback();
});
},
function(callback) {
let tx = $.remote.createTransaction('SetRegularKey', {
account: 'alice',
regular_key: regular_key
});
// As a special feature, each account is allowed to perform
// SetRegularKey transaction without a transaction fee as long as
// the lsfPasswordSpent flag for the account is not set
//
// https://github.com/ripple/rippled/blob/4239880acb5e559446d2067f00dabb31cf102a23/src/ripple/app/transactors/SetRegularKey.cpp#L64-L67
tx.setFixedFee(0);
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tesSUCCESS');
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
$.remote.requestAccountInfo({account: 'alice'}, function(err, info) {
assert.ifError(err);
assert(info.account_data.Flags & password_spent_flag,
'PasswordSpent flag not set');
callback();
});
},
function(callback) {
// The second SetRegularKey transaction with Fee=0 should fail.
// The initial engine_result is telINSUF_FEE_P. ripple-lib waits for the
// transaction's LastLedgerSequence to expire, so the final result is a
// locally-determined failure: tejMaxLedger
let tx = $.remote.createTransaction('SetRegularKey', {
account: 'alice',
regular_key: regular_key
});
tx.setFixedFee(0);
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'telINSUF_FEE_P');
});
testutils.submit_transaction(tx, function(err) {
assert(err);
assert.strictEqual(err.result, 'tejMaxLedger');
callback();
});
},
function(callback) {
let tx = $.remote.createTransaction('Payment', {
account: 'root',
destination: 'alice',
amount: '1'
});
testutils.submit_transaction(tx, function(err, res) {
assert.ifError(err);
assert.strictEqual(res.engine_result, 'tesSUCCESS');
callback();
});
},
function(callback) {
$.remote.requestAccountInfo({account: 'alice'}, function(err, info) {
assert.ifError(err);
assert(!(info.account_data.Flags & password_spent_flag),
'PasswordSpent flag set unexpectedly');
callback();
});
},
], done);
});
test('Disable master key', function(done) {
let account_flags = Remote.flags.account_root;
let regular_key = 'rGLnRYhy5fQK5pxZuMxtsJKrbu5onBpRst';
async.series(
[
function(callback) {
testutils.create_accounts($.remote, 'root', '1000.0', ['alice'], callback);
},
function(callback) {
let tx = $.remote.createTransaction('SetRegularKey', {
account: 'alice',
regular_key: regular_key
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
$.remote.requestAccountInfo({account: 'alice'}, function(err, info) {
assert.ifError(err);
assert.strictEqual(info.account_data.RegularKey, regular_key);
callback();
});
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {
account: 'alice',
set_flag: 'asfDisableMaster'
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {
account: 'alice'
});
testutils.submit_transaction(tx, function(err) {
assert(err);
assert.strictEqual(err.result, 'tejMaxLedger');
assert.strictEqual(tx.summary().result.engine_result, 'tefMASTER_DISABLED');
callback();
});
}
], done);
});
});

View File

@@ -1,138 +0,0 @@
################################### REQUIRES ###################################
assert = require 'assert'
async = require 'async'
{
Amount
Meta
} = require 'ripple-lib'
testutils = require './testutils'
{
pretty_json
server_setup_teardown
} = require './batmans-belt'
#################################### CONFIG ####################################
config = testutils.init_config()
#################################### HELPERS ###################################
tx_blob_submit_factory = (remote, txn) ->
(next) ->
req = remote.request_submit()
req.message.tx_blob = txn
req.once 'error', -> assert false, "unexpected error submitting txns"
req.once 'success', (m) ->
remote.once 'ledger_closed', -> next()
remote.ledger_accept()
req.request()
offer_amounts = (fields) ->
[Amount.from_json(fields.TakerPays),
Amount.from_json(fields.TakerGets)]
executed_offers = (meta) ->
offers = {}
meta.nodes.forEach (n, i) ->
if n.entryType == 'Offer'
[prev_pays, prev_gets] = offer_amounts(n.fieldsPrev)
[final_pays, final_gets] = offer_amounts(n.fields)
summary=
pays_executed: prev_pays.subtract(final_pays).to_text_full()
gets_executed: prev_gets.subtract(final_gets).to_text_full()
pays_final: final_pays.to_text_full()
gets_final: final_gets.to_text_full()
owner: n.fields.Account
offers[n.ledgerIndex] = summary
offers
build_tx_blob_submission_series = (remote, txns_to_submit) ->
series = []
while (txn = txns_to_submit.shift())
series.push tx_blob_submit_factory remote, txn
series
compute_xrp_discrepancy = (fee, meta) ->
before = Amount.from_json(0)
after = Amount.from_json(fee)
meta.nodes.forEach (n, i) ->
if n.entryType == 'AccountRoot'
prev = n.fieldsPrev?.Balance || n.fields.Balance
final = n.fieldsFinal?.Balance || n.fields.Balance
before = before.add(Amount.from_json(prev))
after = after.add(Amount.from_json(final))
before.subtract(after)
suite 'Discrepancy test', ->
suite 'XRP Discrepancy', ->
get_context = server_setup_teardown({server_opts: {ledger_file: 'ledger-6226713.json'}})
test '01030E435C2EEBE2689FED7494BC159A4C9B98D0DF0B23F7DFCE223822237E8C', (done) ->
{remote} = get_context()
txns_to_submit = [
# This is the nasty one ;)
'1200002200020000240000124E61D5438D7EA4C680000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD468400000000000000A69D4D3E7809B4814C8000000000000000000000000434E59000000000041C8BE2C0A6AA17471B9F6D0AF92AAB1C94D5A25732103FC5F96EA61889691EC7A56FB2B859B600DE68C0255BF580D5C22D02EB97AFCE474473045022100D14B60BC6E01E5C19471F87EB00A4BFCA16D039BB91AEE12DA1142E8C4CAE7C2022020E2809CF24DE2BC0C3DCF1A07C469DB415F880485B2B323E5B5AA1D9F6F22D48114AFD96601692A6C6416DBA294F0DA684675A824B28314AFD96601692A6C6416DBA294F0DA684675A824B20112300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF100000000000000000000000000000000000000000300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF01A034782E2DBAC4FB82B601CD50421E8EF24F3A00100000000000000000000000000000000000000000300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD400'
]
series = build_tx_blob_submission_series remote, txns_to_submit
async.series series, ->
hash = '01030E435C2EEBE2689FED7494BC159A4C9B98D0DF0B23F7DFCE223822237E8C'
remote.request_tx hash, (e, m) ->
meta = new Meta(m.meta)
zero = Amount.from_json(0)
discrepancy = compute_xrp_discrepancy(m.Fee, meta)
assert discrepancy.equals(zero), discrepancy.to_text_full()
done()
suite 'RIPD 304', ->
get_context = server_setup_teardown({server_opts: {ledger_file: 'ledger-7145315.json'}})
# Skip - the new rounding code makes this legacy test produce different
# results. Skip this test for now, as new tests which exercise the payment
# engine and the new rounding code are coming soon.
test.skip 'B1A305038D43BCDF3EA1D096E6A0ACC5FB0ECAE0C8F5D3A54AD76A2AA1E20EC4', (done) ->
{remote} = get_context()
txns_to_submit = [
'120008228000000024000030FD2019000030F0201B006D076B68400000000000000F732103325EB29A014DDE22289D0EA989861D481D54D54C727578AB6C2F18BC342D3829744630440220668D06C44144C284E0346EE7785EB41B72EDBB244FE6EE02F317011A07023C63022067A52367AC01941A3FE19477D7F588C862704A44A8A771BCAD6B7A9119B71E9E8114A7C1C74DADB3693C199888A901FC2B7FD0884EE1'
'1200002200020000240000163161D551C37937E080000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD468400000000000000A69D4D0BEC6A319514D00000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D173210342D49ADE3DBC5E8E25F02B4FBB169448157B016BA203A268C3E8CCC9DF1AE39F74463044022069A2B0F79A042CC65C7CCFDF610DEAD8FDA12F53E43061F9F75FAD5B398E657A02200A4A45BB4F31E922A52F843D5CE96D83446992A13393871C31FCD8A52AE4329F81148C4BE4DBAA81F7BC66720E5874EBD2D90C9563EA83148C4BE4DBAA81F7BC66720E5874EBD2D90C9563EA0112100000000000000000000000000000000000000000300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF01585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C101DD39C650A96EDA48334E70CC4A85B8B2E8502CD3300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD400'
'1200082200000000240019B1A520190019B1A46840000000000000327321025718736160FA6632F48EA4354A35AB0340F8D7DC7083799B9C57C3E937D7185174463044022048B3669EDCA795A1897DA3C7328C8526940708DBB3FFAD88CA5DC22D0398A67502206B37796A743105DE05EE1A11BE017404B4F41FA17E6449E390C36F69D8907C078114AFFDCC86D33C153DA185156EB32608ACCF0BC713'
'1200072200000000240019B1A664D550AF2D90A009D80000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD46540000002540BE4006840000000000000327321025718736160FA6632F48EA4354A35AB0340F8D7DC7083799B9C57C3E937D71851744630440220509F09B609573BC8ADDD55449DBD5201A40F6C1C3AA2D5D984ACB54E0F651F2E022019E6AF2937A5E76D8C9A2B5B0C4704D6BE637AAC17F2EE135DA449B0892B728B8114AFFDCC86D33C153DA185156EB32608ACCF0BC713'
# This is the nasty one ;)
'1200002200020000240000163261D551C37937E080000000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD468400000000000000A69D4D0BEC6A319514D00000000000000000000000055534400000000000A20B3C85F482532A9578DBB3950B85CA06594D173210342D49ADE3DBC5E8E25F02B4FBB169448157B016BA203A268C3E8CCC9DF1AE39F74473045022100C79C86BD18BBBC0343F0EB268A7770FDAEC30748ECCB9A6483E2B11488749DC00220544A5FF2D085FA5DD2A003AA9C3F031B8FE3FD4A443B659B9EE84E165795BC0581148C4BE4DBAA81F7BC66720E5874EBD2D90C9563EA83148C4BE4DBAA81F7BC66720E5874EBD2D90C9563EA0112100000000000000000000000000000000000000000300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD4FF01585E1F3BD02A15D6185F8BB9B57CC60DEDDB37C101DD39C650A96EDA48334E70CC4A85B8B2E8502CD3300000000000000000000000004A50590000000000E5C92828261DBAAC933B6309C6F5C72AF020AFD401E5C92828261DBAAC933B6309C6F5C72AF020AFD400'
]
series = build_tx_blob_submission_series remote, txns_to_submit
async.series series, ->
hash = 'B1A305038D43BCDF3EA1D096E6A0ACC5FB0ECAE0C8F5D3A54AD76A2AA1E20EC4'
remote.request_tx hash, (e, m) ->
meta = new Meta(m.meta)
expected = {
"003313896DA56CFA0996B36AF066589EF0E689230E67DA01D13320289C834A93": {
"pays_executed": "955.967853/XRP",
"gets_executed": "445.6722130686/JPY/rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"pays_final": "245,418.978522/XRP",
"gets_final": "114414.3277869564/JPY/rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"owner": "rQLEvSnbgpcUXkTU8VvSzUPX4scukCjuzb"
},
"9847793D6B936812907ED58455FBA4195205ABCACBE28DF9584C3A195A221E59": {
"pays_executed": "4.19284145965/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"gets_executed": "955.967853/XRP",
"pays_final": "13630.84998220238/USD/rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"gets_final": "3,107,833.795934/XRP",
"owner": "rEhKZcz5Ndjm9BzZmmKrtvhXPnSWByssDv"
}
}
## rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb
assert.deepEqual executed_offers(meta), expected
done()

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,644 +0,0 @@
{
"accepted": true,
"accountState": [
{
"Account": "rQLEvSnbgpcUXkTU8VvSzUPX4scukCjuzb",
"BookDirectory": "92466F5377C34C5EA957034339321E217A23FA4E27A31D475B079EDE3AE532A1",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 710,
"TakerGets": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "119860.00000002"
},
"TakerPays": "257099957100",
"index": "003313896DA56CFA0996B36AF066589EF0E689230E67DA01D13320289C834A93"
},
{
"Account": "rEhKZcz5Ndjm9BzZmmKrtvhXPnSWByssDv",
"Balance": "93495113081301",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 7,
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 422,
"index": "0522B5ABDFEBEF28C47AC64AEBCB6B5ABE0A00388141E32E177052B9742D47CF"
},
{
"Flags": 0,
"Indexes": [
"89B369C951B15067BD52EF3F5FEC540DE773C4B6718FE1342222C1C935ED42DF",
"9847793D6B936812907ED58455FBA4195205ABCACBE28DF9584C3A195A221E59"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rEhKZcz5Ndjm9BzZmmKrtvhXPnSWByssDv",
"RootIndex": "07E25CB74E530EF7B1D6718EE48BAC672B31F7E1908EC77D5C0DA82633A7919C",
"index": "07E25CB74E530EF7B1D6718EE48BAC672B31F7E1908EC77D5C0DA82633A7919C"
},
{
"Flags": 0,
"Indexes": [
"707CEC6ABA7BDA95DF712DABD6BC3BB1DD69CEED25977AB643EA94A471DD842C",
"003313896DA56CFA0996B36AF066589EF0E689230E67DA01D13320289C834A93"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rQLEvSnbgpcUXkTU8VvSzUPX4scukCjuzb",
"RootIndex": "19477D184C2E34D30057C81581E4F04DDE2B64542D2CE7F1145B3D4A207E9301",
"index": "19477D184C2E34D30057C81581E4F04DDE2B64542D2CE7F1145B3D4A207E9301"
},
{
"Account": "rHsZHqa5oMQNL5hFm4kfLd47aEMYjPstpg",
"BookDirectory": "BCF012C63E83DAF510C7B6B27FE1045CF913B0CF94049AB04E10B058B79947A8",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "E5EC82E300777C4C0AE24499E2DA175AE00065BE8AC7C91518C37876377EC473",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 1683876,
"TakerGets": "10000000000",
"TakerPays": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "4697.494711257"
},
"index": "19D60E66D4CAB32F0823C5EAA8A58284AD48BAB63CACCBECB76166A990EECECF"
},
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-0.0001000001918501896"
},
"Flags": 131072,
"HighLimit": {
"currency": "USD",
"issuer": "rhS6Pb8oBMKshN6EznMeWCHJNHJuoom63r",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "19DA8116FACDDB48DBFAEEB7036394883D42C67CA23349E3582AC63A61C0C08F"
},
{
"ExchangeRate": "52221700304BE774",
"Flags": 0,
"Indexes": ["E986149D0F53C5F05FCF13751CA959F9BA31FABC0766AE2A4E906C02F10A5F28"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "1ACB79E7B8B4C59269CEAC5CA907D5E8C3BF3B294A33D3D752221700304BE774",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "E5C92828261DBAAC933B6309C6F5C72AF020AFD4",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1",
"index": "1ACB79E7B8B4C59269CEAC5CA907D5E8C3BF3B294A33D3D752221700304BE774"
},
{
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "28771.88711726011"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "JPY",
"issuer": "rhS6Pb8oBMKshN6EznMeWCHJNHJuoom63r",
"value": "10000000"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "47E2DF7CEC8979AFEB877402F1A8A4A514CE6F0159F5BDCA3538BD4A244EE81E"
},
{
"Flags": 0,
"Indexes": [
"8782F28AC73A79162357EB1FB38E0AA5F55C066F0F2ACC774BBF095B21E07E64",
"E232591F55AA7B82F584A5DBE414CA67C15869B0936C875095B5D08810A99EA5",
"7D1F4F82D74930ED8F0CD4D75CD9B41E0A1ED7CAA6508AEF6C13BE57FCE01220",
"B8F0B4940547D30B9706F32D3EB3A8EC60FD5F09499A157BB05549514DB335BC"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"RootIndex": "49DA34D0CCDB7AF9A1B5751ECDC647D6379033B0126D645CD16395E302239BAE",
"index": "49DA34D0CCDB7AF9A1B5751ECDC647D6379033B0126D645CD16395E302239BAE"
},
{
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "692595.1141695322"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "JPY",
"issuer": "rD8FXhcHtG8m1iHTJogoahPx3LJRjT7tpa",
"value": "10000000"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "4D8AD84A38E3B4EE756E7B64CA539DF51FDEF036D96E8427FB461266B55CE97E"
},
{
"Account": "rD8FXhcHtG8m1iHTJogoahPx3LJRjT7tpa",
"Balance": "4049943048",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 5,
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 5681,
"index": "582F150193E8572D608ACED9E978EF85139834CDB24882550F74560D49F15040"
},
{
"Flags": 0,
"Indexes": ["E232591F55AA7B82F584A5DBE414CA67C15869B0936C875095B5D08810A99EA5"],
"LedgerEntryType": "DirectoryNode",
"Owner": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"RootIndex": "6319526CE8F9A8A44D7A870A89DC1B4AD848AA4F066FCB5390A9A268F6E16AEA",
"index": "6319526CE8F9A8A44D7A870A89DC1B4AD848AA4F066FCB5390A9A268F6E16AEA"
},
{
"Account": "rQLEvSnbgpcUXkTU8VvSzUPX4scukCjuzb",
"Balance": "1244946044233",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 10,
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 711,
"index": "6A240B2366203BFB4CA5A14D78E43DA9EF5E6F1AF66B186842FF80876AEA9055"
},
{
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "0"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "JPY",
"issuer": "rHsZHqa5oMQNL5hFm4kfLd47aEMYjPstpg",
"value": "1"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "7A2C8EADCC58C5E7927FBD9C98C010292757282994E751BE1D162C65E6219658",
"PreviousTxnLgrSeq": 7145307,
"index": "6CB0EB288A835747D5FAE5FD036BAE11DB4A4787DE283199BF60C5B69291F418"
},
{
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-160129.7932997147"
},
"Flags": 131072,
"HighLimit": {
"currency": "JPY",
"issuer": "rQLEvSnbgpcUXkTU8VvSzUPX4scukCjuzb",
"value": "100000000"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "707CEC6ABA7BDA95DF712DABD6BC3BB1DD69CEED25977AB643EA94A471DD842C"
},
{
"Account": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"BookDirectory": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE052227E0BAF9B1166",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "4C1104ADBE498DED6F146B82C57E5DFC062C265354E86C0DF7155515F636DCF9",
"PreviousTxnLgrSeq": 7141869,
"Sequence": 573,
"TakerGets": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "3089.919693423499"
},
"TakerPays": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "29.99922032449999"
},
"index": "7D1F4F82D74930ED8F0CD4D75CD9B41E0A1ED7CAA6508AEF6C13BE57FCE01220"
},
{
"Flags": 0,
"Indexes": [
"19DA8116FACDDB48DBFAEEB7036394883D42C67CA23349E3582AC63A61C0C08F",
"E5186A5ED55BFE053D7F7553693A3AE3288933241CA32AEC8C136BF1B2B3238B",
"89B369C951B15067BD52EF3F5FEC540DE773C4B6718FE1342222C1C935ED42DF"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"RootIndex": "7E1247F78EFC74FA9C0AE39F37AF433966615EB9B757D8397C068C2849A8F4A5",
"index": "7E1247F78EFC74FA9C0AE39F37AF433966615EB9B757D8397C068C2849A8F4A5"
},
{
"Account": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"Balance": "141999116",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 14,
"PreviousTxnID": "52C20FF8946459677CC0B30E2F2D5E9452D22221ADFBD35E9C3192DA7AAD6F82",
"PreviousTxnLgrSeq": 7145047,
"Sequence": 582,
"index": "7E7EBE111CB117C19F55CB87A1166D3235D32605AD29F5EFF795D84962FE4D5A"
},
{
"Flags": 0,
"Indexes": [
"E5186A5ED55BFE053D7F7553693A3AE3288933241CA32AEC8C136BF1B2B3238B",
"4D8AD84A38E3B4EE756E7B64CA539DF51FDEF036D96E8427FB461266B55CE97E"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rD8FXhcHtG8m1iHTJogoahPx3LJRjT7tpa",
"RootIndex": "803DB604B91165DFB9BEAE165415AE68D9E2D1C7C4BDC3AB569F6F3D4FB7D2EB",
"index": "803DB604B91165DFB9BEAE165415AE68D9E2D1C7C4BDC3AB569F6F3D4FB7D2EB"
},
{
"Balance": {
"currency": "JPY",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "9975.85778757913"
},
"Flags": 1114112,
"HighLimit": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "JPY",
"issuer": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"value": "1000000"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "52C20FF8946459677CC0B30E2F2D5E9452D22221ADFBD35E9C3192DA7AAD6F82",
"PreviousTxnLgrSeq": 7145047,
"index": "8782F28AC73A79162357EB1FB38E0AA5F55C066F0F2ACC774BBF095B21E07E64"
},
{
"Account": "rhS6Pb8oBMKshN6EznMeWCHJNHJuoom63r",
"Balance": "277438248",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 10,
"PreviousTxnID": "9E793DBDA4D9C663C3D3FC57EBF99C3DA14FE092FCEB585E06FA0DF08123CD88",
"PreviousTxnLgrSeq": 7145267,
"Sequence": 12441,
"index": "87F612640CBC200BAFC0D02A8F484F2E7EA5A06836A443BF1BFF9DE686659FB3"
},
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-37608.51473797789"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rEhKZcz5Ndjm9BzZmmKrtvhXPnSWByssDv",
"value": "50000"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "89B369C951B15067BD52EF3F5FEC540DE773C4B6718FE1342222C1C935ED42DF"
},
{
"ExchangeRate": "5B079EDE3AE532A1",
"Flags": 0,
"Indexes": ["003313896DA56CFA0996B36AF066589EF0E689230E67DA01D13320289C834A93"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "92466F5377C34C5EA957034339321E217A23FA4E27A31D475B079EDE3AE532A1",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "E5C92828261DBAAC933B6309C6F5C72AF020AFD4",
"TakerPaysCurrency": "0000000000000000000000000000000000000000",
"TakerPaysIssuer": "0000000000000000000000000000000000000000",
"index": "92466F5377C34C5EA957034339321E217A23FA4E27A31D475B079EDE3AE532A1"
},
{
"Account": "rEhKZcz5Ndjm9BzZmmKrtvhXPnSWByssDv",
"BookDirectory": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C0F95030898047E",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 413,
"TakerGets": "3119514774512",
"TakerPays": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "13682.08234438571"
},
"index": "9847793D6B936812907ED58455FBA4195205ABCACBE28DF9584C3A195A221E59"
},
{
"Account": "rGJrzrNBfv6ndJmzt1hTUJVx7z8o2bg3of",
"Balance": "20136213182",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 14,
"PreviousTxnID": "9C83F32CEE14164F5E74B92FB18B5F401E0707D1D0B89E3B0F76EE9B22205465",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 12541,
"index": "9A3D8BCEE8B1A6812356F2D15767A72F4AB2F4117A5316F17BFDE6AFF3EDAD14"
},
{
"Flags": 0,
"Indexes": [
"47E2DF7CEC8979AFEB877402F1A8A4A514CE6F0159F5BDCA3538BD4A244EE81E",
"19DA8116FACDDB48DBFAEEB7036394883D42C67CA23349E3582AC63A61C0C08F",
"E986149D0F53C5F05FCF13751CA959F9BA31FABC0766AE2A4E906C02F10A5F28"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rhS6Pb8oBMKshN6EznMeWCHJNHJuoom63r",
"RootIndex": "9D0414606FD28AB1AED055D29F79DFF44F5B1AC33D2A0CBD6A236BEAB0B7F0D6",
"index": "9D0414606FD28AB1AED055D29F79DFF44F5B1AC33D2A0CBD6A236BEAB0B7F0D6"
},
{
"Account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"Balance": "14474614581",
"Domain": "736E6170737761702E7573",
"Flags": 655360,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "85F810B93488EE16BAFDB7FD590E447DA9ECC4BF3F4DAD11B52A820D7B65A952",
"PreviousTxnLgrSeq": 7145095,
"Sequence": 50,
"index": "A3C1529122C3DBD6C96B9DF009FF4896023FE6B4E05A508B1E81F3DCD9A6274B"
},
{
"Account": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"Balance": "276876380",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 1,
"PreviousTxnID": "2B46B3B775C536E77B15B82086FA42904C01414E4C78164F82038ADBCF352621",
"PreviousTxnLgrSeq": 7131717,
"Sequence": 503,
"index": "A5F37C05FBED611F326E48E6F0D14C6BBAC664CE14ACF4FCC0E959FD60330716"
},
{
"Flags": 0,
"Indexes": [
"6CB0EB288A835747D5FAE5FD036BAE11DB4A4787DE283199BF60C5B69291F418",
"19D60E66D4CAB32F0823C5EAA8A58284AD48BAB63CACCBECB76166A990EECECF"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rHsZHqa5oMQNL5hFm4kfLd47aEMYjPstpg",
"RootIndex": "B65B458CE90B410C20AC46F87480569F661F3AB426FF773B2EEB4E75947A5FD6",
"index": "B65B458CE90B410C20AC46F87480569F661F3AB426FF773B2EEB4E75947A5FD6"
},
{
"Account": "rHsZHqa5oMQNL5hFm4kfLd47aEMYjPstpg",
"Balance": "100108450682",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 32,
"PreviousTxnID": "E5EC82E300777C4C0AE24499E2DA175AE00065BE8AC7C91518C37876377EC473",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 1683877,
"index": "B79D156918390AC41C4DDE5F181417D55B01D0D183ACBB1B892FF163C5BC8344"
},
{
"Account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"Balance": "4382136375728",
"Domain": "6269747374616D702E6E6574",
"EmailHash": "5B33B93C7FFE384D53450FC666BB11FB",
"Flags": 131072,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "D7EDB85038C0EAEDB2FA174074FBB741A002B87735DDD2133971A9D3E67101CC",
"PreviousTxnLgrSeq": 7145290,
"Sequence": 542,
"TransferRate": 1002000000,
"index": "B7D526FDDF9E3B3F95C3DC97C353065B0482302500BBB8051A5C090B596C6133"
},
{
"Account": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"BookDirectory": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE05222D49D5E80FAFB",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "7ADF6C4A72AF9E0DE7C11B2A5A336F47E91EFC5A201EE635D56E79D1E58A3C44",
"PreviousTxnLgrSeq": 7133996,
"Sequence": 572,
"TakerGets": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "3060"
},
"TakerPays": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "30"
},
"index": "B8F0B4940547D30B9706F32D3EB3A8EC60FD5F09499A157BB05549514DB335BC"
},
{
"ExchangeRate": "4E10B058B79947A8",
"Flags": 0,
"Indexes": ["19D60E66D4CAB32F0823C5EAA8A58284AD48BAB63CACCBECB76166A990EECECF"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "BCF012C63E83DAF510C7B6B27FE1045CF913B0CF94049AB04E10B058B79947A8",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000004A50590000000000",
"TakerPaysIssuer": "E5C92828261DBAAC933B6309C6F5C72AF020AFD4",
"index": "BCF012C63E83DAF510C7B6B27FE1045CF913B0CF94049AB04E10B058B79947A8"
},
{
"Flags": 0,
"Indexes": [
"6CB0EB288A835747D5FAE5FD036BAE11DB4A4787DE283199BF60C5B69291F418",
"4D8AD84A38E3B4EE756E7B64CA539DF51FDEF036D96E8427FB461266B55CE97E",
"47E2DF7CEC8979AFEB877402F1A8A4A514CE6F0159F5BDCA3538BD4A244EE81E",
"8782F28AC73A79162357EB1FB38E0AA5F55C066F0F2ACC774BBF095B21E07E64",
"707CEC6ABA7BDA95DF712DABD6BC3BB1DD69CEED25977AB643EA94A471DD842C"
],
"LedgerEntryType": "DirectoryNode",
"Owner": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"RootIndex": "DD8763F37822A3129919DA194DC31D9A9FA5BEA547E233B32E4573F0E60D46D3",
"index": "DD8763F37822A3129919DA194DC31D9A9FA5BEA547E233B32E4573F0E60D46D3"
},
{
"ExchangeRate": "4C0F95030898047E",
"Flags": 0,
"Indexes": ["9847793D6B936812907ED58455FBA4195205ABCACBE28DF9584C3A195A221E59"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C0F95030898047E",
"TakerGetsCurrency": "0000000000000000000000000000000000000000",
"TakerGetsIssuer": "0000000000000000000000000000000000000000",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "0A20B3C85F482532A9578DBB3950B85CA06594D1",
"index": "DFA3B6DDAB58C7E8E5D944E736DA4B7046C30E4F460FD9DE4C0F95030898047E"
},
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "66.41236909484335"
},
"Flags": 65536,
"HighLimit": {
"currency": "USD",
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
"value": "0"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "rhsxr2aAddyCKx5iZctebT4Padxv6iWDxb",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "9BDD9E9FCB197AE5AB5C77F57C18C89F777AB499EF87125C8233011D227181D8",
"PreviousTxnLgrSeq": 7143372,
"index": "E232591F55AA7B82F584A5DBE414CA67C15869B0936C875095B5D08810A99EA5"
},
{
"Balance": {
"currency": "USD",
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
"value": "-51.33482690775639"
},
"Flags": 2228224,
"HighLimit": {
"currency": "USD",
"issuer": "rD8FXhcHtG8m1iHTJogoahPx3LJRjT7tpa",
"value": "100000"
},
"HighNode": "0000000000000000",
"LedgerEntryType": "RippleState",
"LowLimit": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "0"
},
"LowNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"index": "E5186A5ED55BFE053D7F7553693A3AE3288933241CA32AEC8C136BF1B2B3238B"
},
{
"Account": "rhS6Pb8oBMKshN6EznMeWCHJNHJuoom63r",
"BookDirectory": "1ACB79E7B8B4C59269CEAC5CA907D5E8C3BF3B294A33D3D752221700304BE774",
"BookNode": "0000000000000000",
"Flags": 0,
"LedgerEntryType": "Offer",
"OwnerNode": "0000000000000000",
"PreviousTxnID": "8C20D6F490017E225B1541A8BC138A45F96D0868CE67CEFFD500B5DA2390D76E",
"PreviousTxnLgrSeq": 7145315,
"Sequence": 12437,
"TakerGets": {
"currency": "JPY",
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
"value": "3965.070032365735"
},
"TakerPays": {
"currency": "USD",
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
"value": "38.04658677730045"
},
"index": "E986149D0F53C5F05FCF13751CA959F9BA31FABC0766AE2A4E906C02F10A5F28"
},
{
"ExchangeRate": "52227E0BAF9B1166",
"Flags": 0,
"Indexes": ["7D1F4F82D74930ED8F0CD4D75CD9B41E0A1ED7CAA6508AEF6C13BE57FCE01220"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE052227E0BAF9B1166",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "E5C92828261DBAAC933B6309C6F5C72AF020AFD4",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3",
"index": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE052227E0BAF9B1166"
},
{
"ExchangeRate": "5222D49D5E80FAFB",
"Flags": 0,
"Indexes": ["B8F0B4940547D30B9706F32D3EB3A8EC60FD5F09499A157BB05549514DB335BC"],
"LedgerEntryType": "DirectoryNode",
"RootIndex": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE05222D49D5E80FAFB",
"TakerGetsCurrency": "0000000000000000000000004A50590000000000",
"TakerGetsIssuer": "E5C92828261DBAAC933B6309C6F5C72AF020AFD4",
"TakerPaysCurrency": "0000000000000000000000005553440000000000",
"TakerPaysIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3",
"index": "FF304540EB391AD26231FC5FC98ECF6E85A41DE173C1FCE05222D49D5E80FAFB"
}
],
"account_hash": "21520663FE5BBAF001849ECB598D8784A73081F7CBDD4A96614FAB5D438DB0AA",
"close_time": 455849230,
"close_time_human": "2014-Jun-12 00:47:10",
"close_time_resolution": 10,
"closed": true,
"hash": "9CB34D86A7C18EE551B9961C771BA94785F57B5CE88676365B9CFD4F36787CC2",
"ledger_hash": "9CB34D86A7C18EE551B9961C771BA94785F57B5CE88676365B9CFD4F36787CC2",
"ledger_index": "7145315",
"parent_hash": "22B5D2A2264612E7AD3D9A58D1CDD9442EFC28ABFB92FE2363B2F1B623909526",
"seqNum": "7145315",
"totalCoins": "99999989148483436",
"total_coins": "99999989148483436",
"transaction_hash": "EAAD60D178E8D2CC428C147EF39407AF2D073D518A7E64788EC29F321C875C5A",
"transactions": []
}

File diff suppressed because one or more lines are too long

View File

@@ -1,851 +0,0 @@
################################### REQUIRES ###################################
extend = require 'extend'
fs = require 'fs'
assert = require 'assert'
async = require 'async'
{
Remote
UInt160
Transaction
Amount
} = require 'ripple-lib'
testutils = require './testutils'
{
LedgerState
LedgerVerifier
TestAccount
} = require './ledger-state'
{
pretty_json
server_setup_teardown
skip_or_only
submit_for_final
suite_test_bailer
} = require './batmans-belt'
################################ FREEZE OVERVIEW ###############################
'''
Freeze Feature Overview
=======================
A frozen line prevents funds from being transferred to anyone except back to the
issuer, yet does not prohibit acquisition of more of the issuer's assets, via
receipt of a Payment or placing offers to buy them.
A trust line's Flags now assigns two bits, for toggling the freeze status of
each side of a trustline.
GlobalFreeze
------------
There is also, a global freeze, toggled by a bit in the AccountRoot Flags, which
freezes ALL trust lines for an account.
Offers can not be created to buy or sell assets issued by an account with
GlobalFreeze set.
Use cases (via (David (JoelKatz) Schwartz)):
There are two basic cases. One is a case where some kind of bug or flaw causes
a large amount of an asset to somehow be created and the gateway hasn't yet
decided how it's going to handle it.
The other is a reissue where one asset is replaced by another. In a community
credit case, say someone tricks you into issuing a huge amount of an asset,
but you set the no freeze flag. You can still set global freeze to protect
others from trading valuable assets for assets you issued that are now,
unfortunately, worthless. And if you're an honest guy, you can set up a new
account and re-issue to people who are legitimate holders
NoFreeze
--------
NoFreeze, is a set only flag bit in the account root.
When this bit is set:
An account may not freeze its side of a trustline
The NoFreeze bit can not be cleared
The GlobalFreeze flag bit can not cleared
GlobalFreeze can be used as a matter of last resort
Flag Definitions
================
LedgerEntry flags
-----------------
RippleState
LowFreeze 0x00400000
HighFreeze 0x00800000
AccountRoot
NoFreeze 0x00200000
GlobalFeeze 0x00400000
Transaction flags
-----------------
TrustSet (used with Flags)
SetFreeze 0x00100000
ClearFreeze 0x00200000
AccountSet (used with SetFlag/ClearFlag)
NoFreeze 6
GlobalFreeze 7
API Implications
================
transaction.Payment
-------------------
Any offers containing frozen funds found in the process of a tesSUCCESS will be
removed from the books.
transaction.OfferCreate
-----------------------
Selling an asset from a globally frozen issuer fails with tecFROZEN
Selling an asset from a frozen line fails with tecUNFUNDED_OFFER
Any offers containing frozen funds found in the process of a tesSUCCESS will be
removed from the books.
request.book_offers
-------------------
All offers selling assets from a frozen line/acount (offers created before a
freeze) will be filtered, except where in a global freeze situation where:
TakerGets.issuer == Account ($frozen_account)
request.path_find & transaction.Payment
---------------------------------------
No Path may contain frozen trustlines, or offers (placed, prior to freezing) of
assets from frozen lines.
request.account_offers
-----------------------
These offers are unfiltered, merely walking the owner directory and reporting
all offers.
'''
################################################################################
Flags =
sle:
AccountRoot:
PasswordSpent: 0x00010000
RequireDestTag: 0x00020000
RequireAuth: 0x00040000
DisallowXRP: 0x00080000
NoFreeze: 0x00200000
GlobalFreeze: 0x00400000
RippleState:
LowFreeze: 0x00400000
HighFreeze: 0x00800000
tx:
SetFlag:
AccountRoot:
NoFreeze: 6
GlobalFreeze: 7
TrustSet:
# New Flags
SetFreeze: 0x00100000
ClearFreeze: 0x00200000
Transaction.flags.TrustSet ||= {};
# Monkey Patch SetFreeze and ClearFreeze into old version of ripple-lib
Transaction.flags.TrustSet.SetFreeze = Flags.tx.TrustSet.SetFreeze
Transaction.flags.TrustSet.ClearFreeze = Flags.tx.TrustSet.ClearFreeze
GlobalFreeze = Flags.tx.SetFlag.AccountRoot.GlobalFreeze
NoFreeze = Flags.tx.SetFlag.AccountRoot.NoFreeze
#################################### CONFIG ####################################
config = testutils.init_config()
#################################### HELPERS ###################################
get_lines = (remote, acc, done) ->
args = {account: acc, ledger: 'validated'}
remote.request_account_lines args, (err, lines) ->
done(lines)
account_set_factory = (remote, ledger, alias_for) ->
(acc, fields, done) ->
tx = remote.transaction()
tx.account_set(acc)
extend tx.tx_json, fields
tx.on 'error', (err) ->
assert !err, ("Unexpected error #{ledger.pretty_json err}\n" +
"don't use this helper if expecting an error")
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected_root = m.metadata.AffectedNodes[0].ModifiedNode
assert.equal alias_for(affected_root.FinalFields.Account), acc
done(affected_root)
make_payment_factory = (remote, ledger) ->
(src, dst, amount, path, on_final) ->
if typeof path == 'function'
on_final = path
path = undefined
src_account = UInt160.json_rewrite src
dst_account = UInt160.json_rewrite dst
dst_amount = Amount.from_json amount
tx = remote.transaction().payment(src_account, dst_account, dst_amount)
if not path?
tx.build_path(true)
else
tx.path_add path.path
tx.send_max path.send_max
tx.on 'error', (err) ->
if err.engine_result?.slice(0,3) == 'tec'
# We can handle this in the `final`
return
assert !err, ("Unexpected error #{ledger.pretty_json err}\n" +
"don't use this helper if expecting an error")
submit_for_final tx, (m) ->
on_final m
create_offer_factory = (remote, ledger) ->
(acc, pays, gets, on_final) ->
tx = remote.transaction().offer_create(acc, pays, gets)
tx.on 'error', (err) ->
if err.engine_result?.slice(0,3) == 'tec'
# We can handle this in the `final`
return
assert !err, ("Unexpected error #{ledger.pretty_json err}\n" +
"don't use this helper if expecting an error")
submit_for_final tx, (m) ->
on_final m
ledger_state_setup = (pre_ledger) ->
post_setup = (context, done) ->
context.ledger = new LedgerState(pre_ledger,
assert,
context.remote,
config)
context.ledger.setup(
#-> # noop logging function
->
->
context.ledger.verifier().do_verify (errors) ->
assert Object.keys(errors).length == 0,
"pre_ledger errors:\n"+ pretty_json errors
done()
)
verify_ledger_state = (ledger, remote, pre_state, done) ->
{config, assert, am} = ledger
verifier = new LedgerVerifier(pre_state, remote, config, assert, am)
verifier.do_verify (errors) ->
assert Object.keys(errors).length == 0,
"ledger_state errors:\n"+ pretty_json errors
done()
book_offers_factory = (remote) ->
(pays, gets, on_book) ->
asset = (a) ->
if typeof a == 'string'
ret = {}
[ret['currency'], ret['issuer']] = a.split('/')
ret
else
a
book=
pays: asset(pays)
gets: asset(gets)
remote.request_book_offers book, (err, book) ->
if err
assert !err, "error with request_book_offers #{err}"
on_book(book)
suite_setup = (state) ->
'''
@state
The ledger state to setup, after starting the server
'''
opts =
setup_func: suiteSetup
teardown_func: suiteTeardown
post_setup: ledger_state_setup(state)
get_context = server_setup_teardown(opts)
helpers = null
helpers_factory = ->
context = {ledger, remote} = get_context()
alog = (obj) -> console.log ledger.pretty_json obj
lines_for = (acc) -> get_lines(remote, arguments...)
alias_for = (acc) -> ledger.am.lookup_alias(acc)
verify_ledger_state_before_suite = (pre) ->
suiteSetup (done) -> verify_ledger_state(ledger, remote, pre, done)
{
context: context
remote: remote
ledger: ledger
lines_for: lines_for
alog: alog
alias_for: alias_for
book_offers: book_offers_factory(remote)
create_offer: create_offer_factory(remote, ledger, alias_for)
account_set: account_set_factory(remote, ledger, alias_for)
make_payment: make_payment_factory(remote, ledger, alias_for)
verify_ledger_state_before_suite: verify_ledger_state_before_suite
}
get_helpers = -> (helpers = helpers ? helpers_factory())
{
get_context: get_context
get_helpers: get_helpers
}
##################################### TESTS ####################################
execute_if_enabled = (fn) ->
enforced = true # freeze tests enforced
fn(global.suite, enforced)
conditional_test_factory = ->
test = suite_test_bailer()
test_if = (b, args...) ->
if b
test(args...)
else
test.skip(args...)
[test, test_if]
execute_if_enabled (suite, enforced) ->
suite 'Freeze Feature', ->
suite 'RippleState Freeze', ->
# test = suite_test_bailer()
[test, test_if] = conditional_test_factory()
h = null
{get_helpers} = suite_setup
accounts:
G1: balance: ['1000.0']
bob:
balance: ['1000.0', '10-100/USD/G1']
alice:
balance: ['1000.0', '100/USD/G1']
offers: [['500.0', '100/USD/G1']]
suite 'Account with line unfrozen (proving operations normally work)', ->
test 'can make Payment on that line', (done) ->
{remote} = h = get_helpers()
h.make_payment 'alice', 'bob', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'can receive Payment on that line', (done) ->
h.make_payment 'bob', 'alice', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
suite 'Is created via a TrustSet with SetFreeze flag', ->
test 'sets LowFreeze | HighFreeze flags', (done) ->
{remote} = h
tx = remote.transaction()
tx.ripple_line_set('G1', '0/USD/bob')
tx.set_flags('SetFreeze')
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
assert affected.length > 1, "only one affected node implies a noop"
ripple_state = affected[1].ModifiedNode
final = ripple_state.FinalFields
assert.equal h.alias_for(final.LowLimit.issuer), 'G1'
assert final.Flags & Flags.sle.RippleState.LowFreeze
assert !(final.Flags & Flags.sle.RippleState.HighFreeze)
done()
suite 'Account with line frozen by issuer', ->
test 'can buy more assets on that line', (done) ->
h.create_offer 'bob', '5/USD/G1', '25.0', (m) ->
meta = m.metadata
assert.equal meta.TransactionResult, 'tesSUCCESS'
line = meta.AffectedNodes[3]['ModifiedNode'].FinalFields
assert.equal h.alias_for(line.HighLimit.issuer), 'bob'
assert.equal line.Balance.value, '-15' # HighLimit means balance inverted
done()
test_if enforced, 'can not sell assets from that line', (done) ->
h.create_offer 'bob', '1.0', '5/USD/G1', (m) ->
assert.equal m.engine_result, 'tecUNFUNDED_OFFER'
done()
test 'can receive Payment on that line', (done) ->
h.make_payment 'alice', 'bob', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test_if enforced, 'can not make Payment from that line', (done) ->
h.make_payment 'bob', 'alice', '1/USD/G1', (m) ->
assert.equal m.engine_result, 'tecPATH_DRY'
done()
suite 'request_account_lines', ->
test 'shows `freeze_peer` and `freeze` respectively', (done) ->
async.parallel [
(next) ->
h.lines_for 'G1', (lines) ->
for line in lines.lines
if h.alias_for(line.account) == 'bob'
assert.equal line.freeze, true
assert.equal line.balance, '-16'
# unless we get here, the test will hang alerting us to
# something amiss
next() # setImmediate ;)
break
(next) ->
h.lines_for 'bob', (lines) ->
for line in lines.lines
if h.alias_for(line.account) == 'G1'
assert.equal line.freeze_peer, true
assert.equal line.balance, '16'
next()
break
], ->
done()
suite 'Is cleared via a TrustSet with ClearFreeze flag', ->
test 'sets LowFreeze | HighFreeze flags', (done) ->
{remote} = h
tx = remote.transaction()
tx.ripple_line_set('G1', '0/USD/bob')
tx.set_flags('ClearFreeze')
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
ripple_state = affected[1].ModifiedNode
final = ripple_state.FinalFields
assert.equal h.alias_for(final.LowLimit.issuer), 'G1'
assert !(final.Flags & Flags.sle.RippleState.LowFreeze)
assert !(final.Flags & Flags.sle.RippleState.HighFreeze)
done()
suite 'Global (AccountRoot) Freeze', ->
# NoFreeze: 0x00200000
# GlobalFreeze: 0x00400000
# test = suite_test_bailer()
[test, test_if] = conditional_test_factory()
h = null
{get_helpers} = suite_setup
accounts:
G1:
balance: ['12000.0']
offers: [['10000.0', '100/USD/G1'], ['100/USD/G1', '10000.0']]
A1:
balance: ['1000.0', '1000/USD/G1']
offers: [['10000.0', '100/USD/G1']]
trusts: ['1200/USD/G1']
A2:
balance: ['20000.0', '100/USD/G1']
trusts: ['200/USD/G1']
offers: [['100/USD/G1', '10000.0']]
A3:
balance: ['20000.0', '100/BTC/G1']
A4:
balance: ['20000.0', '100/BTC/G1']
suite 'Is toggled via AccountSet using SetFlag and ClearFlag', ->
test 'SetFlag GlobalFreeze should set 0x00400000 in Flags', (done) ->
{remote} = h = get_helpers()
h.account_set 'G1', SetFlag: GlobalFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert !(new_flags & Flags.sle.AccountRoot.NoFreeze)
assert (new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
test 'ClearFlag GlobalFreeze should clear 0x00400000 in Flags', (done) ->
{remote} = h = get_helpers()
h.account_set 'G1', ClearFlag: GlobalFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert !(new_flags & Flags.sle.AccountRoot.NoFreeze)
assert !(new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
suite 'Account without GlobalFreeze (proving operations normally work)', ->
suite 'have visible offers', ->
test 'where taker_gets is $unfrozen_issuer', (done) ->
{remote} = h = get_helpers()
h.book_offers 'XRP', 'USD/G1', (book) ->
assert.equal book.offers.length, 2
aliases = (h.alias_for(o.Account) for o in book.offers).sort()
assert.equal aliases[0], 'A1'
assert.equal aliases[1], 'G1'
done()
test 'where taker_pays is $unfrozen_issuer', (done) ->
h.book_offers 'USD/G1', 'XRP', (book) ->
assert.equal book.offers.length, 2
aliases = (h.alias_for(o.Account) for o in book.offers).sort()
assert.equal aliases[0], 'A2'
assert.equal aliases[1], 'G1'
done()
suite 'its assets can be', ->
test 'bought on the market', (next) ->
h.create_offer 'A3', '1/BTC/G1', '1.0', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
next()
test 'sold on the market', (next) ->
h.create_offer 'A4', '1.0', '1/BTC/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
next()
suite 'Payments', ->
test 'direct issues can be sent', (done) ->
{remote} = h = get_helpers()
h.make_payment 'G1', 'A2', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'direct redemptions can be sent', (done) ->
h.make_payment 'A2', 'G1', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'via rippling can be sent', (done) ->
h.make_payment 'A2', 'A1', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'via rippling can be sent back', (done) ->
h.make_payment 'A2', 'A1', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
suite 'Account with GlobalFreeze', ->
suite 'Needs to set GlobalFreeze first', ->
test 'SetFlag GlobalFreeze will toggle back to freeze', (done) ->
h.account_set 'G1', SetFlag: GlobalFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert !(new_flags & Flags.sle.AccountRoot.NoFreeze)
assert (new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
suite 'its assets can\'t be', ->
test_if enforced, 'bought on the market', (next) ->
h.create_offer 'A3', '1/BTC/G1', '1.0', (m) ->
assert.equal m.engine_result, 'tecFROZEN'
next()
test_if enforced, 'sold on the market', (next) ->
h.create_offer 'A4', '1.0', '1/BTC/G1', (m) ->
assert.equal m.engine_result, 'tecFROZEN'
next()
suite 'its offers are filtered', ->
test_if enforced, 'account_offers always '+
'shows their own offers', (done) ->
{remote} = h = get_helpers()
args = { account: 'G1', ledger: 'validated' }
remote.request_account_offers args, (err, res) ->
assert.equal res.offers.length, 2
done()
test.skip 'books_offers(*, $frozen_account/*) shows offers '+
'owned by $frozen_account only', (done) ->
h.book_offers 'XRP', 'USD/G1', (book) ->
# h.alog book.offers
assert.equal book.offers.length, 1
done()
test.skip 'books_offers($frozen_account/*, *) shows '+
'no offers', (done) ->
h.book_offers 'USD/G1', 'XRP', (book) ->
assert.equal book.offers.length, 0
done()
test_if enforced, 'books_offers(*, $frozen_account/*) shows offers '+
'owned by $frozen_account only (broken) ', (done) ->
h.book_offers 'XRP', 'USD/G1', (book) ->
# h.alog book.offers
assert.equal book.offers.length, 2
done()
test_if enforced, 'books_offers($frozen_account/*, *) '+
'shows no offers (broken)', (done) ->
h.book_offers 'USD/G1', 'XRP', (book) ->
assert.equal book.offers.length, 2
done()
suite 'Payments', ->
test 'direct issues can be sent', (done) ->
{remote} = h = get_helpers()
h.make_payment 'G1', 'A2', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'direct redemptions can be sent', (done) ->
h.make_payment 'A2', 'G1', '1/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test_if enforced, 'via rippling cant be sent', (done) ->
h.make_payment 'A2', 'A1', '1/USD/G1', (m) ->
assert.equal m.engine_result, 'tecPATH_DRY'
done()
suite 'Accounts with NoFreeze', ->
test = suite_test_bailer()
h = null
{get_helpers} = suite_setup
accounts:
G1: balance: ['12000.0']
A1: balance: ['1000.0', '1000/USD/G1']
suite 'TrustSet NoFreeze', ->
test 'should set 0x00200000 in Flags', (done) ->
h = get_helpers()
h.account_set 'G1', SetFlag: NoFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert (new_flags & Flags.sle.AccountRoot.NoFreeze)
assert !(new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
test 'can not be cleared', (done) ->
h.account_set 'G1', ClearFlag: NoFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert (new_flags & Flags.sle.AccountRoot.NoFreeze)
assert !(new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
suite 'GlobalFreeze', ->
test 'can set GlobalFreeze', (done) ->
h.account_set 'G1', SetFlag: GlobalFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert (new_flags & Flags.sle.AccountRoot.NoFreeze)
assert (new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
test 'can not unset GlobalFreeze', (done) ->
h.account_set 'G1', ClearFlag: GlobalFreeze, (root) ->
new_flags = root.FinalFields.Flags
assert (new_flags & Flags.sle.AccountRoot.NoFreeze)
assert (new_flags & Flags.sle.AccountRoot.GlobalFreeze)
done()
suite 'their trustlines', ->
test 'can\'t be frozen', (done) ->
{remote} = h = get_helpers()
tx = remote.transaction()
tx.ripple_line_set('G1', '0/USD/A1')
tx.set_flags('SetFreeze')
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
assert.equal affected.length, 1
affected_type = affected[0]['ModifiedNode'].LedgerEntryType
assert.equal affected_type, 'AccountRoot'
done()
suite 'Offers for frozen trustlines (not GlobalFreeze)', ->
# test = suite_test_bailer()
[test, test_if] = conditional_test_factory()
remote = h = null
{get_helpers} = suite_setup
accounts:
G1:
balance: ['1000.0']
A2:
balance: ['2000.0']
trusts: ['1000/USD/G1']
A3:
balance: ['1000.0', '2000/USD/G1']
offers: [['1000.0', '1000/USD/G1']]
A4:
balance: ['1000.0', '2000/USD/G1']
suite 'will be removed by Payment with tesSUCCESS', ->
test 'can normally make a payment partially consuming offer', (done) ->
{remote} = h = get_helpers()
path =
path: [{"currency": "USD", "issuer": "G1"}]
send_max: '1.0'
h.make_payment 'A2', 'G1', '1/USD/G1', path, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
done()
test 'offer was only partially consumed', (done) ->
args = { account: 'A3', ledger: 'validated' }
remote.request_account_offers args, (err, res) ->
assert res.offers.length == 1
assert res.offers[0].taker_gets.value, '999'
done()
test 'someone else creates an offer providing liquidity', (done) ->
h.create_offer 'A4', '999.0', '999/USD/G1', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
done()
test 'owner of partially consumed offer\'s line is frozen', (done) ->
tx = remote.transaction()
tx.ripple_line_set('G1', '0/USD/A3')
tx.set_flags('SetFreeze')
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
ripple_state = affected[1].ModifiedNode
final = ripple_state.FinalFields
assert.equal h.alias_for(final.HighLimit.issuer), 'G1'
assert !(final.Flags & Flags.sle.RippleState.LowFreeze)
assert (final.Flags & Flags.sle.RippleState.HighFreeze)
done()
test 'Can make a payment via the new offer', (done) ->
path =
path: [{"currency": "USD", "issuer": "G1"}]
send_max: '1.0'
h.make_payment 'A2', 'G1', '1/USD/G1', path, (m) ->
# assert.equal m.engine_result, 'tecPATH_PARTIAL' # tecPATH_DRY
assert.equal m.metadata.TransactionResult, 'tesSUCCESS' # tecPATH_DRY
done()
test_if enforced, 'Partially consumed offer was removed by tes* payment', (done) ->
args = { account: 'A3', ledger: 'validated' }
remote.request_account_offers args, (err, res) ->
assert res.offers.length == 0
done()
suite 'will be removed by OfferCreate with tesSUCCESS', ->
test_if enforced, 'freeze the new offer', (done) ->
tx = remote.transaction()
tx.ripple_line_set('G1', '0/USD/A4')
tx.set_flags('SetFreeze')
submit_for_final tx, (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
ripple_state = affected[0].ModifiedNode
final = ripple_state.FinalFields
assert.equal h.alias_for(final.LowLimit.issuer), 'G1'
assert (final.Flags & Flags.sle.RippleState.LowFreeze)
assert !(final.Flags & Flags.sle.RippleState.HighFreeze)
done()
test_if enforced, 'can no longer create a crossing offer', (done) ->
h.create_offer 'A2', '999/USD/G1', '999.0', (m) ->
assert.equal m.metadata?.TransactionResult, 'tesSUCCESS'
affected = m.metadata.AffectedNodes
created = affected[5].CreatedNode
new_fields = created.NewFields
assert.equal h.alias_for(new_fields.Account), 'A2'
done()
test_if enforced, 'offer was removed by offer_create', (done) ->
args = { account: 'A4', ledger: 'validated' }
remote.request_account_offers args, (err, res) ->
assert res.offers.length == 0
done()

View File

@@ -1,202 +0,0 @@
var async = require("async");
var assert = require('assert');
var http = require("http");
var jsonrpc = require("simple-jsonrpc");
var EventEmitter = require('events').EventEmitter;
var Remote = require("ripple-lib").Remote;
var testutils = require("./testutils");
var config = testutils.init_config();
function build_setup(options) {
var setup = testutils.build_setup(options);
return function (done) {
var self = this;
var http_config = config.http_servers["zed"];
self.server_events = new EventEmitter;
self.server = http.createServer(function (req, res) {
// console.log("REQUEST");
var input = "";
req.setEncoding('utf8');
req.on('data', function (buffer) {
// console.log("DATA: %s", buffer);
input = input + buffer;
});
req.on('end', function () {
var request = JSON.parse(input);
// console.log("REQ: %s", JSON.stringify(request, undefined, 2));
self.server_events.emit('request', request, res);
});
req.on('close', function () { });
});
self.server.listen(http_config.port, http_config.ip, void(0), function () {
// console.log("server up: %s %d", http_config.ip, http_config.port);
setup.call(self, done);
});
};
};
function build_teardown() {
var teardown = testutils.build_teardown();
return function (done) {
var self = this;
self.server.close(function () {
// console.log("server closed");
teardown.call(self, done);
});
};
};
suite('JSON-RPC', function() {
var $ = { };
setup(function(done) {
build_setup().call($, done);
});
teardown(function(done) {
build_teardown().call($, done);
});
test('server info', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
client.call('server_info', [ ], function (result) {
// console.log(JSON.stringify(result, undefined, 2));
assert(typeof result === 'object');
assert('info' in result);
done();
});
});
test('subscribe server', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
var http_config = config.http_servers["zed"];
client.call('subscribe', [{
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'server' ],
}], function (result) {
// console.log(JSON.stringify(result, undefined, 2));
assert(typeof result === 'object');
assert('random' in result);
done();
});
});
test('subscribe ledger', function(done) {
var self = this;
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
var http_config = config.http_servers["zed"];
var steps = [
function (callback) {
self.what = "Subscribe.";
client.call('subscribe', [{
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'ledger' ],
}], function (result) {
//console.log(JSON.stringify(result, undefined, 2));
assert(typeof result === 'object');
assert('ledger_index' in result);
callback();
});
},
function (callback) {
self.what = "Accept a ledger.";
$.server_events.once('request', function (request, response) {
// console.log("GOT: %s", JSON.stringify(request, undefined, 2));
assert.strictEqual(1, request.params.seq);
assert.strictEqual(3, request.params.ledger_index);
response.statusCode = 200;
response.end(JSON.stringify({
jsonrpc: "2.0",
result: {},
id: request.id
}));
callback();
});
$.remote.ledger_accept();
},
function (callback) {
self.what = "Accept another ledger.";
$.server_events.once('request', function (request, response) {
// console.log("GOT: %s", JSON.stringify(request, undefined, 2));
assert.strictEqual(2, request.params.seq);
assert.strictEqual(4, request.params.ledger_index);
response.statusCode = 200;
response.end(JSON.stringify({
jsonrpc: "2.0",
result: {},
id: request.id
}));
callback();
});
$.remote.ledger_accept();
}
]
async.waterfall(steps, function(error) {
assert(!error, self.what);
done();
});
});
test('subscribe manifests', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
var http_config = config.http_servers["zed"];
client.call('subscribe', [{
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'manifests' ],
}], function (result) {
assert(typeof result === 'object');
assert(result.status === 'success');
done();
});
});
test('subscribe validations', function(done) {
var rippled_config = testutils.get_server_config(config);
var client = jsonrpc.client("http://" + rippled_config.rpc_ip + ":" + rippled_config.rpc_port);
var http_config = config.http_servers["zed"];
client.call('subscribe', [{
'url' : "http://" + http_config.ip + ":" + http_config.port,
'streams' : [ 'validations' ],
}], function (result) {
assert(typeof result === 'object');
assert(result.status === 'success');
done();
});
});
});

View File

@@ -1,65 +0,0 @@
var assert = require('assert');
var Request = require('ripple-lib').Request;
var makeSuite = require('./declarative-suite').makeSuite;
makeSuite('ledger_data', {dump: 'ledger-full-40000.json'},
{
ledger_data: function (remote, done) {
var limit = 20;
// keep track of indexes we've seen
var indexes = {};
// keep all the ledger_data items from multiple requests in one array
var merged = [];
function requestData (marker, callback) {
if (typeof marker === 'function') {
callback = marker;
marker = undefined;
}
var req = new Request(remote, 'ledger_data');
var params = req.message;
params.ledger_index = 'validated';
params.marker = marker;
params.limit = limit;
params.binary = false;
req.callback(function (e, m) {
assert.equal(typeof m.ledger_index, 'number');
assert.equal(typeof m.ledger_hash, 'string');
// make sure we didn't get some error
assert.ifError(e);
// make sure we aren't getting indexes we've seen before
m.state.forEach(function (s) {
assert(indexes[s.index] === undefined);
indexes[s.index] = true;
merged.push(s);
});
// make another request if we have a marker
if (m.marker) {
// make sure our limit was honoured
assert(m.state.length == limit);
requestData(m.marker, callback);
} else {
// make sure our limit was honoured
assert(m.state.length <= limit);
callback();
}
});
}
requestData(function () {
remote.request_ledger({validated: true, full: true}, function (e, m) {
// compare our stitched together account state array with one from
// the ledegr data command
assert.deepEqual(merged/* .concat('watch me fail') */,
m.ledger.accountState);
done();
});
});
}
}
);

View File

@@ -1,152 +0,0 @@
################################################################################
async = require 'async'
simple_assert = require 'assert'
deep_eq = require 'deep-equal'
testutils = require './testutils'
{
LedgerVerifier
Balance
} = require './ledger-state'
#################################### CONFIG ####################################
config = testutils.init_config()
#################################### HELPERS ###################################
assert = simple_assert
prettyj = pretty_json = (v) -> JSON.stringify(v, undefined, 2)
describe 'Balance', ->
it 'parses native balances', ->
bal = new Balance("1.000")
assert.equal bal.is_native, true
assert.equal bal.limit, null
it 'parses iou balances', ->
bal = new Balance("1.000/USD/bob")
assert.equal bal.is_native, false
assert.equal bal.limit, null
assert.equal bal.amount.currency().to_json(), 'USD'
it 'parses iou balances with limits', ->
bal = new Balance("1-500/USD/bob")
assert.equal bal.is_native, false
assert.equal bal.amount.currency().to_json(), 'USD'
assert.equal bal.limit.to_json().value, '500'
assert.equal bal.amount.to_json().value, '1'
describe 'LedgerVerifier', ->
lv = null
declaration=
accounts:
bob:
balance: ['100.0', '200-500/USD/alice']
offers: [['89.0', '100/USD/alice'], ['89.0', '100/USD/alice']]
# We are using this because mocha and coffee-script is a retarded combination
# unfortunately, which terminates the program silently upon any require time
# exceptions. TODO: investigate obviously, but for the moment this is an
# acceptable workaround.
suiteSetup ->
remote_dummy = {set_secret: (->)}
lv = new LedgerVerifier(declaration, remote_dummy, config, assert)
it 'tracks xrp balances', ->
assert.equal lv.xrp_balances['bob'].to_json(), '100000000'
it 'tracks iou balances', ->
assert.equal lv.iou_balances['bob']['USD/alice'].to_json().value, '200'
it 'tracks iou trust limits', ->
assert.equal lv.trusts['bob']['USD/alice'].to_json().value, '500'
it 'can verify', ->
account_offers = [
{
"account": "bob",
"offers": [
{
"flags": 65536,
"seq": 2,
"taker_gets": {
"currency": "USD",
"issuer": "alice",
"value": "100"
},
"taker_pays": "88000000"
}
]
}
]
account_lines = [{
"account": "bob",
"lines": [
{
"account": "alice",
"balance": "201",
"currency": "USD",
"limit": "500",
"limit_peer": "0",
"quality_in": 0,
"quality_out": 0
},
]
}]
account_infos = [{
"account_data": {
"Account": "bob",
"Balance": "999"+ "999"+ "970",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "3D7823B577A5AF5860273B3DD13CA82D072B63B3B095DE1784604A5B41D7DD1D",
"PreviousTxnLgrSeq": 5,
"Sequence": 3,
"index": "59BEA57D1A27B6A560ECA226ABD10DE80C3ADC6961039908087ACDFA92F71489"
},
"ledger_current_index": 8
}]
errors = lv.verify account_infos, account_lines, account_offers
assert.equal errors.bob.balance['USD/alice'].expected, '200'
assert.equal errors.bob.balance['USD/alice'].actual, '201'
assert.equal errors.bob.balance['XRP'].expected, '100'
assert.equal errors.bob.balance['XRP'].actual, '999.99997'
assert.equal errors.bob.offers[0].taker_pays.actual, '88/XRP'
assert.equal errors.bob.offers[0].taker_pays.expected, '89/XRP'
# {"expected":["89.0","100/USD/alice"],"actual":"missing"}
assert.equal errors.bob.offers[1].actual, 'missing'
expected = {
"bob": {
"balance": {
"XRP": {
"actual": "999.99997",
"expected": "100"
},
"USD/alice": {
"actual": "201",
"expected": "200"
}
},
"offers": [
{
"taker_pays": {
"expected": "89/XRP",
"actual": "88/XRP"
}
},
"missing"
]
}
}

View File

@@ -1,730 +0,0 @@
################################### REQUIRES ###################################
# This gives coffee-script proper file/lines in the exceptions
async = require 'async'
assert = require 'assert'
{
Amount
Remote
Seed
Base
UInt160
Transaction
} = require 'ripple-lib'
testutils = require './testutils'
#################################### HELPERS ###################################
pretty_json = (v) -> JSON.stringify(v, undefined, 2)
exports.TestAccount = class TestAccount
SHA256_RIPEMD160: (bits) ->
sjcl.hash.ripemd160.hash sjcl.hash.sha256.hash(bits)
derive_pair: (passphrase) ->
seed = Seed.from_json(passphrase)
master_seed = seed.to_json()
key_pair = seed.get_key()
pubKey = key_pair.pubKeyHex()
address = key_pair.accountID()
[address, master_seed, key_pair]
constructor: (passphrase) ->
@passphrase = passphrase
[@address, @master_seed, @key_pair] = @derive_pair(passphrase)
parse_balance_and_trust = (val) ->
reg = new RegExp("([0-9.]+)-([0-9.]+)(/[^/]+/[^/]+)")
m = reg.exec val
if m != null
[m[1] + m[3], m[2] + m[3]]
else
undefined
exports.parse_amount = parse_amount = (amt_val) ->
amt = Amount.from_json(amt_val)
if not amt.is_valid()
amt = Amount.from_human(amt_val)
if not amt.is_valid()
amt = null
amt
exports.Balance = class Balance
'''
Represents a parsed balance declaration, which could represent an xrp balance
or an iou balance and optional limit.
@amount
@limit
@balance
'''
constructor: (value) ->
limited = parse_balance_and_trust value
if limited?
[amount, limit] = limited
@amount = parse_amount amount
@limit = parse_amount limit
@is_native = false
else
@amount = parse_amount value
@is_native = @amount.is_native()
@limit = null
################################################################################
class BulkRequests
constructor: (@remote, @assert, @pretty_json) ->
transactor: (fn, args_list, on_each, callback) ->
if args_list.length == 0
return callback()
if not callback?
callback = on_each
on_each = null
@assert callback?, "Must supply a callback"
finalized = {
n: args_list.length
one: ->
if --finalized.n <= 0
callback()
}
#remote = @remote
async.concatSeries(args_list, ((args, callback) =>
tx = @remote.transaction()
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()
# testutils.ledger_close remote, ->
).on('final', (m) =>
finalized.one()
# callback()
)
.on("error", (m) =>
@assert false, @pretty_json m
).submit()
),
=> testutils.ledger_close @remote, ->
)
requester: (fn, args_list, on_each, callback, on_results) ->
if not callback?
callback = on_each
on_each = null
@assert callback?, "Must supply a callback"
async.concatSeries(args_list, ((args, callback) =>
req = fn.apply @remote, (args.map (arg) -> return { account: arg })
on_each?(args..., req)
req.on("success", (m) =>
if m.status?
@assert m.status is "success", "requester failure: #{@pretty_json m}"
callback(null, m)
).on("error", (m) =>
@assert false, @pretty_json m
).request()
),
(error, results_list) ->
on_results?(results_list)
callback(error, results_list)
)
################################# ALIAS MANAGER ################################
class AliasManager
constructor: (@config, remote, aliases) ->
'''
@config
includes `accounts` property, with structure same as that exported
in testconfig.js
@remote
a Remote object
@aliases
A list of aliases
'''
@add_accounts_to_config(@config, aliases)
@set_test_account_secrets(remote, @config)
@realias_issuer = @create_issuer_realiaser()
@alias_lookup = @create_alias_lookup()
create_alias_lookup: ->
lookup = {}
for nick,acc of @config.accounts
lookup[acc.account] = nick
lookup
lookup_alias: (address) ->
@alias_lookup[UInt160.json_rewrite address]
pretty_json: (v) =>
@realias_issuer pretty_json(v)
add_accounts_to_config: (config, accounts) ->
for account in accounts
if not config.accounts[account]?
acc = config.accounts[account] = {}
user = new TestAccount(account)
acc.account = user.address
acc.secret = user.master_seed
set_test_account_secrets: (remote, config) ->
# TODO: config.accounts
for nick,acc of config.accounts
# # Index by nickname ...
remote.set_secret nick, acc.secret
# # ... and by account ID
remote.set_secret acc.account, acc.secret
amount_key: (amt) ->
currency = amt.currency().to_json()
issuer = @realias_issuer amt.issuer().to_json()
"#{currency}/#{issuer}"
create_issuer_realiaser: ->
users = @config.accounts
lookup = {}
accounts = []
for name, user of users
accounts.push user.account
lookup[user.account] = name
realias = new RegExp(accounts.join("|"), "g")
(str) -> str.replace(realias, (match) ->lookup[match])
############################# LEDGER STATE COMPILER ############################
exports.LedgerState = class LedgerState
parse_amount: (amt_val) ->
parse_amount(amt_val)
amount_key: (amt) ->
@am.amount_key amt
record_iou: (account_id, amt)->
key = @amount_key amt
@assert @declaration.accounts[key.split('/')[1]]?,
"Account for #{key} does not exist"
a_ious = @ensure account_id, @ious
@assert !a_ious[key]?,
"Account #{account_id} has more than one amount for #{key}"
a_ious[key] = amt
ensure: (account_id, obj, val) ->
if not obj[account_id]?
obj[account_id] = val ? {}
obj[account_id]
record_xrp: (account_id, amt)->
@assert !@accounts[account_id]?,
"Already declared XRP for #{account_id}"
@accounts[account_id] = amt
record_trust: (account_id, amt, is_balance) ->
key = @amount_key amt
a_trusts = @ensure account_id, @trusts_by_ci
if a_trusts[key]? and !is_balance
cmp = amt.compareTo a_trusts[key]
@assert cmp != - 1,
"Account #{account_id} trust is less than balance for #{key}"
a_trusts[key] = amt
compile_explicit_trusts: ->
for account_id, account of @declaration.accounts
if not account.trusts?
continue
for amt_val in account.trusts
amt = @parse_amount amt_val
@assert amt != null and !amt.is_native(),
"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
@assert account.balance?,
"No balance declared for #{account_id}"
for amt_val in account.balance
trust = null
balance_trust = parse_balance_and_trust(amt_val)
if balance_trust?
[amt_val, trust_val] = balance_trust
trust = @parse_amount trust_val
@assert trust != null,
"Trust amount #{trust_val} specified for #{account_id} "
"is not valid"
amt = @parse_amount amt_val
@assert amt != null,
"Balance amount #{amt_val} specified for #{account_id} "
"is not valid"
if amt.is_native()
xrp_balance = @record_xrp(account_id, amt)
else
@record_iou(account_id, amt)
@record_trust(account_id, trust ? amt, true)
@assert xrp_balance,
"No XRP balanced declared for #{account_id}"
undefined
compile_offers: ->
for account_id, account of @declaration.accounts
if not account.offers?
continue
for offer in account.offers
[pays, gets, splat...] = offer
gets_amt = @parse_amount gets
@assert gets_amt != null,
"For account #{account_id} taker_gets amount #{gets} is invalid"
pays_amt = @parse_amount pays
@assert pays_amt != null,
"For account #{account_id} taker_pays amount #{pays} is invalid"
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 = 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
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}"
key_offers.total = new_total
offers_all.push [pays_amt, gets_amt, splat...]
@offers = []
for account_id, obj of @offers_by_ci
for offer in obj.offers
sliced = offer[0..]
sliced.unshift account_id
@offers.push sliced
# @offers[account_id] = obj.offers
@offers
base_reserve: ->
@declaration.reserve?.base ? "200.0"
incr_reserve: ->
@declaration.reserve?.base ? "50.0"
check_reserves: ->
base_reserve_amt = @base_reserve()
incr_reserve_amt = @incr_reserve()
base_reserve = @parse_amount base_reserve_amt
incr_reserve = @parse_amount incr_reserve_amt
@assert base_reserve != null,
"Base reserve amount #{base_reserve_amt} is invalid"
@assert incr_reserve != null,
"incremental amount #{incr_reserve_amt} is invalid"
for account_id, account of @declaration.accounts
total_needed = base_reserve.clone()
owner_count = 0
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]?
owner_count += Object.keys(@trusts_by_ci[account_id]).length
owner_count_amount = Amount.from_json(String(owner_count))
inc_reserve_n = owner_count_amount.multiply(incr_reserve)
total_needed = total_needed.add(inc_reserve_n)
@assert @accounts[account_id].compareTo(total_needed) != - 1,
"Account #{account_id} needs more XRP for reserve"
@reserves[account_id] = total_needed
format_payments: ->
# We do these first as the following @ious need xrp to issue ious ;0
for account_id, xrps of @accounts
@xrp_payments.push ['root', account_id, xrps]
for account_id, ious of @ious
for curr_issuer, amt of ious
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
setup_alias_manager: ->
@am = new AliasManager(@config, @remote, Object.keys(@declaration.accounts))
@realias_issuer = @am.realias_issuer
pretty_json: (v) =>
@realias_issuer pretty_json(v)
constructor: (declaration, @assert, @remote, @config) ->
@declaration = declaration
@accounts = {} # {$account_id : $xrp_amt}
@trusts_by_ci = {} # {$account_id : {$currency/$issuer : $iou_amt}}
@ious = {} # {$account_id : {$currency/$issuer : $iou_amt}}
@offers_by_ci = {} # {$account_id : {offers: [], $currency/$issuer : {total: $iou_amt}}}
@reserves = {}
@xrp_payments = [] # {$account_id: []}
@trusts = [] # {$account_id: []}
@iou_payments = [] # {$account_id: []}
@offers = [] # {$account_id: []}
@setup_alias_manager()
@compile_accounts_balances_and_implicit_trusts()
@compile_explicit_trusts()
@compile_offers()
@check_reserves()
@format_payments()
@format_trusts()
@add_transaction_fees()
compile_to_rpc_commands: ->
passphrase = (src) ->
if src == 'root'
'masterpassphrase'
else
src
make_tx_json = (src, tt) ->
{"Account": UInt160.json_rewrite(src), "TransactionType": tt}
submit_line = (src, tx_json) ->
"build/rippled submit #{passphrase(src)} '#{JSON.stringify tx_json}'"
lines = []
ledger_accept = -> lines.push('build/rippled ledger_accept')
for [src, dst, amount] in @xrp_payments
tx_json = make_tx_json(src, 'Payment')
tx_json.Destination = UInt160.json_rewrite dst
tx_json.Amount = amount.to_json()
lines.push submit_line(src, tx_json)
ledger_accept()
for [src, limit] in @trusts
tx_json = make_tx_json(src, 'TrustSet')
tx_json.LimitAmount = limit.to_json()
lines.push submit_line(src, tx_json)
ledger_accept()
for [src, dst, amount] in @iou_payments
tx_json = make_tx_json(src, 'Payment')
tx_json.Destination = UInt160.json_rewrite dst
tx_json.Amount = amount.to_json()
lines.push submit_line(src, tx_json)
ledger_accept()
for [src, pays, gets, flags] in @offers
tx = new Transaction({secrets: {}})
tx.offer_create(src, pays, gets)
tx.set_flags(flags)
lines.push submit_line(src, tx.tx_json)
ledger_accept()
lines.join('\n')
verifier: (decl) ->
new LedgerVerifier(decl ? @declaration, @remote, @config, @assert, @am)
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, account_sets]
for [src, args...] in list
extra = extra_fees[src]
extra = if extra? then extra.add(fee) else fee
extra_fees[src] = extra
for [src, dst, amount], ix in @xrp_payments
if extra_fees[dst]?
@xrp_payments[ix][2] = amount.add(extra_fees[dst])
setup: (log, done) ->
LOG = (m) ->
self.what = m
log(m)
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, _ of @accounts)
self = this
Dump = (v) => console.log @pretty_json(v)
Dump = ->
reqs = new BulkRequests(@remote, @assert, @pretty_json)
async.waterfall [
(cb) ->
reqs.transactor(
Transaction::payment,
self.xrp_payments,
((src, dest, amt) ->
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,
self.trusts,
((src, amt) ->
issuer = self.realias_issuer amt.issuer().to_json()
currency = amt.currency().to_json()
LOG("Account `#{src}` trusts account `#{issuer}` for "+
"#{amt.to_text()} #{currency}") ),
cb)
(cb) ->
reqs.transactor(
Transaction::payment,
self.iou_payments,
((src, dest, amt, tx) ->
LOG("Account `#{src}` is making a payment of #{amt.to_text_full()} "+
"to `#{dest}`") ),
cb)
(cb) ->
reqs.transactor(
Transaction::offer_create,
self.offers,
((src, pays, gets, flags, tx) ->
if not tx?
tx = flags
flags = ['Passive']
else
# TODO: icky ;)
delete tx.tx_json.Expiration
tx.set_flags(flags)
LOG("Account `#{src}` is selling #{gets.to_text_full()} "+
"for #{pays.to_text_full()}")),
cb)
(cb) ->
testutils.ledger_close self.remote, cb
], (error) ->
assert !error,
"There was an error @ #{self.what}"
done()
################################ LEDGER VERIFIER ###############################
ensure = (account_id, obj, val) ->
if not obj[account_id]?
obj[account_id] = val ? {}
obj[account_id]
exports.LedgerVerifier = class LedgerVerifier
constructor: (@declaration, @remote, @config, @assert, @am) ->
@am ?= new AliasManager(@config, @remote, Object.keys(@declaration.accounts))
@requester = new BulkRequests(@remote, @assert, @am.pretty_json)
@compile_declaration()
verify_lines: (errors, account_lines) ->
for account in account_lines
# For test sweet ;)
account_alias = @am.lookup_alias account.account
for line in account.lines
peer_alias = @am.lookup_alias line.account
key = "#{line.currency}/#{peer_alias}"
asserted = @iou_balances[account_alias]?[key]
if asserted?
actual = Amount.from_json(
"#{line.balance}/#{line.currency}/#{line.account}")
if not asserted.equals(actual)
balance = (((errors[account_alias] ?= {})['balance'] ?= {}))
balance[key] =
expected: asserted.to_text()
actual: actual.to_text()
asserted = @trusts[account_alias]?[key]
if asserted?
actual = Amount.from_json(
"#{line.limit}/#{line.currency}/#{line.account}")
if not asserted.equals(actual)
limit = (((errors[account_alias] ?= {})['limit'] ?= {}))
limit[key] =
expected: asserted.to_text()
actual: actual.to_text()
verify_infos: (errors, account_infos) ->
for account in account_infos
root = account.account_data
account_alias = @am.lookup_alias root.Account
asserted = @xrp_balances[account_alias]
if asserted?
actual = Amount.from_json root.Balance
if not asserted.equals(actual)
balance = (((errors[account_alias] ?= {})['balance'] ?= {}))
balance['XRP'] =
expected: asserted.to_human()
actual: actual.to_human()
verify_offers: (errors, account_offers) ->
for account in account_offers
account_alias = @am.lookup_alias account.account
get_errors = -> (((errors[account_alias] ?= {})['offers'] ?= []))
assertions = @offers[account_alias]
continue if not assertions?
amount_text = (amt) => @am.realias_issuer amt.to_text_full()
for asserted, ix in assertions
offer = account.offers[ix]
if not offer?
get_errors().push {expected: asserted, actual: 'missing'}
continue
else
# expected_*
[epays, egets] = (parse_amount a for a in asserted)
# actual_*
apays = Amount.from_json offer.taker_pays
agets = Amount.from_json offer.taker_gets
err = {}
if not epays.equals apays
pay_err = (err['taker_pays'] = {})
pay_err['expected'] = amount_text epays
pay_err['actual'] = amount_text apays
if not egets.equals agets
get_err = (err['taker_gets'] = {})
get_err['expected'] = amount_text egets
get_err['actual'] = amount_text agets
if Object.keys(err).length > 0
offer_errors = get_errors()
offer_errors.push err
verify: (account_infos, account_lines, account_offers) ->
errors = {}
# console.log @am.pretty_json account_infos
# console.log @am.pretty_json account_lines
# console.log @am.pretty_json account_offers
@verify_infos errors, account_infos
@verify_lines errors, account_lines
@verify_offers errors, account_offers
errors
do_verify: (done) ->
args_from_keys = (obj) -> ([a] for a in Object.keys obj)
reqs = @requester
lines_args = args_from_keys @iou_balances
info_args = args_from_keys @xrp_balances
offers_args = args_from_keys @offers
async.series [
(cb) ->
reqs.requester(Remote::request_account_info, info_args, cb)
(cb) ->
reqs.requester(Remote::request_account_lines, lines_args, cb)
(cb) ->
reqs.requester(Remote::request_account_offers, offers_args, cb)
], (error, results) =>
assert !error,
"There was an error @ #{error}"
done(@verify(results...))
compile_declaration: ->
@offers = {}
@xrp_balances = {}
@iou_balances = {}
@trusts = {}
@realias_issuer = @am.realias_issuer
record_amount = (account_id, to, amt) =>
key = @am.amount_key amt
ensure(account_id, to)[key] = amt
for account_id, account of @declaration.accounts
if account.offers?
@offers[account_id] = account.offers
if Array.isArray(account.balance)
for value in account.balance
balance = new Balance(value)
if balance.is_native
@xrp_balances[account_id] = balance.amount
else
if balance.limit?
record_amount account_id, @trusts, balance.limit
record_amount account_id, @iou_balances, balance.amount

View File

@@ -1,180 +0,0 @@
var async = require("async");
var assert = require('assert');
var lodash = require('lodash');
var Amount = require("ripple-lib").Amount;
var Remote = require("ripple-lib").Remote;
var Transaction = require("ripple-lib").Transaction;
var Server = require("./server").Server;
var testutils = require("./testutils");
var config = testutils.init_config();
suite('Ledger requests', function() {
var $ = { };
// Array of the ledger output expected from rippled.
// Indexes in this array represent (ledger_index - 1).
var expectedledgers = [
{
"ledger": {
"accepted": true,
"account_hash":
"A21ED30C04C88046FC61DB9DC19375EEDBD365FD8C17286F27127DF804E9CAA6",
"closed": true,
"ledger_index": "1",
"seqNum": "1",
"totalCoins": "100000000000000000",
"total_coins": "100000000000000000",
"transaction_hash":
"0000000000000000000000000000000000000000000000000000000000000000"
},
"ledger_index": 1,
"validated": true
},
{
"ledger": {
"accepted": true,
"account_hash":
"A21ED30C04C88046FC61DB9DC19375EEDBD365FD8C17286F27127DF804E9CAA6",
"close_time_resolution": 30,
"closed": true,
"ledger_index": "2",
"seqNum": "2",
"totalCoins": "100000000000000000",
"total_coins": "100000000000000000",
"transaction_hash":
"0000000000000000000000000000000000000000000000000000000000000000"
},
"ledger_index": 2,
"validated": true
},
{
"ledger": {
"closed": false,
"ledger_index": "3",
"seqNum": "3"
},
"ledger_current_index": 3,
"validated": false
}
];
// Indicates the ledger (as the index into
// expectedLedgers above) that is expected
// from rippled when it is requested by name.
var expectedIndexByLedgerName = {
"validated": 1,
"closed": 1,
"current": 2
};
function filterFields(ledger) {
if (typeof ledger === 'object') {
ledger = lodash.omit(ledger, ['close_time', 'close_time_human', 'hash', 'ledger_hash', 'parent_hash']);
Object.keys(ledger).map(function(key) {
ledger[key] = filterFields(ledger[key]);
});
}
return ledger;
}
function goodLedger(ledgerSelection, self, callback) {
self.what = "Good Ledger " + ledgerSelection;
var request = $.remote.request_ledger(ledgerSelection)
.on('success', function(ledger) {
ledger = filterFields(ledger);
var expectedIndex = expectedIndexByLedgerName[ledgerSelection];
if (typeof expectedIndex === 'undefined')
expectedIndex = ledgerSelection - 1;
assert.deepEqual(ledger, expectedledgers[expectedIndex]);
callback();
})
.on('error', function(ledger) {
assert(false, self.what);
})
.request();
}
setup(function(done) {
testutils.build_setup().call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test("get ledgers by index", function(done) {
var self = this;
async.waterfall([
function (callback) {
goodLedger(1, self, callback);
},
function (callback) {
goodLedger(2, self, callback);
},
function (callback) {
goodLedger(3, self, callback);
},
function (callback) {
self.what = "Bad Ledger (4)";
var expected =
{
"error": "remoteError",
"error_message": "Remote reported an error.",
"remote": {
"error": "lgrNotFound",
"error_code": 20,
"error_message": "ledgerNotFound",
"id": 4,
"request": {
"command": "ledger",
"id": 4,
"ledger_index": 4
},
"status": "error",
"type": "response"
}
};
var request = $.remote.request_ledger(ledgerSelection)
.on('success', function(ledger) {
assert(false, self.what);
})
.on('error', function(ledger) {
assert.deepEqual(ledger, expectedledgers[ledgerSelection - 1]);
callback();
})
.request();
},
],
function(error) {
assert(!error, self.what);
done();
});
});
test("get ledgers by name", function(done) {
var self = this;
async.waterfall([
function (callback) {
goodLedger("validated", self, callback);
},
function (callback) {
goodLedger("closed", self, callback);
},
function (callback) {
goodLedger("current", self, callback);
},
],
function(error) {
assert(!error, self.what);
done();
});
});
});

View File

@@ -1,107 +0,0 @@
require('babel/register');
var extend = require('extend');
var mocha = require('mocha');
var ripplelib = require('ripple-lib');
// Optionally use a more useful (but noisy) logger
if (process.env.USE_RCONSOLE) {
require('rconsole');
};
// Stash a reference away to this
extend(ripplelib, ripplelib._DEPRECATED);
var config = ripplelib.config = {
load: function (newOpts) {
extend(ripplelib.config, newOpts);
return config;
}
}
// camelCase to under_scored API conversion
function attachUnderscored(c) {
var o = ripplelib[c];
Object.keys(o.prototype).forEach(function(key) {
var UPPERCASE = /([A-Z]{1})[a-z]+/g;
if (!UPPERCASE.test(key)) {
return;
}
var underscored = key.replace(UPPERCASE, function(c) {
return '_' + c.toLowerCase();
});
o.prototype[underscored] = o.prototype[key];
});
};
[ 'Remote',
'Request',
'Transaction',
'Account',
'Server'
].forEach(attachUnderscored);
var Remote = ripplelib.Remote;
Remote.from_config = function(obj, trace) {
var serverConfig = (typeof obj === 'string') ? config.servers[obj] : obj;
var remote = new Remote(serverConfig, trace);
function initializeAccount(account) {
var accountInfo = config.accounts[account];
if (typeof accountInfo === 'object') {
if (accountInfo.secret) {
// Index by nickname
remote.setSecret(account, accountInfo.secret);
// Index by account ID
remote.setSecret(accountInfo.account, accountInfo.secret);
}
}
};
if (config.accounts) {
Object.keys(config.accounts).forEach(initializeAccount);
}
return remote;
};
var amountParse = ripplelib.Amount.prototype.parse_json;
ripplelib.Amount.prototype.parse_json = function(j) {
if (typeof j === 'string'/* || typeof j === 'number'*/) {
/*j = String(j);*/
if (j.match(/^\s*\d+\.\d+\s*$/)) {
j = String(Math.floor(parseFloat(j, 10) * 1e6));
}
}
return amountParse.call(this, j);
}
var accountParse = ripplelib.UInt160.prototype.parse_json;
ripplelib.UInt160.prototype.parse_json = function(j) {
if (config.accounts[j]) {
j = config.accounts[j].account;
}
return accountParse.call(this, j);
}
var oldLoader = mocha.prototype.loadFiles
if (!oldLoader.monkeyPatched) {
// Gee thanks Mocha ...
mocha.prototype.loadFiles = function() {
try {
oldLoader.apply(this, arguments);
} catch (e) {
// Normally mocha just silently bails
console.error(e.stack);
// We throw, so mocha doesn't continue trying to run the test suite
throw e;
}
}
mocha.prototype.loadFiles.monkeyPatched = true;
};

View File

@@ -1,6 +0,0 @@
--require ./test/mocha-loader-patch.js
--reporter spec
--compilers coffee:coffee-script/register
--ui tdd
--timeout 10000
--slow 600

View File

@@ -1,53 +0,0 @@
var async = require("async");
var assert = require('assert');
var Remote = require("ripple-lib").Remote;
var testutils = require("./testutils");
var config = testutils.init_config();
suite('Monitor account', function() {
var $ = { };
setup(function(done) {
testutils.build_setup().call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('monitor root', function() {
var self = this;
var steps = [
function (callback) {
self.what = "Create accounts.";
testutils.create_accounts($.remote, "root", "10000", ["alice"], callback);
},
function (callback) {
self.what = "Close ledger.";
$.remote.once('ledger_closed', function () {
callback();
});
$.remote.ledger_accept();
},
function (callback) {
self.what = "Dumping root.";
testutils.account_dump($.remote, "root", function (error) {
assert.ifError(error);
callback();
});
}
]
async.waterfall(steps, function(error) {
assert(!effor, self.what);
done();
});
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,378 +0,0 @@
let assert = require('assert');
let _ = require('lodash');
let async = require('async');
let testutils = require('./testutils');
let config = testutils.init_config();
let accounts = require('./testconfig').accounts;
let Amount = require('ripple-lib').Amount;
let Transaction = require('ripple-lib').Transaction;
suite('MultiSign', function() {
let $ = {};
let opts = {};
setup(function(done) {
testutils.build_setup(opts).call($, done);
});
setup(function(done) {
$.remote.local_signing = false;
testutils.create_accounts(
$.remote,
'root',
Amount.from_human('1000 XRP'),
['alice', 'bob', 'carol'],
done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
function getAliceSequence(callback) {
return $.remote.account('alice')._transactionManager._nextSequence;
}
test('remote signing', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
{account: accounts.carol.account, weight: 2 }
],
signerQuorum: 3
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
[accounts.bob, accounts.carol].forEach(account => {
let signer = mTx.multiSign( account.account, account.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
});
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tesSUCCESS');
assert.deepEqual(res.tx_json.Signers, tx.getMultiSigners());
callback();
});
tx.submit();
}
], done);
});
test('local signing', function(done) {
$.remote.local_signing = true;
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
{account: accounts.carol.account, weight: 2 }
],
signerQuorum: 3
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
[accounts.bob, accounts.carol].forEach(account => {
let signer = mTx.multiSign( account.account, account.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
});
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tesSUCCESS');
assert.deepEqual(res.tx_json.Signers, tx.getMultiSigners());
callback();
});
tx.submit();
}
], done);
});
test('No multi-signers specified for account', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
let signer = mTx.multiSign(accounts.bob.account, accounts.bob.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tefNOT_MULTI_SIGNING');
callback();
});
tx.submit();
}
], done);
});
test('Attempt to use unspecified signer', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
],
signerQuorum: 1
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
let signer = mTx.multiSign(accounts.carol.account, accounts.carol.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tefBAD_SIGNATURE');
callback();
});
tx.submit();
}
], done);
});
test('Unmet quorum', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
{account: accounts.carol.account, weight: 1}
],
signerQuorum: 2
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
let signer = mTx.multiSign(accounts.bob.account, accounts.bob.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'tefBAD_QUORUM');
callback();
});
tx.submit();
}
], done);
});
test('Unreachable quorum', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
],
signerQuorum: 2
});
tx.once('submitted', function(res) {
assert.strictEqual(res.engine_result, 'temBAD_QUORUM');
callback();
});
tx.submit();
},
], done);
});
test('Invalid signature -- modified tx_json', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
],
signerQuorum: 1
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
// Tamper with transaction data prior to multi-signing
mTx.setSequence(getAliceSequence() + 1);
let signer = mTx.multiSign(accounts.bob.account, accounts.bob.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('submitted', function(res) {
assert(res.error)
assert.strictEqual(res.remote.error_message, 'Invalid signature.')
callback();
});
tx.submit();
}
], done);
});
test('Invalid signature -- malformed signer', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
],
signerQuorum: 1
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
let signer = mTx.multiSign(accounts.bob.account, accounts.bob.secret);
// Tamper with signer after multi-signing
signer.Account = accounts.carol.account;
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('submitted', function(res) {
assert(res.error)
assert.strictEqual(res.remote.error_message, 'Invalid signature.')
callback();
});
tx.submit();
}
], done);
});
test('Invalid signature -- SigningPubKey non-empty', function(done) {
async.series([
function(callback) {
let tx = $.remote.createTransaction('SignerListSet', {
account: accounts.alice.account,
signers: [
{account: accounts.bob.account, weight: 1},
],
signerQuorum: 1
});
testutils.submit_transaction(tx, callback);
},
function(callback) {
let tx = $.remote.createTransaction('AccountSet', {account: accounts.alice.account});
tx.setFee(100);
tx.setSequence(getAliceSequence());
tx.setLastLedgerSequenceOffset(5);
let mTx = Transaction.from_json(tx.getMultiSigningJson());
let signer = mTx.multiSign(accounts.bob.account, accounts.bob.secret);
assert(signer.Account);
assert(signer.SigningPubKey);
assert(signer.TxnSignature);
tx.addMultiSigner(signer);
tx.once('presubmit', function(res) {
// SigningPubKey must be empty
tx.setSigningPubKey(tx.getSigningPubKey());
});
tx.once('submitted', function(res) {
assert(res.error);
assert.strictEqual(res.remote.error, 'invalidParams');
callback();
});
tx.submit();
}
], done);
});
});

View File

@@ -1,417 +0,0 @@
var async = require('async');
var assert = require('assert');
var ripple = require('ripple-lib');
var Amount = require('ripple-lib').Amount;
var Remote = require('ripple-lib').Remote;
var Transaction = require('ripple-lib').Transaction;
var Server = require('./server').Server;
var testutils = require('./testutils');
var config = testutils.init_config();
suite('NoRipple', function() {
var $ = { };
setup(function(done) {
testutils.build_setup().call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('set and clear NoRipple', function(done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Create accounts.';
testutils.create_accounts($.remote, 'root', '10000.0', [ 'alice' ], callback);
},
function (callback) {
self.what = 'Check a non-existent credit limit';
$.remote.request_ripple_balance('alice', 'root', 'USD', 'current', function(err) {
assert.strictEqual('remoteError', err.error);
assert.strictEqual('entryNotFound', err.remote.error);
callback();
});
},
function (callback) {
self.what = 'Create a credit limit with NoRipple flag';
var tx = $.remote.transaction();
tx.trustSet('root', '100/USD/alice');
tx.setFlags('NoRipple');
tx.once('submitted', function() {
$.remote.ledger_accept();
});
tx.once('error', callback);
tx.once('proposed', function(res) {
callback();
});
tx.submit();
},
function (callback) {
self.what = 'Check no-ripple sender';
$.remote.requestAccountLines({ account: 'root', ledger: 'validated' }, function(err, m) {
if (err) return callback(err);
assert(typeof m === 'object');
assert(Array.isArray(m.lines));
assert(m.lines[0].no_ripple);
callback();
});
},
function (callback) {
self.what = 'Check no-ripple destination';
$.remote.requestAccountLines({ account: 'alice', ledger: 'validated' }, function(err, m) {
if (err) return callback(err);
assert(typeof m === 'object');
assert(Array.isArray(m.lines));
assert(m.lines[0].no_ripple_peer);
callback();
});
},
function (callback) {
self.what = 'Create a credit limit with ClearNoRipple flag';
var tx = $.remote.transaction();
tx.trustSet('root', '100/USD/alice');
tx.setFlags('ClearNoRipple');
tx.once('submitted', function() {
$.remote.ledger_accept();
});
tx.once('error', callback);
tx.once('proposed', function(res) {
callback();
});
tx.submit();
},
function (callback) {
self.what = 'Check no-ripple cleared sender';
$.remote.requestAccountLines({ account: 'root', ledger: 'validated' }, function(err, m) {
if (err) return callback(err);
assert(typeof m === 'object');
assert(Array.isArray(m.lines));
assert(!m.lines[0].no_ripple);
callback();
});
},
function (callback) {
self.what = 'Check no-ripple cleared destination';
$.remote.requestAccountLines({ account: 'alice', ledger: 'validated' }, function(err, m) {
if (err) return callback(err);
assert(typeof m === 'object');
assert(Array.isArray(m.lines));
assert(!m.lines[0].no_ripple_peer);
callback();
});
}
]
async.series(steps, function(err) {
assert(!err, self.what + ': ' + err);
done();
});
});
test('set NoRipple on line with negative balance', function(done) {
// Setting NoRipple on a line with negative balance should fail
var self = this;
var steps = [
function (callback) {
self.what = 'Create accounts';
testutils.create_accounts(
$.remote,
'root',
'10000.0',
[ 'alice', 'bob', 'carol' ],
callback);
},
function (callback) {
self.what = 'Set credit limits';
testutils.credit_limits($.remote, {
bob: '100/USD/alice',
carol: '100/USD/bob'
}, callback);
},
function (callback) {
self.what = 'Payment';
var tx = $.remote.transaction();
tx.buildPath(true);
tx.payment('alice', 'carol', '50/USD/carol');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
$.remote.ledger_accept();
});
tx.submit(callback);
},
function (callback) {
self.what = 'Set NoRipple alice';
var tx = $.remote.transaction();
tx.trustSet('alice', '100/USD/bob');
tx.setFlags('NoRipple');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
$.remote.ledger_accept();
});
tx.submit(callback);
},
function (callback) {
self.what = 'Set NoRipple carol';
var tx = $.remote.transaction();
tx.trustSet('bob', '100/USD/carol');
tx.setFlags('NoRipple');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
$.remote.ledger_accept();
});
tx.submit(callback);
},
function (callback) {
self.what = 'Find path alice > carol';
var request = $.remote.requestRipplePathFind('alice', 'carol', '1/USD/carol', [ { currency: 'USD' } ]);
request.callback(function(err, paths) {
assert.ifError(err);
assert(Array.isArray(paths.alternatives));
assert.strictEqual(paths.alternatives.length, 1);
callback();
});
},
function (callback) {
$.remote.requestAccountLines({ account: 'alice' }, function(err, res) {
assert.ifError(err);
assert.strictEqual(typeof res, 'object');
assert(Array.isArray(res.lines));
assert.strictEqual(res.lines.length, 1);
assert(!(res.lines[0].no_ripple));
callback();
});
}
]
async.series(steps, function(error) {
assert(!error, self.what + ': ' + error);
done();
});
});
test('pairwise NoRipple', function(done) {
var self = this;
var steps = [
function (callback) {
self.what = 'Create accounts';
testutils.create_accounts(
$.remote,
'root',
'10000.0',
[ 'alice', 'bob', 'carol' ],
callback);
},
function (callback) {
self.what = 'Set credit limits';
testutils.credit_limits($.remote, {
bob: '100/USD/alice',
carol: '100/USD/bob'
}, callback);
},
function (callback) {
self.what = 'Set NoRipple alice';
var tx = $.remote.transaction();
tx.trustSet('bob', '100/USD/alice');
tx.setFlags('NoRipple');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
$.remote.ledger_accept();
});
tx.submit(callback);
},
function (callback) {
self.what = 'Set NoRipple carol';
var tx = $.remote.transaction();
tx.trustSet('bob', '100/USD/carol');
tx.setFlags('NoRipple');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tesSUCCESS');
$.remote.ledger_accept();
});
tx.submit(callback);
},
function (callback) {
self.what = 'Find path alice > carol';
var request = $.remote.requestRipplePathFind('alice', 'carol', '1/USD/carol', [ { currency: 'USD' } ]);
request.callback(function(err, paths) {
assert.ifError(err);
assert(Array.isArray(paths.alternatives));
assert.strictEqual(paths.alternatives.length, 0);
callback();
});
},
function (callback) {
self.what = 'Payment';
var tx = $.remote.transaction();
tx.buildPath(true);
tx.payment('alice', 'carol', '1/USD/carol');
tx.once('submitted', function(m) {
assert.strictEqual(m.engine_result, 'tecPATH_DRY');
$.remote.ledger_accept();
callback();
});
tx.submit();
}
]
async.series(steps, function(error) {
assert(!error, self.what + ': ' + error);
done();
});
});
});
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();
});
});
});

View File

@@ -1,91 +0,0 @@
var async = require("async");
var fs = require("fs");
var path = require("path");
var utils = require("ripple-lib").utils;
// Empty a directory.
// done(err) : err = true if an error occured.
var emptyPath = function(dirPath, done) {
fs.readdir(dirPath, function(err, files) {
if (err) {
done(err);
}
else {
async.some(files.map(function(f) { return path.join(dirPath, f); }), rmPath, done);
}
});
};
// Make a directory and sub-directories.
var mkPath = function(dirPath, mode, done) {
fs.mkdir(dirPath, typeof mode === "string" ? parseInt(mode, 8) : mode, function(e) {
if (!e || e.code === "EEXIST") {
// Created or already exists, done.
done();
}
else if (e.code === "ENOENT") {
// Missing sub dir.
mkPath(path.dirname(dirPath), mode, function(e) {
if (e) {
throw e;
}
else {
mkPath(dirPath, mode, done);
}
});
}
else {
throw e;
}
});
};
// Create directory if needed and empty if needed.
var resetPath = function(dirPath, mode, done) {
mkPath(dirPath, mode, function(e) {
if (e) {
done(e);
}
else {
emptyPath(dirPath, done);
}
});
};
// Remove path recursively.
// done(err)
var rmPath = function(dirPath, done) {
// console.log("rmPath: %s", dirPath);
fs.lstat(dirPath, function(err, stats) {
if (err && err.code == "ENOENT") {
done();
}
if (err) {
done(err);
}
else if (stats.isDirectory()) {
emptyPath(dirPath, function(e) {
if (e) {
done(e);
}
else {
// console.log("rmdir: %s", dirPath); done();
fs.rmdir(dirPath, done);
}
});
}
else {
// console.log("unlink: %s", dirPath); done();
fs.unlink(dirPath, done);
}
});
};
exports.mkPath = mkPath;
exports.resetPath = resetPath;
exports.rmPath = rmPath;
// vim:sw=2:sts=2:ts=8:et

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +0,0 @@
module.exports = {
"Partially crossed completely via bridging": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "200/USD/G1"],
"offers": [["100/USD/G1", "88.0"]]},
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
"takerJoe": {"balance": ["500000.0", "500/USD/G1"]}}},
"offer": ["takerJoe", "500/USD/G2", "500/USD/G1"],
"post_ledger": {"accounts": {"takerJoe": {"balance": ["400/USD/G1", "100/USD/G2"],
"offers": [["400/USD/G2", "400/USD/G1"]]}}}
},
"Partially crossed (halt)": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "200/USD/G1"],
"offers": [["100/USD/G1", "88.0"], ["100/USD/G1", "88.0"]]},
//----/ (2) (3) (5) there's no offers left. Halt
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
// (4)
"takerJoe": {"balance": ["500000.0", "500/USD/G1"]}}},
// (1)
"offer": ["takerJoe", "500/USD/G2", "500/USD/G1"],
// 500,000-88 200+100/USD/G1
"post_ledger": {"accounts": {"alice": {"balance": ["499912.0", "300/USD/G1"],
"offers": [["100/USD/G1", "88.0"]]},
"bob": {"balance": ["500088.0", "400/USD/G2"],
"offers": [/*["88.0", "100/USD/G2"]*/]},
"takerJoe": {"balance": ["100/USD/G2", "400/USD/G1"],
"offers": [["400/USD/G2", "400/USD/G1"]]}}}
},
"Partially crossed completely via bridging (Sell)": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "200/USD/G1"],
"offers": [["200/USD/G1", "176.0", "Sell"]]},
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
"takerJoe": {"balance": ["500000.0", "500/USD/G1"]}}},
"offer": ["takerJoe", "500/USD/G2", "500/USD/G1", "Sell"],
"post_ledger": {"accounts": {"alice": {"balance": ["499912.0", "299.9999999999999/USD/G1"],
"offers": [["100.0000000000001/USD/G1", "88.0"]]},
"takerJoe": {"balance": ["100/USD/G2", "400.0000000000001/USD/G1"],
"offers": [["400.0000000000001/USD/G2", "400.0000000000001/USD/G1"]]}}}
},
"Completely crossed via bridging + direct": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "500/USD/G1", "500/USD/G2"],
"offers": [["50/USD/G1", "50/USD/G2"],
["49/USD/G1", "50/USD/G2"],
["48/USD/G1", "50/USD/G2"],
["47/USD/G1", "50/USD/G2"],
["46/USD/G1", "50/USD/G2"],
["45/USD/G1", "50/USD/G2"],
["44/USD/G1", "50/USD/G2"],
["43/USD/G1", "50/USD/G2"],
["100/USD/G1", "88.0"]]},
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
"takerJoe": {"balance": ["500000.0", "600/USD/G1"]}}},
"offer": ["takerJoe", "500/USD/G2", "500/USD/G1"],
"post_ledger": {"accounts": {"takerJoe": {"balance": ["500/USD/G2", "128/USD/G1"]}}}
},
"Partially crossed via bridging + direct": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "500/USD/G1", "500/USD/G2"],
"offers": [["372/USD/G1", "400/USD/G2"],
["100/USD/G1", "88.0"]]},
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
"takerJoe": {"balance": ["500000.0", "600/USD/G1"]}}},
"offer": ["takerJoe", "600/USD/G2", "600/USD/G1"],
"post_ledger": {"accounts": {"takerJoe": {"balance": ["500/USD/G2", "128/USD/G1"],
"offers": [["100/USD/G2", "100/USD/G1"]]}}}
},
"Partially crossed via bridging + direct 2": {
"pre_ledger": {"accounts": {"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"alice": {"balance": ["500000.0", "500/USD/G1", "500/USD/G2"],
"offers": [["372/USD/G1", "400/USD/G2"],
["100/USD/G1", "88.0"]]},
"bob": {"balance": ["500000.0", "500/USD/G2"],
"offers": [["88.0", "100/USD/G2"]]},
"takerJoe": {"balance": ["500000.0", "600/USD/G1"]}}},
"offer": ["takerJoe", "600/USD/G2", "600/USD/G1", "Sell"],
"post_ledger": {"accounts": {"takerJoe": {"balance": ["500/USD/G2", "128/USD/G1"],
"offers": [["128/USD/G2", "128/USD/G1"]]}}}
}
}

View File

@@ -1,219 +0,0 @@
var async = require('async');
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 $ = { };
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';
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_balances(
$.remote,
{
mtgox: '19999999988',
alice: '19999999988',
bob: '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';
var ob = $.remote.createOrderBook({
currency_pays: 'XRP',
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',
Sequence: 3,
TakerGets: { currency: 'USD',
issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v',
value: '10'
},
TakerPays: '4000',
index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61',
owner_funds: '100',
is_fully_funded: true,
quality: "400",
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);
},
function (ob, callback) {
self.what = 'Check order book tracking';
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',
Sequence: 3,
TakerGets:
{ currency: 'USD',
issuer: 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v',
value: '5' },
quality: "400",
TakerPays: '2000',
index: '2A432F386EF28151AF60885CE201CC9331FF494A163D40531A9D253C97E81D61',
owner_funds: '94.5',
is_fully_funded: true,
taker_gets_funded: '5',
taker_pays_funded: '2000' }
]
assert.deepEqual(offers, expected);
callback();
});
},
];
async.waterfall(steps, function (error) {
assert(!error, self.what + ': ' + error);
done();
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,88 +0,0 @@
module.exports = {
"Path Tests #1 (XRP -> XRP) and #2 (XRP -> IOU)": {
"ledger": {"accounts": {"A1": {"balance": ["100000.0",
"3500/XYZ/G1",
"1200/ABC/G3"],
"trusts": ["5000/XYZ/G1",
"5000/ABC/G3"]},
"A2": {"balance": ["10000.0"],
"trusts": ["5000/XYZ/G2",
"5000/ABC/G3"]},
"A3": {"balance": ["1000.0"],
"trusts": ["1000/ABC/A2"]},
"G1": {"balance": ["1000.0"]},
"G2": {"balance": ["1000.0"]},
"G3": {"balance": ["1000.0"]},
"M1": {"balance": ["1000.0",
"25000/XYZ/G2",
"25000/ABC/G3"],
"offers": [["1000/XYZ/G1",
"1000/XYZ/G2"],
["10000.0",
"1000/ABC/G3"]],
"trusts": ["100000/XYZ/G1",
"100000/ABC/G3",
"100000/XYZ/G2"]}}},
"paths_expected": {"T1": {"A1": {"n_alternatives": 0,
"src": "A1",
"send": "10.0",
"dst": "A2",
"via": "XRP"},
"A2": {"comment": "Send to non existing account",
"src": "A1",
"send_comment": "malformed error not great for 10.0 amount",
"send": "200.0",
"dst": "rBmhuVAvi372AerwzwERGjhLjqkMmAwxX",
"via": "XRP",
"n_alternatives": 0}},
"T2": {"A": {"alternatives": [{"amount": "100.0",
"paths": [
["ABC/G3|$"]
]}],
"src": "A2",
"send": "10/ABC/G3",
"dst": "G3",
"via": "XRP",
"debug": 0,
"n_alternatives": 1},
"B": {"alternatives": [{"amount": "10.0",
"paths": [["ABC/G3|$",
"ABC/G3|G3"]]}],
"src": "A1",
"send": "1/ABC/A2",
"dst": "A2",
"via": "XRP",
"n_alternatives": 1},
"C": {"alternatives": [{"amount": "10.0",
"paths": [["ABC/G3|$",
"ABC/G3|G3",
"ABC/A2|A2"]]}],
"src": "A1",
"send": "1/ABC/A3",
"dst": "A3",
"via": "XRP",
"n_alternatives": 1}}}},
"Path Tests #3 (non-XRP to XRP)": {
"ledger": {"accounts": {"A1": {"balance": ["1000.0",
"1000/ABC/G3"]},
"A2": {"balance": ["1000.0",
"1000/ABC/G3"]},
"G3": {"balance": ["1000.0"]},
"M1": {"balance": ["11000.0",
"1200/ABC/G3"],
"offers": [["1000/ABC/G3",
"10000.0"]],
"trusts": ["100000/ABC/G3"]}}},
"paths_expected": {"T3": {"A": {"alternatives": [{"amount": "1/ABC/A1",
"paths": [["ABC/G3|G3",
"XRP|$"]]}],
"src": "A1",
"dst": "A2",
"debug":false,
"send": "10.0",
"via": "ABC"}}}}
}

View File

@@ -1,12 +0,0 @@
var fs = require('fs');
var path = require('path');
var joinPath = path.join.bind(path, __dirname);
fs.readdirSync(joinPath('ripple-lib')).forEach(function(fileName) {
var src_path = joinPath('ripple-lib', fileName);
var dst_path = joinPath('../node_modules/ripple-lib/dist/npm/core/', fileName);
console.log(src_path + ' > ' + dst_path);
fs.writeFileSync(dst_path, fs.readFileSync(src_path));
});

View File

@@ -1,153 +0,0 @@
exports.test_accounts = [
'rBmhuVAvi372AerwzwERGjhLjqkMmAwxX',
'r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf',
'rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD',
'rrnsYgWn13Z28GtRgznrSUsLfMkvsXCZSu',
'rJsaPnGdeo7BhMnHjuc3n44Mf7Ra1qkSVJ',
'rnYDWQaRdMb5neCGgvFfhw3MBoxmv5LtfH',
'r31PEiKfa3y6xTi7uBcSp7F3nDLvVMmqyi',
'rfM5xD2CY6XB8o1WsWoJ3ZHGkbHU4NYXr',
'r9MB1RNWZChfV3YdLrB3Rm5AoMULewDtiu',
'rH15iZg9KFSi7d1usvcsPerUtg7dhpMbk4',
'rGWYwGaczQWiduWkccFZKXfp5nDRPqNBNS',
'rBU1EP5oMwKxWr1gZnNe7K8GouQTBhzUKs',
'rwiTxuknPNeLDYHHLgajRVetKEEwkYhTaQ',
'rEbq9pWn2knXFTjjuoNNrKgQeGxhmispMi',
'rJfBCsnwSHXjTJ4GH5Ax6Kyw48X977hqyq',
'rado7qRcvPpS8ZL8SNg4SG8kBNksHyqoRa',
'rNgfurDhqvfsVzLr5ZGB3dJysJhRkvJ79F',
'rBtVTnNgX3uR3kyfVyaQ6hjZTdk42ay9Z3',
'rND1XyLAU9G2ydhUgmRo4i2kdrSKgYZc31',
'rHd21p9Gb834Ri4pzRzGFJ7PjRzymWuBWu',
'rhfvFTgpPzCvFPgbA9Nvz42mX92U5ak92m',
'rByT8y4BUX1Kcc6xotEabCgwc5PGsbTfSv',
'rPBrMbL7uGuteU49b2ibcEBSztoWPX4srr',
'rQGRcZn2RyXJL3s7Dfqqzc96Juc7j9j6i9',
'rwZrLewGghBMj29kFq7TPw9h9p5eAQ1LUp',
'rM9qHXk5uifboWrpu9Wte6Gjzbe974nZ4z',
'rBt69hdwMeBgmAtM9YwuFAKxjMqgaBLf3F',
'rHpcrpggafr5NNn7mafPhaeB8PMYKXGahp',
'rsMKP5MSoyve54o7LgwnZzFfGKzAK8SE5F',
'rfN3ccsNxPt41MjHNZRk7ek7q4TpPLqUzL',
'rDRWocPjBdhKZSWZezbsnwNpALcJ6GqSGf',
'rsmJ4tEMWpcK2qcEMp9uoUv4Ht5Nd6cGTV',
'rsxWsnMVRnFozrKJV2VZ1SG6UNbEeHYu16',
'r4KoiD6MpaQNPzBka3FRLREkx6EZFwynY4',
'rUdovjorVqxyemu5jbpfqA6DYDLdD4eYcj',
'r4MrNttmbdiJ7DjWh1MCKW3Kh7kfML46TA',
'rndYw73Btcm9Pv9gssZY1S9UcDUPLnpip7',
'rh8MnoZmAeWyLx7X8bJZqyjZ48mv1og5PS',
'rBoJvU7pcvoy5hjDMMTDNVG4YG85Ed3MEq',
'rs4f1BwdNgXAHWLT8rZgW2T1RKSBNY4iDz',
'rEmhxShqw42EPm7bY7df5uQySZBkQWnqae',
'rNerRdGnbZP6wej22zBdoTUfQKWoMDTH7d',
'rDyXvd2WFALJovh76uLe5kUrJ7QLpgmQYE',
'rUVi1L28AsCvieXP5pMqPHA9WAfsvCDUjU',
'rscuoJN9um2VM4xVv386X5T9APtExFKsbB',
'raeeyPs6g5xQn5jyNQbCZ6QeLrqu3FrFvb',
'r9UqovJD979WTfNEWXxDU2CVj3K1yo2mqG',
'rfRjsAqM1MEuSbWzuLcD6EhSZazgqvZSjy',
'rUL4CAxmfpNqDXsQTPCK9ZJ8zHqhUvDWfw',
'rP6ZRDFZxjQqeAgdBh1YQSQjWNSASpCL7N',
'rsV4AtAqsdyRyZ8s4kaWbM21EPwY5fonx5',
'rHaKEMyJErGY6VaKuTj16fSheTp4BRpWG1',
'rELHJtahsRpSiSj1nfkY5yKRHCCyRgynw4',
'rLYtaGnw4xK86J6mTsLfayyREoYaPPr8Cj',
'rD5pAYUfZypmJRSrJnBy3pYo5ApHqw5Jt5',
'rfYQrqwNXoA8e2gBDmiHAJAMYrASdQvqDm',
'rESt1CB9Sqaj8PYj8SV9x76iwMGBFPzLHb',
'rHZWAXh3NdQbyksKzDRLeP9ui32TcqssHZ',
'rK9iNjw5SozqKj5zNervwQQTLAgu8V813j',
'rUjpFBSmZ8F6cP16VxqpdAXCVCW3rBSZyn',
'raPib2vNQAjhh47fVQ7PswKaX1daNBSs2G',
'rwhuqz7FppLNvLWdxs7TLLW9UDVztFbw9z',
'rJYRe27KXWTjs4P3uu1d4x58Pk5Y13DbUg',
'rLFxCuE2GHq38wFUHpswgHJAcz6EUhPimC',
'rAaQrzi5satsth174EogwdtdxLZRW5n1h',
'rB18Rxdv1aPYtf9nDFpNPJ2HA5BBAqmyoG',
'rDSaTM6nCSrc1vH8pPcTAwQpmvb9Y6M2gw',
'rpmeCBJUpp9ij1nRM23tRGesWjY7chSHqs',
'rwQz7ZkCGdQt7iiuWC3EpbbKwWdL7uLT9C',
'rULwRizwxjBwDjcaA44Tbh9MjM5TZFTcLj',
'rGMZBEGHbSoegfvqXC2ajXe7RM2Z1LK65N',
'rGyFSppE8G7cELAdBU5yL7kWodbw29YdpN',
'rJYN3qZsjWhH6bzXX6ZHMZewkMPHeEyGNb',
'rEgZVpVSs75eEh6KM4HvcvG64p2TZBL4DC',
'rBRfZaqSAkXZYQWBfoy4sN2Q7zZHVGiGwU',
'rwg1DRGXLTzQpoWtS35mDowN4PSFQ732eQ',
'rKZvkY3T6ahhqkWLTQDSdB1MTKFbbnkqBX',
'rGXgpwvaAZ7rBmoSKFUrd83N7WN3Lm4vuX',
'rhCMsQ3SJa5Wb4AvBe27hxBQaQQjDG1LG4',
'rNtvcU3ePYpZnuYKG77pjRxtKJJ1yrutbm',
'rsfK2cTikveAeyvSG8F62c4VFUvZBsH5Rf',
'rJT2LrXe7hH1pMEnhEkCMznwtgKuYJS7uz',
'rE4Fi4GVjo9NY2g6MtMbitjenUZ21zFoSG',
'rp39zV6AFPRJ7yrrL1PSPC1s3oKM2uv2iW',
'raCoTW3mhdK6WGUZUSEuvbFM34CSGRRHut',
'rKZD9yCV7XAgKq3Rj3a5DmqHHkHBd3ZYao',
'rfKtpLEQz8bGCVtQsEzD8cJKeo6AW6J2pD',
'rJqNyWJ3rovwkWFdwPhCc6m3jpFogGzRr9',
'r41fiShunXNNgJjTjqU9whsjnSYU1BXsjY',
'r3uHcWgsNwowCBGF5rhCP5dfdjpYmByBbJ',
'r4GZm8WnwX5E9cr8uGiLX42y7KN2NaLqYn',
'rKBVsMWErdH443FUkaT799CRVyY9XnVyCK',
'razu52accAWWHjxhWEHXNLHWCDhhs1L79p',
'rHaL5e3niiikv6KJG4UARqpcjyD5tKNgyV',
'rMfcPdGcB5y9zEYw91t3QG2Qj1Z7tqms3S',
'r3mqtPNiwkLKisvbnFjM9eC8Rm9JUEXLMD',
'rKJGLaJr5SFjZ43BkDeqWKWAtGy1qAAkPf',
'rM2zQeTDrt6sMM936Gxh263t1qx3hEb7XK',
'rGtqoQJGe4zWenC3yWH9pByTAsGPcjmTU2',
'raNUsR5m8jsmb9o9mpNZGGyV86aZUYPunr',
'rManQrKu85ezooZ11UmXbxejw5EYHqiUZm',
'rKrjCBtwnhQeYkJKrVjDA5CaR5W9NPN2Bb',
'rcwyaWqsvGXMRyVW2KHnGUV2MhJVhVx2p',
'rLozhAmzcwtEgr1bA28GUh2kbf5kFBMhph',
'rJqt8XVcGdpfjZmujoTc6ArHQdZZVhADgM',
'rPthXZ93cGAtHJrnAF58vZz6E1js8o6fVf',
'rL1LH68PG93BuAeLsiM4cxeBtFmxsoGhRB',
'rndqHX6xLknzbgmQPXL4DvhNLRANYf8cDA',
'rNEnd2tV3Z1aL5kQmShmHGM3uciSJ4jakF',
'rGqPPWnLVWDwWXf495qdxp4um1BTw8TBh5',
'rhEDG4mzWGXjjfQp4WMCpZPBDyug5ays9e',
'raR6MJ2dYxj1PECUKnLbeamM1k8FnYsY34',
'rDcC1S6TRNgTMTgpuSzhAZdTdQJ5EHVf8X',
'rLHtg3bWtMKNDYXydd7frGB5pvFbtqzTjg',
'rnHhh7qXu2trXuC8aD3Zm7gvM9YetRkEDB',
'rfwwUrJztrR7PeKzELEoC1cjcBpsqHmxws',
'rw4KzQgZwPJDYX4wHc3NgsLjdR12JGAKsF',
'rPCbN5kfEjNgP7TBN3VDJf8eUhv1zCreRL',
'r32W6FTsguE3hMv3h79eoNVSJutsDDw6rH',
'rBSDvMzyxsea8Zgy8EfryZ7cc1QGrVp9Do',
'rfz1TPVJPQYbpSxi6oTiWyhNqy4J1Vz7VL',
'rKCyiG5sKxqmKoT2NGT9zS86gS26m9HrQa',
'rK7WRdHd6aNqCu2tNbebxYrQkP5u2DouS3',
'rLC3xWV4N3ohGxxzcueDrxPLHNqbXrmpUK',
'rDE2MwprS1vFvknSKLZJfmxKmbVuE2F85',
'r91o1Huejw2qqz37b22Ev8igM4V2Rog1jv',
'rEhGRU4P7pVjuxco5BgoXz1974QESKQ1Wy',
'raVZ3Uk3qmKqe5aV1ifQsRFL6gU7m25Y2L',
'r9p5buUwdMmZ5Um5uK5gC9piRd1fBzMPBN',
'r4ruPrbfwnkp5aDSNzQXnWcEK64hJGRnDG',
'rwsHCfEwi3PzsfcpcaWLbHXZ2zG5FtNX1J',
'r4DkAh7hbTX4E8JPp4kAWkQYazW6236dBB',
'rGZSqs5KHQKEJMCaDS3iyWagymixa9zuCv',
'r9Nn5Le1bourefaZJ79K2h5VnREFpTqjUw',
'rh7NSNpXR9mwWFF8uXvaWjcLVRPfxqQfM9',
'rpdxPDQ8mV8gadRcDBh7kNFj1mxYjfddh5',
'rMHxgnXgo68vDLUj327YtsHjQyiHGjeccj',
'raDd7xZ3RYvKcGiojy9h5UqFV7WULv626i',
'rEHzCw3nAuHj66C3xKnGFxV6zDBAssNdSY',
'rfvWW9qgyNKYVuJ212himJ7mt6fvkUaS7E',
'r9ctoegJfquoj1RHo1bxuW9MjWhbjHyZSA',
'rsNPHLq4CgxGoenX67jpuabKe8hx1QvLZk',
'rLZouhBz2CpT3ULfJjt193gsLNR4TxjdUS',
'rKuUjCThS3DBqfDAupxTvyhkWaskxHoWSP',
'rKwSstBmYbKFM3CFw8hvBqsU8pfcZMgNwA',
'rBN8iGgpbCc6KYNE36D67TeS6t1YXwmTQc',
'rGvBvCyqwn5kPuRUErGD7idtKRb7LtptrS',
'r3h89HbdaYJBVd1wfHV6GxDj6Pf2s5iXnF',
'rfbUVWFke9JzbK6BhkuayCgX2MVoJvgxBk',
'rwTXRBQvPKPMskjp9By1kLJ1pPdRAg77Rz',
'rw4ve76xnh8cPwVpE58i46s39MbQ7x1m5W',
'r32XzGdUP3GuNJXDGxFM2cYkw7K1KrwXdt' ]

View File

@@ -1,192 +0,0 @@
var assert = require('assert');
var Remote = require('ripple-lib').Remote;
var testutils = require('./testutils.js');
var config = testutils.init_config();
suite('Remote functions', function() {
var $ = { };
setup(function(done) {
testutils.build_setup().call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('request_ledger with ledger index', function(done) {
var request = $.remote.request_ledger();
request.ledger_index(3);
request.callback(function(err, m) {
assert(!err);
assert.strictEqual(m.ledger.ledger_index, '3');
done();
});
});
test('request_ledger with ledger index string', function(done) {
var request = $.remote.request_ledger();
request.ledger_index('3');
request.callback(function(err, m) {
assert(err);
assert.strictEqual(err.error, 'remoteError');
assert.strictEqual(err.remote.error, 'invalidParams');
done();
});
});
test('request_ledger with ledger identifier', function(done) {
var request = $.remote.request_ledger();
request.ledger_index('current');
request.callback(function(err, m) {
assert(!err);
assert.strictEqual(m.ledger.ledger_index, '3');
done();
});
});
test('request_ledger_current', function(done) {
$.remote.request_ledger_current(function(err, m) {
assert(!err);
assert.strictEqual(m.ledger_current_index, 3);
done();
});
});
test('request_ledger_hash', function(done) {
$.remote.request_ledger_hash(function(err, m) {
assert(!err);
assert.strictEqual(m.ledger_index, 2);
done();
})
});
test('manual account_root success', function(done) {
var self = this;
$.remote.request_ledger_hash(function(err, r) {
//console.log('result: %s', JSON.stringify(r));
assert(!err);
assert('ledger_hash' in r, 'Result missing property "ledger_hash"');
var request = $.remote.request_ledger_entry('account_root')
.ledger_hash(r.ledger_hash)
.account_root('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
request.callback(function(err, r) {
// console.log('account_root: %s', JSON.stringify(r));
assert(!err);
assert('node' in r, 'Result missing property "node"');
done();
});
});
});
test('account_root remote malformedAddress', function(done) {
var self = this;
$.remote.request_ledger_hash(function(err, r) {
// console.log('result: %s', JSON.stringify(r));
assert(!err);
var request = $.remote.request_ledger_entry('account_root')
.ledger_hash(r.ledger_hash)
.account_root('zHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
request.callback(function(err, r) {
// console.log('account_root: %s', JSON.stringify(r));
assert(err);
assert.strictEqual(err.error, 'remoteError');
assert.strictEqual(err.remote.error, 'malformedAddress');
done();
});
})
});
test('account_root entryNotFound', function(done) {
var self = this;
$.remote.request_ledger_hash(function(err, r) {
// console.log('result: %s', JSON.stringify(r));
assert(!err);
var request = $.remote.request_ledger_entry('account_root')
.ledger_hash(r.ledger_hash)
.account_root('alice');
request.callback(function(err, r) {
// console.log('error: %s', m);
assert(err);
assert.strictEqual(err.error, 'remoteError');
assert.strictEqual(err.remote.error, 'entryNotFound');
done();
});
})
});
test('ledger_entry index', function(done) {
var self = this;
$.remote.request_ledger_hash(function(err, r) {
assert(!err);
var request = $.remote.request_ledger_entry('index')
.ledger_hash(r.ledger_hash)
.account_root('alice')
.index('2B6AC232AA4C4BE41BF49D2459FA4A0347E1B543A4C92FCEE0821C0201E2E9A8');
request.callback(function(err, r) {
assert(!err);
assert('node_binary' in r, 'Result missing property "node_binary"');
done();
});
})
});
test('create account', function(done) {
var self = this;
var root_id = $.remote.account('root')._account_id;
$.remote.request_subscribe().accounts(root_id).request();
$.remote.transaction()
.payment('root', 'alice', '10000.0')
.once('error', done)
.once('proposed', function(res) {
//console.log('Submitted', res);
$.remote.ledger_accept();
})
.once('success', function (r) {
//console.log('account_root: %s', JSON.stringify(r));
// Need to verify account and balance.
done();
})
.submit();
});
test('create account final', function(done) {
var self = this;
var got_proposed;
var got_success;
var root_id = $.remote.account('root')._account_id;
$.remote.request_subscribe().accounts(root_id).request();
var transaction = $.remote.transaction()
.payment('root', 'alice', '10000.0')
transaction.once('submitted', function (m) {
// console.log('proposed: %s', JSON.stringify(m));
// buster.assert.equals(m.result, 'terNO_DST_INSUF_XRP');
assert.strictEqual(m.engine_result, 'tesSUCCESS');
});
transaction.submit(done);
testutils.ledger_wait($.remote, transaction);
});
});
//vim:sw=2:sts=2:ts=8:et

View File

@@ -1,301 +0,0 @@
'use strict';
/*eslint-disable max-len,spaced-comment,array-bracket-spacing,key-spacing*/
/*eslint-disable no-multi-spaces,comma-spacing*/
/*eslint-disable no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
/**
* Data type map.
*
* Mapping of type ids to data types. The type id is specified by the high
*
* For reference, see rippled's definition:
* https://github.com/ripple/rippled/blob/develop/src/ripple/data/protocol
* /SField.cpp
*/
exports.types = [undefined,
// Common
'Int16', // 1
'Int32', // 2
'Int64', // 3
'Hash128', // 4
'Hash256', // 5
'Amount', // 6
'VL', // 7
'Account', // 8
// 9-13 reserved
undefined, // 9
undefined, // 10
undefined, // 11
undefined, // 12
undefined, // 13
'Object', // 14
'Array', // 15
// Uncommon
'Int8', // 16
'Hash160', // 17
'PathSet', // 18
'Vector256' // 19
];
/**
* Field type map.
*
* Mapping of field type id to field type name.
*/
var FIELDS_MAP = exports.fields = {
// Common types
1: { // Int16
1: 'LedgerEntryType',
2: 'TransactionType',
3: 'SignerWeight'
},
2: { // Int32
2: 'Flags',
3: 'SourceTag',
4: 'Sequence',
5: 'PreviousTxnLgrSeq',
6: 'LedgerSequence',
7: 'CloseTime',
8: 'ParentCloseTime',
9: 'SigningTime',
10: 'Expiration',
11: 'TransferRate',
12: 'WalletSize',
13: 'OwnerCount',
14: 'DestinationTag',
// Skip 15
16: 'HighQualityIn',
17: 'HighQualityOut',
18: 'LowQualityIn',
19: 'LowQualityOut',
20: 'QualityIn',
21: 'QualityOut',
22: 'StampEscrow',
23: 'BondAmount',
24: 'LoadFee',
25: 'OfferSequence',
26: 'FirstLedgerSequence',
27: 'LastLedgerSequence',
28: 'TransactionIndex',
29: 'OperationLimit',
30: 'ReferenceFeeUnits',
31: 'ReserveBase',
32: 'ReserveIncrement',
33: 'SetFlag',
34: 'ClearFlag',
35: 'SignerQuorum',
36: 'CancelAfter',
37: 'FinishAfter',
38: 'SignerListID'
},
3: { // Int64
1: 'IndexNext',
2: 'IndexPrevious',
3: 'BookNode',
4: 'OwnerNode',
5: 'BaseFee',
6: 'ExchangeRate',
7: 'LowNode',
8: 'HighNode'
},
4: { // Hash128
1: 'EmailHash'
},
5: { // Hash256
1: 'LedgerHash',
2: 'ParentHash',
3: 'TransactionHash',
4: 'AccountHash',
5: 'PreviousTxnID',
6: 'LedgerIndex',
7: 'WalletLocator',
8: 'RootIndex',
9: 'AccountTxnID',
16: 'BookDirectory',
17: 'InvoiceID',
18: 'Nickname',
19: 'Amendment',
20: 'TicketID',
21: 'Digest'
},
6: { // Amount
1: 'Amount',
2: 'Balance',
3: 'LimitAmount',
4: 'TakerPays',
5: 'TakerGets',
6: 'LowLimit',
7: 'HighLimit',
8: 'Fee',
9: 'SendMax',
16: 'MinimumOffer',
17: 'RippleEscrow',
18: 'DeliveredAmount'
},
7: { // VL
1: 'PublicKey',
2: 'MessageKey',
3: 'SigningPubKey',
4: 'TxnSignature',
5: 'Generator',
6: 'Signature',
7: 'Domain',
8: 'FundCode',
9: 'RemoveCode',
10: 'ExpireCode',
11: 'CreateCode',
12: 'MemoType',
13: 'MemoData',
14: 'MemoFormat',
17: 'Proof'
},
8: { // Account
1: 'Account',
2: 'Owner',
3: 'Destination',
4: 'Issuer',
7: 'Target',
8: 'RegularKey'
},
14: { // Object
1: undefined, // end of Object
2: 'TransactionMetaData',
3: 'CreatedNode',
4: 'DeletedNode',
5: 'ModifiedNode',
6: 'PreviousFields',
7: 'FinalFields',
8: 'NewFields',
9: 'TemplateEntry',
10: 'Memo',
11: 'SignerEntry',
16: 'Signer'
},
15: { // Array
1: undefined, // end of Array
2: 'SigningAccounts',
3: 'Signers',
4: 'SignerEntries',
5: 'Template',
6: 'Necessary',
7: 'Sufficient',
8: 'AffectedNodes',
9: 'Memos'
},
// Uncommon types
16: { // Int8
1: 'CloseResolution',
2: 'Method',
3: 'TransactionResult'
},
17: { // Hash160
1: 'TakerPaysCurrency',
2: 'TakerPaysIssuer',
3: 'TakerGetsCurrency',
4: 'TakerGetsIssuer'
},
18: { // PathSet
1: 'Paths'
},
19: { // Vector256
1: 'Indexes',
2: 'Hashes',
3: 'Amendments'
}
};
var INVERSE_FIELDS_MAP = exports.fieldsInverseMap = {};
Object.keys(FIELDS_MAP).forEach(function (k1) {
Object.keys(FIELDS_MAP[k1]).forEach(function (k2) {
INVERSE_FIELDS_MAP[FIELDS_MAP[k1][k2]] = [Number(k1), Number(k2)];
});
});
var REQUIRED = exports.REQUIRED = 0;
var OPTIONAL = exports.OPTIONAL = 1;
var DEFAULT = exports.DEFAULT = 2;
var base = [['TransactionType', REQUIRED], ['Flags', OPTIONAL], ['SourceTag', OPTIONAL], ['LastLedgerSequence', OPTIONAL], ['Account', REQUIRED], ['Sequence', REQUIRED], ['Fee', REQUIRED], ['OperationLimit', OPTIONAL], ['SigningPubKey', REQUIRED], ['TxnSignature', OPTIONAL], ['AccountTxnID', OPTIONAL], ['Memos', OPTIONAL], ['Signers', OPTIONAL]];
exports.tx = {
AccountSet: [3].concat(base, [['EmailHash', OPTIONAL], ['WalletLocator', OPTIONAL], ['WalletSize', OPTIONAL], ['MessageKey', OPTIONAL], ['Domain', OPTIONAL], ['TransferRate', OPTIONAL], ['SetFlag', OPTIONAL], ['ClearFlag', OPTIONAL]]),
TrustSet: [20].concat(base, [['LimitAmount', OPTIONAL], ['QualityIn', OPTIONAL], ['QualityOut', OPTIONAL]]),
OfferCreate: [7].concat(base, [['TakerPays', REQUIRED], ['TakerGets', REQUIRED], ['Expiration', OPTIONAL], ['OfferSequence', OPTIONAL]]),
OfferCancel: [8].concat(base, [['OfferSequence', REQUIRED]]),
SetRegularKey: [5].concat(base, [['RegularKey', OPTIONAL]]),
Payment: [0].concat(base, [['Destination', REQUIRED], ['Amount', REQUIRED], ['SendMax', OPTIONAL], ['Paths', DEFAULT], ['InvoiceID', OPTIONAL], ['DestinationTag', OPTIONAL]]),
Contract: [9].concat(base, [['Expiration', REQUIRED], ['BondAmount', REQUIRED], ['StampEscrow', REQUIRED], ['RippleEscrow', REQUIRED], ['CreateCode', OPTIONAL], ['FundCode', OPTIONAL], ['RemoveCode', OPTIONAL], ['ExpireCode', OPTIONAL]]),
RemoveContract: [10].concat(base, [['Target', REQUIRED]]),
EnableFeature: [100].concat(base, [['Feature', REQUIRED]]),
EnableAmendment: [100].concat(base, [['Amendment', REQUIRED]]),
SetFee: [101].concat(base, [['BaseFee', REQUIRED], ['ReferenceFeeUnits', REQUIRED], ['ReserveBase', REQUIRED], ['ReserveIncrement', REQUIRED]]),
TicketCreate: [10].concat(base, [['Target', OPTIONAL], ['Expiration', OPTIONAL]]),
TicketCancel: [11].concat(base, [['TicketID', REQUIRED]]),
SignerListSet: [12].concat(base, [['SignerQuorum', REQUIRED], ['SignerEntries', OPTIONAL]]),
SuspendedPaymentCreate: [1].concat(base, [['Destination', REQUIRED], ['Amount', REQUIRED], ['Digest', OPTIONAL], ['CancelAfter', OPTIONAL], ['FinishAfter', OPTIONAL], ['DestinationTag', OPTIONAL]]),
SuspendedPaymentFinish: [2].concat(base, [['Owner', REQUIRED], ['OfferSequence', REQUIRED], ['Method', OPTIONAL], ['Digest', OPTIONAL], ['Proof', OPTIONAL]]),
SuspendedPaymentCancel: [4].concat(base, [['Owner', REQUIRED], ['OfferSequence', REQUIRED]])
};
var sleBase = [['LedgerIndex', OPTIONAL], ['LedgerEntryType', REQUIRED], ['Flags', REQUIRED]];
exports.ledger = {
AccountRoot: [97].concat(sleBase, [['Sequence', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED], ['TransferRate', OPTIONAL], ['WalletSize', OPTIONAL], ['OwnerCount', REQUIRED], ['EmailHash', OPTIONAL], ['PreviousTxnID', REQUIRED], ['AccountTxnID', OPTIONAL], ['WalletLocator', OPTIONAL], ['Balance', REQUIRED], ['MessageKey', OPTIONAL], ['Domain', OPTIONAL], ['Account', REQUIRED], ['RegularKey', OPTIONAL]]),
Contract: [99].concat(sleBase, [['PreviousTxnLgrSeq', REQUIRED], ['Expiration', REQUIRED], ['BondAmount', REQUIRED], ['PreviousTxnID', REQUIRED], ['Balance', REQUIRED], ['FundCode', OPTIONAL], ['RemoveCode', OPTIONAL], ['ExpireCode', OPTIONAL], ['CreateCode', OPTIONAL], ['Account', REQUIRED], ['Owner', REQUIRED], ['Issuer', REQUIRED]]),
DirectoryNode: [100].concat(sleBase, [['IndexNext', OPTIONAL], ['IndexPrevious', OPTIONAL], ['ExchangeRate', OPTIONAL], ['RootIndex', REQUIRED], ['Owner', OPTIONAL], ['TakerPaysCurrency', OPTIONAL], ['TakerPaysIssuer', OPTIONAL], ['TakerGetsCurrency', OPTIONAL], ['TakerGetsIssuer', OPTIONAL], ['Indexes', REQUIRED]]),
EnabledFeatures: [102].concat(sleBase, [['Features', REQUIRED]]),
FeeSettings: [115].concat(sleBase, [['ReferenceFeeUnits', REQUIRED], ['ReserveBase', REQUIRED], ['ReserveIncrement', REQUIRED], ['BaseFee', REQUIRED], ['LedgerIndex', OPTIONAL]]),
GeneratorMap: [103].concat(sleBase, [['Generator', REQUIRED]]),
LedgerHashes: [104].concat(sleBase, [['LedgerEntryType', REQUIRED], ['Flags', REQUIRED], ['FirstLedgerSequence', OPTIONAL], ['LastLedgerSequence', OPTIONAL], ['LedgerIndex', OPTIONAL], ['Hashes', REQUIRED]]),
Nickname: [110].concat(sleBase, [['LedgerEntryType', REQUIRED], ['Flags', REQUIRED], ['LedgerIndex', OPTIONAL], ['MinimumOffer', OPTIONAL], ['Account', REQUIRED]]),
Offer: [111].concat(sleBase, [['LedgerEntryType', REQUIRED], ['Flags', REQUIRED], ['Sequence', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED], ['Expiration', OPTIONAL], ['BookNode', REQUIRED], ['OwnerNode', REQUIRED], ['PreviousTxnID', REQUIRED], ['LedgerIndex', OPTIONAL], ['BookDirectory', REQUIRED], ['TakerPays', REQUIRED], ['TakerGets', REQUIRED], ['Account', REQUIRED]]),
RippleState: [114].concat(sleBase, [['LedgerEntryType', REQUIRED], ['Flags', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED], ['HighQualityIn', OPTIONAL], ['HighQualityOut', OPTIONAL], ['LowQualityIn', OPTIONAL], ['LowQualityOut', OPTIONAL], ['LowNode', OPTIONAL], ['HighNode', OPTIONAL], ['PreviousTxnID', REQUIRED], ['LedgerIndex', OPTIONAL], ['Balance', REQUIRED], ['LowLimit', REQUIRED], ['HighLimit', REQUIRED]]),
SignerList: [83].concat(sleBase, [['OwnerNode', REQUIRED], ['SignerQuorum', REQUIRED], ['SignerEntries', REQUIRED], ['SignerListID', REQUIRED], ['PreviousTxnID', REQUIRED], ['PreviousTxnLgrSeq', REQUIRED]])
};
exports.metadata = [['DeliveredAmount', OPTIONAL], ['TransactionIndex', REQUIRED], ['TransactionResult', REQUIRED], ['AffectedNodes', REQUIRED]];
exports.ter = {
tesSUCCESS: 0,
tecCLAIM: 100,
tecPATH_PARTIAL: 101,
tecUNFUNDED_ADD: 102,
tecUNFUNDED_OFFER: 103,
tecUNFUNDED_PAYMENT: 104,
tecFAILED_PROCESSING: 105,
tecDIR_FULL: 121,
tecINSUF_RESERVE_LINE: 122,
tecINSUF_RESERVE_OFFER: 123,
tecNO_DST: 124,
tecNO_DST_INSUF_XRP: 125,
tecNO_LINE_INSUF_RESERVE: 126,
tecNO_LINE_REDUNDANT: 127,
tecPATH_DRY: 128,
tecUNFUNDED: 129, // Deprecated, old ambiguous unfunded.
tecNO_ALTERNATIVE_KEY: 130,
tecNO_REGULAR_KEY: 131,
tecOWNERS: 132,
tecNO_ISSUER: 133,
tecNO_AUTH: 134,
tecNO_LINE: 135,
tecINSUFF_FEE: 136,
tecFROZEN: 137,
tecNO_TARGET: 138,
tecNO_PERMISSION: 139,
tecNO_ENTRY: 140,
tecINSUFFICIENT_RESERVE: 141,
tecNEED_MASTER_KEY: 142,
tecDST_TAG_NEEDED: 143,
tecINTERNAL: 144,
tecOVERSIZE: 145
};

View File

@@ -1,39 +0,0 @@
'use strict';
// TODO: move in helpers from serializedtypes to utils
function toBytes(n) {
return [n >>> 24, n >>> 16 & 0xff, n >>> 8 & 0xff, n & 0xff];
}
/**
* Prefix for hashing functions.
*
* These prefixes are inserted before the source material used to
* generate various hashes. This is done to put each hash in its own
* "space." This way, two different types of objects with the
* same binary data will produce different hashes.
*
* Each prefix is a 4-byte value with the last byte set to zero
* and the first three bytes formed from the ASCII equivalent of
* some arbitrary string. For example "TXN".
*/
// transaction plus signature to give transaction ID
exports.HASH_TX_ID = 0x54584E00; // 'TXN'
// transaction plus metadata
exports.HASH_TX_NODE = 0x534E4400; // 'TND'
// inner node in tree
exports.HASH_INNER_NODE = 0x4D494E00; // 'MIN'
// leaf node in tree
exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
// inner transaction to sign
exports.HASH_TX_SIGN = 0x53545800; // 'STX'
// inner transaction to sign (TESTNET)
exports.HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
// inner transaction to multisign
exports.HASH_TX_MULTISIGN = 0x534D5400; // 'SMT'
Object.keys(exports).forEach(function (k) {
exports[k + '_BYTES'] = toBytes(exports[k]);
});

View File

@@ -1,177 +0,0 @@
'use strict';
var _Object$keys = require('babel-runtime/core-js/object/keys')['default'];
var _ = require('lodash');
var log = require('./log');
function wrapRemote(Remote) {
/**
* Create options object from positional function arguments
*
* @param {Array} params function parameters
* @param {Array} args function arguments
* @return {Object} keyed options
*/
function makeOptions(command, params, args) {
var result = {};
// log.warn('DEPRECATED: First argument to ' + command + ' request constructor must be an object containing' + ' request properties: ' + params.join(', '));
if (_.isFunction(_.last(args))) {
result.callback = args.pop();
}
return _.merge(result, _.zipObject(params, args));
}
function shiftFront(list, e) {
var ix = 0;
while (list[0] === e) {
list.shift();
ix++;
}
return ix;
}
function addBackwardsCompatibility(compatParams) {
var method = compatParams.method;
var command = compatParams.command;
var _compatParams$staticMethod = compatParams.staticMethod;
var staticMethod = _compatParams$staticMethod === undefined ? false : _compatParams$staticMethod;
var _compatParams$positionals = compatParams.positionals;
var positionals = _compatParams$positionals === undefined ? [] : _compatParams$positionals;
var _compatParams$mappings = compatParams.mappings;
var mappings = _compatParams$mappings === undefined ? {} : _compatParams$mappings;
var _compatParams$hasCallback = compatParams.hasCallback;
var hasCallback = _compatParams$hasCallback === undefined ? true : _compatParams$hasCallback;
var _compatParams$aliases = compatParams.aliases;
var aliases = _compatParams$aliases === undefined ? [] : _compatParams$aliases;
var positionalsStartIx = shiftFront(positionals, '*');
var needsWrapping = positionals.length || _Object$keys(mappings).length;
function wrapFunction(func) {
return function () {
var optionsArg = arguments[positionalsStartIx];
var options = {};
if (hasCallback) {
options.callback = arguments[positionalsStartIx + 1];
}
if (_.isPlainObject(optionsArg)) {
var mapped = _.transform(optionsArg, function (result, v, k) {
var to = mappings[k];
result[to !== undefined ? to : k] = v;
});
_.merge(options, mapped);
} else {
// This hack handles accountRequest type helper helpers
var commandName = positionalsStartIx ? arguments[0] : command;
var _args = _.slice(arguments, positionalsStartIx);
var positionalOptions = makeOptions(commandName, positionals, _args);
_.merge(options, positionalOptions);
}
// Only some `positionals` get remapped to options
var alwaysPositional = _.slice(arguments, 0, positionalsStartIx);
var args = alwaysPositional.concat([options, options.callback]);
return func.apply(this, args);
};
}
var obj = staticMethod ? Remote : Remote.prototype;
// Wrap the function and set the aliases
var wrapped = needsWrapping ? wrapFunction(obj[method]) : obj[method];
aliases.concat(method).forEach(function (name) {
obj[name] = wrapped;
});
}
var remoteMethods = [{
method: 'requestPathFindCreate',
command: 'path_find',
positionals: ['source_account', 'destination_account', 'destination_amount', 'source_currencies'],
mappings: {
src_currencies: 'source_currencies',
src_account: 'source_account',
dst_amount: 'destination_amount',
dst_account: 'destination_account'
}
}, {
method: 'requestRipplePathFind',
command: 'ripple_path_find',
positionals: ['source_account', 'destination_account', 'destination_amount', 'source_currencies'],
mappings: {
src_currencies: 'source_currencies',
src_account: 'source_account',
dst_amount: 'destination_amount',
dst_account: 'destination_account'
}
}, {
method: 'createPathFind',
aliases: ['pathFind'],
command: 'pathfind',
positionals: ['src_account', 'dst_account', 'dst_amount', 'src_currencies']
}, {
method: 'requestTransactionEntry',
command: 'transaction_entry',
positionals: ['hash', 'ledger'],
mappings: { ledger_index: 'ledger', ledger_hash: 'ledger' }
}, {
method: 'requestTransaction',
command: 'tx',
positionals: ['hash', 'ledger'],
mappings: { ledger_index: 'ledger', ledger_hash: 'ledger' },
aliases: ['requestTx']
}, {
method: 'requestBookOffers',
command: 'book_offers',
positionals: ['gets', 'pays', 'taker', 'ledger', 'limit'],
mappings: { taker_pays: 'pays', taker_gets: 'gets' }
}, {
method: 'createOrderBook',
hasCallback: false,
command: 'orderbook',
positionals: ['currency_gets', 'issuer_gets', 'currency_pays', 'issuer_pays']
}, {
method: 'requestTransactionHistory',
command: 'tx_history',
positionals: ['start'],
aliases: ['requestTxHistory']
}, {
method: 'requestWalletAccounts',
command: 'wallet_accounts',
positionals: ['seed']
}, {
method: 'requestSign',
command: 'sign',
positionals: ['secret', 'tx_json']
}, {
method: 'accountSeqCache',
command: 'accountseqcache',
positionals: ['account', 'ledger']
}, {
method: 'requestRippleBalance',
command: 'ripplebalance',
positionals: ['account', 'issuer', 'currency', 'ledger']
}, {
staticMethod: true,
method: 'accountRequest',
command: 'accountrequest(*)',
positionals: ['*', 'account', 'ledger', 'peer', 'limit', 'marker']
}, {
staticMethod: true,
method: 'accountRootRequest',
command: 'accountRootRequest(*)',
positionals: ['*', '*', 'account', 'ledger']
}];
remoteMethods.forEach(addBackwardsCompatibility);
}
module.exports = function wrapAPI(index) {
wrapRemote(index.Remote);
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,740 +0,0 @@
'use strict';
var _ = require('lodash');
var util = require('util');
var assert = require('assert');
var async = require('async');
var EventEmitter = require('events').EventEmitter;
var Transaction = require('./transaction').Transaction;
var RippleError = require('./rippleerror').RippleError;
var PendingQueue = require('./transactionqueue').TransactionQueue;
var log = require('./log').internal.sub('transactionmanager');
/**
* @constructor TransactionManager
* @param {Account} account
*/
function TransactionManager(account) {
EventEmitter.call(this);
var self = this;
this._account = account;
this._accountID = account._account_id;
this._remote = account._remote;
this._nextSequence = undefined;
this._maxFee = this._remote.max_fee;
this._maxAttempts = this._remote.max_attempts;
this._submissionTimeout = this._remote.submission_timeout;
this._pending = new PendingQueue();
this._account.on('transaction-outbound', function (res) {
self._transactionReceived(res);
});
this._remote.on('load_changed', function (load) {
self._adjustFees(load);
});
function updatePendingStatus(ledger) {
self._updatePendingStatus(ledger);
}
this._remote.on('ledger_closed', updatePendingStatus);
function handleReconnect() {
self._handleReconnect(function () {
// Handle reconnect, account_tx procedure first, before
// hooking back into ledger_closed
self._remote.on('ledger_closed', updatePendingStatus);
});
}
this._remote.on('disconnect', function () {
self._remote.removeListener('ledger_closed', updatePendingStatus);
self._remote.once('connect', handleReconnect);
});
// Query server for next account transaction sequence
this._loadSequence();
}
util.inherits(TransactionManager, EventEmitter);
TransactionManager._isNoOp = function (transaction) {
return typeof transaction === 'object' && typeof transaction.tx_json === 'object' && transaction.tx_json.TransactionType === 'AccountSet' && transaction.tx_json.Flags === 0;
};
TransactionManager._isRemoteError = function (error) {
return typeof error === 'object' && error.error === 'remoteError' && typeof error.remote === 'object';
};
TransactionManager._isNotFound = function (error) {
return TransactionManager._isRemoteError(error) && /^(txnNotFound|transactionNotFound)$/.test(error.remote.error);
};
TransactionManager._isTooBusy = function (error) {
return TransactionManager._isRemoteError(error) && error.remote.error === 'tooBusy';
};
/**
* Normalize transactions received from account transaction stream and
* account_tx
*
* @param {Transaction}
* @return {Transaction} normalized
* @api private
*/
TransactionManager.normalizeTransaction = function (tx) {
var transaction = {};
var keys = Object.keys(tx);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
switch (k) {
case 'transaction':
// Account transaction stream
transaction.tx_json = tx[k];
break;
case 'tx':
// account_tx response
transaction.engine_result = tx.meta.TransactionResult;
transaction.result = transaction.engine_result;
transaction.tx_json = tx[k];
transaction.hash = tx[k].hash;
transaction.ledger_index = tx[k].ledger_index;
transaction.type = 'transaction';
transaction.validated = tx.validated;
break;
case 'meta':
case 'metadata':
transaction.metadata = tx[k];
break;
case 'mmeta':
// Don't copy mmeta
break;
default:
transaction[k] = tx[k];
}
}
return transaction;
};
/**
* Handle received transaction from two possible sources
*
* + Account transaction stream (normal operation)
* + account_tx (after reconnect)
*
* @param {Object} transaction
* @api private
*/
TransactionManager.prototype._transactionReceived = function (tx) {
var transaction = TransactionManager.normalizeTransaction(tx);
if (!transaction.validated) {
// Transaction has not been validated
return;
}
if (transaction.tx_json.Account !== this._accountID) {
// Received transaction's account does not match
return;
}
if (this._remote.trace) {
log.info('transaction received:', transaction.tx_json);
}
this._pending.addReceivedSequence(transaction.tx_json.Sequence);
var hash = transaction.tx_json.hash;
var submission = this._pending.getSubmission(hash);
if (!(submission instanceof Transaction)) {
// The received transaction does not correlate to one submitted
this._pending.addReceivedId(hash, transaction);
return;
}
// ND: A `success` handler will `finalize` this later
switch (transaction.engine_result) {
case 'tesSUCCESS':
submission.emit('success', transaction);
break;
default:
submission.emit('error', transaction);
}
};
/**
* Adjust pending transactions' fees in real-time. This does not resubmit
* pending transactions; they will be resubmitted periodically with an updated
* fee (and as a consequence, a new transaction ID) if not already validated
*
* ND: note, that `Fee` is a component of a transactionID
*
* @api private
*/
TransactionManager.prototype._adjustFees = function () {
var self = this;
if (!this._remote.local_fee) {
return;
}
function maxFeeExceeded(transaction) {
// Don't err until attempting to resubmit
transaction.once('presubmit', function () {
transaction.emit('error', 'tejMaxFeeExceeded');
});
}
this._pending.forEach(function (transaction) {
if (transaction._setFixedFee) {
return;
}
var oldFee = transaction.tx_json.Fee;
var newFee = transaction._computeFee();
if (Number(newFee) > self._maxFee) {
// Max transaction fee exceeded, abort submission
maxFeeExceeded(transaction);
return;
}
transaction.tx_json.Fee = newFee;
transaction.emit('fee_adjusted', oldFee, newFee);
if (self._remote.trace) {
log.info('fee adjusted:', transaction.tx_json, oldFee, newFee);
}
});
};
/**
* Get pending transactions
*
* @return {Array} pending transactions
*/
TransactionManager.prototype.getPending = function () {
return this._pending;
};
/**
* Legacy code. Update transaction status after excessive ledgers pass. One of
* either "missing" or "lost"
*
* @param {Object} ledger data
* @api private
*/
TransactionManager.prototype._updatePendingStatus = function (ledger) {
assert.strictEqual(typeof ledger, 'object');
assert.strictEqual(typeof ledger.ledger_index, 'number');
this._pending.forEach(function (transaction) {
if (transaction.finalized) {
return;
}
switch (ledger.ledger_index - transaction.submitIndex) {
case 4:
transaction.emit('missing', ledger);
break;
case 8:
transaction.emit('lost', ledger);
break;
}
if (ledger.ledger_index > transaction.tx_json.LastLedgerSequence) {
// Transaction must fail
transaction.emit('error', new RippleError('tejMaxLedger', 'Transaction LastLedgerSequence exceeded'));
}
});
};
// Fill an account transaction sequence
TransactionManager.prototype._fillSequence = function (tx, callback) {
var self = this;
function submitFill(sequence, fCallback) {
var fillTransaction = self._remote.createTransaction('AccountSet', {
account: self._accountID
});
fillTransaction.tx_json.Sequence = sequence;
// Secrets may be set on a per-transaction basis
if (tx._secret) {
fillTransaction.secret(tx._secret);
}
fillTransaction.once('submitted', fCallback);
fillTransaction.submit();
}
function sequenceLoaded(err, sequence) {
if (typeof sequence !== 'number') {
log.info('fill sequence: failed to fetch account transaction sequence');
return callback();
}
var sequenceDiff = tx.tx_json.Sequence - sequence;
var submitted = 0;
async.whilst(function () {
return submitted < sequenceDiff;
}, function (asyncCallback) {
submitFill(sequence, function (res) {
++submitted;
if (res.engine_result === 'tesSUCCESS') {
self.emit('sequence_filled', err);
}
asyncCallback();
});
}, function () {
if (callback) {
callback();
}
});
}
this._loadSequence(sequenceLoaded);
};
/**
* Load account transaction sequence
*
* @param [Function] callback
* @api private
*/
TransactionManager.prototype._loadSequence = function (callback_) {
var self = this;
var callback = typeof callback_ === 'function' ? callback_ : function () {};
function sequenceLoaded(err, sequence) {
if (err || typeof sequence !== 'number') {
if (self._remote.trace) {
log.info('error requesting account transaction sequence', err);
return;
}
}
self._nextSequence = sequence;
self.emit('sequence_loaded', sequence);
callback(err, sequence);
}
this._account.getNextSequence(sequenceLoaded);
};
/**
* On reconnect, load account_tx in case a pending transaction succeeded while
* disconnected
*
* @param [Function] callback
* @api private
*/
TransactionManager.prototype._handleReconnect = function (callback_) {
var self = this;
var callback = typeof callback_ === 'function' ? callback_ : function () {};
if (!this._pending.length()) {
callback();
return;
}
function handleTransactions(err, transactions) {
if (err || typeof transactions !== 'object') {
if (self._remote.trace) {
log.info('error requesting account_tx', err);
}
callback();
return;
}
if (Array.isArray(transactions.transactions)) {
// Treat each transaction in account transaction history as received
transactions.transactions.forEach(self._transactionReceived, self);
}
callback();
self._loadSequence(function () {
// Resubmit pending transactions after sequence is loaded
self._resubmit();
});
}
var options = {
account: this._accountID,
ledger_index_min: this._pending.getMinLedger(),
ledger_index_max: -1,
binary: true,
parseBinary: true,
limit: 20
};
this._remote.requestAccountTx(options, handleTransactions);
};
/**
* Wait for specified number of ledgers to pass
*
* @param {Number} ledgers
* @param {Function} callback
* @api private
*/
TransactionManager.prototype._waitLedgers = function (ledgers, callback) {
assert.strictEqual(typeof ledgers, 'number');
assert.strictEqual(typeof callback, 'function');
if (ledgers < 1) {
return callback();
}
var self = this;
var closes = 0;
function ledgerClosed() {
if (++closes === ledgers) {
self._remote.removeListener('ledger_closed', ledgerClosed);
callback();
}
}
this._remote.on('ledger_closed', ledgerClosed);
};
/**
* Resubmit pending transactions. If a transaction is specified, it will be
* resubmitted. Otherwise, all pending transactions will be resubmitted
*
* @param [Number] ledgers to wait before resubmitting
* @param [Transaction] pending transactions to resubmit
* @api private
*/
TransactionManager.prototype._resubmit = function (ledgers_, pending_) {
var self = this;
var ledgers = ledgers_;
var pending = pending_;
if (arguments.length === 1) {
pending = ledgers;
ledgers = 0;
}
ledgers = ledgers || 0;
pending = pending instanceof Transaction ? [pending] : this.getPending().getQueue();
function resubmitTransaction(transaction, next) {
if (!transaction || transaction.finalized) {
// Transaction has been finalized, nothing to do
return;
}
// Find ID within cache of received (validated) transaction IDs
var received = transaction.findId(self._pending._idCache);
if (received) {
switch (received.engine_result) {
case 'tesSUCCESS':
transaction.emit('success', received);
break;
default:
transaction.emit('error', received);
}
}
if (!transaction.isResubmittable()) {
// Rather than resubmit, wait for the transaction to fail due to
// LastLedgerSequence's being exceeded. The ultimate error emitted on
// transaction is 'tejMaxLedger'; should be definitive
return;
}
while (self._pending.hasSequence(transaction.tx_json.Sequence)) {
// Sequence number has been consumed by another transaction
transaction.tx_json.Sequence += 1;
if (self._remote.trace) {
log.info('incrementing sequence:', transaction.tx_json);
}
}
if (self._remote.trace) {
log.info('resubmit:', transaction.tx_json);
}
transaction.once('submitted', function (m) {
transaction.emit('resubmitted', m);
next();
});
self._request(transaction);
}
this._waitLedgers(ledgers, function () {
async.eachSeries(pending, resubmitTransaction);
});
};
/**
* Prepare submit request
*
* @param {Transaction} transaction to submit
* @return {Request} submit request
* @api private
*/
TransactionManager.prototype._prepareRequest = function (tx) {
var submitRequest = this._remote.requestSubmit();
if (this._remote.local_signing) {
tx.sign();
var serialized = tx.serialize();
submitRequest.txBlob(serialized.to_hex());
var hash = tx.hash(null, null, serialized);
tx.addId(hash);
} else {
if (tx.hasMultiSigners()) {
submitRequest.message.command = 'submit_multisigned';
}
// ND: `build_path` is completely ignored when doing local signing as
// `Paths` is a component of the signed blob, the `tx_blob` is signed,
// sealed and delivered, and the txn unmodified.
// TODO: perhaps an exception should be raised if build_path is attempted
// while local signing
submitRequest.buildPath(tx._build_path);
submitRequest.secret(tx._secret);
submitRequest.txJson(tx.tx_json);
}
return submitRequest;
};
/**
* Send `submit` request, handle response
*
* @param {Transaction} transaction to submit
* @api private
*/
TransactionManager.prototype._request = function (tx) {
var self = this;
var remote = this._remote;
if (tx.finalized) {
return;
}
if (tx.attempts > this._maxAttempts) {
tx.emit('error', new RippleError('tejAttemptsExceeded'));
return;
}
if (tx.attempts > 0 && !remote.local_signing) {
var errMessage = 'Automatic resubmission requires local signing';
tx.emit('error', new RippleError('tejLocalSigningRequired', errMessage));
return;
}
if (Number(tx.tx_json.Fee) > tx._maxFee) {
tx.emit('error', new RippleError('tejMaxFeeExceeded'));
return;
}
if (remote.trace) {
log.info('submit transaction:', tx.tx_json);
}
function transactionFailed(message) {
if (message.engine_result === 'tefPAST_SEQ') {
// Transaction may succeed after Sequence is updated
self._resubmit(1, tx);
}
}
function transactionRetry() {
// XXX This may no longer be necessary. Instead, update sequence numbers
// after a transaction fails definitively
self._fillSequence(tx, function () {
self._resubmit(1, tx);
});
}
function transactionFailedLocal(message) {
if (message.engine_result === 'telINSUF_FEE_P') {
// Transaction may succeed after Fee is updated
self._resubmit(1, tx);
}
}
function submissionError(error) {
// Either a tem-class error or generic server error such as tooBusy. This
// should be a definitive failure
if (TransactionManager._isTooBusy(error)) {
self._waitLedgers(1, function () {
tx.once('submitted', function (m) {
tx.emit('resubmitted', m);
});
self._request(tx);
});
} else {
self._nextSequence--;
tx.emit('error', error);
}
}
function submitted(message) {
if (tx.finalized) {
return;
}
// ND: If for some unknown reason our hash wasn't computed correctly this
// is an extra measure.
if (message.tx_json && message.tx_json.hash) {
tx.addId(message.tx_json.hash);
}
message.result = message.engine_result || '';
tx.result = message;
tx.responses += 1;
if (remote.trace) {
log.info('submit response:', message);
}
tx.emit('submitted', message);
switch (message.result.slice(0, 3)) {
case 'tes':
tx.emit('proposed', message);
break;
case 'tec':
break;
case 'ter':
transactionRetry(message);
break;
case 'tef':
transactionFailed(message);
break;
case 'tel':
transactionFailedLocal(message);
break;
default:
// tem
submissionError(message);
}
}
function requestTimeout() {
// ND: What if the response is just slow and we get a response that
// `submitted` above will cause to have concurrent resubmit logic streams?
// It's simpler to just mute handlers and look out for finalized
// `transaction` messages.
if (tx.finalized) {
return;
}
tx.emit('timeout');
if (remote.isConnected()) {
if (remote.trace) {
log.info('timeout:', tx.tx_json);
}
self._resubmit(1, tx);
}
}
tx.submitIndex = this._remote.getLedgerSequence() + 1;
if (tx.attempts === 0) {
tx.initialSubmitIndex = tx.submitIndex;
}
var submitRequest = this._prepareRequest(tx);
submitRequest.once('error', submitted);
submitRequest.once('success', submitted);
tx.emit('presubmit');
submitRequest.broadcast().request();
tx.attempts++;
tx.emit('postsubmit');
submitRequest.timeout(self._submissionTimeout, requestTimeout);
};
/**
* Entry point for TransactionManager submission
*
* @param {Transaction} tx
*/
TransactionManager.prototype.submit = function (tx) {
var self = this;
if (typeof this._nextSequence !== 'number') {
// If sequence number is not yet known, defer until it is.
this.once('sequence_loaded', function () {
self.submit(tx);
});
return;
}
if (tx.finalized) {
// Finalized transactions must stop all activity
return;
}
if (!_.isNumber(tx.tx_json.Sequence)) {
// Honor manually-set sequences
tx.setSequence(this._nextSequence++);
}
if (_.isUndefined(tx.tx_json.LastLedgerSequence)) {
tx.setLastLedgerSequence();
}
if (tx.hasMultiSigners()) {
tx.setResubmittable(false);
tx.setSigningPubKey('');
}
tx.once('cleanup', function () {
self.getPending().remove(tx);
});
if (!tx.complete()) {
this._nextSequence -= 1;
return;
}
// ND: this is the ONLY place we put the tx into the queue. The
// TransactionQueue queue is merely a list, so any mutations to tx._hash
// will cause subsequent look ups (eg. inside 'transaction-outbound'
// validated transaction clearing) to fail.
this._pending.push(tx);
this._request(tx);
};
exports.TransactionManager = TransactionManager;

View File

@@ -1,23 +0,0 @@
// npm install ws
// WS_ADDRESS=127.0.0.1:6006 node ripple-websocket.js
var WebSocket = require('ws')
console.log(process.env.WS_ADDRESS)
var ws = new WebSocket('ws://'+process.env.WS_ADDRESS)
ws.on('error', function(error){
console.log(error)
})
ws.on('open', function () {
ws.send(JSON.stringify({
"id": 1,
"command": "server_info"
}))
})
ws.on('message', function(dataString, flags) {
var data = JSON.parse(dataString)
console.log(data)
})

View File

@@ -1,58 +0,0 @@
/**
This is a sample ripple npm integration test intended to be copied as a basis
for new npm tests.
*/
// These three lines are required to initialize any test suite.
var async = require('async');
var testutils = require('./testutils');
var config = testutils.init_config();
// Delete any of these next variables that aren't used in the test.
var Account = require('ripple-lib').UInt160;
var Amount = require('ripple-lib').Amount;
var Currency = require('ripple-lib').UInt160;
var Remote = require('ripple-lib').Remote;
var Server = require('./server').Server;
var Transaction = require('ripple-lib').Transaction;
var assert = require('assert');
var extend = require('extend');
var fs = require('fs');
var http = require('http');
var path = require('path');
suite('Sample test suite', function() {
var $ = {};
var opts = {};
setup(function(done) {
testutils.build_setup(opts).call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test('Sample test', function (done) {
var self = this;
var steps = [
function stepOne(callback) {
self.what = 'Step one of the sample test';
assert(true);
callback();
},
function stepTwo(callback) {
self.what = 'Step two of the sample test';
assert(true);
callback();
},
];
async.waterfall(steps, function (error) {
assert(!error, self.what + ': ' + error);
done();
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +0,0 @@
var assert = require('assert');
var extend = require("extend");
var Server = require("./server").Server;
var testutils = require("./testutils");
var config = testutils.init_config();
suite('Standalone server startup', function() {
test('server start and stop', function(done) {
var host = config.server_default;
var cfg = testutils.get_server_config(config, host);
var alpha = Server.from_config(host, cfg);
alpha.on('started', function () {
alpha.on('stopped', function () {
done();
})
alpha.stop();
})
alpha.start();
});
});

View File

@@ -1,207 +0,0 @@
// Create and stop test servers.
//
// Usage:
// s = new Server(name, config)
// s.verbose() : optional
// .start()
// 'started'
//
// s.stop() : stops server is started.
// 'stopped'
//
// Provide servers
//
// Servers are created in tmp/server/$server
//
var child = require("child_process");
var fs = require("fs");
var path = require("path");
var util = require("util");
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;
var nodeutils = require("./nodeutils");
// Create a server object
function Server(name, config, verbose) {
this.name = name;
this.config = config;
this.started = false;
this.quiet = !verbose;
this.stopping = false;
this.ledger_file = null;
var nodejs_version = process.version.match(/^v(\d+)+\.(\d+)\.(\d+)$/).slice(1,4);
var wanted_version = [ 0, 8, 18 ];
while (wanted_version.length && nodejs_version.length && nodejs_version[0] == wanted_version[0])
{
nodejs_version.shift();
wanted_version.shift();
}
var sgn = !nodejs_version.length && !wanted_version.length
? 0
: nodejs_version.length
? nodejs_version[0] - wanted_version[0]
: -1;
if (sgn < 0) {
console.log("\n*** Node.js version is too low.");
throw "Nodejs version is too low.";
}
};
util.inherits(Server, EventEmitter);
Server.from_config = function (name, config, verbose) {
return new Server(name, config, verbose);
};
Server.prototype.serverPath = function() {
return path.resolve("tmp/server", this.name);
};
Server.prototype.configPath = function() {
return path.join(this.serverPath(), "rippled.cfg");
};
// Write a server's rippled.cfg.
Server.prototype._writeConfig = function(done) {
var self = this;
fs.writeFile(
this.configPath(),
Object.keys(this.config).map(function(o) {
return util.format("[%s]\n%s\n", o, self.config[o]);
}).join(""),
'utf8', done);
};
Server.prototype.set_ledger_file = function(fn) {
this.ledger_file = __dirname + '/fixtures/' + fn;
}
// Spawn the server.
Server.prototype._serverSpawnSync = function() {
var self = this;
var rippledpath = this.config.rippled_path;
assert(rippledpath, "rippled_path not provided");
var args = [
"-a",
"-v",
"--conf=rippled.cfg"
];
if (this.config.rippled_args) {
args = this.config.rippled_args.concat(args);
};
if (this.ledger_file != null) {
args.push('--ledgerfile=' + this.ledger_file)
};
var options = {
cwd: this.serverPath(),
env: process.env,
stdio: this.quiet ? 'pipe': 'inherit'
};
// Spawn in standalone mode for now.
this.child = child.spawn(rippledpath, args, options);
if (!this.quiet)
console.log("server: start %s: %s --conf=%s",
this.child.pid,
rippledpath,
args.join(" "),
this.configPath());
var stderr = [];
self.child.stderr.on('data', function(buf) { stderr.push(buf); });
// By default, just log exits.
this.child.on('exit', function(code, signal) {
if (!self.quiet) console.log("server: spawn: server exited code=%s: signal=%s", code, signal);
self.emit('exited');
// Dump server logs on an abnormal exit
if (self.quiet && (!self.stopping)) {
process.stderr.write("rippled stderr:\n");
for (var i = 0; i < stderr.length; i++) {
process.stderr.write(stderr[i]);
};
};
// If could not exec: code=127, signal=null
// If regular exit: code=0, signal=null
// Fail the test if the server has not called "stop".
assert(self.stopping, 'Server died with signal '+signal);
});
};
// Prepare server's working directory.
Server.prototype._makeBase = function (done) {
var serverpath = this.serverPath();
var self = this;
// Reset the server directory, build it if needed.
nodeutils.resetPath(serverpath, '0777', function (e) {
if (e) throw e;
self._writeConfig(done);
});
};
Server.prototype.verbose = function () {
this.quiet = false;
return this;
};
// Create a standalone server.
// Prepare the working directory and spawn the server.
Server.prototype.start = function () {
var self = this;
if (!this.quiet) console.log("server: start: %s: %s", this.name, JSON.stringify(this.config));
this._makeBase(function (e) {
if (e) throw e;
self._serverSpawnSync();
self.emit('started');
});
return this;
};
// Stop a standalone server.
Server.prototype.stop = function () {
var self = this;
self.stopping = true;
if (!this.child) {
console.log("server: stop: can't stop");
return;
}
// Update the on exit to invoke done.
this.child.on('exit', function (code, signal) {
if (!self.quiet) console.log("server: stop: server exited");
self.stopped = true;
self.emit('stopped');
delete self.child;
});
this.child.kill();
return this;
};
exports.Server = Server;
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,652 +0,0 @@
var async = require("async");
var assert = require('assert');
var Amount = require("ripple-lib").Amount;
var Remote = require("ripple-lib").Remote;
var Transaction = require("ripple-lib").Transaction;
var Server = require("./server").Server;
var testutils = require("./testutils");
var config = testutils.init_config();
function makeOffer($, account, takerpays, takergets, callback) {
$.remote.transaction()
.offer_create(account, takerpays, takergets)
.on('submitted', function (m) {
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
// console.log("PROPOSED: offer_create: Result %s. TakerGets %s. TakerPays %s.", m.engine_result, JSON.stringify(m.tx_json.TakerGets), JSON.stringify(m.tx_json.TakerPays));
assert.strictEqual('tesSUCCESS', m.engine_result);
$.remote
.once('ledger_closed', function (message) {
// console.log("LEDGER_CLOSED: %d: %s", message.ledger_index, message.ledger_hash);
assert(message);
})
.ledger_accept();
})
.on('final', function (m) {
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
// console.log("FINAL: offer_create: Result %s. Validated %s. TakerGets %s. TakerPays %s.", m.engine_result, m.validated, JSON.stringify(m.tx_json.TakerGets), JSON.stringify(m.tx_json.TakerPays));
callback(m.metadata.TransactionResult !== 'tesSUCCESS');
})
.submit();
}
function makeOfferWithEvent($, account, takerpays, takergets, callback) {
$.remote.once('transaction', function(m) {
// console.log("TRANSACTION: %s", JSON.stringify(m));
// console.log("TRANSACTION: meta: %s, takerpays: %s, takergets: %s", m.meta.AffectedNodes.length, Amount.from_json(m.transaction.TakerPays).to_human_full(), Amount.from_json(m.transaction.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(takergets).to_human_full(),
Amount.from_json(m.transaction.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(takerpays).to_human_full(),
Amount.from_json(m.transaction.TakerPays).to_human_full());
assert.strictEqual("OfferCreate", m.transaction.TransactionType);
// We don't get quality from the event
var foundOffer = false;
var foundRoot = false;
for(var n = m.meta.AffectedNodes.length-1; n >= 0; --n) {
var node = m.meta.AffectedNodes[n];
// console.log(node);
if(node.CreatedNode &&
node.CreatedNode.LedgerEntryType === "Offer") {
foundOffer = true;
var fields = node.CreatedNode.NewFields;
assert.strictEqual(Amount.from_json(takergets).to_human_full(),
Amount.from_json(fields.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(takerpays).to_human_full(),
Amount.from_json(fields.TakerPays).to_human_full());
} else if (node.ModifiedNode) {
assert(node.ModifiedNode.LedgerEntryType != "Offer");
if(node.ModifiedNode.LedgerEntryType === "AccountRoot") {
foundRoot = true;
var mod = node.ModifiedNode;
assert(mod.FinalFields.OwnerCount ==
mod.PreviousFields.OwnerCount + 1);
}
}
}
assert(foundOffer);
assert(foundRoot);
callback(m.engine_result !== 'tesSUCCESS');
});
makeOffer($, account, takerpays, takergets, function () {});
}
function matchOffers(expected, actual) {
assert.strictEqual(expected.length, actual.length);
var found = [];
for(var i = 0; i < actual.length; ++i) {
var offer = actual[i];
// console.log("Got: ", offer);
for(var j = 0; j < expected.length; ++j) {
var expectedOffer = expected[j];
// console.log("checking: ", expectedOffer);
if(Amount.from_json(expectedOffer.takerGets).to_human_full() ===
Amount.from_json(offer.TakerGets).to_human_full()
&& Amount.from_json(expectedOffer.takerPays).to_human_full() ===
Amount.from_json(offer.TakerPays).to_human_full()
&& expectedOffer.quality === offer.quality) {
// console.log("FOUND");
found.push(expectedOffer);
expected.splice(j, 1);
break;
}
}
}
if(expected.length != 0 || actual.length != found.length) {
console.log("Received: ", actual.length);
for(i = 0; i < actual.length; ++i) {
var offer = actual[i];
console.log(" TakerGets: %s, TakerPays %s",
Amount.from_json(offer.TakerGets).to_human_full(),
Amount.from_json(offer.TakerPays).to_human_full());
}
console.log("Found: ", found.length);
for(i = 0; i < found.length; ++i) {
var offer = found[i];
console.log(" TakerGets: %s, TakerPays %s", offer.takerGets,
offer.takerPays);
}
console.log("Not found: ", expected.length);
for(i = 0; i < expected.length; ++i) {
var offer = expected[i];
console.log(" TakerGets: %s, TakerPays %s", offer.takerGets,
offer.takerPays);
}
}
assert.strictEqual(0, expected.length);
assert.strictEqual(actual.length, found.length);
}
function buildOfferFunctions($, offers) {
var functions = [];
for(var i = 0; i < offers.length; ++i) {
var offer = offers[i];
functions.push(function(offer) {
// console.log("Offer pre: ", offer);
return function(callback) {
// console.log("Offer in: ", offer);
makeOffer($, "root", offer.takerPays, offer.takerGets, callback);
}
}(offer));
}
return functions;
}
suite("Subscribe book tests", function() {
var $ = { };
setup(function(done) {
testutils.build_setup().call($, done);
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test("One side: Empty book", function (done) {
var self = this;
async.waterfall([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(0, res.offers.length);
assert(!res.asks);
assert(!res.bids);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("One side: Offers in book", function (done) {
var self = this;
var askTakerPays = "500";
var askTakerGets = "100/usd/root";
var askQuality = "5";
var bidTakerPays = askTakerGets;
var bidTakerGets = "200";
var bidQuality = "0.5";
async.waterfall([
function(callback) {
// Create an ask: TakerPays 500, TakerGets 100/USD
makeOffer($, "root", askTakerPays, askTakerGets, callback);
},
function(callback) {
// Create an bid: TakerPays 100/USD, TakerGets 1200
makeOffer($, "root", bidTakerPays, bidTakerGets, callback);
},
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(1, res.offers.length);
var bid = res.offers[0];
assert.strictEqual(Amount.from_json(bidTakerGets).to_human_full(),
Amount.from_json(bid.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(bidTakerPays).to_human_full(),
Amount.from_json(bid.TakerPays).to_human_full());
assert.strictEqual(bidQuality, bid.quality);
assert(!res.asks);
assert(!res.bids);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Both sides: Empty book", function (done) {
var self = this;
async.waterfall([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(0, res.asks.length);
assert.strictEqual(0, res.bids.length);
assert(!res.offers);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Both sides: Offers in book", function (done) {
var self = this;
var askTakerPays = "500";
var askTakerGets = "100/USD/root";
var askQuality = "5";
var bidTakerPays = askTakerGets;
var bidTakerGets = "200";
var bidQuality = "0.5";
async.waterfall([
function(callback) {
// Create an ask: TakerPays 500, TakerGets 100/USD
makeOffer($, "root", askTakerPays, askTakerGets, callback);
},
function(callback) {
// Create an bid: TakerPays 100/USD, TakerGets 1200
makeOffer($, "root", bidTakerPays, bidTakerGets, callback);
},
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(1, res.asks.length);
var ask = res.asks[0];
assert.strictEqual(Amount.from_json(askTakerGets).to_human_full(),
Amount.from_json(ask.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(askTakerPays).to_human_full(),
Amount.from_json(ask.TakerPays).to_human_full());
assert.strictEqual(askQuality, ask.quality);
assert.strictEqual(1, res.bids.length);
var bid = res.bids[0];
assert.strictEqual(Amount.from_json(bidTakerGets).to_human_full(),
Amount.from_json(bid.TakerGets).to_human_full());
assert.strictEqual(Amount.from_json(bidTakerPays).to_human_full(),
Amount.from_json(bid.TakerPays).to_human_full());
assert.strictEqual(bidQuality, bid.quality);
assert(!res.offers);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Multiple books: One side: Empty book", function (done) {
var self = this;
async.waterfall([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.addBook({
"taker_gets" : {
"currency" : "CNY", "issuer" : "root"
},
"taker_pays" : {
"currency" : "JPY", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(0, res.offers.length);
assert(!res.asks);
assert(!res.bids);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700/CNY/root", "100/JPY/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/JPY/root", "75/CNY/root", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Multiple books: One side: Offers in book", function (done) {
var self = this;
var asks = [
{ takerPays: "500", takerGets: "100/usd/root", quality: "5" },
{ takerPays: "500/cny/root", takerGets: "100/jpy/root", quality: "5" },
];
var bids = [
{ takerPays: "100/usd/root", takerGets: "200", quality: "0.5" },
{ takerPays: "100/jpy/root", takerGets: "200/cny/root", quality: "0.5" },
];
var functions = buildOfferFunctions($, asks)
.concat(buildOfferFunctions($, bids));
async.waterfall(
functions.concat([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.addBook({
"taker_gets" : {
"currency" : "CNY", "issuer" : "root"
},
"taker_pays" : {
"currency" : "JPY", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
matchOffers(bids, res.offers);
assert(!res.asks);
assert(!res.bids);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700/CNY/root", "100/JPY/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/JPY/root", "75/CNY/root", callback);
}
]), function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Multiple books: Both sides: Empty book", function (done) {
var self = this;
async.waterfall([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "CNY", "issuer" : "root"
},
"taker_pays" : {
"currency" : "JPY", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
assert.strictEqual(0, res.asks.length);
assert.strictEqual(0, res.bids.length);
assert(!res.offers);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700/CNY/root", "100/JPY/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/JPY/root", "75/CNY/root", callback);
}
], function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
test("Multiple books: Both sides: Offers in book", function (done) {
var self = this;
var asks = [
{ takerPays: "500", takerGets: "100/usd/root", quality: "5" },
{ takerPays: "500/cny/root", takerGets: "100/jpy/root", quality: "5" },
];
var bids = [
{ takerPays: "100/usd/root", takerGets: "200", quality: "0.5" },
{ takerPays: "100/jpy/root", takerGets: "200/cny/root", quality: "0.5" },
];
var functions = buildOfferFunctions($, asks)
.concat(buildOfferFunctions($, bids));
async.waterfall(
functions.concat([
function (callback) {
var request = $.remote.requestSubscribe(null);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "XRP"
},
"taker_pays" : {
"currency" : "USD", "issuer" : "root"
}
}, true);
request.addBook({
"both" : true,
"taker_gets" : {
"currency" : "CNY", "issuer" : "root"
},
"taker_pays" : {
"currency" : "JPY", "issuer" : "root"
}
}, true);
request.once('success', function(res) {
// console.log("SUBSCRIBE: %s", JSON.stringify(res));
matchOffers(asks, res.asks);
matchOffers(bids, res.bids);
assert(!res.offers);
callback(null);
});
request.once('error', function(err) {
// console.log(err);
callback(err);
});
request.request();
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700", "100/USD/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/USD/root", "75", callback);
},
function (callback) {
// Make another ask. Make sure we get notified
makeOfferWithEvent($, "root", "700/CNY/root", "100/JPY/root", callback);
},
function (callback) {
// Make another bid. Make sure we get notified
makeOfferWithEvent($, "root", "100/JPY/root", "75/CNY/root", callback);
}
]), function (error) {
// console.log("result: error=%s", error);
assert(!error, self.what || "Unspecified Error");
done();
});
});
});

View File

@@ -1,48 +0,0 @@
//
// Configuration for unit tests: not to be locally customized.
//
// Configuration for test accounts.
exports.accounts = {
// Users
"alice" : {
'account' : "rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn",
'secret' : "alice",
},
"bob" : {
'account' : "rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK",
'secret' : "bob",
},
"carol" : {
'account' : "rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW",
'secret' : "carol",
},
"dan" : {
'account' : "rJ85Mok8YRNxSo7NnxKGrPuk29uAeZQqwZ",
'secret' : "dan",
},
// Nexuses
"bitstamp" : {
'account' : "r4jKmc2nQb5yEU6eycefiNKGHTU5NQJASx",
'secret' : "bitstamp",
},
"mtgox" : {
'account' : "rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v",
'secret' : "mtgox",
},
// Merchants
"amazon" : {
'account' : "rhheXqX7bDnXePJeMHhubDDvw2uUTtenPd",
'secret' : "amazon",
},
// Master account
"root" : {
'account' : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
'secret' : "masterpassphrase",
},
};
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,723 +0,0 @@
var async = require('async');
var assert = require('assert');
var extend = require('extend');
var Amount = require('ripple-lib').Amount;
var Remote = require('ripple-lib').Remote;
var Transaction = require('ripple-lib').Transaction;
var UInt160 = require('ripple-lib').UInt160;
var Server = require('./server').Server;
var path = require("path");
var server = { };
function get_config() {
var cfg = require(__dirname + '/config-example');
// See if the person testing wants to override the configuration by creating a
// file called test/config.js.
try {
cfg = extend({}, cfg, require(__dirname + '/config'));
} catch (e) { }
return cfg;
};
function init_config() {
return require('ripple-lib').config.load(get_config());
};
var get_server_config = exports.get_server_config =
function(config, host) {
config = config || init_config();
host = host || config.server_default;
// Override the config with the command line if provided.
var override = {};
// --noserver -> no_server
if (process.env["npm_config_noserver"]) {
override["no_server"] = true;
} else {
override["no_server"] = process.argv.indexOf("--noserver") > -1;
}
// --rippled -> rippled_path
var index = -1;
if (process.env["npm_config_rippled"]) {
override["rippled_path"] = path.resolve(process.cwd(),
process.env["npm_config_rippled"]);
} else if ((index = process.argv.indexOf("--rippled")) > -1) {
if (index < process.argv.length) {
override["rippled_path"] = path.resolve(process.cwd(),
process.argv[index + 1]);
}
} else {
for (var i = process.argv.length-1; i >= 0; --i) {
var arg = process.argv[i].split("=", 2);
if (arg.length === 2 && arg[0] === "--rippled") {
override["rippled_path"] = path.resolve(process.cwd(), arg[1]);
break;
}
}
}
return extend({}, config.default_server_config, config.servers[host],
override);
}
function prepare_tests(tests, fn) {
var tests = typeof tests === 'string' ? [ tests ] : tests;
var result = [ ];
for (var i in tests) {
result.push(fn(tests[i], i));
}
return result;
};
exports.definer_matching = function(matchers, def) {
return function (name, func)
{
var definer = def;
var skip_if_not_match = matchers.skip_if_not_match || [];
var skip_if_match = matchers.skip_if_match || [];
for (var i = 0; i < skip_if_not_match.length; i++) {
var regex = skip_if_not_match[i];
if (!~name.search(regex)) {
definer = definer.skip;
break;
}
}
for (var i = 0; i < skip_if_match.length; i++) {
var regex = skip_if_match[i];
if (~name.search(regex)) {
definer = definer.skip;
break;
}
}
definer(name, func);
};
};
/**
* Helper called by test cases to generate a setUp routine.
*
* By default you would call this without options, but it is useful to
* be able to plug options in during development for quick and easy
* debugging.
*
* @example
* buster.testCase('Foobar', {
* setUp: testutils.build_setup({verbose: true}),
* // ...
* });
*
* @param opts {Object} These options allow quick-and-dirty test-specific
* customizations of your test environment.
* @param opts.verbose {Bool} Enable all debug output (then cover your ears
* and run)
* @param opts.verbose_ws {Bool} Enable tracing in the Remote class. Prints
* websocket traffic.
* @param opts.verbose_server {Bool} Set the -v option when running rippled.
* @param opts.no_server {Bool} Don't auto-run rippled.
* @param host {String} Identifier for the host configuration to be used.
*/
function build_setup(opts, host) {
var config = get_config();
var host = host || config.server_default;
var opts = opts || {};
// Normalize options
if (opts.verbose) {
opts.verbose_ws = true;
opts.verbose_server = true;
}
function setup(done) {
var self = this;
self.compute_fees_amount_for_txs = function(txs) {
var fee_units = Transaction.fee_units['default'] * txs;
return self.remote.fee_tx(fee_units);
};
self.amount_for = function(options) {
var reserve = self.remote.reserve(options.ledger_entries || 0);
var fees = self.compute_fees_amount_for_txs(options.default_transactions || 0);
return reserve.add(fees).add(options.extra || 0);
};
this.store = this.store || {};
this.store[host] = this.store[host] || { };
var data = this.store[host];
data.opts = opts;
var steps = [
function run_server(callback) {
var server_config = get_server_config(config, host);
if (opts.no_server || server_config.no_server) {
return callback();
}
data.server = Server.from_config(host, server_config, !!opts.verbose_server);
// Setting undefined is a noop here
if (data.opts.ledger_file != null) {
data.server.set_ledger_file(data.opts.ledger_file);
}
data.server.once('started', function() {
callback();
});
data.server.once('exited', function () {
// If know the remote, tell it server is gone.
if (self.remote) {
try {
self.remote.setServerFatal();
} catch (e) {
self.remote.serverFatal();
}
}
});
server[host] = data.server;
data.server.start();
},
function connect_websocket(callback) {
self.remote = data.remote = Remote.from_config(host, !!opts.verbose_ws);
self.remote.once('connected', function() {
// self.remote.once('ledger_closed', function() {
callback();
});
self.remote.connect();
}
];
async.series(steps, done);
};
return setup;
};
/**
* Generate tearDown routine.
*
* @param host {String} Identifier for the host configuration to be used.
*/
function build_teardown(host) {
var config = get_config();
var host = host || config.server_default;
function teardown(done) {
var data = this.store[host];
var opts = data.opts;
var server_config = get_server_config(config, host);
var series = [
function disconnect_websocket(callback) {
data.remote.once('disconnected', callback)
data.remote.once('error', function (m) {
//console.log('server error: ', m);
})
data.remote.disconnect();
},
function stop_server(callback) {
if (opts.no_server || server_config.no_server) {
callback();
} else {
data.server.once('stopped', callback)
data.server.stop();
delete server[host];
}
}
];
if (!(opts.no_server || server_config.no_server) && data.server.stopped) {
done()
} else {
async.series(series, done);
}
};
return teardown;
};
function account_dump(remote, account, callback) {
var self = this;
this.what = 'Get latest account_root';
var request = remote.request_ledger_entry('account_root');
request.ledger_hash(remote.ledger_hash());
request.account_root('root');
request.callback(function(err, r) {
assert(!err, self.what);
if (err) {
//console.log('error: %s', m);
callback(err);
} else {
//console.log('account_root: %s', JSON.stringify(r, undefined, 2));
callback();
}
});
// get closed ledger hash
// get account root
// 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);
tx.submit();
tx.once('proposed', function(){callback();});
}
var fund_account =
exports.fund_account =
function(remote, src, account, amount, callback) {
// Cache the seq as 1.
// Otherwise, when other operations attempt to opperate async against the account they may get confused.
remote.set_account_seq(account, 1);
var tx = remote.transaction();
tx.payment(src, account, amount);
tx.once('proposed', function (result) {
//console.log('proposed: %s', JSON.stringify(result));
callback(result.engine_result === 'tesSUCCESS' ? null : result);
});
tx.once('error', function (result) {
//console.log('error: %s', JSON.stringify(result));
callback(result);
});
tx.submit();
}
var create_account =
exports.create_account =
function(remote, src, account, amount, options, callback) {
if (typeof options === 'function') {
callback = options;
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.
var info = remote.requestAccountInfo({account: account});
info.once('success', function(result) {
// The account exists. Fail by returning an error to callback.
callback(new Error("Account " + account + " already exists"));
});
info.once('error', function(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, callback);
});
info.request();
};
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, options, callback);
}, function(){
options = extend({default_rippling: true}, options);
// If we don't want to set default rippling, then bail, otherwise
if (!options.default_rippling) {
return callback();
}
// close the ledger, so all the accounts always exist, then
remote.ledger_accept(function(){
// set the default rippling flag, on all the accounts
async.forEach(accounts, function(account, callback){
set_account_flag(remote, account, 8, callback);
}, function(){
remote.ledger_accept(function(){callback();});
});
});
});
}
function credit_limit(remote, src, amount, callback) {
assert.strictEqual(arguments.length, 4);
var _m = amount.match(/^(\d+\/...\/[^\:]+)(?::(\d+)(?:,(\d+))?)?$/);
if (!_m) {
//console.log('credit_limit: parse error: %s', amount);
return callback(new Error('parse_error'));
}
// console.log('credit_limit: parsed: %s', JSON.stringify(_m, undefined, 2));
var account_limit = _m[1];
var quality_in = _m[2];
var quality_out = _m[3];
if (quality_in) {
quality_in = Number(quality_in);
}
if (quality_out) {
quality_out = Number(quality_out);
}
var tx = remote.transaction()
tx.ripple_line_set(src, account_limit, quality_in, quality_out)
tx.once('proposed', function (m) {
// console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error());
});
tx.once('error', function (m) {
// console.log('error: %s', JSON.stringify(m));
callback(m);
});
tx.submit();
};
function verify_limit(remote, src, amount, callback) {
assert.strictEqual(arguments.length, 4);
var _m = amount.match(/^(\d+\/...\/[^\:]+)(?::(\d+)(?:,(\d+))?)?$/);
if (!_m) {
// console.log('credit_limit: parse error: %s', amount);
return callback(new Error('parse_error'));
}
// console.log('_m', _m.length, _m);
// console.log('verify_limit: parsed: %s', JSON.stringify(_m, undefined, 2));
var account_limit = _m[1];
var quality_in = Number(_m[2]);
var quality_out = Number(_m[3]);
var limit = Amount.from_json(account_limit);
var options = {
account: src,
issuer: limit.issuer().to_json(),
currency: limit.currency().to_json(),
ledger: 'current'
};
remote.request_ripple_balance(options, function(err, m) {
if (err) {
callback(err);
} else {
assert(m.account_limit.equals(limit));
assert(isNaN(quality_in) || m.account_quality_in === quality_in);
assert(isNaN(quality_out) || m.account_quality_out === quality_out);
callback(null);
}
});
};
function credit_limits(remote, balances, callback) {
assert.strictEqual(arguments.length, 3);
var limits = [ ];
for (var src in balances) {
prepare_tests(balances[src], function(amount) {
limits.push({
source: src,
amount: amount
});
});
}
function iterator(limit, callback) {
credit_limit(remote, limit.source, limit.amount, callback);
}
async.eachSeries(limits, iterator, callback);
};
function ledger_close(remote, callback) {
remote.once('ledger_closed', function (m) {
callback();
});
remote.ledger_accept();
};
function payment(remote, src, dst, amount, callback) {
assert.strictEqual(arguments.length, 5);
var tx = remote.transaction();
tx.payment(src, dst, amount);
tx.once('proposed', function (m) {
// console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error());
});
tx.once('error', function (m) {
// console.log('error: %s', JSON.stringify(m));
callback(m);
});
tx.submit();
};
function payments(remote, balances, callback) {
assert.strictEqual(arguments.length, 3);
var sends = [ ];
for (var src in balances) {
prepare_tests(balances[src], function(amount_json) {
sends.push({
source: src,
destination : Amount.from_json(amount_json).issuer().to_json(),
amount : amount_json
});
});
}
function iterator(send, callback) {
payment(remote, send.source, send.destination, send.amount, callback);
}
async.eachSeries(sends, iterator, callback);
};
function transfer_rate(remote, src, billionths, callback) {
assert.strictEqual(arguments.length, 4);
var tx = remote.transaction();
tx.account_set(src);
tx.transfer_rate(billionths);
tx.once('proposed', function (m) {
// console.log('proposed: %s', JSON.stringify(m));
callback(m.engine_result === 'tesSUCCESS' ? null : new Error());
});
tx.once('error', function (m) {
// console.log('error: %s', JSON.stringify(m));
callback(m);
});
tx.submit();
};
function verify_balance(remote, src, amount_json, callback) {
assert.strictEqual(arguments.length, 4);
var amount_req = Amount.from_json(amount_json);
src = UInt160.json_rewrite(src);
if (amount_req.is_native()) {
var options = {account: src, ledger: 'current'};
remote.request_account_balance(options, function(err, amount_act) {
if (err) {
return callback(err);
}
var valid_balance = amount_act.equals(amount_req, true);
if (!valid_balance) {
//console.log('verify_balance: failed: %s / %s',
//amount_act.to_text_full(),
//amount_req.to_text_full());
}
callback(valid_balance ? null : new Error('Balance invalid: ' + amount_json + ' / ' + amount_act.to_json()));
});
} else {
var issuer = amount_req.issuer().to_json();
var currency = amount_req.currency().to_json();
var options = {
account: src,
issuer: issuer,
currency: currency,
ledger: 'current'
};
remote.request_ripple_balance(options, function(err, m) {
if (err) {
return callback(err);
}
// console.log('BALANCE: %s', JSON.stringify(m));
// console.log('account_balance: %s', m.account_balance.to_text_full());
// console.log('account_limit: %s', m.account_limit.to_text_full());
// console.log('issuer_balance: %s', m.issuer_balance.to_text_full());
// console.log('issuer_limit: %s', m.issuer_limit.to_text_full());
var account_balance = Amount.from_json(m.account_balance);
var valid_balance = account_balance.equals(amount_req, true);
if (!valid_balance) {
//console.log('verify_balance: failed: %s vs %s / %s: %s',
//src,
//account_balance.to_text_full(),
//amount_req.to_text_full(),
//account_balance.not_equals_why(amount_req, true));
}
callback(valid_balance ? null : new Error('Balance invalid: ' + amount_json + ' / ' + account_balance.to_json()));
})
}
};
function verify_balances(remote, balances, callback) {
var tests = [ ];
for (var src in balances) {
prepare_tests(balances[src], function(amount) {
tests.push({ source: src, amount: amount });
});
}
function iterator(test, callback) {
verify_balance(remote, test.source, test.amount, callback)
}
async.every(tests, iterator, callback);
};
// --> owner: account
// --> seq: sequence number of creating transaction.
// --> taker_gets: json amount
// --> taker_pays: json amount
function verify_offer(remote, owner, seq, taker_pays, taker_gets, callback) {
assert.strictEqual(arguments.length, 6);
var request = remote.request_ledger_entry('offer')
request.offer_id(owner, seq)
request.callback(function(err, m) {
var wrong = err
|| !Amount.from_json(m.node.TakerGets).equals(Amount.from_json(taker_gets), true)
|| !Amount.from_json(m.node.TakerPays).equals(Amount.from_json(taker_pays), true);
if (wrong) {
//console.log('verify_offer: failed: %s', JSON.stringify(m));
}
callback(wrong ? (err || new Error()) : null);
});
};
function verify_offer_not_found(remote, owner, seq, callback) {
assert.strictEqual(arguments.length, 4);
var request = remote.request_ledger_entry('offer');
request.offer_id(owner, seq);
request.once('success', function (m) {
//console.log('verify_offer_not_found: found offer: %s', JSON.stringify(m));
callback(new Error('entryFound'));
});
request.once('error', function (m) {
// console.log('verify_offer_not_found: success: %s', JSON.stringify(m));
var is_not_found = m.error === 'remoteError' && m.remote.error === 'entryNotFound';
if (is_not_found) {
callback(null);
} else {
callback(new Error());
}
});
request.request();
};
function verify_owner_count(remote, account, count, callback) {
assert(arguments.length === 4);
var options = { account: account, ledger: 'current' };
remote.request_owner_count(options, function(err, owner_count) {
//console.log('owner_count: %s/%d', owner_count, value);
callback(owner_count === count ? null : new Error());
});
};
function verify_owner_counts(remote, counts, callback) {
var tests = prepare_tests(counts, function(account) {
return { account: account, count: counts[account] };
});
function iterator(test, callback) {
verify_owner_count(remote, test.account, test.count, callback)
}
async.every(tests, iterator, callback);
};
var isCI = Boolean(process.env.CI);
function ledger_wait(remote, tx) {
;(function nextLedger() {
remote.once('ledger_closed', function() {
if (!tx.finalized) {
setTimeout(nextLedger, isCI ? 400 : 100);
}
});
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;
exports.create_accounts = create_accounts;
exports.credit_limit = credit_limit;
exports.credit_limits = credit_limits;
exports.get_config = get_config;
exports.init_config = init_config;
exports.ledger_close = ledger_close;
exports.payment = payment;
exports.payments = payments;
exports.transfer_rate = transfer_rate;
exports.verify_balance = verify_balance;
exports.verify_balances = verify_balances;
exports.verify_limit = verify_limit;
exports.verify_offer = verify_offer;
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() {
Object.keys(server).forEach(function(host) {
server[host].stop();
});
});
// vim:sw=2:sts=2:ts=8:et

View File

@@ -1,396 +0,0 @@
/* -------------------------------- REQUIRES -------------------------------- */
var async = require("async");
var assert = require('assert');
var UInt160 = require("ripple-lib").UInt160;
var testutils = require("./testutils");
var config = testutils.init_config();
/* --------------------------------- CONSTS --------------------------------- */
// some parts of the test take a LONG time
var SKIP_TICKET_EXPIRY_PHASE = !process.env.CI &&
!process.env.RUN_TICKET_EXPIRY;
var ROOT_ACCOUNT = UInt160.json_rewrite('root');
var ALICE_ACCOUNT = UInt160.json_rewrite('alice');
/* --------------------------------- HELPERS -------------------------------- */
var prettyj = function(obj) {
return JSON.stringify(obj, undefined, 2);
}
// The create a transaction, submit it, pass a ledger pattern is pervasive
var submit_transaction_factory = function(remote) {
return function(kwargs, cb) {
tx = remote.transaction();
tx.tx_json = kwargs;
tx.submit(cb);
testutils.ledger_wait(remote, tx);
};
};
/* ---------------------------------- TESTS --------------------------------- */
suite.skip("Ticket tests", function() {
var $ = { };
var submit_tx;
setup(function(done) {
testutils.build_setup().call($, function(){
submit_tx = submit_transaction_factory($.remote);
done();
});
});
teardown(function(done) {
testutils.build_teardown().call($, done);
});
test("Delete a non existent ticket", function(done) {
submit_tx(
{
TransactionType: 'TicketCancel',
Account: ROOT_ACCOUNT,
TicketID: '0000000000000000000000000000000000000000000000000000000000000001'
},
function(err, message){
assert.equal(err.engine_result, 'tecNO_ENTRY');
done();
}
);
});
test("Create a ticket with non existent Target", function(done) {
submit_tx(
{
TransactionType: 'TicketCreate',
Account: ROOT_ACCOUNT,
Target: ALICE_ACCOUNT
},
function(err, message){
assert.equal(err.engine_result, 'tecNO_TARGET');
done();
}
);
});
test("Create a ticket where Target == Account. Target unsaved", function(done) {
submit_tx(
{
Account: ROOT_ACCOUNT,
Target : ROOT_ACCOUNT,
TransactionType:'TicketCreate'
},
function(err, message){
assert.ifError(err);
assert.deepEqual(
message.metadata.AffectedNodes[1],
{"CreatedNode":
{"LedgerEntryType": "Ticket",
"LedgerIndex": "7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3",
/*Note there's no `Target` saved in the ticket */
"NewFields": {"Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
"Sequence": 1}}});
done();
}
);
});
test("Create a ticket, then delete by creator", function (done) {
var steps = [
function(callback) {
submit_tx(
{
TransactionType: 'TicketCreate',
Account: ROOT_ACCOUNT,
},
function(err, message){
var affected = message.metadata.AffectedNodes;
var account = affected[0].ModifiedNode;
var ticket = affected[1].CreatedNode;
assert.equal(ticket.LedgerEntryType, 'Ticket');
assert.equal(account.LedgerEntryType, 'AccountRoot');
assert.equal(account.PreviousFields.OwnerCount, 0);
assert.equal(account.FinalFields.OwnerCount, 1);
assert.equal(ticket.NewFields.Sequence, account.PreviousFields.Sequence);
assert.equal(ticket.LedgerIndex, '7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3');
callback();
}
);
},
function delete_ticket(callback) {
submit_tx(
{
TransactionType: 'TicketCancel',
Account: ROOT_ACCOUNT,
TicketID: '7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3'
},
function(err, message){
assert.ifError(err);
assert.equal(message.engine_result, 'tesSUCCESS');
var affected = message.metadata.AffectedNodes;
var account = affected[0].ModifiedNode;
var ticket = affected[1].DeletedNode;
var directory = affected[2].DeletedNode;
assert.equal(ticket.LedgerEntryType, 'Ticket');
assert.equal(account.LedgerEntryType, 'AccountRoot');
assert.equal(directory.LedgerEntryType, 'DirectoryNode');
callback();
}
);
}
]
async.waterfall(steps, done.bind(this));
});
test("Expiration - future", function (done) {
// 1000 seconds is an arbitrarily chosen amount of time to expire the
// ticket. The main thing is that it's not in the past (no ticket ledger
// entry would be created) or 0 (malformed)
seconds_from_now = 1000;
var expiration = $.remote._ledger_time + seconds_from_now;
submit_tx(
{
Account: ROOT_ACCOUNT,
TransactionType: 'TicketCreate',
Expiration: expiration,
},
function(err, message){
assert.ifError(err);
var affected = message.metadata.AffectedNodes;
var account = affected[0].ModifiedNode;
var ticket = affected[1].CreatedNode;
assert.equal(ticket.LedgerEntryType, 'Ticket');
assert.equal(account.LedgerEntryType, 'AccountRoot');
assert.equal(account.PreviousFields.OwnerCount, 0);
assert.equal(account.FinalFields.OwnerCount, 1);
assert.equal(ticket.NewFields.Sequence, account.PreviousFields.Sequence);
assert.equal(ticket.NewFields.Expiration, expiration);
done();
}
);
});
test("Expiration - past", function (done) {
var expiration = $.remote._ledger_time - 1000;
submit_tx(
{
Account: ROOT_ACCOUNT,
TransactionType: 'TicketCreate',
Expiration: expiration,
},
function(err, message){
assert.ifError(err);
assert.equal(message.engine_result, 'tesSUCCESS');
var affected = message.metadata.AffectedNodes;
assert.equal(affected.length, 1); // just the account
var account = affected[0].ModifiedNode;
assert.equal(account.FinalFields.Account, ROOT_ACCOUNT);
done();
}
);
});
test("Expiration - zero", function (done) {
var expiration = 0;
submit_tx(
{
Account: ROOT_ACCOUNT,
TransactionType: 'TicketCreate',
Expiration: expiration,
},
function(err, message) {
assert.equal(err.engine_result, 'temBAD_EXPIRATION');
done();
}
);
});
test("Create a ticket, delete by Target", function (done) {
var steps = [
function create_alice(callback) {
testutils.create_accounts($.remote, "root", "10000.0", ["alice"], callback);
},
function create_ticket(callback) {
var Account = ROOT_ACCOUNT;
var Target = ALICE_ACCOUNT;
submit_tx(
{
TransactionType: 'TicketCreate',
Account: Account,
Target: Target,
},
function(err, message) {
assert.ifError(err);
assert.deepEqual(message.metadata.AffectedNodes[1],
{
CreatedNode: {
LedgerEntryType: "Ticket",
LedgerIndex: "C231BA31A0E13A4D524A75F990CE0D6890B800FF1AE75E51A2D33559547AC1A2",
NewFields: {
Account: Account,
Target: Target,
Sequence: 2,
}
}
});
callback();
}
);
},
function alice_cancels_ticket(callback) {
submit_tx(
{
Account: ALICE_ACCOUNT,
TransactionType: 'TicketCancel',
TicketID: 'C231BA31A0E13A4D524A75F990CE0D6890B800FF1AE75E51A2D33559547AC1A2',
},
function(err, message) {
assert.ifError(err);
assert.equal(message.engine_result, 'tesSUCCESS');
assert.deepEqual(
message.metadata.AffectedNodes[2],
{"DeletedNode":
{"FinalFields": {
"Account": ROOT_ACCOUNT,
"Flags": 0,
"OwnerNode": "0000000000000000",
"Sequence": 2,
"Target": ALICE_ACCOUNT},
"LedgerEntryType": "Ticket",
"LedgerIndex":
"C231BA31A0E13A4D524A75F990CE0D6890B800FF1AE75E51A2D33559547AC1A2"}}
);
callback();
}
);
}
]
async.waterfall(steps, done.bind(this));
});
test("Create a ticket, let it expire, delete by a random", function (done) {
this.timeout(55000);
var remote = $.remote;
var expiration = $.remote._ledger_time + 1;
steps = [
function create_ticket(callback) {
submit_tx(
{
Account: ROOT_ACCOUNT,
TransactionType: 'TicketCreate',
Expiration: expiration,
},
function(err, message) {
callback(null, message);
}
);
},
function verify_creation(message, callback){
var affected = message.metadata.AffectedNodes;
var account = affected[0].ModifiedNode;
var ticket = affected[1].CreatedNode;
assert.equal(ticket.LedgerEntryType, 'Ticket');
assert.equal(account.LedgerEntryType, 'AccountRoot');
assert.equal(account.PreviousFields.OwnerCount, 0);
assert.equal(account.FinalFields.OwnerCount, 1);
assert.equal(ticket.NewFields.Sequence, account.PreviousFields.Sequence);
assert.equal(ticket.LedgerIndex, '7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3');
callback();
},
function create_account_for_issuing_expiration(callback){
testutils.create_accounts($.remote,
"root", "1000.0", ["alice"], callback);
},
function delete_ticket(callback) {
submit_tx(
{
TransactionType: 'TicketCancel',
Account: ALICE_ACCOUNT,
TicketID: '7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3',
},
function(err, message) {
// at this point we are unauthorized
// but it should be expired soon!
assert.equal(err.engine_result, 'tecNO_PERMISSION');
callback();
}
);
},
function close_ledger(callback) {
if (SKIP_TICKET_EXPIRY_PHASE) {
return done();
};
setTimeout(callback, 10001);
},
function close_ledger(callback) {
remote.ledger_accept(function(){callback();});
},
function close_ledger(callback) {
setTimeout(callback, 10001);
},
function close_ledger(callback) {
remote.ledger_accept(function(){callback();});
},
function close_ledger(callback) {
setTimeout(callback, 10001);
},
function close_ledger(callback) {
remote.ledger_accept(function(){callback();});
},
function delete_ticket(callback) {
submit_tx(
{
TransactionType: 'TicketCancel',
Account: ALICE_ACCOUNT,
TicketID: '7F58A0AE17775BA3404D55D406DD1C2E91EADD7AF3F03A26877BCE764CCB75E3',
},
function(err, message) {
callback(null, message);
}
);
},
function verify_deletion (message, callback){
assert.equal(message.engine_result, 'tesSUCCESS');
var affected = message.metadata.AffectedNodes;
var account = affected[0].ModifiedNode;
var ticket = affected[1].DeletedNode;
var account2 = affected[2].ModifiedNode;
var directory = affected[3].DeletedNode;
assert.equal(ticket.LedgerEntryType, 'Ticket');
assert.equal(account.LedgerEntryType, 'AccountRoot');
assert.equal(directory.LedgerEntryType, 'DirectoryNode');
assert.equal(account2.LedgerEntryType, 'AccountRoot');
callback();
}
]
async.waterfall(steps, done.bind(this));
});
});
// vim:sw=2:sts=2:ts=8:etq

View File

@@ -1,492 +0,0 @@
/* -------------------------------- REQUIRES -------------------------------- */
var async = require("async");
var assert = require('assert');
var assert = require('assert');
var UInt160 = require('ripple-lib').UInt160;
var Remote = require('ripple-lib').Remote;
var Server = require('ripple-lib').Server;
var Request = require('ripple-lib').Request;
var testutils = require("./testutils");
var config = testutils.init_config();
var http = require('http');
var request = require('request');
/* --------------------------------- CONFIG --------------------------------- */
// So we can connect to the ports with self signed certificates.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var uniport_test_config = {
// We have one main WebSocket connection that we inherit from
// `testutils.build_setup`. We use that, configured on a non admin, non ssl
// port, to determine readiness for tests on the other ports. This Remote's
// trace is configured in the normal way, however we can set trace for each of
// the test connections on the various ports.
remote_trace : false,
// We can be a bit more exhaustive, but it will cost us 2 seconds, unless
// we use `skip_tests_matching` to `test.skip` them.
define_redundant_tests : false,
skip_tests_matching : [
// /redundant/
// /can not issue/,
// /http/
// /ws/
],
skip_tests_not_matching : [
// /wrong password/,
// /ws/
],
skip_ports_not_matching : [
// /admin/,
// /password/,
// /http/
],
skip_ports_matching : [
// /admin/,
// /password/,
// /http/
]
};
/* --------------------------------- HELPERS -------------------------------- */
function for_each_item (o, f) {
for (var k in o) {
if (o.hasOwnProperty(k)) {
f(k, o[k], o);
}
}
};
function pretty_json (o) {
return JSON.stringify(o, undefined, 2);
};
function client_protocols (conf) {
return conf.protocol.split(',').filter(function (p) {return p !== 'peer';});
};
function same_protocol_opposite_security (protocol) {
switch(protocol)
{
case 'ws':
return 'wss';
case 'http':
return 'https';
case 'wss':
return 'ws';
case 'https':
return 'http';
default:
throw new Error('unknown protocol '+ protocol);
}
};
function one_invocation_function (f) {
var already_done = false;
return function done_once() {
try {
if (!already_done) {
f();
}
} finally {
already_done = true;
}
};
};
function mark_redundant_tests (normalizer) {
var tested = [];
return function (test_name, func) {
var normed = normalizer ? normalizer(test_name) : test_name;
var redundant = ~tested.indexOf(normed);
if (redundant)
test_name += ' (redundant)';
else
tested.push(normed);
if (!redundant || uniport_test_config.define_redundant_tests) {
define_test(test_name, func);
}
};
};
function add_credentials_to_request (message, credentials, wrong_pass,
wrong_user)
{
message.admin_user = credentials.admin_user;
message.admin_password = credentials.admin_password;
if (wrong_pass) {
if (wrong_pass === 'send_object_instead_of_string') {
message.admin_password = {admin_password: message.admin_password};
} else {
message.admin_password += '_';
}
}
if (wrong_user) {
message.admin_user += '_';
}
};
var define_test = testutils.definer_matching(
{
skip_if_not_match:uniport_test_config.skip_tests_not_matching,
skip_if_match:uniport_test_config.skip_tests_matching
},
global.test );
var define_suite = testutils.definer_matching(
{
skip_if_not_match:uniport_test_config.skip_ports_not_matching,
skip_if_match:uniport_test_config.skip_ports_matching
},
global.suite );
/* ---------------------------------- TESTS --------------------------------- */
function test_websocket_admin_command (test_declaration,
protocol,
port_conf, done)
{
var expect_success = test_declaration.expect_success;
var expect_failure = !expect_success;
var require_pass = Boolean(port_conf.admin_password);
var send_credentials = test_declaration.send_credentials;
var wrong_pass = test_declaration.wrong_pass;
var wrong_user = test_declaration.wrong_user;
var config = {
servers : [protocol + '://127.0.0.1:' + port_conf.port],
'trace' : uniport_test_config.remote_trace
};
var remote = Remote.from_config(config);
if (require_pass) {
remote.on('prepare_subscribe', function (request){
request.once('error', function (e, m){
assert.notEqual(e.remote.error,
'forbidden',
'Need credentials for non admin request (subscribe)');
});
});
}
remote.connect( function () {
var before_accept = remote._ledger_current_index;
var request = new Request(remote, 'ledger_accept');
if (send_credentials) {
add_credentials_to_request( request.message, port_conf,
wrong_pass, wrong_user);
}
request.callback(function (error, response){
// Disconnect
remote.disconnect();
function create_error (message) {
var struct = {port_conf: port_conf,
request: request.message,
error: error,
response: response,
test_failure: message};
return pretty_json(struct);
};
if (error) {
assert(expect_failure,
create_error('unexpect failure to issue admin command'));
if (expect_failure) {
if (require_pass && (!send_credentials || wrong_pass || wrong_user)) {
assert.equal(error.remote.error,
'forbidden',
create_error('should be forbidden'));
} else if (!require_pass) {
assert.equal(error.remote.error,
'forbidden',
create_error('should be forbidden'));
}
}
}
if (response) {
if (expect_success) {
assert.equal((before_accept + 1), response.ledger_current_index,
create_error('admin command should work but did not'));
} else {
assert.equal(before_accept, response.ledger_current_index,
create_error('admin command worked but should not have'));
}
}
done();
});
});
};
function test_http_admin_command (test_declaration, protocol, conf, done)
{
var expect_success = test_declaration.expect_success;
var expect_failure = !expect_success;
var require_pass = Boolean(conf.admin_password);
var send_credentials = test_declaration.send_credentials;
var wrong_pass = test_declaration.wrong_pass;
var wrong_user = test_declaration.wrong_user;
var url = protocol+'://localhost:'+conf.port + '/';
var post_options = {
url: url,
json: true,
body: {
method: 'ledger_accept',
}
};
if (send_credentials) {
var credentials = {};
post_options.body.params = [credentials];
add_credentials_to_request(credentials, conf, wrong_pass, wrong_user);
}
request.post(post_options, function (err, response, body) {
function create_error (message) {
var struct = {port_conf: conf,
request: post_options,
error: err,
response: body,
statusCode: response.statusCode,
test_failure: message};
return pretty_json(struct);
};
if (err) {
assert(!err, String(err));
}
if (expect_failure)
{
if (!body) {
assert.equal(response.statusCode, 403);
assert (false, create_error("we expect some kind of response body"));
}
else if (typeof body == 'string') {
assert.equal(response.statusCode, 403);
assert.equal(body.trim(), 'Forbidden');
}
else {
assert(body.result.status != 'success',
create_error("succeded when shouldn't have"));
}
done();
}
else {
var msg = "expected 200 got " + response.statusCode+'\n'+pretty_json(body);
assert.equal(response.statusCode, 200, msg);
if (body && body.result) {
assert.equal(body.result.status, 'success', pretty_json(body));
}
done();
}
});
};
function test_admin_command (test_declaration, protocol,
port_conf, done) {
var type = protocol.slice(0, 2);
if (type == 'ws') {
test_websocket_admin_command(test_declaration, protocol, port_conf, done);
}
else if (type == 'ht') {
test_http_admin_command(test_declaration, protocol, port_conf, done);
}
else {
throw new Error('unknown protocol: ' + protocol);
}
};
function test_cant_connect (port_conf, protocol, done) {
var type = protocol.slice(0, 2);
if (type == 'ws') {
var done_once = one_invocation_function (done);
var WebSocket = Server.websocketConstructor();
var ws_url = protocol+'://localhost:'+port_conf.port + '/';
var ws = new WebSocket(ws_url);
ws.onopen = function () {
assert(false);
};
if (protocol == 'wss') {
setTimeout(function () {
//console.log("----> Timeout, readyState: " + ws.readyState)
//assert.equal(ws.readyState, 0);
assert(true);
done_once();
}, 200);
}
ws.onerror = ws.onclose = function (m) {
//console.log("----> Close, readyState: " + ws.readyState)
assert(true);
done_once();
};
} else if (type == 'ht') {
var url = protocol+'://localhost:'+port_conf.port + '/';
var post_options = {
url: url,
json: true,
body: {
method: 'server_info',
}
};
request.post(post_options, function (err, response, body) {
assert(!body);
assert(!response);
assert(err);
done();
});
} else {
throw new Error('unknown protocol: ' + protocol);
}
};
suite("Uniport tests", function () {
var $ = { };
suiteSetup(function (done) {
testutils.build_setup({}, 'uniport_tests').call($, function () {
done();
});
});
suiteTeardown(function (done) {
testutils.build_teardown('uniport_tests').call($, done);
});
suite('connection', function () {
var define_test = mark_redundant_tests();
for_each_item(config.uniport_test_ports, function (name, conf) {
var protocols = client_protocols(conf);
define_suite(name, function () {
['http', 'https', 'ws', 'wss'].forEach(function (p) {
var op = same_protocol_opposite_security(p);
if (!(~protocols.indexOf(p)) &&
~(protocols.indexOf(op))) {
var test_name = "can't connect using " + p +
" with only " + op + " set";
define_test( test_name,
function (done){
test_cant_connect(conf, p, done);
});
}
});
});
});
});
suite('admin commands', function () {
var define_test = mark_redundant_tests(function (test_name) {
// The password checking should be the same regardless of protocol,
// so we normalize the test name (used to determine test redundancy).
return test_name.replace('wss', 'ws')
.replace('https', 'http');
});
for_each_item(config.uniport_test_ports, function (name, conf) {
define_suite(name, function () {
var protocols = client_protocols(conf);
var allow_admin = conf.admin !== '';
var require_pass = Boolean(conf.admin_password);
function test_for (protocol, params) {
return function (done) {
test_admin_command(params, protocol, conf, done);
};
};
if (allow_admin && require_pass) {
protocols.forEach(function (protocol) {
var can_not_issue_admin_commands =
'can not issue admin commands on '+ protocol + ' ';
define_test(
('can issue admin commands on '+protocol+
' with correct credentials'),
test_for(protocol,
{expect_success: true, send_credentials: true})
);
define_test(
can_not_issue_admin_commands + 'with wrong password',
test_for(protocol, {expect_success: false,
send_credentials: true,
wrong_pass: true})
);
define_test(
can_not_issue_admin_commands + 'with garbage password',
test_for(protocol, {expect_success: false,
send_credentials: true,
wrong_pass: 'send_object_instead_of_string'})
);
define_test(
can_not_issue_admin_commands + ' with wrong user',
test_for(protocol, {expect_success: false,
send_credentials: true,
wrong_user: true})
);
define_test(
can_not_issue_admin_commands + 'without credentials',
test_for(protocol, {expect_success: false,
send_credentials: false})
);
});
}
else if (allow_admin)
{
protocols.forEach(function (protocol) {
define_test(
'can issue admin commands on ' + protocol,
test_for(protocol, {expect_success: true,
send_credentials: false})
);
});
}
else if (!allow_admin)
{
protocols.forEach(function (protocol) {
define_test(
'can not issue admin commands on ' + protocol,
test_for(protocol, {expect_success: false,
send_credentials: false})
);
});
}
});
});
});
});

View File

@@ -1,59 +0,0 @@
var assert = require('assert');
var extend = require("extend");
var Server = require("./server").Server;
var Remote = require("ripple-lib").Remote;
var testutils = require('./testutils');
var config = testutils.init_config();
suite('WebSocket connection', function() {
var server;
setup(function(done) {
this.timeout(2000);
var host = config.server_default;
var cfg = testutils.get_server_config(config, host);
if (cfg.no_server) {
done();
} else {
server = Server.from_config(host, cfg);
server.once('started', done)
server.start();
}
});
teardown(function(done) {
this.timeout(2000);
var cfg = testutils.get_server_config(config);
if (cfg.no_server) {
done();
} else {
server.on('stopped', done);
server.stop();
}
});
test('WebSocket connect and disconnect', function(done) {
// This timeout probably doesn't need to be this long, because
// the test itself completes in less than half a second.
// However, some of the overhead, especially on Windows can
// push the measured time out this far.
this.timeout(3000);
var host = config.server_default;
var alpha = Remote.from_config(host);
alpha.on('connected', function () {
alpha.on('disconnected', function () {
// CLOSED
done();
});
alpha.disconnect();
})
alpha.connect();
});
});
// vim:sw=2:sts=2:ts=8:et