mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-30 23:45:48 +00:00
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:
committed by
Nik Bougalis
parent
698ea58b39
commit
e3ff30657c
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
14
appveyor.yml
14
appveyor.yml
@@ -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" }
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
31
package.json
31
package.json
@@ -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"
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
}}
|
||||
);
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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()
|
||||
BIN
test/fixtures/account-tx-transactions.db
vendored
BIN
test/fixtures/account-tx-transactions.db
vendored
Binary file not shown.
3866
test/fixtures/ledger-6226713.json
vendored
3866
test/fixtures/ledger-6226713.json
vendored
File diff suppressed because it is too large
Load Diff
644
test/fixtures/ledger-7145315.json
vendored
644
test/fixtures/ledger-7145315.json
vendored
@@ -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": []
|
||||
}
|
||||
1
test/fixtures/ledger-full-40000.json
vendored
1
test/fixtures/ledger-full-40000.json
vendored
File diff suppressed because one or more lines are too long
@@ -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()
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
180
test/ledger.js
180
test/ledger.js
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
--require ./test/mocha-loader-patch.js
|
||||
--reporter spec
|
||||
--compilers coffee:coffee-script/register
|
||||
--ui tdd
|
||||
--timeout 10000
|
||||
--slow 600
|
||||
@@ -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
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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
|
||||
2003
test/offer-test.js
2003
test/offer-test.js
File diff suppressed because it is too large
Load Diff
@@ -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"]]}}}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
1404
test/path-test.js
1404
test/path-test.js
File diff suppressed because it is too large
Load Diff
@@ -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"}}}}
|
||||
}
|
||||
@@ -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));
|
||||
});
|
||||
@@ -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' ]
|
||||
@@ -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
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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]);
|
||||
});
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
1345
test/send-test.js
1345
test/send-test.js
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
});
|
||||
});
|
||||
207
test/server.js
207
test/server.js
@@ -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
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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})
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user