mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
Merge branch 'develop' into release
This commit is contained in:
@@ -5,13 +5,16 @@ TOTAL_NODES="$2"
|
||||
|
||||
typecheck() {
|
||||
npm install -g flow-bin
|
||||
flow --version
|
||||
npm run typecheck
|
||||
}
|
||||
|
||||
lint() {
|
||||
echo "eslint $(node_modules/.bin/eslint --version)"
|
||||
npm list babel-eslint | grep babel-eslint
|
||||
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
|
||||
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
|
||||
echo "plugins: [flowtype]" >> ./eslintrc
|
||||
echo "parser: babel-eslint" >> ./eslintrc
|
||||
node_modules/.bin/eslint -c ./eslintrc $(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$")
|
||||
}
|
||||
|
||||
|
||||
103
npm-shrinkwrap.json
generated
103
npm-shrinkwrap.json
generated
@@ -22,6 +22,11 @@
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "3.1.1",
|
||||
"from": "bn.js@*",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.1.tgz"
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-2.0.0.tgz",
|
||||
@@ -36,6 +41,18 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.0.3",
|
||||
"from": "hash.js@*",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"https-proxy-agent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
||||
@@ -102,6 +119,76 @@
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"from": "ripple-address-codec@>=1.6.0 <2.0.0",
|
||||
"dependencies": {
|
||||
"hash.js": {
|
||||
"version": "1.0.3",
|
||||
"from": "hash.js@>=1.0.3 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"from": "x-address-codec@>=0.6.0 <0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
"from": "base-x@>=1.0.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-keypairs": {
|
||||
"version": "0.8.0",
|
||||
"from": "ripple-keypairs@>=0.8.0 <0.9.0",
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "3.1.1",
|
||||
"from": "bn.js@>=3.1.1 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.1.tgz"
|
||||
},
|
||||
"brorand": {
|
||||
"version": "1.0.5",
|
||||
"from": "brorand@>=1.0.5 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "5.1.0",
|
||||
"from": "elliptic@>=5.1.0 <6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.1.0.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hash.js": {
|
||||
"version": "1.0.3",
|
||||
"from": "hash.js@>=1.0.3 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1",
|
||||
"from": "inherits@>=2.0.1 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-lib-transactionparser": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
||||
@@ -112,19 +199,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-wallet-generator": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
|
||||
},
|
||||
"sjcl-extended": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "git://github.com/ripple/sjcl-extended.git#d8cf8b22e7d97193c54e1f65113e3edcf200ca17",
|
||||
"dependencies": {
|
||||
"sjcl": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.3.tgz"
|
||||
}
|
||||
}
|
||||
"sjcl-codec": {
|
||||
"version": "0.1.0",
|
||||
"from": "sjcl-codec@*"
|
||||
},
|
||||
"ws": {
|
||||
"version": "0.7.2",
|
||||
|
||||
19
package.json
19
package.json
@@ -18,25 +18,28 @@
|
||||
"async": "~0.9.0",
|
||||
"babel-runtime": "^5.5.4",
|
||||
"bignumber.js": "^2.0.3",
|
||||
"bn.js": "^3.1.1",
|
||||
"es6-promisify": "^2.0.0",
|
||||
"extend": "~1.2.1",
|
||||
"hash.js": "^1.0.3",
|
||||
"https-proxy-agent": "^1.0.0",
|
||||
"is-my-json-valid": "^2.12.0",
|
||||
"lodash": "^3.1.0",
|
||||
"lru-cache": "~2.5.0",
|
||||
"ripple-address-codec": "^1.6.0",
|
||||
"ripple-keypairs": "^0.8.0",
|
||||
"ripple-lib-transactionparser": "^0.5.0",
|
||||
"ripple-wallet-generator": "^1.0.3",
|
||||
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
|
||||
"sjcl-codec": "0.1.0",
|
||||
"ws": "~0.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"assert-diff": "^1.0.1",
|
||||
"babel": "^5.5.4",
|
||||
"babel-core": "^5.5.4",
|
||||
"babel-eslint": "^3.1.23",
|
||||
"babel-loader": "^5.0.0",
|
||||
"babel": "^5.8.21",
|
||||
"babel-core": "^5.8.22",
|
||||
"babel-eslint": "^4.0.5",
|
||||
"babel-loader": "^5.3.2",
|
||||
"coveralls": "~2.10.0",
|
||||
"eslint": "^1.0.0",
|
||||
"eslint": "^1.2.0",
|
||||
"eslint-plugin-flowtype": "^1.0.0",
|
||||
"eventemitter2": "^0.4.14",
|
||||
"flow-bin": "^0.14",
|
||||
@@ -59,7 +62,7 @@
|
||||
"prepublish": "npm run clean && npm run compile",
|
||||
"test": "istanbul test _mocha",
|
||||
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint --reset -c eslintrc src/",
|
||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
|
||||
"perf": "./scripts/perf_test.sh"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
'use strict';
|
||||
|
||||
var fs = require('fs');
|
||||
var Amount = require('../dist/npm').Amount;
|
||||
var Ledger = require('../dist/npm').Ledger;
|
||||
var ripple = require('../dist/npm')._DEPRECATED;
|
||||
var Amount = ripple.Amount;
|
||||
var Ledger = ripple.Ledger;
|
||||
|
||||
function parse_options(from, flags) {
|
||||
var argv = from.slice(),
|
||||
opts_ = {argv: argv};
|
||||
var argv = from.slice();
|
||||
var opts_ = {argv: argv};
|
||||
|
||||
flags.forEach(function(f) {
|
||||
// Do we have the flag?
|
||||
|
||||
@@ -9,10 +9,12 @@ module.exports = {
|
||||
dropsToXrp: utils.dropsToXrp,
|
||||
xrpToDrops: utils.xrpToDrops,
|
||||
toRippledAmount: utils.toRippledAmount,
|
||||
generateAddress: utils.generateAddress,
|
||||
composeAsync: utils.composeAsync,
|
||||
wrapCatch: utils.wrapCatch,
|
||||
convertExceptions: utils.convertExceptions,
|
||||
convertKeysFromSnakeCaseToCamelCase:
|
||||
utils.convertKeysFromSnakeCaseToCamelCase,
|
||||
promisify: utils.promisify
|
||||
promisify: utils.promisify,
|
||||
isValidAddress: require('./schema-validator').isValidAddress
|
||||
};
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
const validator = require('is-my-json-valid');
|
||||
const core = require('./utils').core;
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
|
||||
let SCHEMAS = {};
|
||||
|
||||
function isValidAddress(address) {
|
||||
return core.UInt160.is_valid(address);
|
||||
function isValidAddress(address: string): boolean {
|
||||
return typeof address === 'string' && address.length > 0 &&
|
||||
address[0] === 'r' &&
|
||||
core.UInt160.is_valid(address);
|
||||
}
|
||||
|
||||
function isValidLedgerHash(ledgerHash) {
|
||||
@@ -33,6 +36,9 @@ function endsWith(str, suffix) {
|
||||
function loadSchemas(dir) {
|
||||
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
|
||||
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
|
||||
const titles = _.map(schemas, schema => schema.title);
|
||||
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
|
||||
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
|
||||
return _.indexBy(schemas, 'title');
|
||||
}
|
||||
|
||||
@@ -64,6 +70,7 @@ function schemaValidate(schemaName: string, object: any): void {
|
||||
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
|
||||
module.exports = {
|
||||
schemaValidate: schemaValidate,
|
||||
isValidAddress: isValidAddress,
|
||||
loadSchema: loadSchema,
|
||||
SCHEMAS: SCHEMAS
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"properties": {
|
||||
"value": {
|
||||
"description": "The balance on the trustline",
|
||||
"$ref": "signed-value"
|
||||
"$ref": "signedValue"
|
||||
},
|
||||
"currency": {
|
||||
"description": "The three-character code or hex string used to denote currencies",
|
||||
|
||||
39
src/api/common/schemas/get-ledger.json
Normal file
39
src/api/common/schemas/get-ledger.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getLedger",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accepted": {"type": "boolean"},
|
||||
"closed": {"type": "boolean"},
|
||||
"stateHash": {"$ref": "hash256"},
|
||||
"closeTime": {"type": "integer", "minimum": 0},
|
||||
"closeTimeResolution": {"type": "integer", "minimum": 1},
|
||||
"closeFlags": {"type": "integer", "minimum": 0},
|
||||
"ledgerHash": {"$ref": "hash256"},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"parentLedgerHash": {"$ref": "hash256"},
|
||||
"parentCloseTime": {"type": "integer", "minimum": 0},
|
||||
"totalDrops": {"$ref": "value"},
|
||||
"transactionHash": {"$ref": "hash256"},
|
||||
"transactions": {"type": "array", "items": {"type": "object"}},
|
||||
"rawTransactions": {"type": "string"},
|
||||
"transactionHashes": {"type": "array", "items": {"$ref": "hash256"}},
|
||||
"rawState": {"type": "string"},
|
||||
"stateHashes": {"type": "array", "items": {"$ref": "hash256"}}
|
||||
},
|
||||
"required": [
|
||||
"accepted",
|
||||
"closed",
|
||||
"stateHash",
|
||||
"closeTime",
|
||||
"closeTimeResolution",
|
||||
"closeFlags",
|
||||
"ledgerHash",
|
||||
"ledgerVersion",
|
||||
"parentLedgerHash",
|
||||
"parentCloseTime",
|
||||
"totalDrops",
|
||||
"transactionHash"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
13
src/api/common/schemas/ledger-options.json
Normal file
13
src/api/common/schemas/ledger-options.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "ledger-options",
|
||||
"description": "Options for getLedger",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"includeAllData": {"type": "boolean"},
|
||||
"includeTransactions": {"type": "boolean"},
|
||||
"includeState": {"type": "boolean"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
24
src/api/common/schemas/max-adjustment.json
Normal file
24
src/api/common/schemas/max-adjustment.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "maxAdjustment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"maxAmount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tag": {
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
},
|
||||
"required": ["address", "maxAmount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "order-change",
|
||||
"title": "orderChange",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"direction": {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"description": "Key is the maker's ripple address; value is an array of changes",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "order-change"}
|
||||
"items": {"$ref": "orderChange"}
|
||||
}
|
||||
},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"title": "payment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"paths": {"type": "string"},
|
||||
"memos": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "value",
|
||||
"title": "signedValue",
|
||||
"description": "A string representation of a floating point number",
|
||||
"type": "string",
|
||||
"pattern": "[-]?^[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
|
||||
"pattern": "^[-]?[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ const BigNumber = require('bignumber.js');
|
||||
const core = require('../../core');
|
||||
const errors = require('./errors');
|
||||
const es6promisify = require('es6-promisify');
|
||||
const keypairs = require('ripple-keypairs');
|
||||
|
||||
type Amount = {currency: string, issuer: string, value: string}
|
||||
|
||||
@@ -27,6 +28,11 @@ function toRippledAmount(amount: Amount): string|Amount {
|
||||
};
|
||||
}
|
||||
|
||||
function generateAddress(options?: Object): Object {
|
||||
const {accountID, seed} = keypairs.generateWallet(options);
|
||||
return {secret: seed, address: accountID};
|
||||
}
|
||||
|
||||
type AsyncFunction = (...x: any) => void
|
||||
|
||||
function wrapCatch(asyncFunction: AsyncFunction): AsyncFunction {
|
||||
@@ -86,7 +92,7 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||
return obj;
|
||||
}
|
||||
|
||||
function promisify<T>(asyncFunction: AsyncFunction): Function {
|
||||
function promisify(asyncFunction: AsyncFunction): Function {
|
||||
return es6promisify(wrapCatch(asyncFunction));
|
||||
}
|
||||
|
||||
@@ -95,6 +101,7 @@ module.exports = {
|
||||
dropsToXrp,
|
||||
xrpToDrops,
|
||||
toRippledAmount,
|
||||
generateAddress,
|
||||
composeAsync,
|
||||
wrapCatch,
|
||||
convertExceptions,
|
||||
|
||||
@@ -9,17 +9,16 @@ function error(text) {
|
||||
return new ValidationError(text);
|
||||
}
|
||||
|
||||
function validateAddressAndSecret(obj: {address: string, secret: string}): void {
|
||||
function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
): void {
|
||||
const address = obj.address;
|
||||
const secret = obj.secret;
|
||||
schemaValidate('address', address);
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
try {
|
||||
core.Seed.from_json(secret).get_key(address);
|
||||
} catch (exception) {
|
||||
throw error('secret does not match address');
|
||||
if (!core.Seed.from_json(secret).is_valid()) {
|
||||
throw error('secret is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +56,7 @@ module.exports = {
|
||||
secret: validateSecret,
|
||||
currency: _.partial(schemaValidate, 'currency'),
|
||||
identifier: _.partial(schemaValidate, 'hash256'),
|
||||
ledgerVersion: _.partial(schemaValidate, 'ledgerVersion'),
|
||||
sequence: _.partial(schemaValidate, 'sequence'),
|
||||
order: _.partial(schemaValidate, 'order'),
|
||||
orderbook: _.partial(schemaValidate, 'orderbook'),
|
||||
@@ -74,6 +74,7 @@ module.exports = {
|
||||
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
|
||||
getLedgerOptions: _.partial(validateOptions, 'ledger-options'),
|
||||
options: _.partial(validateOptions, 'options'),
|
||||
instructions: _.partial(schemaValidate, 'instructions')
|
||||
};
|
||||
|
||||
@@ -28,7 +28,10 @@ const sign = require('./transaction/sign');
|
||||
const submit = require('./transaction/submit');
|
||||
const errors = require('./common').errors;
|
||||
const convertExceptions = require('./common').convertExceptions;
|
||||
const generateWallet = convertExceptions(common.core.Wallet.generate);
|
||||
const generateAddress = convertExceptions(common.generateAddress);
|
||||
const computeLedgerHash = require('./offline/ledgerhash');
|
||||
const getLedger = require('./ledger/ledger');
|
||||
const isValidAddress = common.isValidAddress;
|
||||
|
||||
function RippleAPI(options: {}) {
|
||||
const _options = _.assign({}, options, {automatic_resubmission: false});
|
||||
@@ -52,6 +55,7 @@ RippleAPI.prototype = {
|
||||
getOrderbook,
|
||||
getSettings,
|
||||
getAccountInfo,
|
||||
getLedger,
|
||||
|
||||
preparePayment,
|
||||
prepareTrustline,
|
||||
@@ -61,7 +65,9 @@ RippleAPI.prototype = {
|
||||
sign,
|
||||
submit,
|
||||
|
||||
generateWallet,
|
||||
computeLedgerHash,
|
||||
isValidAddress,
|
||||
generateAddress,
|
||||
errors
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,43 @@ const removeUndefined = require('./parse/utils').removeUndefined;
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
|
||||
function formatAccountInfo(response) {
|
||||
type AccountData = {
|
||||
Sequence: number,
|
||||
Account: string,
|
||||
Balance: string,
|
||||
Flags: number,
|
||||
LedgerEntryType: string,
|
||||
OwnerCount: number,
|
||||
PreviousTxnID: string,
|
||||
AccountTxnID?: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
index: string
|
||||
}
|
||||
|
||||
type AccountDataResponse = {
|
||||
account_data: AccountData,
|
||||
ledger_current_index?: number,
|
||||
ledger_hash?: string,
|
||||
ledger_index: number,
|
||||
validated: boolean
|
||||
}
|
||||
|
||||
type AccountInfoOptions = {
|
||||
ledgerVersion?: number
|
||||
}
|
||||
|
||||
type AccountInfoCallback = (err: any, data: AccountInfoResponse) => void
|
||||
|
||||
type AccountInfoResponse = {
|
||||
sequence: number,
|
||||
xrpBalance: string,
|
||||
ownerCount: number,
|
||||
previousInitiatedTransactionID: string,
|
||||
previousAffectingTransactionID: string,
|
||||
previousAffectingTransactionLedgerVersion: number
|
||||
}
|
||||
|
||||
function formatAccountInfo(response: AccountDataResponse) {
|
||||
const data = response.account_data;
|
||||
return removeUndefined({
|
||||
sequence: data.Sequence,
|
||||
@@ -18,21 +54,24 @@ function formatAccountInfo(response) {
|
||||
});
|
||||
}
|
||||
|
||||
function getAccountInfoAsync(account, options, callback) {
|
||||
function getAccountInfoAsync(account: string, options: AccountInfoOptions,
|
||||
callback: AccountInfoCallback
|
||||
) {
|
||||
validate.address(account);
|
||||
validate.getAccountInfoOptions(options);
|
||||
|
||||
const request = {
|
||||
account: account,
|
||||
ledger: options.ledgerVersion
|
||||
ledger: options.ledgerVersion || 'validated'
|
||||
};
|
||||
|
||||
this.remote.requestAccountInfo(request,
|
||||
composeAsync(formatAccountInfo, callback));
|
||||
}
|
||||
|
||||
function getAccountInfo(account: string, options={}) {
|
||||
return utils.promisify(getAccountInfoAsync.bind(this))(account, options);
|
||||
function getAccountInfo(account: string, options: AccountInfoOptions={}
|
||||
): Promise<AccountInfoResponse> {
|
||||
return utils.promisify(getAccountInfoAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getAccountInfo;
|
||||
|
||||
@@ -25,7 +25,7 @@ function formatBalances(balances) {
|
||||
}
|
||||
|
||||
function getTrustlinesAsync(account, options, callback) {
|
||||
getTrustlines.bind(this)(account, options)
|
||||
getTrustlines.call(this, account, options)
|
||||
.then(data => callback(null, data))
|
||||
.catch(callback);
|
||||
}
|
||||
@@ -43,7 +43,7 @@ function getBalancesAsync(account, options, callback) {
|
||||
}
|
||||
|
||||
function getBalances(account: string, options={}) {
|
||||
return utils.promisify(getBalancesAsync.bind(this))(account, options);
|
||||
return utils.promisify(getBalancesAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getBalances;
|
||||
|
||||
26
src/api/ledger/ledger.js
Normal file
26
src/api/ledger/ledger.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const parseLedger = require('./parse/ledger');
|
||||
|
||||
function getLedgerAsync(options, callback) {
|
||||
validate.getLedgerOptions(options);
|
||||
|
||||
const request = {
|
||||
ledger: options.ledgerVersion || 'validated',
|
||||
expand: options.includeAllData,
|
||||
transactions: options.includeTransactions,
|
||||
accounts: options.includeState
|
||||
};
|
||||
|
||||
this.remote.requestLedger(request,
|
||||
composeAsync(response => parseLedger(response.ledger), callback));
|
||||
}
|
||||
|
||||
function getLedger(options={}) {
|
||||
return utils.promisify(getLedgerAsync).call(this, options);
|
||||
}
|
||||
|
||||
module.exports = getLedger;
|
||||
@@ -10,7 +10,8 @@ const composeAsync = utils.common.composeAsync;
|
||||
// account is to specify a "perspective", which affects which unfunded offers
|
||||
// are returned
|
||||
function getBookOffers(remote, account, ledgerVersion, limit,
|
||||
takerGets, takerPays, callback) {
|
||||
takerGets, takerPays, callback
|
||||
) {
|
||||
remote.requestBookOffers(utils.renameCounterpartyToIssuerInOrder({
|
||||
taker_gets: takerGets,
|
||||
taker_pays: takerPays,
|
||||
@@ -77,7 +78,7 @@ function getOrderbookAsync(account, orderbook, options, callback) {
|
||||
}
|
||||
|
||||
function getOrderbook(account: string, orderbook: Object, options={}) {
|
||||
return utils.promisify(getOrderbookAsync.bind(this))(
|
||||
return utils.promisify(getOrderbookAsync).call(this,
|
||||
account, orderbook, options);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ const composeAsync = utils.common.composeAsync;
|
||||
const parseAccountOrder = require('./parse/account-order');
|
||||
|
||||
function requestAccountOffers(remote, address, ledgerVersion, options,
|
||||
marker, limit, callback) {
|
||||
marker, limit, callback
|
||||
) {
|
||||
remote.requestAccountOffers({
|
||||
account: address,
|
||||
marker: marker,
|
||||
@@ -34,7 +35,7 @@ function getOrdersAsync(account, options, callback) {
|
||||
}
|
||||
|
||||
function getOrders(account: string, options={}) {
|
||||
return utils.promisify(getOrdersAsync.bind(this))(account, options);
|
||||
return utils.promisify(getOrdersAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getOrders;
|
||||
|
||||
50
src/api/ledger/parse/ledger.js
Normal file
50
src/api/ledger/parse/ledger.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const removeUndefined = require('./utils').removeUndefined;
|
||||
const parseTransaction = require('./transaction');
|
||||
|
||||
function parseTransactions(transactions) {
|
||||
if (_.isEmpty(transactions)) {
|
||||
return {};
|
||||
}
|
||||
if (_.isString(transactions[0])) {
|
||||
return {transactionHashes: transactions};
|
||||
}
|
||||
return {
|
||||
transactions: _.map(transactions, parseTransaction),
|
||||
rawTransactions: JSON.stringify(transactions)
|
||||
};
|
||||
}
|
||||
|
||||
function parseState(state) {
|
||||
if (_.isEmpty(state)) {
|
||||
return {};
|
||||
}
|
||||
if (_.isString(state[0])) {
|
||||
return {stateHashes: state};
|
||||
}
|
||||
return {rawState: JSON.stringify(state)};
|
||||
}
|
||||
|
||||
function parseLedger(ledger: Object): Object {
|
||||
return removeUndefined(_.assign({
|
||||
accepted: ledger.accepted,
|
||||
closed: ledger.closed,
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: ledger.close_time,
|
||||
closeTimeResolution: ledger.close_time_resolution,
|
||||
closeFlags: ledger.close_flags,
|
||||
ledgerHash: ledger.hash || ledger.ledger_hash,
|
||||
ledgerVersion: parseInt(ledger.ledger_index || ledger.seqNum, 10),
|
||||
parentLedgerHash: ledger.parent_hash,
|
||||
parentCloseTime: ledger.parent_close_time,
|
||||
totalDrops: ledger.total_coins || ledger.totalCoins,
|
||||
transactionHash: ledger.transaction_hash
|
||||
},
|
||||
parseTransactions(ledger.transactions),
|
||||
parseState(ledger.accountState)
|
||||
));
|
||||
}
|
||||
|
||||
module.exports = parseLedger;
|
||||
@@ -41,7 +41,7 @@ function parsePayment(tx: Object): Object {
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
amount: removeGenericCounterparty(
|
||||
maxAmount: removeGenericCounterparty(
|
||||
parseAmount(tx.SendMax || tx.Amount), tx.Account),
|
||||
tag: tx.SourceTag
|
||||
};
|
||||
|
||||
@@ -15,8 +15,11 @@ function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseQuality(quality) {
|
||||
return (new BigNumber(quality)).shift(-9).toNumber();
|
||||
function parseQuality(quality?: number) {
|
||||
if (typeof quality === 'number') {
|
||||
return (new BigNumber(quality)).shift(-9).toNumber();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseTrustline(tx: Object): Object {
|
||||
|
||||
@@ -46,7 +46,7 @@ function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
_.omit(utils.common.toRippledAmount(amount), 'value'));
|
||||
}
|
||||
|
||||
remote.requestRipplePathFind(params,
|
||||
remote.createPathFind(params,
|
||||
composeAsync(_.partial(addParams, params), callback));
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ function getPathsAsync(pathfind, callback) {
|
||||
}
|
||||
|
||||
function getPaths(pathfind: Object) {
|
||||
return utils.promisify(getPathsAsync.bind(this))(pathfind);
|
||||
return utils.promisify(getPathsAsync).call(this, pathfind);
|
||||
}
|
||||
|
||||
module.exports = getPaths;
|
||||
|
||||
@@ -30,7 +30,7 @@ function getSettingsAsync(account, options, callback) {
|
||||
|
||||
const request = {
|
||||
account: account,
|
||||
ledger: options.ledgerVersion
|
||||
ledger: options.ledgerVersion || 'validated'
|
||||
};
|
||||
|
||||
this.remote.requestAccountInfo(request,
|
||||
@@ -38,7 +38,7 @@ function getSettingsAsync(account, options, callback) {
|
||||
}
|
||||
|
||||
function getSettings(account: string, options={}) {
|
||||
return utils.promisify(getSettingsAsync.bind(this))(account, options);
|
||||
return utils.promisify(getSettingsAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getSettings;
|
||||
|
||||
147
src/api/ledger/transaction-types.js
Normal file
147
src/api/ledger/transaction-types.js
Normal file
@@ -0,0 +1,147 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
|
||||
type Outcome = {
|
||||
result: string,
|
||||
timestamp?: string,
|
||||
fee: string,
|
||||
balanceChanges: Object,
|
||||
orderbookChanges: Object,
|
||||
ledgerVersion: number,
|
||||
indexInLedger: number
|
||||
}
|
||||
|
||||
type Adjustment = {
|
||||
address: string,
|
||||
amount: {
|
||||
currency: string,
|
||||
counterparty?: string,
|
||||
value: string
|
||||
},
|
||||
tag?: number
|
||||
}
|
||||
|
||||
type Trustline = {
|
||||
currency: string,
|
||||
counterparty: string,
|
||||
limit: string,
|
||||
qualityIn?: number,
|
||||
qualityOut?: number,
|
||||
ripplingDisabled?: boolean,
|
||||
authorized?: boolean,
|
||||
frozen?: boolean
|
||||
}
|
||||
|
||||
type Settings = {
|
||||
passwordSpent?: boolean,
|
||||
requireDestinationTag?: boolean,
|
||||
requireAuthorization?: boolean,
|
||||
disallowIncomingXRP?: boolean,
|
||||
disableMasterKey?: boolean,
|
||||
enableTransactionIDTracking?: boolean,
|
||||
noFreeze?: boolean,
|
||||
globalFreeze?: boolean,
|
||||
defaultRipple?: boolean,
|
||||
emailHash?: string,
|
||||
walletLocator?: string,
|
||||
walletSize?: number,
|
||||
messageKey?: string,
|
||||
domain?: string,
|
||||
transferRate?: number,
|
||||
signers?: string,
|
||||
regularKey?: string
|
||||
}
|
||||
|
||||
type OrderCancellation = {
|
||||
orderSequence: number
|
||||
}
|
||||
|
||||
type Memo = {
|
||||
type?: string,
|
||||
format?: string,
|
||||
data?: string
|
||||
}
|
||||
|
||||
type Amount = {
|
||||
value: string,
|
||||
currency: string,
|
||||
counterparty?: string
|
||||
}
|
||||
|
||||
type Payment = {
|
||||
source: Adjustment,
|
||||
destination: Adjustment,
|
||||
paths?: string,
|
||||
memos?: Array<Memo>,
|
||||
invoiceID?: string,
|
||||
allowPartialPayment?: boolean,
|
||||
noDirectRipple?: boolean,
|
||||
limitQuality?: boolean
|
||||
}
|
||||
|
||||
type PaymentTransaction = {
|
||||
type: string,
|
||||
specification: Payment,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type Order = {
|
||||
direction: string,
|
||||
quantity: Amount,
|
||||
totalPrice: Amount,
|
||||
immediateOrCancel?: boolean,
|
||||
fillOrKill?: boolean,
|
||||
passive?: boolean
|
||||
}
|
||||
|
||||
type OrderTransaction = {
|
||||
type: string,
|
||||
specification: Order,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type OrderCancellationTransaction = {
|
||||
type: string,
|
||||
specification: OrderCancellation,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type TrustlineTransaction = {
|
||||
type: string,
|
||||
specification: Trustline,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
type SettingsTransaction = {
|
||||
type: string,
|
||||
specification: Settings,
|
||||
outcome: Outcome,
|
||||
id: string,
|
||||
address: string,
|
||||
sequence: number
|
||||
}
|
||||
|
||||
export type TransactionOptions = {
|
||||
minLedgerVersion?: number,
|
||||
maxLedgerVersion?: number
|
||||
}
|
||||
|
||||
export type GetTransactionResponse = PaymentTransaction | OrderTransaction |
|
||||
OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction
|
||||
|
||||
export type GetTransactionResponseCallback =
|
||||
(err?: ?Error, data?: GetTransactionResponse) => void
|
||||
|
||||
export type CallbackType = (err?: ?Error, data?: Object) => void
|
||||
@@ -8,7 +8,15 @@ const validate = utils.common.validate;
|
||||
const errors = utils.common.errors;
|
||||
const RippleError = require('../../core/rippleerror').RippleError;
|
||||
|
||||
function attachTransactionDate(remote, tx, callback) {
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
import type {CallbackType, GetTransactionResponse,
|
||||
GetTransactionResponseCallback, TransactionOptions}
|
||||
from './transaction-types';
|
||||
|
||||
function attachTransactionDate(remote: Remote, tx: Object,
|
||||
callback: CallbackType
|
||||
) {
|
||||
if (tx.date) {
|
||||
callback(null, tx);
|
||||
return;
|
||||
@@ -29,22 +37,24 @@ function attachTransactionDate(remote, tx, callback) {
|
||||
});
|
||||
}
|
||||
|
||||
function isTransactionInRange(tx, options) {
|
||||
function isTransactionInRange(tx: Object, options: TransactionOptions) {
|
||||
return (!options.minLedgerVersion
|
||||
|| tx.ledger_index >= options.minLedgerVersion)
|
||||
&& (!options.maxLedgerVersion
|
||||
|| tx.ledger_index <= options.maxLedgerVersion);
|
||||
}
|
||||
|
||||
function getTransactionAsync(identifier, options, callback) {
|
||||
function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
callback: GetTransactionResponseCallback
|
||||
) {
|
||||
validate.identifier(identifier);
|
||||
validate.getTransactionOptions(options);
|
||||
|
||||
const remote = this.remote;
|
||||
const maxLedgerVersion = Math.min(options.maxLedgerVersion,
|
||||
const maxLedgerVersion = Math.min(options.maxLedgerVersion || Infinity,
|
||||
remote.getLedgerSequence());
|
||||
|
||||
function callbackWrapper(error_, tx) {
|
||||
function callbackWrapper(error_?: Error, tx?: Object) {
|
||||
let error = error_;
|
||||
if (error instanceof RippleError && error.remote &&
|
||||
error.remote.error === 'txnNotFound') {
|
||||
@@ -56,23 +66,28 @@ function getTransactionAsync(identifier, options, callback) {
|
||||
options.minLedgerVersion, maxLedgerVersion)) {
|
||||
callback(new errors.MissingLedgerHistoryError('Transaction not found,'
|
||||
+ ' but the server\'s ledger history is incomplete'));
|
||||
} else if (!error && !isTransactionInRange(tx, options)) {
|
||||
} else if (!error && tx && !isTransactionInRange(tx, options)) {
|
||||
callback(new errors.NotFoundError('Transaction not found'));
|
||||
} else if (error) {
|
||||
callback(error);
|
||||
} else if (!tx) {
|
||||
callback(new Error('Internal error'));
|
||||
} else {
|
||||
callback(error, parseTransaction(tx));
|
||||
}
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
_.partial(remote.requestTx.bind(remote), {hash: identifier, binary: false}),
|
||||
_.partial(remote.requestTx.bind(remote),
|
||||
{hash: identifier, binary: false}),
|
||||
_.partial(attachTransactionDate, remote)
|
||||
], callbackWrapper);
|
||||
}
|
||||
|
||||
function getTransaction(identifier: string, options={}) {
|
||||
return utils.promisify(getTransactionAsync.bind(this))(identifier, options);
|
||||
function getTransaction(identifier: string,
|
||||
options: TransactionOptions={}
|
||||
): Promise<GetTransactionResponse> {
|
||||
return utils.promisify(getTransactionAsync).call(this, identifier, options);
|
||||
}
|
||||
|
||||
module.exports = getTransaction;
|
||||
|
||||
@@ -15,6 +15,21 @@ function parseAccountTxTransaction(tx) {
|
||||
return parseTransaction(tx.tx);
|
||||
}
|
||||
|
||||
function counterpartyFilter(filters, tx) {
|
||||
if (!filters.counterparty) {
|
||||
return true;
|
||||
}
|
||||
if (tx.address === filters.counterparty || (
|
||||
tx.specification && (
|
||||
(tx.specification.destination &&
|
||||
tx.specification.destination.address === filters.counterparty) ||
|
||||
(tx.specification.counterparty === filters.counterparty)
|
||||
))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function transactionFilter(address, filters, tx) {
|
||||
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
|
||||
return false;
|
||||
@@ -28,8 +43,7 @@ function transactionFilter(address, filters, tx) {
|
||||
if (filters.initiated === false && tx.address === address) {
|
||||
return false;
|
||||
}
|
||||
if (filters.counterparty && tx.address !== filters.counterparty
|
||||
&& tx.specification.destination.address !== filters.counterparty) {
|
||||
if (filters.counterparty && !counterpartyFilter(filters, tx)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -104,7 +118,7 @@ function getTransactionsAsync(account, options, callback) {
|
||||
|
||||
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
|
||||
if (options.start) {
|
||||
getTransaction.bind(this)(options.start).then(tx => {
|
||||
getTransaction.call(this, options.start).then(tx => {
|
||||
const ledgerVersion = tx.outcome.ledgerVersion;
|
||||
const bound = options.earliestFirst ?
|
||||
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
|
||||
@@ -118,7 +132,7 @@ function getTransactionsAsync(account, options, callback) {
|
||||
}
|
||||
|
||||
function getTransactions(account: string, options={}) {
|
||||
return utils.promisify(getTransactionsAsync.bind(this))(account, options);
|
||||
return utils.promisify(getTransactionsAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTransactions;
|
||||
|
||||
@@ -10,7 +10,8 @@ function currencyFilter(currency, trustline) {
|
||||
}
|
||||
|
||||
function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
|
||||
callback) {
|
||||
callback
|
||||
) {
|
||||
const requestOptions = {
|
||||
account: address,
|
||||
ledger: ledgerVersion,
|
||||
@@ -31,7 +32,8 @@ function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
|
||||
|
||||
function getTrustlinesAsync(account: string, options: {currency: string,
|
||||
counterparty: string, limit: number, ledgerVersion: number},
|
||||
callback: () => void): void {
|
||||
callback: () => void
|
||||
): void {
|
||||
validate.address(account);
|
||||
validate.getTrustlinesOptions(options);
|
||||
|
||||
@@ -43,7 +45,7 @@ function getTrustlinesAsync(account: string, options: {currency: string,
|
||||
}
|
||||
|
||||
function getTrustlines(account: string, options={}) {
|
||||
return utils.promisify(getTrustlinesAsync.bind(this))(account, options);
|
||||
return utils.promisify(getTrustlinesAsync).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTrustlines;
|
||||
|
||||
@@ -5,6 +5,7 @@ const assert = require('assert');
|
||||
const common = require('../common');
|
||||
const dropsToXrp = common.dropsToXrp;
|
||||
const composeAsync = common.composeAsync;
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
type Callback = (err: any, data: any) => void
|
||||
|
||||
@@ -13,8 +14,9 @@ function clamp(value: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
function getXRPBalance(remote: any, address: string, ledgerVersion?: number,
|
||||
callback: Callback): void {
|
||||
function getXRPBalance(remote: Remote, address: string, ledgerVersion?: number,
|
||||
callback: Callback
|
||||
): void {
|
||||
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
|
||||
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
|
||||
}
|
||||
@@ -24,7 +26,8 @@ type Getter = (marker: ?string, limit: number, callback: Callback) => void
|
||||
// If the marker is omitted from a response, you have reached the end
|
||||
// getter(marker, limit, callback), callback(error, {marker, results})
|
||||
function getRecursiveRecur(getter: Getter, marker?: string, limit: number,
|
||||
callback: Callback): void {
|
||||
callback: Callback
|
||||
): void {
|
||||
getter(marker, limit, (error, data) => {
|
||||
if (error) {
|
||||
return callback(error);
|
||||
@@ -81,19 +84,21 @@ function signum(num) {
|
||||
* @returns {Number} [-1, 0, 1]
|
||||
*/
|
||||
|
||||
type Outcome = {outcome: {ledgerVersion: string, indexInLedger: string}};
|
||||
type Outcome = {outcome: {ledgerVersion: number, indexInLedger: number}};
|
||||
|
||||
function compareTransactions(first: Outcome, second: Outcome): number {
|
||||
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
|
||||
return signum(Number(first.outcome.indexInLedger) -
|
||||
Number(second.outcome.indexInLedger));
|
||||
if (!first.outcome || !second.outcome) {
|
||||
return 0;
|
||||
}
|
||||
return Number(first.outcome.ledgerVersion) <
|
||||
Number(second.outcome.ledgerVersion) ? -1 : 1;
|
||||
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger);
|
||||
}
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
|
||||
}
|
||||
|
||||
function hasCompleteLedgerRange(remote: any, minLedgerVersion: number,
|
||||
maxLedgerVersion: number): boolean {
|
||||
function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number,
|
||||
maxLedgerVersion?: number
|
||||
): boolean {
|
||||
const firstLedgerVersion = 32570; // earlier versions have been lost
|
||||
return remote.getServer().hasLedgerRange(
|
||||
minLedgerVersion || firstLedgerVersion,
|
||||
|
||||
74
src/api/offline/ledgerhash.js
Normal file
74
src/api/offline/ledgerhash.js
Normal file
@@ -0,0 +1,74 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const common = require('../common');
|
||||
|
||||
function convertLedgerHeader(header) {
|
||||
return {
|
||||
accepted: header.accepted,
|
||||
closed: header.closed,
|
||||
account_hash: header.stateHash,
|
||||
close_time: header.closeTime,
|
||||
close_time_resolution: header.closeTimeResolution,
|
||||
close_flags: header.closeFlags,
|
||||
hash: header.ledgerHash,
|
||||
ledger_hash: header.ledgerHash,
|
||||
ledger_index: header.ledgerVersion.toString(),
|
||||
seqNum: header.ledgerVersion.toString(),
|
||||
parent_hash: header.parentLedgerHash,
|
||||
parent_close_time: header.parentCloseTime,
|
||||
total_coins: header.totalDrops,
|
||||
totalCoins: header.totalDrops,
|
||||
transaction_hash: header.transactionHash
|
||||
};
|
||||
}
|
||||
|
||||
function hashLedgerHeader(ledgerHeader) {
|
||||
const header = convertLedgerHeader(ledgerHeader);
|
||||
return common.core.Ledger.calculateLedgerHash(header);
|
||||
}
|
||||
|
||||
function computeTransactionHash(ledger) {
|
||||
if (ledger.rawTransactions === undefined) {
|
||||
return ledger.transactionHash;
|
||||
}
|
||||
const transactions = JSON.parse(ledger.rawTransactions);
|
||||
const txs = _.map(transactions, tx => {
|
||||
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {});
|
||||
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
|
||||
tx.meta ? {metaData: tx.meta} : {});
|
||||
return renameMeta;
|
||||
});
|
||||
const ledgerObject = common.core.Ledger.from_json({transactions: txs});
|
||||
const transactionHash = ledgerObject.calc_tx_hash().to_hex();
|
||||
if (ledger.transactionHash !== undefined
|
||||
&& ledger.transactionHash !== transactionHash) {
|
||||
throw new common.errors.ValidationError('transactionHash in header'
|
||||
+ ' does not match computed hash of transactions');
|
||||
}
|
||||
return transactionHash;
|
||||
}
|
||||
|
||||
function computeStateHash(ledger) {
|
||||
if (ledger.rawState === undefined) {
|
||||
return ledger.stateHash;
|
||||
}
|
||||
const state = JSON.parse(ledger.rawState);
|
||||
const ledgerObject = common.core.Ledger.from_json({accountState: state});
|
||||
const stateHash = ledgerObject.calc_account_hash().to_hex();
|
||||
if (ledger.stateHash !== undefined && ledger.stateHash !== stateHash) {
|
||||
throw new common.errors.ValidationError('stateHash in header'
|
||||
+ ' does not match computed hash of state');
|
||||
}
|
||||
return stateHash;
|
||||
}
|
||||
|
||||
function computeLedgerHash(ledger: Object): string {
|
||||
const hashes = {
|
||||
transactionHash: computeTransactionHash(ledger),
|
||||
stateHash: computeStateHash(ledger)
|
||||
};
|
||||
return hashLedgerHeader(_.assign({}, ledger, hashes));
|
||||
}
|
||||
|
||||
module.exports = computeLedgerHash;
|
||||
@@ -4,11 +4,41 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const common = require('../common');
|
||||
import type {Remote} from '../../core/remote';
|
||||
|
||||
// If a ledger is not received in this time, consider the connection offline
|
||||
const CONNECTION_TIMEOUT = 1000 * 30;
|
||||
|
||||
function isUpToDate(remote): boolean {
|
||||
type GetServerInfoResponse = {
|
||||
buildVersion: string,
|
||||
completeLedgers: string,
|
||||
hostid: string,
|
||||
ioLatencyMs: number,
|
||||
load?: {
|
||||
jobTypes: Array<Object>,
|
||||
threads: number
|
||||
},
|
||||
lastClose: {
|
||||
convergeTimeS: number,
|
||||
proposers: number
|
||||
},
|
||||
loadFactor: number,
|
||||
peers: number,
|
||||
pubkeyNode: string,
|
||||
pubkeyValidator?: string,
|
||||
serverState: string,
|
||||
validatedLedger: {
|
||||
age: number,
|
||||
baseFeeXrp: number,
|
||||
hash: string,
|
||||
reserveBaseXrp: number,
|
||||
reserveIncXrp: number,
|
||||
seq: number
|
||||
},
|
||||
validationQuorum: number
|
||||
}
|
||||
|
||||
function isUpToDate(remote: Remote): boolean {
|
||||
const server = remote.getServer();
|
||||
return Boolean(server) && (remote._stand_alone
|
||||
|| (Date.now() - server._lastLedgerClose) <= CONNECTION_TIMEOUT);
|
||||
@@ -18,13 +48,17 @@ function isConnected(): boolean {
|
||||
return Boolean(this.remote._ledger_current_index) && isUpToDate(this.remote);
|
||||
}
|
||||
|
||||
function getServerInfoAsync(callback: (err: any, data: any) => void): void {
|
||||
function getServerInfoAsync(
|
||||
callback: (err: any, data?: GetServerInfoResponse) => void
|
||||
): void {
|
||||
this.remote.requestServerInfo((error, response) => {
|
||||
if (error) {
|
||||
const message = _.get(error, ['remote', 'error_message'], error.message);
|
||||
const message =
|
||||
_.get(error, ['remote', 'error_message'], error.message);
|
||||
callback(new common.errors.RippledNetworkError(message));
|
||||
} else {
|
||||
callback(null, common.convertKeysFromSnakeCaseToCamelCase(response.info));
|
||||
callback(null,
|
||||
common.convertKeysFromSnakeCaseToCamelCase(response.info));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -37,19 +71,19 @@ function getLedgerVersion(): number {
|
||||
return this.remote.getLedgerSequence();
|
||||
}
|
||||
|
||||
function connect() {
|
||||
function connect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.connect(() => callback(null));
|
||||
})();
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
function disconnect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
})();
|
||||
}
|
||||
|
||||
function getServerInfo() {
|
||||
function getServerInfo(): Promise<GetServerInfoResponse> {
|
||||
return common.promisify(getServerInfoAsync.bind(this))();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const toRippledAmount = utils.common.toRippledAmount;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
|
||||
function isXRPToXRPPayment(payment) {
|
||||
const sourceCurrency = _.get(payment, 'source.amount.currency');
|
||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
|
||||
const destinationCurrency = _.get(payment, 'destination.amount.currency');
|
||||
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP';
|
||||
}
|
||||
@@ -23,8 +23,8 @@ function applyAnyCounterpartyEncoding(payment) {
|
||||
// https://ripple.com/build/transactions/
|
||||
// #special-issuer-values-for-sendmax-and-amount
|
||||
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
|
||||
if (isIOUWithoutCounterparty(payment.source.amount)) {
|
||||
payment.source.amount.counterparty = payment.source.address;
|
||||
if (isIOUWithoutCounterparty(payment.source.maxAmount)) {
|
||||
payment.source.maxAmount.counterparty = payment.source.address;
|
||||
}
|
||||
if (isIOUWithoutCounterparty(payment.destination.amount)) {
|
||||
payment.destination.amount.counterparty = payment.destination.address;
|
||||
@@ -71,7 +71,7 @@ function createPaymentTransaction(account, payment) {
|
||||
// temREDUNDANT_SEND_MAX removed in:
|
||||
// https://github.com/ripple/rippled/commit/
|
||||
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
|
||||
transaction.sendMax(toRippledAmount(payment.source.amount));
|
||||
transaction.sendMax(toRippledAmount(payment.source.maxAmount));
|
||||
|
||||
if (payment.paths) {
|
||||
transaction.paths(JSON.parse(payment.paths));
|
||||
|
||||
@@ -15,15 +15,13 @@ const validate = utils.common.validate;
|
||||
* some arbitrary string. For example "TXN".
|
||||
*/
|
||||
const HASH_TX_ID = 0x54584E00; // 'TXN'
|
||||
const HASH_TX_SIGN = 0x53545800; // 'STX'
|
||||
const HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
|
||||
|
||||
function getKeyPair(secret) {
|
||||
return core.Seed.from_json(secret).get_key();
|
||||
}
|
||||
|
||||
function getPublicKeyHex(keypair) {
|
||||
return keypair.to_hex_pub();
|
||||
return keypair.pubKeyHex();
|
||||
}
|
||||
|
||||
function serialize(txJSON) {
|
||||
@@ -34,17 +32,12 @@ function hashSerialization(serialized, prefix) {
|
||||
return serialized.hash(prefix || HASH_TX_ID).to_hex();
|
||||
}
|
||||
|
||||
function hashJSON(txJSON, prefix) {
|
||||
return hashSerialization(serialize(txJSON), prefix);
|
||||
}
|
||||
|
||||
function signingHash(txJSON, isTestNet=false) {
|
||||
return hashJSON(txJSON, isTestNet ? HASH_TX_SIGN_TESTNET : HASH_TX_SIGN);
|
||||
function signingData(txJSON) {
|
||||
return core.Transaction.from_json(txJSON).signingData().buffer;
|
||||
}
|
||||
|
||||
function computeSignature(txJSON, keypair) {
|
||||
const signature = keypair.sign(signingHash(txJSON));
|
||||
return core.sjcl.codec.hex.fromBits(signature).toUpperCase();
|
||||
return keypair.signHex(signingData(txJSON));
|
||||
}
|
||||
|
||||
function sign(txJSON: {Account: string; SigningPubKey: string,
|
||||
|
||||
@@ -12,13 +12,14 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const util = require('util');
|
||||
const extend = require('extend');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const TransactionManager = require('./transactionmanager').TransactionManager;
|
||||
const sjcl = require('./utils').sjcl;
|
||||
const Base = require('./base').Base;
|
||||
const util = require('util');
|
||||
const {createAccountID} = require('ripple-keypairs');
|
||||
const {encodeAccountID} = require('ripple-address-codec');
|
||||
const {EventEmitter} = require('events');
|
||||
const {hexToArray} = require('./utils');
|
||||
const {TransactionManager} = require('./transactionmanager');
|
||||
const {UInt160} = require('./uint160');
|
||||
|
||||
/**
|
||||
* @constructor Account
|
||||
@@ -41,7 +42,7 @@ function Account(remote, account) {
|
||||
this._entry = { };
|
||||
|
||||
function listenerAdded(type) {
|
||||
if (~Account.subscribeEvents.indexOf(type)) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestSubscribe()
|
||||
.addAccount(self._account_id)
|
||||
@@ -54,7 +55,7 @@ function Account(remote, account) {
|
||||
this.on('newListener', listenerAdded);
|
||||
|
||||
function listenerRemoved(type) {
|
||||
if (~Account.subscribeEvents.indexOf(type)) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
self._subs -= 1;
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestUnsubscribe()
|
||||
@@ -376,12 +377,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
Account._publicKeyToAddress = function(public_key) {
|
||||
// Based on functions in /src/js/ripple/keypair.js
|
||||
function hexToUInt160(publicKey) {
|
||||
const bits = sjcl.codec.hex.toBits(publicKey);
|
||||
const hash = sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
||||
const address = UInt160.from_bits(hash);
|
||||
address.set_version(Base.VER_ACCOUNT_ID);
|
||||
|
||||
return address.to_json();
|
||||
return encodeAccountID(createAccountID(hexToArray(publicKey)));
|
||||
}
|
||||
|
||||
if (UInt160.is_valid(public_key)) {
|
||||
|
||||
127
src/core/base.js
127
src/core/base.js
@@ -1,18 +1,11 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const sjcl = require('./utils').sjcl;
|
||||
const utils = require('./utils');
|
||||
|
||||
const BN = require('bn.js');
|
||||
const extend = require('extend');
|
||||
const convertBase = require('./baseconverter');
|
||||
const {encode, decode} = require('ripple-address-codec');
|
||||
|
||||
const Base = {};
|
||||
|
||||
const alphabets = Base.alphabets = {
|
||||
ripple: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
||||
tipple: 'RPShNAF39wBUDnEGHJKLM4pQrsT7VWXYZ2bcdeCg65jkm8ofqi1tuvaxyz',
|
||||
bitcoin: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
};
|
||||
|
||||
extend(Base, {
|
||||
VER_NONE: 1,
|
||||
VER_NODE_PUBLIC: 28,
|
||||
@@ -21,134 +14,44 @@ extend(Base, {
|
||||
VER_ACCOUNT_PUBLIC: 35,
|
||||
VER_ACCOUNT_PRIVATE: 34,
|
||||
VER_FAMILY_GENERATOR: 41,
|
||||
VER_FAMILY_SEED: 33
|
||||
});
|
||||
|
||||
function sha256(bytes) {
|
||||
return sjcl.codec.bytes.fromBits(
|
||||
sjcl.hash.sha256.hash(sjcl.codec.bytes.toBits(bytes)));
|
||||
}
|
||||
|
||||
function encodeString(alphabet, input) {
|
||||
if (input.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const leadingZeros = _.takeWhile(input, function(d) {
|
||||
return d === 0;
|
||||
});
|
||||
const out = convertBase(input, 256, 58).map(function(digit) {
|
||||
if (digit < 0 || digit >= alphabet.length) {
|
||||
throw new Error('Value ' + digit + ' is out of bounds for encoding');
|
||||
}
|
||||
return alphabet[digit];
|
||||
});
|
||||
const prefix = leadingZeros.map(function() {
|
||||
return alphabet[0];
|
||||
});
|
||||
return prefix.concat(out).join('');
|
||||
}
|
||||
|
||||
function decodeString(indexes, input) {
|
||||
if (input.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const input58 = input.split('').map(function(c) {
|
||||
const charCode = c.charCodeAt(0);
|
||||
if (charCode >= indexes.length || indexes[charCode] === -1) {
|
||||
throw new Error('Character ' + c + ' is not valid for encoding');
|
||||
}
|
||||
return indexes[charCode];
|
||||
});
|
||||
const leadingZeros = _.takeWhile(input58, function(d) {
|
||||
return d === 0;
|
||||
});
|
||||
const out = convertBase(input58, 58, 256);
|
||||
return leadingZeros.concat(out);
|
||||
}
|
||||
|
||||
function Base58(alphabet) {
|
||||
const indexes = utils.arraySet(128, -1);
|
||||
for (let i = 0; i < alphabet.length; i++) {
|
||||
indexes[alphabet.charCodeAt(i)] = i;
|
||||
}
|
||||
return {
|
||||
decode: decodeString.bind(null, indexes),
|
||||
encode: encodeString.bind(null, alphabet)
|
||||
};
|
||||
}
|
||||
|
||||
Base.encoders = {};
|
||||
Object.keys(alphabets).forEach(function(alphabet) {
|
||||
Base.encoders[alphabet] = new Base58(alphabets[alphabet]);
|
||||
VER_FAMILY_SEED: 33,
|
||||
VER_ED25519_SEED: [0x01, 0xE1, 0x4B]
|
||||
});
|
||||
|
||||
// --> input: big-endian array of bytes.
|
||||
// <-- string at least as long as input.
|
||||
Base.encode = function(input, alpha) {
|
||||
return this.encoders[alpha || 'ripple'].encode(input);
|
||||
Base.encode = function(input, alphabet) {
|
||||
return encode(input, {alphabet});
|
||||
};
|
||||
|
||||
// --> input: String
|
||||
// <-- array of bytes or undefined.
|
||||
Base.decode = function(input, alpha) {
|
||||
Base.decode = function(input, alphabet) {
|
||||
if (typeof input !== 'string') {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return this.encoders[alpha || 'ripple'].decode(input);
|
||||
return decode(input, {alphabet});
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
Base.verify_checksum = function(bytes) {
|
||||
const computed = sha256(sha256(bytes.slice(0, -4))).slice(0, 4);
|
||||
const checksum = bytes.slice(-4);
|
||||
return _.isEqual(computed, checksum);
|
||||
};
|
||||
|
||||
// --> input: Array
|
||||
// <-- String
|
||||
Base.encode_check = function(version, input, alphabet) {
|
||||
const buffer = [].concat(version, input);
|
||||
const check = sha256(sha256(buffer)).slice(0, 4);
|
||||
|
||||
return Base.encode([].concat(buffer, check), alphabet);
|
||||
return encode(input, {version, alphabet});
|
||||
};
|
||||
|
||||
// --> input : String
|
||||
// <-- NaN || sjcl.bn
|
||||
// <-- NaN || BN
|
||||
Base.decode_check = function(version, input, alphabet) {
|
||||
const buffer = Base.decode(input, alphabet);
|
||||
|
||||
if (!buffer || buffer.length < 5) {
|
||||
try {
|
||||
const decoded = decode(input, {version, alphabet});
|
||||
return new BN(decoded);
|
||||
} catch (e) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
// Single valid version
|
||||
if (typeof version === 'number' && buffer[0] !== version) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
// Multiple allowed versions
|
||||
if (Array.isArray(version) && _.every(version, function(v) {
|
||||
return v !== buffer[0];
|
||||
})) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
if (!Base.verify_checksum(buffer)) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
// We'll use the version byte to add a leading zero, this ensures JSBN doesn't
|
||||
// intrepret the value as a negative number
|
||||
buffer[0] = 0;
|
||||
|
||||
return sjcl.bn.fromBits(
|
||||
sjcl.codec.bytes.toBits(buffer.slice(0, -4)));
|
||||
};
|
||||
|
||||
exports.Base = Base;
|
||||
|
||||
@@ -27,13 +27,4 @@ exports._test = {
|
||||
RangeSet: require('./rangeset').RangeSet
|
||||
};
|
||||
|
||||
// Important: We do not guarantee any specific version of SJCL or for any
|
||||
// specific features to be included. The version and configuration may change at
|
||||
// any time without warning.
|
||||
//
|
||||
// However, for programs that are tied to a specific version of ripple.js like
|
||||
// the official client, it makes sense to expose the SJCL instance so we don't
|
||||
// have to include it twice.
|
||||
exports.sjcl = require('./utils').sjcl;
|
||||
exports.Wallet = require('ripple-wallet-generator')({sjcl: exports.sjcl});
|
||||
exports.types = require('./serializedtypes');
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint new-cap: 1*/
|
||||
|
||||
var sjcl = require('./utils').sjcl;
|
||||
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var UInt256 = require('./uint256').UInt256;
|
||||
var Base = require('./base').Base;
|
||||
|
||||
function KeyPair() {
|
||||
this._curve = sjcl.ecc.curves.k256;
|
||||
this._secret = null;
|
||||
this._pubkey = null;
|
||||
}
|
||||
|
||||
KeyPair.from_bn_secret = function(j) {
|
||||
return (j instanceof this) ? j.clone() : (new this()).parse_bn_secret(j);
|
||||
};
|
||||
|
||||
KeyPair.prototype.parse_bn_secret = function(j) {
|
||||
this._secret = new sjcl.ecc.ecdsa.secretKey(sjcl.ecc.curves.k256, j);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {sjcl.ecc.ecdsa.publicKey} public key
|
||||
*/
|
||||
KeyPair.prototype._pub = function() {
|
||||
var curve = this._curve;
|
||||
|
||||
if (!this._pubkey && this._secret) {
|
||||
var exponent = this._secret._exponent;
|
||||
|
||||
this._pubkey = new sjcl.ecc.ecdsa.publicKey(curve, curve.G.mult(exponent));
|
||||
}
|
||||
|
||||
return this._pubkey;
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {sjcl.bitArray} public key bits in compressed form
|
||||
*/
|
||||
KeyPair.prototype._pub_bits = function() {
|
||||
var pub = this._pub();
|
||||
|
||||
if (!pub) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var point = pub._point, y_even = point.y.mod(2).equals(0);
|
||||
|
||||
return sjcl.bitArray.concat(
|
||||
[sjcl.bitArray.partial(8, y_even ? 0x02 : 0x03)],
|
||||
point.x.toBits(this._curve.r.bitLength())
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {String} public key bytes in compressed form, hex encoded.
|
||||
*/
|
||||
KeyPair.prototype.to_hex_pub = function() {
|
||||
var bits = this._pub_bits();
|
||||
|
||||
if (!bits) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.hex.fromBits(bits).toUpperCase();
|
||||
};
|
||||
|
||||
function sha256_ripemd160(bits) {
|
||||
return sjcl.hash.ripemd160.hash(sjcl.hash.sha256.hash(bits));
|
||||
}
|
||||
|
||||
KeyPair.prototype.get_address = function() {
|
||||
var bits = this._pub_bits();
|
||||
|
||||
if (!bits) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var hash = sha256_ripemd160(bits);
|
||||
|
||||
var address = UInt160.from_bits(hash);
|
||||
address.set_version(Base.VER_ACCOUNT_ID);
|
||||
return address;
|
||||
};
|
||||
|
||||
KeyPair.prototype.sign = function(hash) {
|
||||
var PARANOIA_256_BITS = 6; // sjcl constant for ensuring 256 bits of entropy
|
||||
hash = UInt256.from_json(hash);
|
||||
var sig = this._secret.sign(hash.to_bits(), PARANOIA_256_BITS);
|
||||
sig = this._secret.canonicalizeSignature(sig);
|
||||
return this._secret.encodeDER(sig);
|
||||
};
|
||||
|
||||
exports.KeyPair = KeyPair;
|
||||
@@ -1,19 +1,19 @@
|
||||
/* eslint-disable valid-jsdoc */
|
||||
'use strict';
|
||||
var Transaction = require('./transaction').Transaction;
|
||||
var SHAMap = require('./shamap').SHAMap;
|
||||
var SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
|
||||
var SerializedObject = require('./serializedobject').SerializedObject;
|
||||
var stypes = require('./serializedtypes');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Currency = require('./currency').Currency;
|
||||
const BigNumber = require('bignumber.js');
|
||||
const Transaction = require('./transaction').Transaction;
|
||||
const SHAMap = require('./shamap').SHAMap;
|
||||
const SHAMapTreeNode = require('./shamap').SHAMapTreeNode;
|
||||
const SerializedObject = require('./serializedobject').SerializedObject;
|
||||
const stypes = require('./serializedtypes');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Currency = require('./currency').Currency;
|
||||
|
||||
function Ledger() {
|
||||
this.ledger_json = {};
|
||||
}
|
||||
|
||||
Ledger.from_json = function(v) {
|
||||
var ledger = new Ledger();
|
||||
const ledger = new Ledger();
|
||||
ledger.parse_json(v);
|
||||
return ledger;
|
||||
};
|
||||
@@ -23,14 +23,13 @@ Ledger.space = require('./ledgerspaces');
|
||||
/**
|
||||
* Generate the key for an AccountRoot entry.
|
||||
*
|
||||
* @param {String|UInt160} account Ripple Account
|
||||
* @param {String|UInt160} accountArg - Ripple Account
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcAccountRootEntryHash =
|
||||
Ledger.prototype.calcAccountRootEntryHash = function(account) {
|
||||
account = UInt160.from_json(account);
|
||||
|
||||
var index = new SerializedObject();
|
||||
Ledger.prototype.calcAccountRootEntryHash = function(accountArg) {
|
||||
const account = UInt160.from_json(accountArg);
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.account.charCodeAt(0)]);
|
||||
index.append(account.to_bytes());
|
||||
@@ -41,17 +40,15 @@ Ledger.prototype.calcAccountRootEntryHash = function(account) {
|
||||
/**
|
||||
* Generate the key for an Offer entry.
|
||||
*
|
||||
* @param {String|UInt160} account Ripple Account
|
||||
* @param {Number} sequence Sequence number of the OfferCreate transaction
|
||||
* @param {String|UInt160} accountArg - Ripple Account
|
||||
* @param {Number} sequence - Sequence number of the OfferCreate transaction
|
||||
* that instantiated this offer.
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcOfferEntryHash =
|
||||
Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
|
||||
account = UInt160.from_json(account);
|
||||
sequence = parseInt(sequence, 10);
|
||||
|
||||
var index = new SerializedObject();
|
||||
Ledger.prototype.calcOfferEntryHash = function(accountArg, sequence) {
|
||||
const account = UInt160.from_json(accountArg);
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.offer.charCodeAt(0)]);
|
||||
index.append(account.to_bytes());
|
||||
@@ -65,17 +62,17 @@ Ledger.prototype.calcOfferEntryHash = function(account, sequence) {
|
||||
*
|
||||
* The ordering of the two account parameters does not matter.
|
||||
*
|
||||
* @param {String|UInt160} account1 First Ripple Account
|
||||
* @param {String|UInt160} account2 Second Ripple Account
|
||||
* @param {String|Currency} currency The currency code
|
||||
* @param {String|UInt160} _account1 - First Ripple Account
|
||||
* @param {String|UInt160} _account2 - Second Ripple Account
|
||||
* @param {String|Currency} _currency - The currency code
|
||||
* @return {UInt256}
|
||||
*/
|
||||
Ledger.calcRippleStateEntryHash =
|
||||
Ledger.prototype.calcRippleStateEntryHash = function(
|
||||
account1, account2, currency) {
|
||||
currency = Currency.from_json(currency);
|
||||
account1 = UInt160.from_json(account1);
|
||||
account2 = UInt160.from_json(account2);
|
||||
_account1, _account2, _currency) {
|
||||
const currency = Currency.from_json(_currency);
|
||||
const account1 = UInt160.from_json(_account1);
|
||||
const account2 = UInt160.from_json(_account2);
|
||||
|
||||
if (!account1.is_valid()) {
|
||||
throw new Error('Invalid first account');
|
||||
@@ -87,18 +84,14 @@ Ledger.prototype.calcRippleStateEntryHash = function(
|
||||
throw new Error('Invalid currency');
|
||||
}
|
||||
|
||||
// The lower ID has to come first
|
||||
if (account1.to_bn().greaterEquals(account2.to_bn())) {
|
||||
var tmp = account2;
|
||||
account2 = account1;
|
||||
account1 = tmp;
|
||||
}
|
||||
|
||||
var index = new SerializedObject();
|
||||
const swap = account1.greater_than(account2);
|
||||
const lowAccount = swap ? account2 : account1;
|
||||
const highAccount = swap ? account1 : account2;
|
||||
const index = new SerializedObject();
|
||||
|
||||
index.append([0, Ledger.space.rippleState.charCodeAt(0)]);
|
||||
index.append(account1.to_bytes());
|
||||
index.append(account2.to_bytes());
|
||||
index.append(lowAccount.to_bytes());
|
||||
index.append(highAccount.to_bytes());
|
||||
index.append(currency.to_bytes());
|
||||
|
||||
return index.hash();
|
||||
@@ -109,13 +102,13 @@ Ledger.prototype.parse_json = function(v) {
|
||||
};
|
||||
|
||||
Ledger.prototype.calc_tx_hash = function() {
|
||||
var tx_map = new SHAMap();
|
||||
const tx_map = new SHAMap();
|
||||
|
||||
this.ledger_json.transactions.forEach(function(tx_json) {
|
||||
var tx = Transaction.from_json(tx_json);
|
||||
var meta = SerializedObject.from_json(tx_json.metaData);
|
||||
const tx = Transaction.from_json(tx_json);
|
||||
const meta = SerializedObject.from_json(tx_json.metaData);
|
||||
|
||||
var data = new SerializedObject();
|
||||
const data = new SerializedObject();
|
||||
stypes.VariableLength.serialize(data, tx.serialize().to_hex());
|
||||
stypes.VariableLength.serialize(data, meta.to_hex());
|
||||
tx_map.add_item(tx.hash(), data, SHAMapTreeNode.TYPE_TRANSACTION_MD);
|
||||
@@ -125,22 +118,23 @@ Ledger.prototype.calc_tx_hash = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* @param options .sanity_test {Boolean}
|
||||
* @return hash of shamap
|
||||
* @param {Object} options - object
|
||||
*
|
||||
* If `true`, will serialize each accountState item to binary and then back to
|
||||
* json before finally serializing for hashing. This is mostly to expose any
|
||||
* issues with ripple-lib's binary <--> json codecs.
|
||||
* @param {Boolean} [options.sanity_test=false] - If `true`, will serialize each
|
||||
* accountState item to binary and then back to json before finally
|
||||
* serializing for hashing. This is mostly to expose any issues with
|
||||
* ripple-lib's binary <--> json codecs.
|
||||
*
|
||||
* @return {UInt256} - hash of shamap
|
||||
*/
|
||||
Ledger.prototype.calc_account_hash = function(options) {
|
||||
var account_map = new SHAMap();
|
||||
var erred;
|
||||
const account_map = new SHAMap();
|
||||
let erred;
|
||||
|
||||
this.ledger_json.accountState.forEach(function(le) {
|
||||
var data = SerializedObject.from_json(le);
|
||||
let data = SerializedObject.from_json(le);
|
||||
|
||||
var json;
|
||||
let json;
|
||||
if (options && options.sanity_test) {
|
||||
try {
|
||||
json = data.to_json();
|
||||
@@ -163,4 +157,24 @@ Ledger.prototype.calc_account_hash = function(options) {
|
||||
return account_map.hash();
|
||||
};
|
||||
|
||||
// see rippled Ledger::updateHash()
|
||||
Ledger.calculateLedgerHash =
|
||||
Ledger.prototype.calculateLedgerHash = function(ledgerHeader) {
|
||||
const so = new SerializedObject();
|
||||
const prefix = 0x4C575200;
|
||||
const totalCoins = (new BigNumber(ledgerHeader.total_coins)).toString(16);
|
||||
|
||||
stypes.Int32.serialize(so, Number(ledgerHeader.ledger_index));
|
||||
stypes.Int64.serialize(so, totalCoins);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.parent_hash);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.transaction_hash);
|
||||
stypes.Hash256.serialize(so, ledgerHeader.account_hash);
|
||||
stypes.Int32.serialize(so, ledgerHeader.parent_close_time);
|
||||
stypes.Int32.serialize(so, ledgerHeader.close_time);
|
||||
stypes.Int8.serialize(so, ledgerHeader.close_time_resolution);
|
||||
stypes.Int8.serialize(so, ledgerHeader.close_flags);
|
||||
|
||||
return so.hash(prefix).to_hex();
|
||||
};
|
||||
|
||||
exports.Ledger = Ledger;
|
||||
|
||||
@@ -11,8 +11,8 @@ const Amount = require('./amount').Amount;
|
||||
* the 'end' and 'superceded' events.
|
||||
*/
|
||||
|
||||
function PathFind(remote, src_account, dst_account,
|
||||
dst_amount, src_currencies) {
|
||||
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.remote = remote;
|
||||
@@ -59,7 +59,8 @@ PathFind.prototype.create = function() {
|
||||
};
|
||||
|
||||
PathFind.prototype.close = function() {
|
||||
this.remote.request_path_find_close().broadcast().request();
|
||||
this.removeAllListeners('update');
|
||||
this.remote.requestPathFindClose().broadcast().request();
|
||||
this.emit('end');
|
||||
this.emit('close');
|
||||
};
|
||||
@@ -73,7 +74,7 @@ PathFind.prototype.notify_update = function(message) {
|
||||
// looking for.
|
||||
if (this.src_account === src_account &&
|
||||
this.dst_account === dst_account &&
|
||||
this.dst_amount.equals(dst_amount)) {
|
||||
dst_amount.equals(this.dst_amount)) {
|
||||
this.emit('update', message);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,6 +65,7 @@ function Remote(options = {}) {
|
||||
this._transaction_listeners = 0;
|
||||
this._received_tx = new LRU({max: 100});
|
||||
this._cur_path_find = null;
|
||||
this._queued_path_finds = [];
|
||||
|
||||
if (this.local_signing) {
|
||||
// Local signing implies local fees and sequences
|
||||
@@ -90,7 +91,7 @@ function Remote(options = {}) {
|
||||
this._books = { };
|
||||
|
||||
// Secrets that we know about.
|
||||
// Secrets can be set by calling set_secret(account, secret).
|
||||
// Secrets can be set by calling setSecret(account, secret).
|
||||
// account : secret
|
||||
this.secrets = { };
|
||||
|
||||
@@ -205,7 +206,7 @@ Remote.TRANSACTION_EVENTS = [
|
||||
'transaction_all'
|
||||
];
|
||||
|
||||
// Flags for ledger entries. In support of account_root().
|
||||
// Flags for ledger entries. In support of accountRoot().
|
||||
Remote.flags = {
|
||||
// AccountRoot
|
||||
account_root: {
|
||||
@@ -1746,8 +1747,8 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback_) {
|
||||
const request = this.requestSubscribe(feeds);
|
||||
|
||||
function serverSubscribed(message) {
|
||||
self._stand_alone = !!message.stand_alone;
|
||||
self._testnet = !!message.testnet;
|
||||
self._stand_alone = Boolean(message.stand_alone);
|
||||
self._testnet = Boolean(message.testnet);
|
||||
self._handleLedgerClosed(message, server);
|
||||
self.emit('subscribed');
|
||||
}
|
||||
@@ -1934,9 +1935,14 @@ Remote.prototype.findAccount = function(accountID) {
|
||||
* @return {PathFind}
|
||||
*/
|
||||
|
||||
function createPathFind(options_) {
|
||||
function createPathFind(options_, callback) {
|
||||
const options = {};
|
||||
|
||||
if (this._cur_path_find !== null) {
|
||||
this._queued_path_finds.push({options: options_, callback: callback});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_.isPlainObject(options_)) {
|
||||
_.merge(options, options_);
|
||||
} else {
|
||||
@@ -1955,10 +1961,21 @@ function createPathFind(options_) {
|
||||
this._cur_path_find.notify_superceded();
|
||||
}
|
||||
|
||||
pathFind.create();
|
||||
if (callback) {
|
||||
pathFind.on('update', (data) => {
|
||||
if (data.full_reply) {
|
||||
pathFind.close();
|
||||
callback(null, data);
|
||||
}
|
||||
});
|
||||
pathFind.on('error', (error) => {
|
||||
pathFind.close();
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
this._cur_path_find = pathFind;
|
||||
|
||||
pathFind.create();
|
||||
return pathFind;
|
||||
}
|
||||
|
||||
@@ -2277,7 +2294,7 @@ Remote.prototype.requestRipplePathFind = function(options_, callback_) {
|
||||
destination_account: options_.dst_account,
|
||||
destination_amount: options_.dst_amount,
|
||||
source_currencies: options_.src_currencies
|
||||
}, options_);
|
||||
}, options_);
|
||||
} else {
|
||||
_.merge(options, makeOptions(
|
||||
'ripple_path_find',
|
||||
@@ -2325,7 +2342,7 @@ Remote.prototype.requestPathFindCreate = function(options_, callback_) {
|
||||
destination_account: options_.dst_account,
|
||||
destination_amount: options_.dst_amount,
|
||||
source_currencies: options_.src_currencies
|
||||
}, options_);
|
||||
}, options_);
|
||||
} else {
|
||||
_.merge(options, makeOptions(
|
||||
'path_find',
|
||||
@@ -2369,6 +2386,11 @@ Remote.prototype.requestPathFindClose = function(callback) {
|
||||
|
||||
request.message.subcommand = 'close';
|
||||
request.callback(callback);
|
||||
this._cur_path_find = null;
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
125
src/core/seed.js
125
src/core/seed.js
@@ -4,18 +4,18 @@
|
||||
// Seed support
|
||||
//
|
||||
|
||||
const {KeyPair, KeyType} = require('ripple-keypairs');
|
||||
const {decodeSeed, encodeSeed} = require('ripple-address-codec');
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
|
||||
const Base = require('./base').Base;
|
||||
const UInt = require('./uint').UInt;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const KeyPair = require('./keypair').KeyPair;
|
||||
|
||||
const Seed = extend(function() {
|
||||
this._curve = sjcl.ecc.curves.k256;
|
||||
this._value = NaN;
|
||||
this._type = KeyType.secp256k1;
|
||||
}, UInt);
|
||||
|
||||
Seed.width = 16;
|
||||
@@ -34,7 +34,7 @@ Seed.prototype.parse_json = function(j) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
}
|
||||
if (!this.is_valid()) {
|
||||
if (!this.is_valid() && j[0] !== 's') {
|
||||
this.parse_passphrase(j);
|
||||
}
|
||||
}
|
||||
@@ -52,9 +52,19 @@ Seed.prototype.parse_base58 = function(j) {
|
||||
if (!j.length || j[0] !== 's') {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
this._value = Base.decode_check(Base.VER_FAMILY_SEED, j);
|
||||
try {
|
||||
const {bytes, type} = decodeSeed(j);
|
||||
this._value = new BN(bytes);
|
||||
this._type = type;
|
||||
} catch (e) {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.set_ed25519 = function() {
|
||||
this._type = KeyType.ed25519;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -63,10 +73,9 @@ Seed.prototype.parse_passphrase = function(j) {
|
||||
throw new Error('Passphrase must be a string');
|
||||
}
|
||||
|
||||
const hash = sjcl.hash.sha512.hash(sjcl.codec.utf8String.toBits(j));
|
||||
const bits = sjcl.bitArray.bitSlice(hash, 0, 128);
|
||||
|
||||
this.parse_bits(bits);
|
||||
const phraseBytes = sjclcodec.bytes.fromBits(sjclcodec.utf8String.toBits(j));
|
||||
const hash = hashjs.sha512().update(phraseBytes).digest();
|
||||
this.parse_bytes(hash.slice(0, 16));
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -75,100 +84,14 @@ Seed.prototype.to_json = function() {
|
||||
if (!(this.is_valid())) {
|
||||
return NaN;
|
||||
}
|
||||
|
||||
const output = Base.encode_check(Base.VER_FAMILY_SEED, this.to_bytes());
|
||||
|
||||
return output;
|
||||
return encodeSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
function append_int(a, i) {
|
||||
return [].concat(a, i >> 24, (i >> 16) & 0xff, (i >> 8) & 0xff, i & 0xff);
|
||||
}
|
||||
|
||||
function firstHalfOfSHA512(bytes) {
|
||||
return sjcl.bitArray.bitSlice(
|
||||
sjcl.hash.sha512.hash(sjcl.codec.bytes.toBits(bytes)),
|
||||
0, 256
|
||||
);
|
||||
}
|
||||
|
||||
// Removed a `*` so this JSDoc-ish syntax is ignored.
|
||||
// This will soon all change anyway.
|
||||
/*
|
||||
* @param account
|
||||
* {undefined} take first, default, KeyPair
|
||||
*
|
||||
* {Number} specifies the account number of the KeyPair
|
||||
* desired.
|
||||
*
|
||||
* {Uint160} (from_json able), specifies the address matching the KeyPair
|
||||
* that is desired.
|
||||
*
|
||||
* @param maxLoops (optional)
|
||||
* {Number} specifies the amount of attempts taken
|
||||
* to generate a matching KeyPair
|
||||
*
|
||||
*/
|
||||
Seed.prototype.get_key = function(account, maxLoops) {
|
||||
let account_number = 0, address;
|
||||
let max_loops = maxLoops || 1;
|
||||
|
||||
Seed.prototype.get_key = function() {
|
||||
if (!this.is_valid()) {
|
||||
throw new Error('Cannot generate keys from invalid seed!');
|
||||
}
|
||||
if (account) {
|
||||
if (typeof account === 'number') {
|
||||
account_number = account;
|
||||
max_loops = account_number + 1;
|
||||
} else {
|
||||
address = UInt160.from_json(account);
|
||||
}
|
||||
}
|
||||
|
||||
let private_gen, public_gen;
|
||||
const curve = this._curve;
|
||||
let i = 0;
|
||||
|
||||
do {
|
||||
private_gen = sjcl.bn.fromBits(
|
||||
firstHalfOfSHA512(append_int(this.to_bytes(), i)));
|
||||
i++;
|
||||
} while (!curve.r.greaterEquals(private_gen));
|
||||
|
||||
public_gen = curve.G.mult(private_gen);
|
||||
|
||||
let sec;
|
||||
let key_pair;
|
||||
|
||||
do {
|
||||
|
||||
i = 0;
|
||||
|
||||
do {
|
||||
sec = sjcl.bn.fromBits(
|
||||
firstHalfOfSHA512(
|
||||
append_int(
|
||||
append_int(public_gen.toBytesCompressed(), account_number)
|
||||
,
|
||||
i
|
||||
)));
|
||||
i++;
|
||||
} while (!curve.r.greaterEquals(sec));
|
||||
|
||||
account_number++;
|
||||
sec = sec.add(private_gen).mod(curve.r);
|
||||
key_pair = KeyPair.from_bn_secret(sec);
|
||||
|
||||
if (max_loops-- <= 0) {
|
||||
// We are almost certainly looking for an account that would take same
|
||||
// value of $too_long {forever, ...}
|
||||
throw new Error('Too many loops looking for KeyPair yielding ' +
|
||||
address.to_json() + ' from ' + this.to_json());
|
||||
}
|
||||
|
||||
} while (address && !key_pair.get_address().equals(address));
|
||||
|
||||
return key_pair;
|
||||
return KeyPair.fromSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const binformat = require('./binformat');
|
||||
const stypes = require('./serializedtypes');
|
||||
const utils = require('./utils');
|
||||
const UInt256 = require('./uint256').UInt256;
|
||||
|
||||
const sjcl = utils.sjcl;
|
||||
|
||||
const TRANSACTION_TYPES = { };
|
||||
|
||||
Object.keys(binformat.tx).forEach(function(key) {
|
||||
@@ -28,20 +28,16 @@ Object.keys(binformat.ter).forEach(function(key) {
|
||||
TRANSACTION_RESULTS[binformat.ter[key]] = key;
|
||||
});
|
||||
|
||||
function normalize_sjcl_bn_hex(string) {
|
||||
const hex = string.slice(2); // remove '0x' prefix
|
||||
// now strip leading zeros
|
||||
const i = _.findIndex(hex, function(c) {
|
||||
return c !== '0';
|
||||
});
|
||||
return i >= 0 ? hex.slice(i) : '0';
|
||||
function fieldType(fieldName) {
|
||||
const fieldDef = binformat.fieldsInverseMap[fieldName];
|
||||
return binformat.types[fieldDef[0]];
|
||||
}
|
||||
|
||||
function SerializedObject(buf) {
|
||||
if (Array.isArray(buf) || (Buffer && Buffer.isBuffer(buf))) {
|
||||
this.buffer = buf;
|
||||
} else if (typeof buf === 'string') {
|
||||
this.buffer = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(buf));
|
||||
this.buffer = sjclcodec.bytes.fromBits(sjclcodec.hex.toBits(buf));
|
||||
} else if (!buf) {
|
||||
this.buffer = [];
|
||||
} else {
|
||||
@@ -205,11 +201,11 @@ SerializedObject.prototype.read = readOrPeek(true);
|
||||
SerializedObject.prototype.peek = readOrPeek(false);
|
||||
|
||||
SerializedObject.prototype.to_bits = function() {
|
||||
return sjcl.codec.bytes.toBits(this.buffer);
|
||||
return sjclcodec.bytes.toBits(this.buffer);
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_hex = function() {
|
||||
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
return sjclcodec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
};
|
||||
|
||||
SerializedObject.prototype.to_json = function() {
|
||||
@@ -231,12 +227,12 @@ SerializedObject.prototype.to_json = function() {
|
||||
return output;
|
||||
};
|
||||
|
||||
SerializedObject.jsonify_structure = function(structure, field_name) {
|
||||
SerializedObject.jsonify_structure = function(structure, fieldName) {
|
||||
let output;
|
||||
|
||||
switch (typeof structure) {
|
||||
case 'number':
|
||||
switch (field_name) {
|
||||
switch (fieldName) {
|
||||
case 'LedgerEntryType':
|
||||
output = LEDGER_ENTRY_TYPES[structure];
|
||||
break;
|
||||
@@ -257,11 +253,10 @@ SerializedObject.jsonify_structure = function(structure, field_name) {
|
||||
|
||||
if (typeof structure.to_json === 'function') {
|
||||
output = structure.to_json();
|
||||
} else if (structure instanceof sjcl.bn) {
|
||||
output = ('0000000000000000' +
|
||||
normalize_sjcl_bn_hex(structure.toString())
|
||||
.toUpperCase()
|
||||
).slice(-16);
|
||||
} else if (structure instanceof BN) {
|
||||
// We assume that any BN is a UInt64 field
|
||||
assert.equal(fieldType(fieldName), 'Int64');
|
||||
output = utils.arrayToHex(structure.toArray('bn', 8));
|
||||
} else {
|
||||
// new Array or Object
|
||||
output = new structure.constructor();
|
||||
@@ -307,11 +302,9 @@ SerializedObject.prototype.hash = function(prefix) {
|
||||
|
||||
// Copy buffer to temporary buffer
|
||||
sign_buffer.append(this.buffer);
|
||||
const bytes = hashjs.sha512().update(sign_buffer.buffer).digest();
|
||||
|
||||
const bits = sjcl.codec.bytes.toBits(sign_buffer.buffer);
|
||||
const sha512hex = sjcl.codec.hex.fromBits(sjcl.hash.sha512.hash(bits));
|
||||
|
||||
return UInt256.from_hex(sha512hex.substr(0, 64).toUpperCase());
|
||||
return UInt256.from_bytes(bytes.slice(0, 32));
|
||||
};
|
||||
|
||||
// DEPRECATED
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const BN = require('bn.js');
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./currency').Currency;
|
||||
const binformat = require('./binformat');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const SJCL_BN = sjcl.bn;
|
||||
|
||||
const UInt128 = require('./uint128').UInt128;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
@@ -44,8 +44,7 @@ function isHexInt64String(val) {
|
||||
return isString(val) && /^[0-9A-F]{0,16}$/i.test(val);
|
||||
}
|
||||
|
||||
function serializeBits(so, bits, noLength) {
|
||||
const byteData = sjcl.codec.bytes.fromBits(bits);
|
||||
function serializeBytes(so, byteData, noLength) {
|
||||
if (!noLength) {
|
||||
SerializedType.serialize_varint(so, byteData.length);
|
||||
}
|
||||
@@ -53,23 +52,12 @@ function serializeBits(so, bits, noLength) {
|
||||
}
|
||||
|
||||
function serializeHex(so, hexData, noLength) {
|
||||
serializeBits(so, sjcl.codec.hex.toBits(hexData), noLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* parses bytes as hex
|
||||
*
|
||||
* @param {Array} byte_array bytes
|
||||
* @return {String} hex string
|
||||
*/
|
||||
function convertByteArrayToHex(byte_array) {
|
||||
return sjcl.codec.hex.fromBits(sjcl.codec.bytes.toBits(byte_array))
|
||||
.toUpperCase();
|
||||
serializeBytes(so, utils.hexToArray(hexData), noLength);
|
||||
}
|
||||
|
||||
function convertHexToString(hexString) {
|
||||
const bits = sjcl.codec.hex.toBits(hexString);
|
||||
return sjcl.codec.utf8String.fromBits(bits);
|
||||
const bits = sjclcodec.hex.toBits(hexString);
|
||||
return sjclcodec.utf8String.fromBits(bits);
|
||||
}
|
||||
|
||||
function sort_fields(keys) {
|
||||
@@ -308,25 +296,27 @@ const STInt64 = exports.Int64 = new SerializedType({
|
||||
if (value < 0) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
bigNumObject = new SJCL_BN(value, 10);
|
||||
bigNumObject = new BN(value, 10);
|
||||
} else if (isString(value)) {
|
||||
if (!isHexInt64String(value)) {
|
||||
throw new Error('Not a valid hex Int64.');
|
||||
}
|
||||
bigNumObject = new SJCL_BN(value, 16);
|
||||
} else if (value instanceof SJCL_BN) {
|
||||
if (!value.greaterEquals(0)) {
|
||||
bigNumObject = new BN(value, 16);
|
||||
} else if (value instanceof BN) {
|
||||
if (value.cmpn(0) < 0) {
|
||||
throw new Error('Negative value for unsigned Int64 is invalid.');
|
||||
}
|
||||
bigNumObject = value;
|
||||
} else {
|
||||
throw new Error('Invalid type for Int64');
|
||||
throw new Error('Invalid type for Int64: ' + (typeof value) + ' value');
|
||||
}
|
||||
serializeBits(so, bigNumObject.toBits(64), true); // noLength = true
|
||||
// `'be'` means big endian, and the following arg is the byte length, which
|
||||
// it will pad with 0s to if not enough bytes, or throw if over
|
||||
serializeBytes(so, bigNumObject.toArray('be', 8), /* noLength= */true);
|
||||
},
|
||||
parse: function(so) {
|
||||
const bytes = so.read(8);
|
||||
return SJCL_BN.fromBits(sjcl.codec.bytes.toBits(bytes));
|
||||
return new BN(bytes);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -338,7 +328,7 @@ const STHash128 = exports.Hash128 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash128');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt128.from_bytes(so.read(16));
|
||||
@@ -353,7 +343,7 @@ const STHash256 = exports.Hash256 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash256');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt256.from_bytes(so.read(32));
|
||||
@@ -368,7 +358,7 @@ const STHash160 = exports.Hash160 = new SerializedType({
|
||||
if (!hash.is_valid()) {
|
||||
throw new Error('Invalid Hash160');
|
||||
}
|
||||
serializeBits(so, hash.to_bits(), true); // noLength = true
|
||||
serializeBytes(so, hash.to_bytes(), true); // noLength = true
|
||||
},
|
||||
parse: function(so) {
|
||||
return UInt160.from_bytes(so.read(20));
|
||||
@@ -442,7 +432,7 @@ exports.Quality = new SerializedType({
|
||||
lo = parseInt(mantissaHex.slice(-8), 16);
|
||||
}
|
||||
|
||||
const valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
const valueBytes = sjclcodec.bytes.fromBits([hi, lo]);
|
||||
|
||||
so.append(valueBytes);
|
||||
}
|
||||
@@ -482,7 +472,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
valueHex = '0' + valueHex;
|
||||
}
|
||||
|
||||
valueBytes = sjcl.codec.bytes.fromBits(sjcl.codec.hex.toBits(valueHex));
|
||||
valueBytes = sjclcodec.bytes.fromBits(sjclcodec.hex.toBits(valueHex));
|
||||
// Clear most significant two bits - these bits should already be 0 if
|
||||
// Amount enforces the range correctly, but we'll clear them anyway just
|
||||
// so this code can make certain guarantees about the encoded value.
|
||||
@@ -516,7 +506,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
lo = parseInt(mantissaHex.slice(-8), 16);
|
||||
}
|
||||
|
||||
valueBytes = sjcl.codec.bytes.fromBits([hi, lo]);
|
||||
valueBytes = sjclcodec.bytes.fromBits([hi, lo]);
|
||||
}
|
||||
|
||||
so.append(valueBytes);
|
||||
@@ -582,7 +572,7 @@ const STVL = exports.VariableLength = exports.VL = new SerializedType({
|
||||
},
|
||||
parse: function(so) {
|
||||
const len = this.parse_varint(so);
|
||||
return convertByteArrayToHex(so.read(len));
|
||||
return utils.arrayToHex(so.read(len));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -594,7 +584,7 @@ const STAccount = exports.Account = new SerializedType({
|
||||
if (!account.is_valid()) {
|
||||
throw new Error('Invalid account!');
|
||||
}
|
||||
serializeBits(so, account.to_bits());
|
||||
serializeBytes(so, account.to_bytes());
|
||||
},
|
||||
parse: function(so) {
|
||||
const len = this.parse_varint(so);
|
||||
@@ -807,27 +797,27 @@ exports.STMemo = new SerializedType({
|
||||
if (parsedType !== 'unformatted_memo') {
|
||||
output.parsed_memo_type = parsedType;
|
||||
}
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_type field
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
if (output.MemoFormat !== undefined) {
|
||||
try {
|
||||
output.parsed_memo_format = convertHexToString(output.MemoFormat);
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we don't know what's in the binary, apparently it's not a UTF-8
|
||||
// string
|
||||
// this is fine, we won't add the parsed_memo_format field
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
if (output.MemoData !== undefined) {
|
||||
@@ -842,7 +832,7 @@ exports.STMemo = new SerializedType({
|
||||
// otherwise see if we can parse text
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
/*eslint-disable no-empty*/
|
||||
/* eslint-disable no-empty */
|
||||
} catch(e) {
|
||||
// empty
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
@@ -850,7 +840,7 @@ exports.STMemo = new SerializedType({
|
||||
// this is fine, we won't add the parsed_memo_data, the user has to
|
||||
// parse themselves
|
||||
}
|
||||
/*eslint-enable no-empty*/
|
||||
/* eslint-enable no-empty */
|
||||
}
|
||||
|
||||
so.read(1);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const url = require('url');
|
||||
const HttpsProxyAgent = require('https-proxy-agent');
|
||||
@@ -621,7 +623,7 @@ Server.prototype._handleLedgerClosed = function(message) {
|
||||
Server.prototype._handleServerStatus = function(message) {
|
||||
// This message is only received when online.
|
||||
// As we are connected, it is the definitive final state.
|
||||
const isOnline = ~Server.onlineStates.indexOf(message.server_status);
|
||||
const isOnline = _.includes(Server.onlineStates, message.server_status);
|
||||
|
||||
this._setState(isOnline ? 'online' : 'offline');
|
||||
|
||||
@@ -732,7 +734,7 @@ Server.prototype._handleResponseSubscribe = function(message) {
|
||||
this._ledgerRanges.parseAndAddRanges(message.validated_ledgers);
|
||||
}
|
||||
|
||||
if (~Server.onlineStates.indexOf(message.server_status)) {
|
||||
if (_.includes(Server.onlineStates, message.server_status)) {
|
||||
this._setState('online');
|
||||
}
|
||||
};
|
||||
@@ -905,6 +907,9 @@ Server.prototype._feeTxUnit = function() {
|
||||
*/
|
||||
|
||||
Server.prototype._reserve = function(ownerCount) {
|
||||
// We should be in a valid state before calling this method
|
||||
assert(this._reserve_base && this._reserve_inc);
|
||||
|
||||
const reserve_base = Amount.from_json(String(this._reserve_base));
|
||||
const reserve_inc = Amount.from_json(String(this._reserve_inc));
|
||||
const owner_count = ownerCount || 0;
|
||||
@@ -913,7 +918,7 @@ Server.prototype._reserve = function(ownerCount) {
|
||||
throw new Error('Owner count must not be negative.');
|
||||
}
|
||||
|
||||
return reserve_base.add(reserve_inc.product_human(owner_count));
|
||||
return reserve_base.add(reserve_inc.multiply(owner_count));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@ const util = require('util');
|
||||
const lodash = require('lodash');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const utils = require('./utils');
|
||||
const sjcl = require('./utils').sjcl;
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./amount').Currency;
|
||||
const UInt160 = require('./amount').UInt160;
|
||||
@@ -344,8 +344,8 @@ Transaction.prototype._computeFee = function() {
|
||||
}
|
||||
|
||||
switch (fees.length) {
|
||||
case 0: return undefined;
|
||||
case 1: return String(fees[0]);
|
||||
case 0: return undefined;
|
||||
case 1: return String(fees[0]);
|
||||
}
|
||||
|
||||
fees.sort(function ascending(a, b) {
|
||||
@@ -399,8 +399,8 @@ Transaction.prototype.complete = function() {
|
||||
if (typeof this.tx_json.SigningPubKey === 'undefined') {
|
||||
try {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
const key = seed.get_key(this.tx_json.Account);
|
||||
this.tx_json.SigningPubKey = key.to_hex_pub();
|
||||
const key = seed.get_key();
|
||||
this.tx_json.SigningPubKey = key.pubKeyHex();
|
||||
} catch(e) {
|
||||
this.emit('error', new RippleError(
|
||||
'tejSecretInvalid', 'Invalid secret'));
|
||||
@@ -469,13 +469,13 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
||||
return asUINT256 ? hash : hash.to_hex();
|
||||
};
|
||||
|
||||
Transaction.prototype.sign = function(testnet) {
|
||||
Transaction.prototype.sign = function() {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
const prev_sig = this.tx_json.TxnSignature;
|
||||
|
||||
delete this.tx_json.TxnSignature;
|
||||
|
||||
const hash = this.signingHash(testnet);
|
||||
const hash = this.signingHash();
|
||||
|
||||
// If the hash is the same, we can re-use the previous signature
|
||||
if (prev_sig && hash === this.previousSigningHash) {
|
||||
@@ -483,10 +483,8 @@ Transaction.prototype.sign = function(testnet) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const key = seed.get_key(this.tx_json.Account);
|
||||
const sig = key.sign(hash);
|
||||
const hex = sjcl.codec.hex.fromBits(sig).toUpperCase();
|
||||
|
||||
const key = seed.get_key();
|
||||
const hex = key.signHex(this.signingData().buffer);
|
||||
this.tx_json.TxnSignature = hex;
|
||||
this.previousSigningHash = hash;
|
||||
|
||||
@@ -781,8 +779,8 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
}
|
||||
|
||||
function convertStringToHex(string) {
|
||||
const utf8String = sjcl.codec.utf8String.toBits(string);
|
||||
return sjcl.codec.hex.fromBits(utf8String).toUpperCase();
|
||||
const utf8String = sjclcodec.utf8String.toBits(string);
|
||||
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
|
||||
}
|
||||
|
||||
const memo = {};
|
||||
|
||||
@@ -534,9 +534,9 @@ TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
// sealed and delivered, and the txn unmodified.
|
||||
// TODO: perhaps an exception should be raised if build_path is attempted
|
||||
// while local signing
|
||||
submitRequest.build_path(tx._build_path);
|
||||
submitRequest.buildPath(tx._build_path);
|
||||
submitRequest.secret(tx._secret);
|
||||
submitRequest.tx_json(tx.tx_json);
|
||||
submitRequest.txJson(tx.tx_json);
|
||||
}
|
||||
|
||||
return submitRequest;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint new-cap: 1*/
|
||||
/* eslint new-cap: 1 */
|
||||
|
||||
const assert = require('assert');
|
||||
const lodash = require('lodash');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const utils = require('./utils');
|
||||
const sjcl = utils.sjcl;
|
||||
const BN = require('bn.js');
|
||||
|
||||
//
|
||||
// Abstract UInt class
|
||||
@@ -14,7 +15,7 @@ const sjcl = utils.sjcl;
|
||||
//
|
||||
|
||||
function UInt() {
|
||||
// Internal form: NaN or sjcl.bn
|
||||
// Internal form: NaN or BN
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
@@ -67,15 +68,6 @@ UInt.from_bytes = function(j) {
|
||||
return (new this()).parse_bytes(j);
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_bn = function(j) {
|
||||
if (j instanceof this) {
|
||||
return j.clone();
|
||||
}
|
||||
|
||||
return (new this()).parse_bn(j);
|
||||
};
|
||||
|
||||
// Return a new UInt from j.
|
||||
UInt.from_number = function(j) {
|
||||
if (j instanceof this) {
|
||||
@@ -108,16 +100,33 @@ UInt.prototype.copyTo = function(d) {
|
||||
return d;
|
||||
};
|
||||
|
||||
UInt.prototype.equals = function(d) {
|
||||
return this.is_valid() && d.is_valid() && this._value.equals(d._value);
|
||||
UInt.prototype.equals = function(o) {
|
||||
return this.is_valid() &&
|
||||
o.is_valid() &&
|
||||
// This throws but the expression will short circuit
|
||||
this.cmp(o) === 0;
|
||||
};
|
||||
|
||||
UInt.prototype.cmp = function(o) {
|
||||
assert(this.is_valid() && o.is_valid());
|
||||
return this._value.cmp(o._value);
|
||||
};
|
||||
|
||||
UInt.prototype.greater_than = function(o) {
|
||||
return this.cmp(o) > 0;
|
||||
};
|
||||
|
||||
UInt.prototype.less_than = function(o) {
|
||||
return this.cmp(o) < 0;
|
||||
};
|
||||
|
||||
UInt.prototype.is_valid = function() {
|
||||
return this._value instanceof sjcl.bn;
|
||||
return this._value instanceof BN;
|
||||
};
|
||||
|
||||
UInt.prototype.is_zero = function() {
|
||||
return this.is_valid() && this._value.equals(new sjcl.bn(0));
|
||||
// cmpn means cmp with N)umber
|
||||
return this.is_valid() && this._value.cmpn(0) === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -150,14 +159,14 @@ UInt.prototype.parse_generic = function(j) {
|
||||
case subclass.STR_ZERO:
|
||||
case subclass.ACCOUNT_ZERO:
|
||||
case subclass.HEX_ZERO:
|
||||
this._value = new sjcl.bn(0);
|
||||
this._value = new BN(0);
|
||||
break;
|
||||
|
||||
case '1':
|
||||
case subclass.STR_ONE:
|
||||
case subclass.ACCOUNT_ONE:
|
||||
case subclass.HEX_ONE:
|
||||
this._value = new sjcl.bn(1);
|
||||
this._value = new BN(1);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -165,7 +174,7 @@ UInt.prototype.parse_generic = function(j) {
|
||||
switch (j.length) {
|
||||
case subclass.width:
|
||||
const hex = utils.arrayToHex(utils.stringToArray(j));
|
||||
this._value = new sjcl.bn(hex, 16);
|
||||
this._value = new BN(hex, 16);
|
||||
break;
|
||||
case subclass.width * 2:
|
||||
// Assume hex, check char set
|
||||
@@ -187,7 +196,7 @@ UInt.prototype.parse_generic = function(j) {
|
||||
|
||||
UInt.prototype.parse_hex = function(j) {
|
||||
if (new RegExp(`^[0-9A-Fa-f]{${this.constructor.width * 2}}$`).test(j)) {
|
||||
this._value = new sjcl.bn(j, 16);
|
||||
this._value = new BN(j, 16);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
@@ -198,24 +207,12 @@ UInt.prototype.parse_hex = function(j) {
|
||||
};
|
||||
|
||||
UInt.prototype.parse_bits = function(j) {
|
||||
if (sjcl.bitArray.bitLength(j) === this.constructor.width * 8) {
|
||||
this._value = sjcl.bn.fromBits(j);
|
||||
// let bytes = sjcl.codec.bytes.fromBits(j);
|
||||
// this.parse_bytes(bytes);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
this._update();
|
||||
|
||||
return this;
|
||||
return this.parse_bytes(sjclcodec.bytes.fromBits(j));
|
||||
};
|
||||
|
||||
|
||||
UInt.prototype.parse_bytes = function(j) {
|
||||
if (Array.isArray(j) && j.length === this.constructor.width) {
|
||||
const bits = sjcl.codec.bytes.toBits(j);
|
||||
this._value = sjcl.bn.fromBits(bits);
|
||||
this._value = new BN(j);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
@@ -225,26 +222,13 @@ UInt.prototype.parse_bytes = function(j) {
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
UInt.prototype.parse_json = UInt.prototype.parse_hex;
|
||||
|
||||
UInt.prototype.parse_bn = function(j) {
|
||||
if ((j instanceof sjcl.bn) && j.bitLength() <= this.constructor.width * 8) {
|
||||
this._value = new sjcl.bn(j);
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
this._update();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
UInt.prototype.parse_number = function(j) {
|
||||
this._value = NaN;
|
||||
|
||||
if (typeof j === 'number' && isFinite(j) && j >= 0) {
|
||||
this._value = new sjcl.bn(j);
|
||||
this._value = new BN(j);
|
||||
}
|
||||
|
||||
this._update();
|
||||
@@ -258,7 +242,7 @@ UInt.prototype.to_bytes = function() {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.bytes.fromBits(this.to_bits());
|
||||
return this._value.toArray('be', this.constructor.width);
|
||||
};
|
||||
|
||||
UInt.prototype.to_hex = function() {
|
||||
@@ -266,27 +250,18 @@ UInt.prototype.to_hex = function() {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sjcl.codec.hex.fromBits(this.to_bits()).toUpperCase();
|
||||
return utils.arrayToHex(this.to_bytes());
|
||||
};
|
||||
|
||||
UInt.prototype.to_json = UInt.prototype.to_hex;
|
||||
|
||||
// Convert from internal form.
|
||||
UInt.prototype.to_bits = function() {
|
||||
if (!this.is_valid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this._value.toBits(this.constructor.width * 8);
|
||||
};
|
||||
|
||||
UInt.prototype.to_bn = function() {
|
||||
if (!this.is_valid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bits = this.to_bits();
|
||||
|
||||
return sjcl.bn.fromBits(bits);
|
||||
return sjclcodec.bytes.toBits(this.to_bytes());
|
||||
};
|
||||
|
||||
exports.UInt = UInt;
|
||||
|
||||
@@ -77,7 +77,7 @@ function hexToArray(h) {
|
||||
|
||||
function arrayToHex(a) {
|
||||
return a.map(function(byteValue) {
|
||||
const hex = byteValue.toString(16);
|
||||
const hex = byteValue.toString(16).toUpperCase();
|
||||
return hex.length > 1 ? hex : '0' + hex;
|
||||
}).join('');
|
||||
}
|
||||
@@ -166,6 +166,4 @@ exports.fromTimestamp = fromTimestamp;
|
||||
exports.getMantissaDecimalString = getMantissaDecimalString;
|
||||
exports.getMantissa16FromString = getMantissa16FromString;
|
||||
|
||||
exports.sjcl = require('sjcl-extended');
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
120
test/api-test.js
120
test/api-test.js
@@ -11,8 +11,6 @@ const requests = fixtures.requests;
|
||||
const responses = fixtures.responses;
|
||||
const addresses = require('./fixtures/addresses');
|
||||
const hashes = require('./fixtures/hashes');
|
||||
const MockPRNG = require('./mock-prng');
|
||||
const sjcl = common.core.sjcl;
|
||||
const address = addresses.ACCOUNT;
|
||||
const validate = common.validate;
|
||||
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
||||
@@ -36,13 +34,7 @@ function checkResult(expected, schemaName, response) {
|
||||
if (schemaName) {
|
||||
schemaValidator.schemaValidate(schemaName, response);
|
||||
}
|
||||
}
|
||||
|
||||
function withDeterministicPRNG(f) {
|
||||
const prng = sjcl.random;
|
||||
sjcl.random = new MockPRNG();
|
||||
f();
|
||||
sjcl.random = prng;
|
||||
return response;
|
||||
}
|
||||
|
||||
describe('RippleAPI', function() {
|
||||
@@ -151,11 +143,9 @@ describe('RippleAPI', function() {
|
||||
|
||||
it('sign', function() {
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
withDeterministicPRNG(() => {
|
||||
const result = this.api.sign(requests.sign, secret);
|
||||
assert.deepEqual(result, responses.sign);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
const result = this.api.sign(requests.sign, secret);
|
||||
assert.deepEqual(result, responses.sign);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
|
||||
it('submit', function() {
|
||||
@@ -214,6 +204,14 @@ describe('RippleAPI', function() {
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline no quality', function() {
|
||||
const hash =
|
||||
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not validated', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
|
||||
@@ -414,10 +412,12 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.getTrustlines, 'getTrustlines'));
|
||||
});
|
||||
|
||||
it('generateWallet', function() {
|
||||
withDeterministicPRNG(() => {
|
||||
assert.deepEqual(this.api.generateWallet(), responses.generateWallet);
|
||||
});
|
||||
it('generateAddress', function() {
|
||||
function random() {
|
||||
return _.fill(Array(16), 0);
|
||||
}
|
||||
assert.deepEqual(this.api.generateAddress({random}),
|
||||
responses.generateAddress);
|
||||
});
|
||||
|
||||
it('getSettings', function() {
|
||||
@@ -556,6 +556,29 @@ describe('RippleAPI', function() {
|
||||
assert.strictEqual(this.api.getLedgerVersion(), 8819951);
|
||||
});
|
||||
|
||||
it('getLedger', function() {
|
||||
return this.api.getLedger().then(
|
||||
_.partial(checkResult, responses.getLedger.header, 'getLedger'));
|
||||
});
|
||||
|
||||
it('getLedger - full, then computeLedgerHash', function() {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeState: true,
|
||||
includeAllData: true,
|
||||
ledgerVersion: 38129
|
||||
};
|
||||
return this.api.getLedger(request).then(
|
||||
_.partial(checkResult, responses.getLedger.full, 'getLedger'))
|
||||
.then(response => {
|
||||
const ledger = _.assign({}, response,
|
||||
{parentCloseTime: response.closeTime});
|
||||
const hash = this.api.computeLedgerHash(ledger);
|
||||
assert.strictEqual(hash,
|
||||
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
|
||||
});
|
||||
});
|
||||
|
||||
it('ledger utils - compareTransactions', function() {
|
||||
let first = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||
let second = {outcome: {ledgerVersion: 1, indexInLedger: 200}};
|
||||
@@ -582,7 +605,7 @@ describe('RippleAPI', function() {
|
||||
it('ledger utils - getRecursive', function(done) {
|
||||
function getter(marker, limit, callback) {
|
||||
if (marker === undefined) {
|
||||
callback(null, {marker: 'A', results: [1]});
|
||||
callback(null, {marker: 'A', limit: limit, results: [1]});
|
||||
} else {
|
||||
callback(new Error(), null);
|
||||
}
|
||||
@@ -647,21 +670,14 @@ describe('RippleAPI', function() {
|
||||
});
|
||||
|
||||
it('addressAndSecret', function() {
|
||||
const wrongSecret = {address: address,
|
||||
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
||||
};
|
||||
assert.throws(_.partial(validate.addressAndSecret, wrongSecret),
|
||||
this.api.errors.ValidationError);
|
||||
const noSecret = {address: address};
|
||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.addressAndSecret, noSecret),
|
||||
/Parameter missing/);
|
||||
const badSecret = {address: address, secret: 'bad'};
|
||||
const badSecret = {address: address, secret: 'sbad'};
|
||||
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
||||
this.api.errors.ValidationError);
|
||||
assert.throws(_.partial(validate.addressAndSecret, badSecret),
|
||||
/not match/);
|
||||
const goodWallet = {address: 'rpZMK8hwyrBvLorFNWHRCGt88nCJWbixur',
|
||||
secret: 'shzjfakiK79YQdMjy4h8cGGfQSV6u'
|
||||
};
|
||||
@@ -753,9 +769,53 @@ describe('RippleAPI - offline', function() {
|
||||
};
|
||||
return api.prepareSettings(address, settings, instructions).then(txJSON => {
|
||||
assert.deepEqual(txJSON, responses.prepareSettings.flags);
|
||||
withDeterministicPRNG(() => {
|
||||
assert.deepEqual(api.sign(txJSON, secret), responses.sign);
|
||||
});
|
||||
assert.deepEqual(api.sign(txJSON, secret), responses.sign);
|
||||
});
|
||||
});
|
||||
|
||||
it('computeLedgerHash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = requests.computeLedgerHash.header;
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - with transactions', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.omit(requests.computeLedgerHash.header,
|
||||
'transactionHash');
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - incorrent transaction_hash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.assign({}, requests.computeLedgerHash.header,
|
||||
{transactionHash:
|
||||
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9'});
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
assert.throws(() => api.computeLedgerHash(header));
|
||||
});
|
||||
|
||||
it('isValidAddress - valid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(api.isValidAddress(address));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(!api.isValidAddress(address.slice(0, -1) + 'a'));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid - hex representation', function() {
|
||||
const api = new RippleAPI();
|
||||
const hex = '6e3efa86a5eb0a3c5dc9beb3a204783bb00e1913';
|
||||
assert(!api.isValidAddress(hex));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -31,11 +31,11 @@ describe('Base', function() {
|
||||
describe('decode_check', function() {
|
||||
it('rrrrrrrrrrrrrrrrrrrrrhoLvTp', function() {
|
||||
const decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrrhoLvTp');
|
||||
assert(decoded.equals(0));
|
||||
assert(decoded.cmpn(0) === 0);
|
||||
});
|
||||
it('rrrrrrrrrrrrrrrrrrrrBZbvji', function() {
|
||||
const decoded = Base.decode_check(0, 'rrrrrrrrrrrrrrrrrrrrBZbvji');
|
||||
assert(decoded.equals(1));
|
||||
assert(decoded.cmpn(1) === 0);
|
||||
});
|
||||
});
|
||||
describe('decode-encode identity', function() {
|
||||
|
||||
474
test/fixtures/api/requests/compute-ledger-hash-transactions.json
vendored
Normal file
474
test/fixtures/api/requests/compute-ledger-hash-transactions.json
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
[
|
||||
{
|
||||
"hash": "f8f337dee5d5b238a10af4a4d56926ba26c83ee7af5a5a6474340c56f9252df3",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1608,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "120000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "03BC0973F997BC6384BE455B163519A3E96BC2D725C37F7172D5FED5DD38E2A357",
|
||||
"TxnSignature": "3045022100D80A1802B00AEEF9FDFDE594B0D568217A312D54E6337B8519C0D699841EFB96022067F6913B13D0EC2354C5A67CE0A41AE4181A09CD08A1BB0638D128D357961006",
|
||||
"Account": "rDPL68aNpdfp9h59R4QT5R6B1Z2W9oRc51",
|
||||
"Destination": "rE4S4Xw8euysJ3mt7gmK8EhhYEwmALpb3R"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 6,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202381,
|
||||
"PreviousTxnID": "8FFB65C6907C9679C5F8AADA97072CD1B8FE4955FC6A614AC87408AE7C9088AD",
|
||||
"LedgerIndex": "B07B367ABF05243A536986DEC74684E983BBBDDF443ADE9CDC43A22D6E6A1420",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1608,
|
||||
"Balance": "61455842701"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1609,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "61335827701",
|
||||
"Account": "rDPL68aNpdfp9h59R4QT5R6B1Z2W9oRc51"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "B01591A2353CD39EFAC989D542EE37591F60CF9BB2B66526C8C958774813407E",
|
||||
"LedgerIndex": "F77EB82FA9593E695F22155C00C569A570CF32316BEFDFF0B16BADAFF2ACFF19",
|
||||
"PreviousFields": {
|
||||
"Balance": "26762033252"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 6448,
|
||||
"OwnerCount": 3,
|
||||
"Balance": "26882033252",
|
||||
"Account": "rE4S4Xw8euysJ3mt7gmK8EhhYEwmALpb3R"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "f8d5de632b1d8b64e577c46912cce483d6df4fd4e2cf4a3d586a099de3b27021",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 18874,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "120000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "035D097E75D4B35345CEB30F9B1D18CB81165FE6ADD02481AA5B02B5F9C8107EE1",
|
||||
"TxnSignature": "304402203D80E8BC71908AB345948AB71FB7B8DE239DD79636D96D3C5BDA2B2F192A5EEA0220686413D69BF0D813FC61DABD437AEFAAE69925D3E10FCD5B2C4D90B5AF7B883D",
|
||||
"Account": "rnHScgV6wSP9sR25uYWiMo3QYNA5ybQ7cH",
|
||||
"Destination": "rwnnfHDaEAwXaVji52cWWizbHVMs2Cz5K9"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 5,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202429,
|
||||
"PreviousTxnID": "B1F39887411C1771998F38502EDF33170F9F5659503DB9DE642EBA896B5F198B",
|
||||
"LedgerIndex": "2AAA3361C593C4DE7ABD9A607B3CA7070A3F74E3C3F2FDE4DDB9484E47ED056E",
|
||||
"PreviousFields": {
|
||||
"Sequence": 18874,
|
||||
"Balance": "13795295558367"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 18875,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "13795175543367",
|
||||
"Account": "rnHScgV6wSP9sR25uYWiMo3QYNA5ybQ7cH"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202416,
|
||||
"PreviousTxnID": "00CF9C7BE3EBAF76893C6A3F6D10B4D89F8D856C97B9D44938CF1682132ACEB8",
|
||||
"LedgerIndex": "928582D6F6942B18F3462FA04BA99F476B64FEB9921BFAD583182DC28CB74187",
|
||||
"PreviousFields": {
|
||||
"Balance": "17674359316"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1710,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "17794359316",
|
||||
"Account": "rwnnfHDaEAwXaVji52cWWizbHVMs2Cz5K9"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "e9004490a92413e92dacd621ac73fd434a8950c350f7572ffeaf4d6aaf8fc288",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1615,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "400000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "03ACFAA11628C558AB5E7FA64705F442BDAABA6E9D318B30E010BC87CDEA8D1D7D",
|
||||
"TxnSignature": "3045022100A3530C2E983FB05DFF27172C649494291F7BEBA2E6A59EEAF945CB9728D1DB5E022015BCA0E9D69760224DD7C2B68F3BC1F239D89C3397161AA3901C2E04EE31C18F",
|
||||
"Account": "razcSDpwds1aTeqDphqzBr7ay1ZELYAWTm",
|
||||
"Destination": "rhuqJAE2UfhGCvkR7Ve35bvm39JmRvFML4"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 4,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202394,
|
||||
"PreviousTxnID": "99E8F8988390F5A8DF69BBA4F04705E5085EE91B27583D28210D37B7513F10BB",
|
||||
"LedgerIndex": "17CF549DFC0813DDC44559C89E99B4C1D033D59FF379AD948CBEC141F179293D",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1615,
|
||||
"Balance": "45875786250"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1616,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "45475771250",
|
||||
"Account": "razcSDpwds1aTeqDphqzBr7ay1ZELYAWTm"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "9EC0784393DA95BB3B38FABC59FEFEE34BA8487DD892B9EAC1D70E483D1B0FA6",
|
||||
"LedgerIndex": "EB13399E9A69F121BEDA810F1AE9CB4023B4B09C5055CB057B572029B2FC8DD4",
|
||||
"PreviousFields": {
|
||||
"Balance": "76953067090"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 601,
|
||||
"OwnerCount": 4,
|
||||
"Balance": "77353067090",
|
||||
"Account": "rhuqJAE2UfhGCvkR7Ve35bvm39JmRvFML4"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "d44bff924d23211b82b8f604af6d92f260f8dd13103a96f03e48825c4a978fd6",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1674,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "800000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "028F28D78FDA74222F4008F012247DF3BBD42B90CE4CFD87E29598196108E91B52",
|
||||
"TxnSignature": "3044022065A003194D91E774D180BE47D4E086BB2624BC8F6DB7C655E135D5C6C03BBC7C02205DC961C2B7A06D701B29C2116ACF6F84CC84205FF44411576C15507852ECC31C",
|
||||
"Account": "rQGLp9nChtWkdgcHjj6McvJithN2S2HJsP",
|
||||
"Destination": "rEUubanepAAugnNJY1gxEZLDnk9W5NCoFU"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 3,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202409,
|
||||
"PreviousTxnID": "6A9B73C13B8A74BCDB64B5ADFE3D8FFEAC7928B82CFD6C9A35254D7798AD0688",
|
||||
"LedgerIndex": "D1A7795E8E997E7DE65D64283FD7CEEB5E43C2E5C4A794C2CFCEC6724E03F464",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1674,
|
||||
"Balance": "8774844732"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1675,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "7974829732",
|
||||
"Account": "rQGLp9nChtWkdgcHjj6McvJithN2S2HJsP"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202388,
|
||||
"PreviousTxnID": "ECE994DA817228D9170D22C01CE1BF5B17FFE1AE6404FF215719C1049E9939E0",
|
||||
"LedgerIndex": "E5EA9215A6D41C4E20C831ACE436E5B75F9BA2A9BD4325BA65BD9D44F5E13A08",
|
||||
"PreviousFields": {
|
||||
"Balance": "9077529029"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1496,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "9877529029",
|
||||
"Account": "rEUubanepAAugnNJY1gxEZLDnk9W5NCoFU"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "c978d915bfb17687335cbfc4b207d9e7213bcee35b468c2eee016cdce4edb6e4",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "OfferCreate",
|
||||
"Sequence": 289444,
|
||||
"OfferSequence": 289443,
|
||||
"LastLedgerSequence": 15202441,
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Fee": "10000",
|
||||
"SigningPubKey": "024D129D4F5A12D4C5A9E9D1E4AC447BBE3496F182FAE82F7709C7EB9F12DBC697",
|
||||
"TxnSignature": "3044022041EBE6B06BA493867F4FFBD72E5D6253F97306E1E82DABDF9649E15B1151B59F0220539C589F40174471C067FDC761A2B791F36F1A3C322734B43DB16880E489BD81",
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq",
|
||||
"Memos": [
|
||||
{
|
||||
"Memo": {
|
||||
"MemoType": "6F666665725F636F6D6D656E74",
|
||||
"MemoData": "72655F6575722368656467655F726970706C65",
|
||||
"parsed_memo_type": "offer_comment"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 2,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"CreatedNode": {
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "2069A6F3B349C246630536B3A0D18FECF0B088D6846ED74D56762096B972ADBE",
|
||||
"NewFields": {
|
||||
"Sequence": 289444,
|
||||
"BookDirectory": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "68E8826D6545315B54943AF0D6A45264598F2DE8A71CB9EFA97C9F4456078BE8",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"RootIndex": "68E8826D6545315B54943AF0D6A45264598F2DE8A71CB9EFA97C9F4456078BE8",
|
||||
"Owner": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"DeletedNode": {
|
||||
"LedgerEntryType": "Offer",
|
||||
"LedgerIndex": "9AC6C83397287FDFF4DB7ED6D96DA060CF32ED6593B18C332EEDFE833AE48E1C",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 289443,
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"BookNode": "0000000000000000",
|
||||
"OwnerNode": "0000000000000000",
|
||||
"PreviousTxnID": "6C1B0818CA470DBD5EFC28FC863862B0DF9D9F659475612446806401C56E3B28",
|
||||
"BookDirectory": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPays": {
|
||||
"value": "19.99999999991",
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"TakerGets": {
|
||||
"value": "20.88367500010602",
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||
},
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "DirectoryNode",
|
||||
"LedgerIndex": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"ExchangeRate": "5422061A1FB5AF31",
|
||||
"RootIndex": "D3C7DF102A0CEDB307D6F471B0CE679C5C206D8227D9BB2E5422061A1FB5AF31",
|
||||
"TakerPaysCurrency": "0000000000000000000000004555520000000000",
|
||||
"TakerPaysIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3",
|
||||
"TakerGetsCurrency": "0000000000000000000000005553440000000000",
|
||||
"TakerGetsIssuer": "DD39C650A96EDA48334E70CC4A85B8B2E8502CD3"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202438,
|
||||
"PreviousTxnID": "6C1B0818CA470DBD5EFC28FC863862B0DF9D9F659475612446806401C56E3B28",
|
||||
"LedgerIndex": "D8614A045CBA0F0081B23FD80CA87E7D08651FA02450C7BEE1B480836F0DC95D",
|
||||
"PreviousFields": {
|
||||
"Sequence": 289444,
|
||||
"Balance": "3712981021"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 289445,
|
||||
"OwnerCount": 13,
|
||||
"Balance": "3712971021",
|
||||
"Account": "rD8LigXE7165r3VWhSQ4FwzJy7PNrTMwUq"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "31b34fd7c90cdc6cf680a814debc6f616c69275c0e99711f904de088a8ed4b28",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "AccountSet",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 387262,
|
||||
"LastLedgerSequence": 15202440,
|
||||
"Fee": "10500",
|
||||
"SigningPubKey": "027DFE042DC2BD07D2E88DD526A5FBF816C831C25CA0BB62A3BF320A3B2BA6DB5C",
|
||||
"TxnSignature": "30440220572D89688D9F9DB9874CDDDD3EBDCB5808A836982864C81F185FBC54FAD1A7B902202E09AAA6D65EECC9ACDEA7F70D8D2EE024152C7B288FA9E42C427260CF922F58",
|
||||
"Account": "rn6uAt46Xi6uxA2dRCtqaJyM3aaP6V9WWM"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 1,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202429,
|
||||
"PreviousTxnID": "212D4BFAD4DFB0887B57AB840A8385F31FC2839FFD4169A824280565CC2885C0",
|
||||
"LedgerIndex": "317481AD6274D399F50E13EF447825DA628197E6262B80642DAE0D8300D77E55",
|
||||
"PreviousFields": {
|
||||
"Sequence": 387262,
|
||||
"Balance": "207020609"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 387263,
|
||||
"OwnerCount": 22,
|
||||
"Balance": "207010109",
|
||||
"Account": "rn6uAt46Xi6uxA2dRCtqaJyM3aaP6V9WWM"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
},
|
||||
{
|
||||
"hash": "260bc2964ffe6d81cb25c152f8054ffb2ce6ed04ff89d8d0d0559bc14bef0e46",
|
||||
"date": "2015-08-12T01:01:10+00:00",
|
||||
"ledger_index": 15202439,
|
||||
"tx": {
|
||||
"TransactionType": "Payment",
|
||||
"Flags": 2147483648,
|
||||
"Sequence": 1673,
|
||||
"LastLedgerSequence": 15202446,
|
||||
"Amount": "1700000000",
|
||||
"Fee": "15000",
|
||||
"SigningPubKey": "02C26CF5D395A1CB352BE10D5AAB73FE27FC0AFAE0BD6121E55D097EBDCF394E11",
|
||||
"TxnSignature": "304402204190B6DC7D14B1CC8DDAA87F1C01FEDA6D67D598D65E1AA19D4ADE937ED14B720220662EE404438F415AD3335B9FBA1A4C2A5F72AA387740D8A011A8C53346481B1D",
|
||||
"Account": "rEE77T1E5vEFcEB9zM92jBD3rPs3kPdS1j",
|
||||
"Destination": "r3AsrDRMNYaKNCofo9a5Us7R66RAzTigiU"
|
||||
},
|
||||
"meta": {
|
||||
"TransactionIndex": 0,
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202352,
|
||||
"PreviousTxnID": "6B3D159578F8E1CEBB268DBC5209ADB35DD075F463855886421D307026D27C67",
|
||||
"LedgerIndex": "AB5EBD00C6F12DEC32B1687A51948ADF07DC2ABDD7485E9665DCE5268039B461",
|
||||
"PreviousFields": {
|
||||
"Balance": "23493344926"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1775,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "25193344926",
|
||||
"Account": "r3AsrDRMNYaKNCofo9a5Us7R66RAzTigiU"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"PreviousTxnLgrSeq": 15202236,
|
||||
"PreviousTxnID": "A2C23A20377BA7A90F77F01F8E337B64E22C929C5490E2E9698A7A9BFFEC592A",
|
||||
"LedgerIndex": "C67232D5308CBE1A8C3D75284D98CC1623D906DB30774C06B3F4934BC1DE5CEE",
|
||||
"PreviousFields": {
|
||||
"Sequence": 1673,
|
||||
"Balance": "17034504878"
|
||||
},
|
||||
"FinalFields": {
|
||||
"Flags": 0,
|
||||
"Sequence": 1674,
|
||||
"OwnerCount": 0,
|
||||
"Balance": "15334489878",
|
||||
"Account": "rEE77T1E5vEFcEB9zM92jBD3rPs3kPdS1j"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
}
|
||||
]
|
||||
14
test/fixtures/api/requests/compute-ledger-hash.json
vendored
Normal file
14
test/fixtures/api/requests/compute-ledger-hash.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"stateHash": "D9ABF622DA26EEEE48203085D4BC23B0F77DC6F8724AC33D975DA3CA492D2E44",
|
||||
"closeTime": 492656470,
|
||||
"parentCloseTime": 492656460,
|
||||
"closeFlags": 0,
|
||||
"closeTimeHuman": "2015-Aug-12 01:01:10",
|
||||
"closeTimeResolution": 10,
|
||||
"closed": true,
|
||||
"ledgerVersion": 15202439,
|
||||
"parentLedgerHash": "12724A65B030C15A1573AA28B1BBB5DF3DA4589AA3623675A31CAE69B23B1C4E",
|
||||
"totalDrops": "99998831688050493",
|
||||
"transactionHash": "325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C8"
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J"
|
||||
},
|
||||
"destination": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"value": "1000002",
|
||||
"currency": "XRP"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"source": {
|
||||
"address": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J"
|
||||
},
|
||||
"destination": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"value": "1000002",
|
||||
"currency": "XRP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,4 +9,4 @@
|
||||
"currency": "XRP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
test/fixtures/api/requests/index.js
vendored
4
test/fixtures/api/requests/index.js
vendored
@@ -20,5 +20,9 @@ module.exports = {
|
||||
NotAcceptCurrency: require('./getpaths/not-accept-currency'),
|
||||
NoPaths: require('./getpaths/no-paths'),
|
||||
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies')
|
||||
},
|
||||
computeLedgerHash: {
|
||||
header: require('./compute-ledger-hash'),
|
||||
transactions: require('./compute-ledger-hash-transactions')
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"value": "0.01",
|
||||
"currency": "XRP"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"value": "0.01",
|
||||
"currency": "USD"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"value": "0.01",
|
||||
"currency": "USD",
|
||||
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
|
||||
|
||||
4
test/fixtures/api/responses/generate-address.json
vendored
Normal file
4
test/fixtures/api/responses/generate-address.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"address": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
|
||||
"secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"address": "ra2d1wnhDFyZGFz62wT3GSRhAEjpLuqfnj",
|
||||
"secret": "ss4JedFMSQJa4mHQz3VZ58ZG4TYWg"
|
||||
}
|
||||
40
test/fixtures/api/responses/get-ledger-full.json
vendored
Normal file
40
test/fixtures/api/responses/get-ledger-full.json
vendored
Normal file
File diff suppressed because one or more lines are too long
14
test/fixtures/api/responses/get-ledger.json
vendored
Normal file
14
test/fixtures/api/responses/get-ledger.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"closed": true,
|
||||
"stateHash": "EC028EC32896D537ECCA18D18BEBE6AE99709FEFF9EF72DBD3A7819E918D8B96",
|
||||
"closeTime": 464908910,
|
||||
"closeTimeResolution": 10,
|
||||
"closeFlags": 0,
|
||||
"ledgerHash": "0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A0F",
|
||||
"ledgerVersion": 9038214,
|
||||
"parentLedgerHash": "4BB9CBE44C39DC67A1BE849C7467FE1A6D1F73949EA163C38A0121A15E04FFDE",
|
||||
"parentCloseTime": 464908900,
|
||||
"totalDrops": "99999973964317514",
|
||||
"transactionHash": "ECB730839EB55B1B114D5D1AD2CD9A932C35BA9AB6D3A8C2F08935EAC2BAC239"
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"specification": {
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"currency": "XRP",
|
||||
"value": "1.112209"
|
||||
}
|
||||
|
||||
27
test/fixtures/api/responses/get-transaction-trust-no-quality.json
vendored
Normal file
27
test/fixtures/api/responses/get-transaction-trust-no-quality.json
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"type": "trustline",
|
||||
"address": "r9UHu5CWni1qRY7Q4CfFZLGvXo2pGQy96b",
|
||||
"sequence": 245,
|
||||
"id": "BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002",
|
||||
"specification": {
|
||||
"limit": "1",
|
||||
"currency": "USD",
|
||||
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2015-07-08T20:29:20.000Z",
|
||||
"fee": "0.012",
|
||||
"balanceChanges": {
|
||||
"r9UHu5CWni1qRY7Q4CfFZLGvXo2pGQy96b": [
|
||||
{
|
||||
"currency": "XRP",
|
||||
"value": "-0.012"
|
||||
}
|
||||
]
|
||||
},
|
||||
"orderbookChanges": {},
|
||||
"ledgerVersion": 14518100,
|
||||
"indexInLedger": 1
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@
|
||||
],
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"currency": "XRP",
|
||||
"value": "1.112209"
|
||||
}
|
||||
@@ -104,7 +104,7 @@
|
||||
],
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"currency": "XRP",
|
||||
"value": "1.112209"
|
||||
}
|
||||
|
||||
7
test/fixtures/api/responses/index.js
vendored
7
test/fixtures/api/responses/index.js
vendored
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
generateWallet: require('./generate-wallet.json'),
|
||||
generateAddress: require('./generate-address.json'),
|
||||
getAccountInfo: require('./get-account-info.json'),
|
||||
getBalances: require('./get-balances.json'),
|
||||
getOrderbook: require('./get-orderbook.json'),
|
||||
@@ -23,10 +23,15 @@ module.exports = {
|
||||
trackingOff: require('./get-transaction-settings-tracking-off.json'),
|
||||
setRegularKey: require('./get-transaction-settings-set-regular-key.json'),
|
||||
trustlineFrozenOff: require('./get-transaction-trust-set-frozen-off.json'),
|
||||
trustlineNoQuality: require('./get-transaction-trust-no-quality.json'),
|
||||
notValidated: require('./get-transaction-not-validated.json')
|
||||
},
|
||||
getTransactions: require('./get-transactions.json'),
|
||||
getTrustlines: require('./get-trustlines.json'),
|
||||
getLedger: {
|
||||
header: require('./get-ledger'),
|
||||
full: require('./get-ledger-full')
|
||||
},
|
||||
prepareOrderCancellation: require('./prepare-order-cancellation.json'),
|
||||
prepareOrder: require('./prepare-order.json'),
|
||||
prepareOrderSell: require('./prepare-order-sell.json'),
|
||||
|
||||
4
test/fixtures/api/responses/sign.json
vendored
4
test/fixtures/api/responses/sign.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"signedTransaction": "12000322000000002400000017201B0086955368400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402207660BDEF67105CE1EBA9AD35DC7156BAB43FF1D47633199EE257D70B6B9AAFBF022045A812486A675750B5A3F37131E9F92299728D37FF6BB7195CA5EE881268CB4C770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
|
||||
"id": "29D23159EBA79170DCA5EF467CBC15114DBD35B7A8C3DBF76809BA354D00D250"
|
||||
"signedTransaction": "12000322000000002400000017201B0086955368400000000000000C732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100E3C85A98C3D48E2D746B3993D36C38A8922D9499E7D20B54883511C9CFF7F70A02201790168F671AC558E1B62B911CA7AC5B1D8E454898BE6F6E9D87758076683459770A726970706C652E636F6D81145E7B112523F68D2F5E879DB4EAC51C6698A69304",
|
||||
"id": "7AFE2F2FBE72467C47CCDD6DBA890AB3C97A708C335983F77AF32C4308C73633"
|
||||
}
|
||||
|
||||
2
test/fixtures/api/rippled/account-tx.js
vendored
2
test/fixtures/api/rippled/account-tx.js
vendored
@@ -228,7 +228,7 @@ module.exports = function(request, options={}) {
|
||||
marker: marker === undefined ? undefined : String(marker),
|
||||
transactions: [
|
||||
{
|
||||
ledger_index: 348860 - Number(marker),
|
||||
ledger_index: 348860 - Number(marker || 100),
|
||||
tx_blob: SerializedObject.from_json(tx).to_hex(),
|
||||
meta: SerializedObject.from_json(meta).to_hex(),
|
||||
validated: options.validated
|
||||
|
||||
9
test/fixtures/api/rippled/index.js
vendored
9
test/fixtures/api/rippled/index.js
vendored
@@ -16,10 +16,10 @@ module.exports = {
|
||||
book_offers: require('./book-offers'),
|
||||
server_info: require('./server-info'),
|
||||
server_info_error: require('./server-info-error'),
|
||||
ripple_path_find: {
|
||||
generate: require('./ripple-path-find'),
|
||||
sendUSD: require('./ripple-path-find-send-usd'),
|
||||
XrpToXrp: require('./ripple-path-find-xrp-to-xrp')
|
||||
path_find: {
|
||||
generate: require('./path-find'),
|
||||
sendUSD: require('./path-find-send-usd'),
|
||||
XrpToXrp: require('./path-find-xrp-to-xrp')
|
||||
},
|
||||
tx: {
|
||||
Payment: require('./tx/payment.json'),
|
||||
@@ -31,6 +31,7 @@ module.exports = {
|
||||
OfferCancel: require('./tx/offer-cancel.json'),
|
||||
TrustSet: require('./tx/trust-set.json'),
|
||||
TrustSetFrozenOff: require('./tx/trust-set-frozen-off.json'),
|
||||
TrustSetNoQuality: require('./tx/trust-set-no-quality.json'),
|
||||
NotFound: require('./tx/not-found.json'),
|
||||
NoLedgerIndex: require('./tx/no-ledger-index.json'),
|
||||
NoLedgerFound: require('./tx/no-ledger-found.json'),
|
||||
|
||||
2
test/fixtures/api/rippled/ledger.json
vendored
2
test/fixtures/api/rippled/ledger.json
vendored
@@ -7,9 +7,11 @@
|
||||
"accepted": true,
|
||||
"account_hash": "EC028EC32896D537ECCA18D18BEBE6AE99709FEFF9EF72DBD3A7819E918D8B96",
|
||||
"close_time": 464908910,
|
||||
"parent_close_time": 464908900,
|
||||
"close_time_human": "2014-Sep-24 21:21:50",
|
||||
"close_time_resolution": 10,
|
||||
"closed": true,
|
||||
"close_flags": 0,
|
||||
"hash": "0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A0F",
|
||||
"ledger_hash": "0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A0F",
|
||||
"ledger_index": "9038214",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"id": 0,
|
||||
"result": {
|
||||
"full_reply": true,
|
||||
"alternatives": [
|
||||
{
|
||||
"paths_canonical": [],
|
||||
@@ -72,6 +73,12 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"source_account": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"value": "0.000001",
|
||||
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||
},
|
||||
"destination_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"destination_currencies": [
|
||||
"JOE",
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"id": 1,
|
||||
"result": {
|
||||
"full_reply": true,
|
||||
"alternatives": [],
|
||||
"source_account": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J",
|
||||
"destination_amount": "2",
|
||||
"destination_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"destination_currencies": [
|
||||
"JOE",
|
||||
@@ -7,6 +7,9 @@ function(request_id, sendingAccount, destinationAccount, destinationAmount) {
|
||||
'status': 'success',
|
||||
'type': 'response',
|
||||
'result': {
|
||||
'full_reply': true,
|
||||
'source_account': sendingAccount,
|
||||
'destination_amount': destinationAmount,
|
||||
'alternatives': [
|
||||
{
|
||||
'paths_canonical': [],
|
||||
@@ -308,6 +311,7 @@ function(request_id, sendingAccount, destinationAccount) {
|
||||
'status': 'success',
|
||||
'type': 'response',
|
||||
'result': {
|
||||
'full_reply': true,
|
||||
'alternatives': [
|
||||
{
|
||||
'paths_canonical': [],
|
||||
51
test/fixtures/api/rippled/tx/trust-set-no-quality.json
vendored
Normal file
51
test/fixtures/api/rippled/tx/trust-set-no-quality.json
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"id": 0,
|
||||
"result": {
|
||||
"Account": "r9UHu5CWni1qRY7Q4CfFZLGvXo2pGQy96b",
|
||||
"Fee": "12000",
|
||||
"Flags": 0,
|
||||
"LastLedgerSequence": 14518103,
|
||||
"LimitAmount": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "1"
|
||||
},
|
||||
"Sequence": 245,
|
||||
"SigningPubKey": "036A749E3B7187E43E8936E3D83A7030989325249E03803F12B7F64BAACABA6025",
|
||||
"TransactionType": "TrustSet",
|
||||
"TxnSignature": "3045022100AFE1ADDA62C7604495C8CBBE8308471E9F55C7D646571621541F6E1B66BA1EE30220108E914EB9DF876E9B525ADDBDD378DFFDEDF72CD9030FE76909CA2CE73FAA6D",
|
||||
"date": 489702560,
|
||||
"hash": "BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002",
|
||||
"inLedger": 14518100,
|
||||
"ledger_index": 14518100,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "r9UHu5CWni1qRY7Q4CfFZLGvXo2pGQy96b",
|
||||
"Balance": "44491746",
|
||||
"Domain": "726970706C652E636F6D",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 4,
|
||||
"Sequence": 246
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "4AD70690C6FF8A069F8AE00B09F70E9B732360026E8085050D314432091A59C9",
|
||||
"PreviousFields": {
|
||||
"Balance": "44503746",
|
||||
"Sequence": 245
|
||||
},
|
||||
"PreviousTxnID": "7ACFD6E9A0EC17A9872212D81788658C886A19EF5D6CA9737241D91953E588CD",
|
||||
"PreviousTxnLgrSeq": 14518099
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 1,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
},
|
||||
"validated": true
|
||||
},
|
||||
"status": "success",
|
||||
"type": "response"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
var assert = require('assert');
|
||||
var Seed = require('ripple-lib').Seed;
|
||||
|
||||
describe('KeyPair', function() {
|
||||
it('can generate an address', function () {
|
||||
var seed = Seed.from_json("masterpassphrase");
|
||||
var address = seed.get_key().get_address();
|
||||
assert.strictEqual(address.to_json(), 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
@@ -1,30 +0,0 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
|
||||
const SEED =
|
||||
'3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BD0';
|
||||
|
||||
function MockPRNG(seed) {
|
||||
if (seed && seed.length < 8) {
|
||||
throw new Error('seed must be a hex string of at least 8 characters');
|
||||
}
|
||||
this.position = 0;
|
||||
this.seed = seed || SEED;
|
||||
}
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
MockPRNG.prototype.addEntropy = function(data, estimatedEntropy, source) {};
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
MockPRNG.prototype.randomWord = function() {
|
||||
const i = this.position;
|
||||
this.position = (i + 8) % this.seed.length;
|
||||
const data = this.seed + this.seed.slice(8);
|
||||
return parseInt(data.slice(i, i + 8), 16);
|
||||
};
|
||||
|
||||
MockPRNG.prototype.randomWords = function(n) {
|
||||
return _.times(n, () => this.randomWord());
|
||||
};
|
||||
|
||||
module.exports = MockPRNG;
|
||||
@@ -8,6 +8,7 @@ const addresses = require('./fixtures/addresses');
|
||||
const hashes = require('./fixtures/hashes');
|
||||
const transactionsResponse = require('./fixtures/api/rippled/account-tx');
|
||||
const accountLinesResponse = require('./fixtures/api/rippled/account-lines');
|
||||
const fullLedger = require('./fixtures/ledger-full-38129.json');
|
||||
|
||||
function isUSD(json) {
|
||||
return json === 'USD' || json === '0000000000000000000000005553440000000000';
|
||||
@@ -17,8 +18,32 @@ function isBTC(json) {
|
||||
return json === 'BTC' || json === '0000000000000000000000004254430000000000';
|
||||
}
|
||||
|
||||
function createResponse(request, response) {
|
||||
return JSON.stringify(_.assign({}, response, {id: request.id}));
|
||||
function createResponse(request, response, overrides={}) {
|
||||
const result = _.assign({}, response.result, overrides);
|
||||
const change = response.result && !_.isEmpty(overrides) ?
|
||||
{id: request.id, result: result} : {id: request.id};
|
||||
return JSON.stringify(_.assign({}, response, change));
|
||||
}
|
||||
|
||||
function createLedgerResponse(request, response) {
|
||||
const newResponse = JSON.parse(createResponse(request, response));
|
||||
if (newResponse.result && newResponse.result.ledger) {
|
||||
if (!request.transactions) {
|
||||
delete newResponse.result.ledger.transactions;
|
||||
}
|
||||
if (!request.accounts) {
|
||||
delete newResponse.result.ledger.accountState;
|
||||
}
|
||||
// the following fields were not in the ledger response in the past
|
||||
if (newResponse.result.ledger.close_flags === undefined) {
|
||||
newResponse.result.ledger.close_flags = 0;
|
||||
}
|
||||
if (newResponse.result.ledger.parent_close_time === undefined) {
|
||||
newResponse.result.ledger.parent_close_time =
|
||||
newResponse.result.ledger.close_time - 10;
|
||||
}
|
||||
}
|
||||
return JSON.stringify(newResponse);
|
||||
}
|
||||
|
||||
module.exports = function(port) {
|
||||
@@ -116,11 +141,15 @@ module.exports = function(port) {
|
||||
mock.on('request_ledger', function(request, conn) {
|
||||
assert.strictEqual(request.command, 'ledger');
|
||||
if (request.ledger_index === 34) {
|
||||
conn.send(createResponse(request, fixtures.ledgerNotFound));
|
||||
conn.send(createLedgerResponse(request, fixtures.ledgerNotFound));
|
||||
} else if (request.ledger_index === 9038215) {
|
||||
conn.send(createResponse(request, fixtures.ledgerWithoutCloseTime));
|
||||
conn.send(createLedgerResponse(request, fixtures.ledgerWithoutCloseTime));
|
||||
} else if (request.ledger_index === 38129) {
|
||||
const response = _.assign({}, fixtures.ledger,
|
||||
{result: {ledger: fullLedger}});
|
||||
conn.send(createLedgerResponse(request, response));
|
||||
} else {
|
||||
conn.send(createResponse(request, fixtures.ledger));
|
||||
conn.send(createLedgerResponse(request, fixtures.ledger));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -161,6 +190,9 @@ module.exports = function(port) {
|
||||
} else if (request.transaction ===
|
||||
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B') {
|
||||
conn.send(createResponse(request, fixtures.tx.TrustSetFrozenOff));
|
||||
} else if (request.transaction ===
|
||||
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002') {
|
||||
conn.send(createResponse(request, fixtures.tx.TrustSetNoQuality));
|
||||
} else if (request.transaction ===
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10') {
|
||||
conn.send(createResponse(request, fixtures.tx.NotValidated));
|
||||
@@ -216,14 +248,20 @@ module.exports = function(port) {
|
||||
}
|
||||
});
|
||||
|
||||
mock.on('request_ripple_path_find', function(request, conn) {
|
||||
mock.on('request_path_find', function(request, conn) {
|
||||
let response = null;
|
||||
if (request.subcommand === 'close') {
|
||||
return;
|
||||
}
|
||||
if (request.source_account === addresses.OTHER_ACCOUNT) {
|
||||
response = createResponse(request, fixtures.ripple_path_find.sendUSD);
|
||||
response = createResponse(request, fixtures.path_find.sendUSD);
|
||||
} else if (request.source_account === addresses.THIRD_ACCOUNT) {
|
||||
response = createResponse(request, fixtures.ripple_path_find.XrpToXrp);
|
||||
response = createResponse(request, fixtures.path_find.XrpToXrp, {
|
||||
destination_amount: request.destination_amount,
|
||||
destination_address: request.destination_address
|
||||
});
|
||||
} else {
|
||||
response = fixtures.ripple_path_find.generate.generateIOUPaymentPaths(
|
||||
response = fixtures.path_find.generate.generateIOUPaymentPaths(
|
||||
request.id, request.source_account, request.destination_account,
|
||||
request.destination_amount);
|
||||
}
|
||||
|
||||
@@ -935,11 +935,10 @@ describe('Remote', function() {
|
||||
remote.feeTxUnit(10).to_json();
|
||||
});
|
||||
});
|
||||
it('Get reserve', function() {
|
||||
it('reserve() before reserve rate known', function() {
|
||||
remote._connected = true;
|
||||
remote._servers[0]._connected = true;
|
||||
assert.strictEqual(remote.reserve(1).to_json(), 'NaN');
|
||||
remote._servers = [];
|
||||
// Throws because the server has not had reserve_inc, reserve_base set
|
||||
assert.throws(function() {
|
||||
remote.reserve(10).to_json();
|
||||
});
|
||||
|
||||
@@ -4,17 +4,15 @@
|
||||
const assert = require('assert');
|
||||
const Seed = require('ripple-lib').Seed;
|
||||
|
||||
function assert_helper(seed_json, address_or_nth, expected) {
|
||||
const seed = Seed.from_json(seed_json);
|
||||
const keypair = seed.get_key(address_or_nth, 500);
|
||||
assert.strictEqual(keypair.to_hex_pub(), expected);
|
||||
}
|
||||
|
||||
describe('Seed', function() {
|
||||
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function() {
|
||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
||||
});
|
||||
it('can create ed25519 seeds from a phrase', function() {
|
||||
const seed = Seed.from_json('phrase').set_ed25519().to_json();
|
||||
assert.strictEqual(seed, 'sEdT7U4WpkoiH6wBoNeLzDi1eu9N64Y');
|
||||
});
|
||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function() {
|
||||
const seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||
@@ -36,66 +34,13 @@ describe('Seed', function() {
|
||||
const seed = new Seed().parse_base58('Xs');
|
||||
assert(!seed.is_valid());
|
||||
});
|
||||
it('can generate many addresses', function() {
|
||||
|
||||
const test_data = [
|
||||
// Format:
|
||||
// [passphrase, address, nth-for-seed, expected-public-key]
|
||||
['masterpassphrase', 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', 0,
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020'],
|
||||
['masterpassphrase', 'r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP', 1,
|
||||
'02CD8C4CE87F86AAD1D9D18B03DE28E6E756F040BD72A9C127862833EB90D60BAD'],
|
||||
['masterpassphrase', 'rLpAd4peHUMBPbVJASMYK5GTBUSwXRD9nx', 2,
|
||||
'0259A57642A6F4AEFC9B8062AF453FDEEEAC5572BA602BB1DBD5EF011394C6F9FC'],
|
||||
['otherpassphrase', 'rpe3YWSVwGU2PmUzebAPg2deBXHtmba7hJ', 0,
|
||||
'022235A3DB2CAE57C60B7831929611D58867F86D28C0AD3C82473CC4A84990D01B'],
|
||||
['otherpassphrase', 'raAPC2gALSmsTkXR4wUwQcPgX66kJuLv2S', 5,
|
||||
'03F0619AFABE08D22D98C8721895FE3673B6174168949976F2573CE1138C124994'],
|
||||
['yetanotherpassphrase', 'rKnM44fS48qrGiDxB5fB5u64vHVJwjDPUo', 0,
|
||||
'0385AD049327EF7E5EC429350A15CEB23955037DE99660F6E70C11C5ABF4407036'],
|
||||
['yetanotherpassphrase', 'rMvkT1RHPfsZwTFbKDKBEisa5U4d2a9V8n', 1,
|
||||
'023A2876EA130CBE7BBA0573C2DB4C4CEB9A7547666915BD40366CDC6150CF54DC']
|
||||
];
|
||||
|
||||
for (let nth = 0; nth < test_data.length; nth++) {
|
||||
const seed_json = test_data[nth][0];
|
||||
const address = test_data[nth][1];
|
||||
const nth_for_seed = test_data[nth][2];
|
||||
const expected = test_data[nth][3];
|
||||
|
||||
// `seed.get_key($ripple_address)` is arguably an ill concieved feature
|
||||
// as it needs to generate `nth` many keypairs and generate hashed public
|
||||
// keys (addresses) for equality tests ??
|
||||
// Would need remote.set_secret(address, private_key_not_seed) ??
|
||||
assert_helper(seed_json, address, expected);
|
||||
|
||||
// This isn't too bad as it only needs to generate one keypair `seq`
|
||||
assert_helper(seed_json, nth_for_seed, expected);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('should return the key_pair for a valid account and secret pair', function() {
|
||||
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
||||
const seed = Seed.from_json('shsWGZcmZz6YsWWmcnpfr6fLTdtFV');
|
||||
const keyPair = seed.get_key(address);
|
||||
assert.strictEqual(keyPair.get_address().to_json(), address);
|
||||
assert.strictEqual(keyPair.to_hex_pub(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
|
||||
const keyPair = seed.get_key();
|
||||
assert.strictEqual(keyPair.accountID(), address);
|
||||
assert.strictEqual(keyPair.pubKeyHex(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
|
||||
});
|
||||
|
||||
it('should not find a KeyPair for a secret that does not belong to the given account', function() {
|
||||
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
||||
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
||||
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
||||
try {
|
||||
seed.get_key(address);
|
||||
assert(false, 'should throw an error');
|
||||
} catch(e) {
|
||||
assert.strictEqual(e.message, 'Too many loops looking for KeyPair yielding ' + address + ' from ' + secret);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -6,11 +6,11 @@ const assert = require('assert');
|
||||
const lodash = require('lodash');
|
||||
const SerializedObject = require('ripple-lib').SerializedObject;
|
||||
const Amount = require('ripple-lib').Amount;
|
||||
const sjcl = require('ripple-lib').sjcl;
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
|
||||
// Shortcuts
|
||||
const hex = sjcl.codec.hex;
|
||||
const utf8 = sjcl.codec.utf8String;
|
||||
const hex = sjclcodec.hex;
|
||||
const utf8 = sjclcodec.utf8String;
|
||||
|
||||
describe('Serialized object', function() {
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('Serialized object', function() {
|
||||
|
||||
describe('#from_json(v).to_json() == v', function() {
|
||||
it('outputs same as passed to from_json', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
Amount: '274579388',
|
||||
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
@@ -50,14 +50,14 @@ describe('Serialized object', function() {
|
||||
TransactionType: 'Payment',
|
||||
TxnSignature: '30450221009DA3A42DD25E3B22EC45AD8BA8FC7A954264264A816D300B2DF69F814D7D4DD2022072C9627F97EEC6DA13DE841E06E2CD985EF06A0FBB15DDBF0800D0730C8986BF'
|
||||
};
|
||||
let output_json = SerializedObject.from_json(input_json).to_json();
|
||||
const output_json = SerializedObject.from_json(input_json).to_json();
|
||||
assert.deepEqual(input_json, output_json);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#from_json(v).to_json() == v -- invalid amount', function() {
|
||||
it('outputs same as passed to from_json', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
Account: 'rUR9gTCcrUY9fMkz9rwcM9urPREh3LKXoW',
|
||||
Fee: '10',
|
||||
Flags: 0,
|
||||
@@ -81,7 +81,7 @@ describe('Serialized object', function() {
|
||||
|
||||
describe('#from_json(v).to_json() == v -- invalid amount, strict_mode = false', function() {
|
||||
it('outputs same as passed to from_json', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
Account: 'rUR9gTCcrUY9fMkz9rwcM9urPREh3LKXoW',
|
||||
Fee: '10',
|
||||
Flags: 0,
|
||||
@@ -97,9 +97,9 @@ describe('Serialized object', function() {
|
||||
TxnSignature: 'FFFFFF210085C6AE945643150E6D450CF796E45D74FB24B4E03E964A29CC6AFFEB346C77C80221009BE1B6678CF6C2E61F8F2696144C75AFAF66DF4FC0733DF9118EDEFEEFE33243'
|
||||
};
|
||||
|
||||
let strictMode = Amount.strict_mode;
|
||||
const strictMode = Amount.strict_mode;
|
||||
Amount.strict_mode = false;
|
||||
let output_json = SerializedObject.from_json(input_json).to_json();
|
||||
const output_json = SerializedObject.from_json(input_json).to_json();
|
||||
assert.deepEqual(input_json, output_json);
|
||||
Amount.strict_mode = strictMode;
|
||||
});
|
||||
@@ -107,7 +107,7 @@ describe('Serialized object', function() {
|
||||
|
||||
describe('#from_json', function() {
|
||||
it('understands TransactionType as a Number', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
// no non required fields
|
||||
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
Amount: '274579388',
|
||||
@@ -117,14 +117,14 @@ describe('Serialized object', function() {
|
||||
SigningPubKey: '02',// VL field ;)
|
||||
TransactionType: 0 //
|
||||
};
|
||||
let output_json = SerializedObject.from_json(input_json).to_json();
|
||||
const output_json = SerializedObject.from_json(input_json).to_json();
|
||||
|
||||
assert.equal(0, input_json.TransactionType);
|
||||
assert.equal('Payment', output_json.TransactionType);
|
||||
});
|
||||
|
||||
it('understands LedgerEntryType as a Number', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
// no, non required fields
|
||||
LedgerEntryType: 100,
|
||||
Flags: 0,
|
||||
@@ -132,13 +132,13 @@ describe('Serialized object', function() {
|
||||
RootIndex: '000360186E008422E06B72D5B275E29EE3BE9D87A370F424E0E7BF613C465909'
|
||||
};
|
||||
|
||||
let output_json = SerializedObject.from_json(input_json).to_json();
|
||||
const output_json = SerializedObject.from_json(input_json).to_json();
|
||||
assert.equal(100, input_json.LedgerEntryType);
|
||||
assert.equal('DirectoryNode', output_json.LedgerEntryType);
|
||||
});
|
||||
|
||||
it('checks for missing required fields', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
TransactionType: 'Payment',
|
||||
// no non required fields
|
||||
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
@@ -150,7 +150,7 @@ describe('Serialized object', function() {
|
||||
};
|
||||
|
||||
Object.keys(input_json).slice(1).forEach(function(k) {
|
||||
let bad_json = lodash.merge({}, input_json);
|
||||
const bad_json = lodash.merge({}, input_json);
|
||||
delete bad_json[k];
|
||||
|
||||
assert.strictEqual(bad_json[k], undefined);
|
||||
@@ -161,7 +161,7 @@ describe('Serialized object', function() {
|
||||
});
|
||||
});
|
||||
it('checks for unknown fields', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
TransactionType: 'Payment',
|
||||
// no non required fields
|
||||
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
@@ -173,7 +173,7 @@ describe('Serialized object', function() {
|
||||
};
|
||||
|
||||
Object.keys(input_json).slice(1).forEach(function(k) {
|
||||
let bad_json = lodash.merge({}, input_json);
|
||||
const bad_json = lodash.merge({}, input_json);
|
||||
bad_json[k + 'z'] = bad_json[k];
|
||||
|
||||
assert.throws(function() {
|
||||
@@ -187,7 +187,7 @@ describe('Serialized object', function() {
|
||||
// Peercover actually had a problem submitting transactions without a `Fee`
|
||||
// and rippled was only informing of 'transaction is invalid'
|
||||
it('should throw an Error when there is a missing field', function() {
|
||||
let input_json = {
|
||||
const input_json = {
|
||||
Account: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
Amount: '274579388',
|
||||
Destination: 'r4qLSAzv4LZ9TLsR7diphGwKnSEAMQTSjS',
|
||||
@@ -234,7 +234,7 @@ describe('Serialized object', function() {
|
||||
}
|
||||
];
|
||||
|
||||
let so = SerializedObject.from_json(input_json).to_json();
|
||||
const so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'text';
|
||||
input_json.Memos[0].Memo.parsed_memo_data = 'some data';
|
||||
@@ -257,7 +257,7 @@ describe('Serialized object', function() {
|
||||
}
|
||||
];
|
||||
|
||||
let so = SerializedObject.from_json(input_json).to_json();
|
||||
const so = SerializedObject.from_json(input_json).to_json();
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'application/json';
|
||||
input_json.Memos[0].Memo.MemoType = convertStringToHex('test');
|
||||
@@ -280,7 +280,7 @@ describe('Serialized object', function() {
|
||||
}
|
||||
];
|
||||
|
||||
let so = SerializedObject.from_json(input_json).to_json();
|
||||
const so = SerializedObject.from_json(input_json).to_json();
|
||||
delete input_json.Memos[0].Memo.ignored;
|
||||
input_json.Memos[0].Memo.parsed_memo_type = 'test';
|
||||
input_json.Memos[0].Memo.parsed_memo_format = 'json';
|
||||
@@ -338,7 +338,7 @@ describe('Serialized object', function() {
|
||||
TxnSignature: '304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9'
|
||||
};
|
||||
|
||||
let serializedHex = '12000022800000002400000126201B009EC2FF614000000000000001684000000000002EE0732103D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C517446304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9811426C4CFB3BD05A9AA23936F2E81634C66A9820C9483143DD06317D19C6110CAFF150AE528F58843BE2CA1F9EA7C05696D616765E1F1';
|
||||
const serializedHex = '12000022800000002400000126201B009EC2FF614000000000000001684000000000002EE0732103D642E6457B8AB4D140E2C66EB4C484FAFB1BF267CB578EC4815FE6CD06379C517446304402206B53EDFA6EFCF6FE5BA76C81BABB60A3B55E9DE8A1462DEDC5F387879575E498022015AE7B59AA49E735D7F2E252802C4406CD00689BCE5057C477FE979D38D2DAC9811426C4CFB3BD05A9AA23936F2E81634C66A9820C9483143DD06317D19C6110CAFF150AE528F58843BE2CA1F9EA7C05696D616765E1F1';
|
||||
assert.strictEqual(SerializedObject.from_json(input_json).to_hex(), serializedHex);
|
||||
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -305,7 +305,7 @@ describe('Server', function() {
|
||||
|
||||
server.emit('message', {
|
||||
type: 'serverStatus',
|
||||
load_base: 256 * 1,
|
||||
load_base: 256,
|
||||
load_factor: 256 * 10,
|
||||
server_status: 'full'
|
||||
});
|
||||
@@ -1063,6 +1063,13 @@ describe('Server', function() {
|
||||
assert.strictEqual(server._reserve().to_json(), '20000000');
|
||||
});
|
||||
|
||||
it('Compute reserve, positive OwnerCount', function() {
|
||||
const server = new Server(new Remote(), 'ws://localhost:5748');
|
||||
server._reserve_base = 20000000;
|
||||
server._reserve_inc = 5000000;
|
||||
assert.strictEqual(server._reserve(4).to_json(), '40000000');
|
||||
});
|
||||
|
||||
it('Cache hostid', function(done) {
|
||||
const wss = new ws.Server({
|
||||
port: 5748
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('Signing', function() {
|
||||
it('SigningPubKey 1 (ripple-client issue #245)', function() {
|
||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
const key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||
const pub = key.to_hex_pub();
|
||||
const pub = key.pubKeyHex();
|
||||
assert.strictEqual(
|
||||
pub,
|
||||
'0396941B22791A448E5877A44CE98434DB217D6FB97D63F0DAD23BE49ED45173C9');
|
||||
@@ -20,7 +20,7 @@ describe('Signing', function() {
|
||||
it('SigningPubKey 2 (master seed)', function() {
|
||||
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
||||
const key = seed.get_key('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
const pub = key.to_hex_pub();
|
||||
const pub = key.pubKeyHex();
|
||||
assert.strictEqual(
|
||||
pub,
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020');
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
const ws = require('ws');
|
||||
const lodash = require('lodash');
|
||||
const assert = require('assert-diff');
|
||||
const sjcl = require('ripple-lib').sjcl;
|
||||
const Remote = require('ripple-lib').Remote;
|
||||
const SerializedObject = require('ripple-lib').SerializedObject;
|
||||
const Transaction = require('ripple-lib').Transaction;
|
||||
@@ -48,11 +47,6 @@ describe('TransactionManager', function() {
|
||||
let account;
|
||||
let transactionManager;
|
||||
|
||||
before(function() {
|
||||
sjcl.random.addEntropy(
|
||||
'3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BD', 256);
|
||||
});
|
||||
|
||||
beforeEach(function(done) {
|
||||
rippled = new ws.Server({port: 5763});
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ const Transaction = require('ripple-lib').Transaction;
|
||||
const TransactionQueue = require('ripple-lib').TransactionQueue;
|
||||
const Remote = require('ripple-lib').Remote;
|
||||
const Server = require('ripple-lib').Server;
|
||||
const sjcl = require('ripple-lib').sjcl;
|
||||
|
||||
const transactionResult = {
|
||||
engine_result: 'tesSUCCESS',
|
||||
@@ -54,11 +53,6 @@ for (let i = 0; i <= 127; i++) {
|
||||
}
|
||||
|
||||
describe('Transaction', function() {
|
||||
before(function() {
|
||||
sjcl.random.addEntropy(
|
||||
'3045022100A58B0460BC5092CB4F96155C19125A4E079C870663F1D5E8BBC9BD', 256);
|
||||
});
|
||||
|
||||
it('Success listener', function(done) {
|
||||
const transaction = new Transaction();
|
||||
|
||||
@@ -454,7 +448,6 @@ describe('Transaction', function() {
|
||||
transaction.SigningPubKey = undefined;
|
||||
transaction.tx_json.Account = 'rMWwx3Ma16HnqSd4H6saPisihX9aKpXxHJ';
|
||||
transaction._secret = 'sh2pTicynUEG46jjR4EoexHcQEoijX';
|
||||
|
||||
transaction.once('error', function(err) {
|
||||
assert.strictEqual(err.result, 'tejSecretInvalid');
|
||||
done();
|
||||
@@ -553,6 +546,36 @@ describe('Transaction', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
describe('ed25519 signing', function() {
|
||||
it('can accept an ed25519 seed for ._secret', function() {
|
||||
const expectedPub = 'EDD3993CDC6647896C455F136648B7750' +
|
||||
'723B011475547AF60691AA3D7438E021D';
|
||||
|
||||
const expectedSig = 'C3646313B08EED6AF4392261A31B961F' +
|
||||
'10C66CB733DB7F6CD9EAB079857834C8' +
|
||||
'B0334270A2C037E63CDCCC1932E08328' +
|
||||
'82B7B7066ECD2FAEDEB4A83DF8AE6303';
|
||||
|
||||
const tx_json = {
|
||||
Account: 'rJZdUusLDtY9NEsGea7ijqhVrXv98rYBYN',
|
||||
Amount: '1000',
|
||||
Destination: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
Fee: '10',
|
||||
Flags: 2147483648,
|
||||
Sequence: 1,
|
||||
TransactionType: 'Payment'
|
||||
};
|
||||
|
||||
const tx = Transaction.from_json(tx_json);
|
||||
tx.setSecret('sEd7rBGm5kxzauRTAV2hbsNz7N45X91');
|
||||
tx.complete();
|
||||
tx.sign();
|
||||
|
||||
assert.strictEqual(tx_json.SigningPubKey, expectedPub);
|
||||
assert.strictEqual(tx_json.TxnSignature, expectedSig);
|
||||
});
|
||||
});
|
||||
|
||||
describe('signing', function() {
|
||||
const tx_json = {
|
||||
SigningPubKey: '021FED5FD081CE5C4356431267D04C6E2167E4112C897D5E10335D4E22B4DA49ED',
|
||||
@@ -1318,11 +1341,11 @@ describe('Transaction', function() {
|
||||
const expected = [
|
||||
{
|
||||
Memo:
|
||||
{
|
||||
MemoType: '6D657373616765',
|
||||
MemoFormat: '6A736F6E',
|
||||
MemoData: '7B22737472696E67223A2276616C7565222C22626F6F6C223A747275652C22696E7465676572223A317D'
|
||||
}
|
||||
{
|
||||
MemoType: '6D657373616765',
|
||||
MemoFormat: '6A736F6E',
|
||||
MemoData: '7B22737472696E67223A2276616C7565222C22626F6F6C223A747275652C22696E7465676572223A317D'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -2014,7 +2037,7 @@ describe('Transaction', function() {
|
||||
const queue = new TransactionQueue();
|
||||
|
||||
// Randomized submit indexes
|
||||
[
|
||||
const indexes = [
|
||||
28093,
|
||||
456944,
|
||||
347213,
|
||||
@@ -2025,8 +2048,9 @@ describe('Transaction', function() {
|
||||
925550,
|
||||
872298,
|
||||
543305
|
||||
]
|
||||
.forEach(function(index) {
|
||||
];
|
||||
|
||||
indexes.forEach(function(index) {
|
||||
const tx = new Transaction();
|
||||
tx.initialSubmitIndex = index;
|
||||
queue.push(tx);
|
||||
|
||||
Reference in New Issue
Block a user