mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-12-06 17:27:59 +00:00
feat: Jest Test Runner (#2170)
This commit is contained in:
4
.github/workflows/nodejs.yml
vendored
4
.github/workflows/nodejs.yml
vendored
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x, 18.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x, 18.x]
|
||||
node-version: [14.x, 16.x, 18.x]
|
||||
|
||||
services:
|
||||
rippled:
|
||||
|
||||
4
.ncurc.json
Normal file
4
.ncurc.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"reject": [
|
||||
]
|
||||
}
|
||||
8
.vscode/settings.json
vendored
8
.vscode/settings.json
vendored
@@ -39,4 +39,12 @@
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.hg/store/**": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"**/.git": true,
|
||||
"**/node_modules": true,
|
||||
"**/tmp": true,
|
||||
"**/docs/**/*.html": true,
|
||||
"**/fixtures/**/*.json": true,
|
||||
"**/docs/assets": true
|
||||
},
|
||||
}
|
||||
|
||||
@@ -122,8 +122,6 @@ If your code connects to the ledger (ex. Adding a new transaction type) it's han
|
||||
|
||||
All integration tests should be written in the `test/integration` folder, with new `Requests` and `Transactions` tests being in their respective folders.
|
||||
|
||||
One last note for integration tests is that all imports from `xrpl.js` should be from `xrpl-local` instead of `../../src`. This is required for the integraiton tests to run in the browser.
|
||||
|
||||
For an example of how to write an integration test for `xrpl.js`, you can look at the [Payment integration test](./packages/xrpl/test/integration/transactions/payment.ts).
|
||||
|
||||
## Generate reference docs
|
||||
|
||||
@@ -25,7 +25,7 @@ All of which works in Node.js (tested for v14+) & web browsers (tested for Chrom
|
||||
|
||||
### Requirements
|
||||
|
||||
- **[Node.js v14](https://nodejs.org/)** is recommended. We also support v12 and v16. Other versions may work but are not frequently tested.
|
||||
+ **[Node.js v14](https://nodejs.org/)** is recommended. We also support v16 and v18. Other versions may work but are not frequently tested.
|
||||
|
||||
### Installing xrpl.js
|
||||
|
||||
|
||||
20
jest.config.base.js
Normal file
20
jest.config.base.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { TextDecoder, TextEncoder } = require("util");
|
||||
|
||||
module.exports = {
|
||||
roots: ["<rootDir>/src"],
|
||||
transform: {
|
||||
"^.+\\.ts$": "ts-jest",
|
||||
},
|
||||
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
|
||||
collectCoverage: true,
|
||||
verbose: true,
|
||||
testEnvironment: "node",
|
||||
globals: {
|
||||
TextDecoder: TextDecoder,
|
||||
TextEncoder: TextEncoder,
|
||||
error: console.error,
|
||||
warn: console.warn,
|
||||
info: console.info,
|
||||
debug: console.debug,
|
||||
},
|
||||
};
|
||||
8
jest.config.js
Normal file
8
jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const path = require("path");
|
||||
const base = require("./jest.config.base.js");
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
projects: ["<rootDir>/packages/**/jest.config.js"],
|
||||
coverageDirectory: "<rootDir>/coverage/",
|
||||
};
|
||||
17071
package-lock.json
generated
17071
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -8,7 +8,9 @@
|
||||
"lint": "lerna run lint --stream",
|
||||
"clean": "lerna run clean --stream",
|
||||
"build": "lerna run build --stream",
|
||||
"docgen": "lerna run docgen --stream"
|
||||
"docgen": "lerna run docgen --stream",
|
||||
"update:check": "npx npm-check-updates --configFileName .ncurc.json",
|
||||
"update:confirm": "npx npm-check-updates --configFileName .ncurc.json -u"
|
||||
},
|
||||
"dependencies": {
|
||||
"ripple-address-codec": "file:packages/ripple-address-codec",
|
||||
@@ -17,11 +19,12 @@
|
||||
"xrpl": "file:packages/xrpl"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/brorand": "^1.0.30",
|
||||
"@types/chai": "^4.2.21",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/jest": "^29.2.2",
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^17.0.14",
|
||||
"@types/node": "^14.18.35",
|
||||
"@types/puppeteer": "5.4.6",
|
||||
"@types/ws": "^8.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
||||
@@ -40,15 +43,15 @@
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.24.1",
|
||||
"eslint-plugin-jsdoc": "^37.1.0",
|
||||
"eslint-plugin-mocha": "^10.0.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.14",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"expect": "^29.3.1",
|
||||
"https-browserify": "^1.0.0",
|
||||
"jest": "^26.0.1",
|
||||
"jest": "^29.3.1",
|
||||
"jest-mock": "^29.3.1",
|
||||
"lerna": "^4.0.0",
|
||||
"mocha": "^10",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^15",
|
||||
"path-browserify": "1.0.1",
|
||||
@@ -59,7 +62,7 @@
|
||||
"source-map-support": "^0.5.16",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"stream-http": "3.2.0",
|
||||
"ts-jest": "^26.4.4",
|
||||
"ts-jest": "^29.0.3",
|
||||
"ts-loader": "^9.2.5",
|
||||
"ts-node": "^10.2.1",
|
||||
"typescript": "^4.4.2",
|
||||
@@ -69,7 +72,7 @@
|
||||
"webpack-cli": "^4.2.0"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
"./packages/*"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
|
||||
@@ -40,5 +40,6 @@ module.exports = {
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'jsdoc/require-param': 'off',
|
||||
'tsdoc/syntax': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# ripple-address-codec
|
||||
|
||||
## Unreleased
|
||||
### Changed
|
||||
- All tests now use the Jest test runner and have been refactored for consistency across all packages
|
||||
|
||||
## 4.2.4 (2022-04-21)
|
||||
### Fixed
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// Jest configuration for api
|
||||
const base = require('../../jest.config.base.js')
|
||||
|
||||
module.exports = {
|
||||
"roots": [
|
||||
"<rootDir>/src"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.tsx?$": "ts-jest"
|
||||
},
|
||||
}
|
||||
...base,
|
||||
displayName: 'ripple-address-codec',
|
||||
}
|
||||
|
||||
245
packages/ripple-address-codec/package-lock.json
generated
Normal file
245
packages/ripple-address-codec/package-lock.json
generated
Normal file
@@ -0,0 +1,245 @@
|
||||
{
|
||||
"name": "ripple-address-codec",
|
||||
"version": "4.2.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ripple-address-codec",
|
||||
"version": "4.2.4",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"base-x": "^3.0.9",
|
||||
"create-hash": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/base-x": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
"md5.js": "^1.3.4",
|
||||
"ripemd160": "^2.0.1",
|
||||
"sha.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hash-base": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
"safe-buffer": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/ripemd160": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"sha.js": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
|
||||
"integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"cipher-base": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
|
||||
"integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"requires": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
"md5.js": "^1.3.4",
|
||||
"ripemd160": "^2.0.1",
|
||||
"sha.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"hash-base": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
|
||||
"integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^3.6.0",
|
||||
"safe-buffer": "^5.2.0"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"ripemd160": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
|
||||
"integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||
},
|
||||
"sha.js": {
|
||||
"version": "2.4.11",
|
||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
|
||||
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"requires": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"base-x": "3.0.9",
|
||||
"base-x": "^3.0.9",
|
||||
"create-hash": "^1.1.2"
|
||||
},
|
||||
"repository": {
|
||||
@@ -21,7 +21,7 @@
|
||||
"prepublishOnly": "tslint -b ./ && jest",
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"test": "jest",
|
||||
"test": "jest --verbose false --silent=false ./src/*.test.js",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@ const {
|
||||
classicAddressToXAddress,
|
||||
xAddressToClassicAddress,
|
||||
isValidXAddress,
|
||||
encodeXAddress
|
||||
encodeXAddress,
|
||||
} = require('./index')
|
||||
|
||||
const testCases = [
|
||||
@@ -10,137 +10,137 @@ const testCases = [
|
||||
'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
false,
|
||||
'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCqkr7wxCcNcfZ6p5GZ'
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCqkr7wxCcNcfZ6p5GZ',
|
||||
],
|
||||
[
|
||||
'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
1,
|
||||
'X7AcgcsBL6XDcUb289X4mJ8djcdyKaGZMhc9YTE92ehJ2Fu',
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCvbJNZbi37gBGkRkbE'
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCvbJNZbi37gBGkRkbE',
|
||||
],
|
||||
[
|
||||
'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
14,
|
||||
'X7AcgcsBL6XDcUb289X4mJ8djcdyKaGo2K5VpXpmCqbV2gS',
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCvqXVCALUGJGSbNV3x'
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCvqXVCALUGJGSbNV3x',
|
||||
],
|
||||
[
|
||||
'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
11747,
|
||||
'X7AcgcsBL6XDcUb289X4mJ8djcdyKaLFuhLRuNXPrDeJd9A',
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCziiNHtUukubF2Mg6t'
|
||||
'T719a5UwUCnEs54UsxG9CJYYDhwmFCziiNHtUukubF2Mg6t',
|
||||
],
|
||||
[
|
||||
'rLczgQHxPhWtjkaQqn3Q6UM8AbRbbRvs5K',
|
||||
false,
|
||||
'XVZVpQj8YSVpNyiwXYSqvQoQqgBttTxAZwMcuJd4xteQHyt',
|
||||
'TVVrSWtmQQssgVcmoMBcFQZKKf56QscyWLKnUyiuZW8ALU4'
|
||||
'TVVrSWtmQQssgVcmoMBcFQZKKf56QscyWLKnUyiuZW8ALU4',
|
||||
],
|
||||
[
|
||||
'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
|
||||
false,
|
||||
'X7YenJqxv3L66CwhBSfd3N8RzGXxYqPopMGMsCcpho79rex',
|
||||
'T77wVQzA8ntj9wvCTNiQpNYLT5hmhRsFyXDoMLqYC4BzQtV'
|
||||
'T77wVQzA8ntj9wvCTNiQpNYLT5hmhRsFyXDoMLqYC4BzQtV',
|
||||
],
|
||||
[
|
||||
'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
|
||||
58,
|
||||
'X7YenJqxv3L66CwhBSfd3N8RzGXxYqV56ZkTCa9UCzgaao1',
|
||||
'T77wVQzA8ntj9wvCTNiQpNYLT5hmhR9kej6uxm4jGcQD7rZ'
|
||||
'T77wVQzA8ntj9wvCTNiQpNYLT5hmhR9kej6uxm4jGcQD7rZ',
|
||||
],
|
||||
[
|
||||
'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
23480,
|
||||
'X7d3eHCXzwBeWrZec1yT24iZerQjYL8m8zCJ16ACxu1BrBY',
|
||||
'T7YChPFWifjCAXLEtg5N74c7fSAYsvSokwcmBPBUZWhxH5P'
|
||||
'T7YChPFWifjCAXLEtg5N74c7fSAYsvSokwcmBPBUZWhxH5P',
|
||||
],
|
||||
[
|
||||
'rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW',
|
||||
11747,
|
||||
'X7d3eHCXzwBeWrZec1yT24iZerQjYLo2CJf8oVC5CMWey5m',
|
||||
'T7YChPFWifjCAXLEtg5N74c7fSAYsvTcc7nEfwuEEvn5Q4w'
|
||||
'T7YChPFWifjCAXLEtg5N74c7fSAYsvTcc7nEfwuEEvn5Q4w',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
false,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQn49b3qD26PK7FcGSKE'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQn49b3qD26PK7FcGSKE',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
0,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV8AqEL4xcZj5whKbmc',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSy8RHqGHoGJ59spi2'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSy8RHqGHoGJ59spi2',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
1,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV8xvjGQTYPiAx6gwDC',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSz1uDimDdPYXzSpyw'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnSz1uDimDdPYXzSpyw',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
2,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV8zpDURx7DzBCkrQE7',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTryP9tG9TW8GeMBmd'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTryP9tG9TW8GeMBmd',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
32,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtVoYiC9UvKfjKar4LJe',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnT2oqaCDzMEuCDAj1j'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnT2oqaCDzMEuCDAj1j',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
276,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtVoKj3MnFGMXEFMnvJV',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTMgJJYfAbsiPsc6Zg'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnTMgJJYfAbsiPsc6Zg',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
65591,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtVozpjdhPQVdt3ghaWw',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQn7ryu2W6njw7mT1jmS'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQn7ryu2W6njw7mT1jmS',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
16781933,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtVqrDUk2vDpkTjPsY73',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnVsw45sDtGHhLi27Qa'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnVsw45sDtGHhLi27Qa',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
4294967294,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV1kAsixQTdMjbWi39u',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnX8tDFQ53itLNqs6vU'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnX8tDFQ53itLNqs6vU',
|
||||
],
|
||||
[
|
||||
'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
4294967295,
|
||||
'XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi',
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnXoy6kSDh6rZzApc69'
|
||||
'TVE26TYGhfLC7tQDno7G8dGtxSkYQnXoy6kSDh6rZzApc69',
|
||||
],
|
||||
[
|
||||
'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
|
||||
false,
|
||||
'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2gYsjNFQLKYW33DzBm',
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwLZzuUG5rZnaewsahi'
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwLZzuUG5rZnaewsahi',
|
||||
],
|
||||
[
|
||||
'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
|
||||
0,
|
||||
'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2m4Er6SnvjVLpMWPjR',
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwRQUBetPbyrvXSTuxU'
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwRQUBetPbyrvXSTuxU',
|
||||
],
|
||||
[
|
||||
'rPEPPER7kfTD9w2To4CQk6UCfuHM9c6GDY',
|
||||
13371337,
|
||||
'XV5sbjUmgPpvXv4ixFWZ5ptAYZ6PD2qwGkhgc48zzcx6Gkr',
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwVUDvp3vhpXbNhLwJi'
|
||||
]
|
||||
'TVd2rqMkYL2AyS97NdELcpeiprNBjwVUDvp3vhpXbNhLwJi',
|
||||
],
|
||||
]
|
||||
|
||||
;[false, true].forEach(isTestAddress => {
|
||||
;[false, true].forEach((isTestAddress) => {
|
||||
const MAX_32_BIT_UNSIGNED_INT = 4294967295
|
||||
const network = isTestAddress ? ' (test)' : ' (main)'
|
||||
|
||||
@@ -149,13 +149,17 @@ const testCases = [
|
||||
const classicAddress = testCase[0]
|
||||
const tag = testCase[1] !== false ? testCase[1] : false
|
||||
const xAddress = isTestAddress ? testCase[3] : testCase[2]
|
||||
test(`Converts ${classicAddress}${tag ? ':' + tag : ''} to ${xAddress}${network}`, () => {
|
||||
expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(xAddress)
|
||||
test(`Converts ${classicAddress}${
|
||||
tag ? `:${tag}` : ''
|
||||
} to ${xAddress}${network}`, () => {
|
||||
expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(
|
||||
xAddress,
|
||||
)
|
||||
const myClassicAddress = xAddressToClassicAddress(xAddress)
|
||||
expect(myClassicAddress).toEqual({
|
||||
classicAddress,
|
||||
tag,
|
||||
test: isTestAddress
|
||||
test: isTestAddress,
|
||||
})
|
||||
expect(isValidXAddress(xAddress)).toBe(true)
|
||||
})
|
||||
@@ -177,23 +181,28 @@ const testCases = [
|
||||
test(`Invalid classic address: Converting ${classicAddress}${network} throws`, () => {
|
||||
expect(() => {
|
||||
classicAddressToXAddress(classicAddress, false, isTestAddress)
|
||||
}).toThrowError(new Error('invalid_input_size: decoded data must have length >= 5'))
|
||||
}).toThrowError(
|
||||
new Error('invalid_input_size: decoded data must have length >= 5'),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
{
|
||||
const highAndLowAccounts = [
|
||||
Buffer.from('00'.repeat(20), 'hex'),
|
||||
Buffer.from('00'.repeat(19) + '01', 'hex'),
|
||||
Buffer.from(`${'00'.repeat(19)}01`, 'hex'),
|
||||
Buffer.from('01'.repeat(20), 'hex'),
|
||||
Buffer.from('FF'.repeat(20), 'hex')
|
||||
Buffer.from('FF'.repeat(20), 'hex'),
|
||||
]
|
||||
|
||||
highAndLowAccounts.forEach(accountId => {
|
||||
[false, 0, 1, MAX_32_BIT_UNSIGNED_INT].forEach(t => {
|
||||
const tag = (t | false)
|
||||
highAndLowAccounts.forEach((accountId) => {
|
||||
const testCases = [false, 0, 1, MAX_32_BIT_UNSIGNED_INT]
|
||||
testCases.forEach((t) => {
|
||||
const tag = t | false
|
||||
const xAddress = encodeXAddress(accountId, tag, isTestAddress)
|
||||
test(`Encoding ${accountId.toString('hex')}${tag ? ':' + tag : ''} to ${xAddress} has expected length`, () => {
|
||||
test(`Encoding ${accountId.toString('hex')}${
|
||||
tag ? `:${tag}` : ''
|
||||
} to ${xAddress} has expected length`, () => {
|
||||
expect(xAddress.length).toBe(47)
|
||||
})
|
||||
})
|
||||
@@ -237,7 +246,9 @@ test(`Invalid Account ID throws`, () => {
|
||||
})
|
||||
|
||||
test(`isValidXAddress returns false for invalid X-address`, () => {
|
||||
expect(isValidXAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8zeUygYrCgrPh')).toBe(false)
|
||||
expect(
|
||||
isValidXAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8zeUygYrCgrPh'),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
test(`Converts X7AcgcsBL6XDcUb... to r9cZA1mLK5R5A... and tag: false`, () => {
|
||||
@@ -245,12 +256,14 @@ test(`Converts X7AcgcsBL6XDcUb... to r9cZA1mLK5R5A... and tag: false`, () => {
|
||||
const tag = false
|
||||
const xAddress = 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ'
|
||||
const isTestAddress = false
|
||||
expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(xAddress)
|
||||
expect(classicAddressToXAddress(classicAddress, tag, isTestAddress)).toBe(
|
||||
xAddress,
|
||||
)
|
||||
const myClassicAddress = xAddressToClassicAddress(xAddress)
|
||||
expect(myClassicAddress).toEqual({
|
||||
classicAddress,
|
||||
tag,
|
||||
test: isTestAddress
|
||||
test: isTestAddress,
|
||||
})
|
||||
expect(isValidXAddress(xAddress)).toBe(true)
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ function isBufferForTestAddress(buf: Buffer): boolean {
|
||||
if (PREFIX_BYTES.test.equals(decodedPrefix)) {
|
||||
return true
|
||||
}
|
||||
|
||||
throw new Error('Invalid X-address: bad prefix')
|
||||
}
|
||||
|
||||
|
||||
@@ -17,28 +17,38 @@ function toBytes(hex) {
|
||||
* @param hex Hexadecimal representation of expected decoded data
|
||||
*/
|
||||
function makeEncodeDecodeTest(encoder, decoder, base58, hex) {
|
||||
test(`can translate between ${hex} and ${base58}`, function() {
|
||||
test(`can translate between ${hex} and ${base58}`, function () {
|
||||
const actual = encoder(toBytes(hex))
|
||||
expect(actual).toBe(base58)
|
||||
})
|
||||
test(`can translate between ${base58} and ${hex})`, function() {
|
||||
test(`can translate between ${base58} and ${hex})`, function () {
|
||||
const buf = decoder(base58)
|
||||
expect(toHex(buf)).toBe(hex)
|
||||
})
|
||||
}
|
||||
|
||||
makeEncodeDecodeTest(api.encodeAccountID, api.decodeAccountID, 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072')
|
||||
makeEncodeDecodeTest(
|
||||
api.encodeAccountID,
|
||||
api.decodeAccountID,
|
||||
'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072',
|
||||
)
|
||||
|
||||
makeEncodeDecodeTest(api.encodeNodePublic, api.decodeNodePublic,
|
||||
makeEncodeDecodeTest(
|
||||
api.encodeNodePublic,
|
||||
api.decodeNodePublic,
|
||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828',
|
||||
)
|
||||
|
||||
makeEncodeDecodeTest(api.encodeAccountPublic, api.decodeAccountPublic,
|
||||
'aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3',
|
||||
'023693F15967AE357D0327974AD46FE3C127113B1110D6044FD41E723689F81CC6')
|
||||
makeEncodeDecodeTest(
|
||||
api.encodeAccountPublic,
|
||||
api.decodeAccountPublic,
|
||||
'aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3',
|
||||
'023693F15967AE357D0327974AD46FE3C127113B1110D6044FD41E723689F81CC6',
|
||||
)
|
||||
|
||||
test('can decode arbitrary seeds', function() {
|
||||
test('can decode arbitrary seeds', function () {
|
||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
expect(toHex(decoded.bytes)).toBe('4C3A1D213FBDFB14C7C28D609469B341')
|
||||
expect(decoded.type).toBe('ed25519')
|
||||
@@ -48,7 +58,7 @@ test('can decode arbitrary seeds', function() {
|
||||
expect(decoded2.type).toBe('secp256k1')
|
||||
})
|
||||
|
||||
test('can pass a type as second arg to encodeSeed', function() {
|
||||
test('can pass a type as second arg to encodeSeed', function () {
|
||||
const edSeed = 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2'
|
||||
const decoded = api.decodeSeed(edSeed)
|
||||
const type = 'ed25519'
|
||||
@@ -57,125 +67,161 @@ test('can pass a type as second arg to encodeSeed', function() {
|
||||
expect(api.encodeSeed(decoded.bytes, type)).toBe(edSeed)
|
||||
})
|
||||
|
||||
test('isValidClassicAddress - secp256k1 address valid', function() {
|
||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw1')).toBe(true)
|
||||
test('isValidClassicAddress - secp256k1 address valid', function () {
|
||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw1')).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test('isValidClassicAddress - ed25519 address valid', function() {
|
||||
expect(api.isValidClassicAddress('rLUEXYuLiQptky37CqLcm9USQpPiz5rkpD')).toBe(true)
|
||||
test('isValidClassicAddress - ed25519 address valid', function () {
|
||||
expect(api.isValidClassicAddress('rLUEXYuLiQptky37CqLcm9USQpPiz5rkpD')).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test('isValidClassicAddress - invalid', function() {
|
||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw2')).toBe(false)
|
||||
test('isValidClassicAddress - invalid', function () {
|
||||
expect(api.isValidClassicAddress('rU6K7V3Po4snVhBBaU29sesqs2qTQJWDw2')).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
test('isValidClassicAddress - empty', function() {
|
||||
test('isValidClassicAddress - empty', function () {
|
||||
expect(api.isValidClassicAddress('')).toBe(false)
|
||||
})
|
||||
|
||||
describe('encodeSeed', function() {
|
||||
|
||||
it('encodes a secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFF', 'hex'), 'secp256k1')
|
||||
describe('encodeSeed', function () {
|
||||
it('encodes a secp256k1 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFF', 'hex'),
|
||||
'secp256k1',
|
||||
)
|
||||
expect(result).toBe('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
})
|
||||
|
||||
it('encodes low secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('00000000000000000000000000000000', 'hex'), 'secp256k1')
|
||||
it('encodes low secp256k1 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('00000000000000000000000000000000', 'hex'),
|
||||
'secp256k1',
|
||||
)
|
||||
expect(result).toBe('sp6JS7f14BuwFY8Mw6bTtLKWauoUs')
|
||||
})
|
||||
|
||||
it('encodes high secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'), 'secp256k1')
|
||||
it('encodes high secp256k1 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'),
|
||||
'secp256k1',
|
||||
)
|
||||
expect(result).toBe('saGwBRReqUNKuWNLpUAq8i8NkXEPN')
|
||||
})
|
||||
|
||||
it('encodes an ed25519 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('4C3A1D213FBDFB14C7C28D609469B341', 'hex'), 'ed25519')
|
||||
it('encodes an ed25519 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('4C3A1D213FBDFB14C7C28D609469B341', 'hex'),
|
||||
'ed25519',
|
||||
)
|
||||
expect(result).toBe('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
})
|
||||
|
||||
it('encodes low ed25519 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('00000000000000000000000000000000', 'hex'), 'ed25519')
|
||||
it('encodes low ed25519 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('00000000000000000000000000000000', 'hex'),
|
||||
'ed25519',
|
||||
)
|
||||
expect(result).toBe('sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE')
|
||||
})
|
||||
|
||||
it('encodes high ed25519 seed', function() {
|
||||
const result = api.encodeSeed(Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'), 'ed25519')
|
||||
it('encodes high ed25519 seed', function () {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex'),
|
||||
'ed25519',
|
||||
)
|
||||
expect(result).toBe('sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG')
|
||||
})
|
||||
|
||||
test('attempting to encode a seed with less than 16 bytes of entropy throws', function() {
|
||||
test('attempting to encode a seed with less than 16 bytes of entropy throws', function () {
|
||||
expect(() => {
|
||||
api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7B', 'hex'), 'secp256k1')
|
||||
api.encodeSeed(
|
||||
Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7B', 'hex'),
|
||||
'secp256k1',
|
||||
)
|
||||
}).toThrow('entropy must have length 16')
|
||||
})
|
||||
|
||||
test('attempting to encode a seed with more than 16 bytes of entropy throws', function() {
|
||||
test('attempting to encode a seed with more than 16 bytes of entropy throws', function () {
|
||||
expect(() => {
|
||||
api.encodeSeed(Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFFFF', 'hex'), 'secp256k1')
|
||||
api.encodeSeed(
|
||||
Buffer.from('CF2DE378FBDD7E2EE87D486DFB5A7BFFFF', 'hex'),
|
||||
'secp256k1',
|
||||
)
|
||||
}).toThrow('entropy must have length 16')
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeSeed', function() {
|
||||
|
||||
it('can decode an Ed25519 seed', function() {
|
||||
describe('decodeSeed', function () {
|
||||
it('can decode an Ed25519 seed', function () {
|
||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
expect(toHex(decoded.bytes)).toBe('4C3A1D213FBDFB14C7C28D609469B341')
|
||||
expect(decoded.type).toBe('ed25519')
|
||||
})
|
||||
|
||||
it('can decode a secp256k1 seed', function() {
|
||||
it('can decode a secp256k1 seed', function () {
|
||||
const decoded = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
expect(toHex(decoded.bytes)).toBe('CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
||||
expect(decoded.type).toBe('secp256k1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('encodeAccountID', function() {
|
||||
|
||||
it('can encode an AccountID', function() {
|
||||
const encoded = api.encodeAccountID(Buffer.from('BA8E78626EE42C41B46D46C3048DF3A1C3C87072', 'hex'))
|
||||
describe('encodeAccountID', function () {
|
||||
it('can encode an AccountID', function () {
|
||||
const encoded = api.encodeAccountID(
|
||||
Buffer.from('BA8E78626EE42C41B46D46C3048DF3A1C3C87072', 'hex'),
|
||||
)
|
||||
expect(encoded).toBe('rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN')
|
||||
})
|
||||
|
||||
test('unexpected length should throw', function() {
|
||||
test('unexpected length should throw', function () {
|
||||
expect(() => {
|
||||
api.encodeAccountID(Buffer.from('ABCDEF', 'hex'))
|
||||
}).toThrow(
|
||||
'unexpected_payload_length: bytes.length does not match expectedLength'
|
||||
'unexpected_payload_length: bytes.length does not match expectedLength',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeNodePublic', function() {
|
||||
|
||||
it('can decode a NodePublic', function() {
|
||||
const decoded = api.decodeNodePublic('n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH')
|
||||
expect(toHex(decoded)).toBe('0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
||||
describe('decodeNodePublic', function () {
|
||||
it('can decode a NodePublic', function () {
|
||||
const decoded = api.decodeNodePublic(
|
||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
||||
)
|
||||
expect(toHex(decoded)).toBe(
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
test('encodes 123456789 with version byte of 0', () => {
|
||||
expect(api.codec.encode(Buffer.from('123456789'), {
|
||||
versions: [0],
|
||||
expectedLength: 9
|
||||
})).toBe('rnaC7gW34M77Kneb78s')
|
||||
expect(
|
||||
api.codec.encode(Buffer.from('123456789'), {
|
||||
versions: [0],
|
||||
expectedLength: 9,
|
||||
}),
|
||||
).toBe('rnaC7gW34M77Kneb78s')
|
||||
})
|
||||
|
||||
test('multiple versions with no expected length should throw', () => {
|
||||
expect(() => {
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0, 1]
|
||||
versions: [0, 1],
|
||||
})
|
||||
}).toThrow('expectedLength is required because there are >= 2 possible versions')
|
||||
}).toThrow(
|
||||
'expectedLength is required because there are >= 2 possible versions',
|
||||
)
|
||||
})
|
||||
|
||||
test('attempting to decode data with length < 5 should throw', () => {
|
||||
expect(() => {
|
||||
api.codec.decode('1234', {
|
||||
versions: [0]
|
||||
versions: [0],
|
||||
})
|
||||
}).toThrow('invalid_input_size: decoded data must have length >= 5')
|
||||
})
|
||||
@@ -183,15 +229,17 @@ test('attempting to decode data with length < 5 should throw', () => {
|
||||
test('attempting to decode data with unexpected version should throw', () => {
|
||||
expect(() => {
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [2]
|
||||
versions: [2],
|
||||
})
|
||||
}).toThrow('version_invalid: version bytes do not match any of the provided version(s)')
|
||||
}).toThrow(
|
||||
'version_invalid: version bytes do not match any of the provided version(s)',
|
||||
)
|
||||
})
|
||||
|
||||
test('invalid checksum should throw', () => {
|
||||
expect(() => {
|
||||
api.codec.decode('123456789', {
|
||||
versions: [0, 1]
|
||||
versions: [0, 1],
|
||||
})
|
||||
}).toThrow('checksum_invalid')
|
||||
})
|
||||
@@ -199,48 +247,51 @@ test('invalid checksum should throw', () => {
|
||||
test('empty payload should throw', () => {
|
||||
expect(() => {
|
||||
api.codec.decode('', {
|
||||
versions: [0, 1]
|
||||
versions: [0, 1],
|
||||
})
|
||||
}).toThrow('invalid_input_size: decoded data must have length >= 5')
|
||||
})
|
||||
|
||||
test('decode data', () => {
|
||||
expect(api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0]
|
||||
})).toStrictEqual({
|
||||
expect(
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0],
|
||||
}),
|
||||
).toStrictEqual({
|
||||
version: [0],
|
||||
bytes: Buffer.from('123456789'),
|
||||
type: null
|
||||
type: null,
|
||||
})
|
||||
})
|
||||
|
||||
test('decode data with expected length', function() {
|
||||
expect(api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
test('decode data with expected length', function () {
|
||||
expect(
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0],
|
||||
expectedLength: 9
|
||||
})
|
||||
).toStrictEqual({
|
||||
version: [0],
|
||||
bytes: Buffer.from('123456789'),
|
||||
type: null
|
||||
})
|
||||
expectedLength: 9,
|
||||
}),
|
||||
).toStrictEqual({
|
||||
version: [0],
|
||||
bytes: Buffer.from('123456789'),
|
||||
type: null,
|
||||
})
|
||||
})
|
||||
|
||||
test('decode data with wrong expected length should throw', function() {
|
||||
test('decode data with wrong expected length should throw', function () {
|
||||
expect(() => {
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0],
|
||||
expectedLength: 8
|
||||
expectedLength: 8,
|
||||
})
|
||||
}).toThrow(
|
||||
'version_invalid: version bytes do not match any of the provided version(s)'
|
||||
'version_invalid: version bytes do not match any of the provided version(s)',
|
||||
)
|
||||
expect(() => {
|
||||
api.codec.decode('rnaC7gW34M77Kneb78s', {
|
||||
versions: [0],
|
||||
expectedLength: 10
|
||||
expectedLength: 10,
|
||||
})
|
||||
}).toThrow(
|
||||
'version_invalid: version bytes do not match any of the provided version(s)'
|
||||
'version_invalid: version bytes do not match any of the provided version(s)',
|
||||
)
|
||||
})
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
* Codec class
|
||||
*/
|
||||
|
||||
import * as baseCodec from 'base-x'
|
||||
import * as createHash from 'create-hash'
|
||||
import baseCodec = require('base-x')
|
||||
import type { BaseConverter } from 'base-x'
|
||||
import createHash = require('create-hash')
|
||||
|
||||
import { seqEqual, concatArgs } from './utils'
|
||||
|
||||
class Codec {
|
||||
private readonly _sha256: (bytes: Uint8Array) => Buffer
|
||||
private readonly _alphabet: string
|
||||
private readonly _codec: baseCodec.BaseConverter
|
||||
private readonly _codec: BaseConverter
|
||||
|
||||
public constructor(options: {
|
||||
sha256: (bytes: Uint8Array) => Buffer
|
||||
@@ -56,7 +57,7 @@ class Codec {
|
||||
): {
|
||||
version: number[]
|
||||
bytes: Buffer
|
||||
type: string | null
|
||||
type: 'ed25519' | 'secp256k1' | null
|
||||
} {
|
||||
const versions = opts.versions
|
||||
const types = opts.versionTypes
|
||||
@@ -204,7 +205,7 @@ export function decodeSeed(
|
||||
): {
|
||||
version: number[]
|
||||
bytes: Buffer
|
||||
type: string | null
|
||||
type: 'ed25519' | 'secp256k1' | null
|
||||
} {
|
||||
return codecWithXrpAlphabet.decode(seed, opts)
|
||||
}
|
||||
|
||||
4
packages/ripple-address-codec/tsconfig.eslint.json
Normal file
4
packages/ripple-address-codec/tsconfig.eslint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts", "src/*.test.ts"]
|
||||
}
|
||||
@@ -13,7 +13,8 @@
|
||||
"preserveConstEnums": false,
|
||||
"suppressImplicitAnyIndexErrors": false,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true
|
||||
"declaration": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
|
||||
ignorePatterns: ['jest.config.js', '.eslintrc.js'],
|
||||
|
||||
// Specify global variables that are predefined
|
||||
env: {
|
||||
browser: true, // Enable browser global variables
|
||||
@@ -103,19 +105,19 @@ module.exports = {
|
||||
'max-lines-per-function': 'off',
|
||||
'require-unicode-regexp': 'off',
|
||||
'no-undef-init': 'off',
|
||||
'curly': 'off',
|
||||
'eqeqeq': 'off',
|
||||
curly: 'off',
|
||||
eqeqeq: 'off',
|
||||
'no-console': 'off',
|
||||
'max-classes-per-file': 'off',
|
||||
'operator-assignment': 'off',
|
||||
'class-methods-use-this': 'off',
|
||||
'no-else-return': 'off',
|
||||
'yoda': 'off',
|
||||
yoda: 'off',
|
||||
'max-depth': 'off',
|
||||
'multiline-comment-style': 'off',
|
||||
'one-var': 'off',
|
||||
'no-negated-condition': 'off',
|
||||
'radix': 'off',
|
||||
radix: 'off',
|
||||
'no-nested-ternary': 'off',
|
||||
'no-useless-concat': 'off',
|
||||
'object-shorthand': 'off',
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
10.22.0
|
||||
@@ -1,6 +1,8 @@
|
||||
# ripple-binary-codec Release History
|
||||
|
||||
## Unreleased
|
||||
### Changed
|
||||
- All tests now use the Jest test runner and have been refactored for consistency across all packages
|
||||
|
||||
## 1.4.2 (2022-06-27)
|
||||
- Fixed standard currency codes with lowercase and allowed symbols not decoding into standard codes.
|
||||
|
||||
8
packages/ripple-binary-codec/jest.config.js
Normal file
8
packages/ripple-binary-codec/jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// Jest configuration for api
|
||||
const base = require('../../jest.config.base.js')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
roots: [...base.roots, '<rootDir>/test'],
|
||||
displayName: 'ripple-binary-codec',
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
"build": "tsc -b && copyfiles ./src/enums/definitions.json ./dist/enums/",
|
||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo",
|
||||
"prepare": "npm run build && npm test",
|
||||
"test": "jest",
|
||||
"test": "jest --verbose false --silent=false ./test/*.test.js",
|
||||
"lint": "eslint . --ext .ts --ext .test.js"
|
||||
},
|
||||
"repository": {
|
||||
@@ -38,6 +38,6 @@
|
||||
"readmeFilename": "README.md",
|
||||
"prettier": "@xrplf/prettier-config",
|
||||
"engines": {
|
||||
"node": ">=10.22.0"
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { FieldInstance } from './enums'
|
||||
import { STObject } from './types/st-object'
|
||||
import { JsonObject } from './types/serialized-type'
|
||||
import { Buffer } from 'buffer/'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
|
||||
/**
|
||||
* Construct a BinaryParser
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HashPrefix } from './hash-prefixes'
|
||||
import * as createHash from 'create-hash'
|
||||
import createHash = require('create-hash')
|
||||
import { Hash256 } from './types/hash-256'
|
||||
import { BytesList } from './serdes/binary-serializer'
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
@@ -102,7 +102,7 @@ function decodeQuality(value: string): string {
|
||||
return quality.decode(value).toString()
|
||||
}
|
||||
|
||||
export = {
|
||||
export {
|
||||
decode,
|
||||
encode,
|
||||
encodeForSigning,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { UInt32 } from './types/uint-32'
|
||||
import { UInt8 } from './types/uint-8'
|
||||
import { BinaryParser } from './serdes/binary-parser'
|
||||
import { JsonObject } from './types/serialized-type'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
|
||||
/**
|
||||
* Computes the hash of a list of objects
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { coreTypes } from './types'
|
||||
import { Decimal } from 'decimal.js'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@ import { BinaryParser } from '../serdes/binary-parser'
|
||||
import { AccountID } from './account-id'
|
||||
import { Currency } from './currency'
|
||||
import { JsonObject, SerializedType } from './serialized-type'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BytesList } from '../serdes/binary-serializer'
|
||||
import { BinaryParser } from '../serdes/binary-parser'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
type JSON = string | number | boolean | null | undefined | JSON[] | JsonObject
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UInt } from './uint'
|
||||
import { BinaryParser } from '../serdes/binary-parser'
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
import { isInstance } from 'big-integer'
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as bigInt from 'big-integer'
|
||||
import bigInt = require('big-integer')
|
||||
import { Comparable } from './serialized-type'
|
||||
import { Buffer } from 'buffer/'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { loadFixture } = require('./utils')
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { Amount } = coreTypes
|
||||
const fixtures = loadFixture('data-driven-tests.json')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const fixtures = require('./fixtures/codec-fixtures.json')
|
||||
const { decode, encode, decodeLedgerData } = require('../dist')
|
||||
const { decode, encode, decodeLedgerData } = require('../src')
|
||||
|
||||
function json(object) {
|
||||
return JSON.stringify(object)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const Decimal = require('decimal.js')
|
||||
|
||||
const { encodeAccountID } = require('ripple-address-codec')
|
||||
const { binary } = require('../dist/coretypes')
|
||||
const { binary } = require('../src/coretypes')
|
||||
const { Amount, Hash160 } = coreTypes
|
||||
const { makeParser, readJSON } = binary
|
||||
const { Field, TransactionType } = require('./../dist/enums')
|
||||
const { Field, TransactionType } = require('./../src/enums')
|
||||
const { parseHexOnly, hexOnly, loadFixture } = require('./utils')
|
||||
const fixtures = loadFixture('data-driven-tests.json')
|
||||
const { BytesList } = require('../dist/serdes/binary-serializer')
|
||||
const { BytesList } = require('../src/serdes/binary-serializer')
|
||||
const { Buffer } = require('buffer/')
|
||||
|
||||
const __ = hexOnly
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { binary } = require('../dist/coretypes')
|
||||
const { encode, decode } = require('../dist')
|
||||
const { binary } = require('../src/coretypes')
|
||||
const { encode, decode } = require('../src')
|
||||
const { makeParser, BytesList, BinarySerializer } = binary
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { UInt8, UInt16, UInt32, UInt64, STObject } = coreTypes
|
||||
const bigInt = require('big-integer')
|
||||
const { Buffer } = require('buffer/')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { Hash128, Hash160, Hash256, AccountID, Currency } = coreTypes
|
||||
const { Buffer } = require('buffer/')
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const {
|
||||
transactionTreeHash,
|
||||
ledgerHash,
|
||||
accountStateHash,
|
||||
} = require('../dist/ledger-hashes')
|
||||
} = require('../src/ledger-hashes')
|
||||
|
||||
describe('Ledger Hashes', function () {
|
||||
function testFactory(ledgerFixture) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { encode, decode } = require('../dist')
|
||||
const { encode, decode } = require('../src')
|
||||
|
||||
let str =
|
||||
'1100612200000000240000000125000068652D0000000055B6632D6376A2D9319F20A1C6DCCB486432D1E4A79951229D4C3DE2946F51D56662400009184E72A00081140DD319918CD5AE792BF7EC80D63B0F01B4573BBC'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { encode, decode } = require('../dist')
|
||||
const { encode, decode } = require('../src')
|
||||
|
||||
let json = {
|
||||
Account: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { quality } = require('../dist/coretypes')
|
||||
const { quality } = require('../src/coretypes')
|
||||
|
||||
describe('Quality encode/decode', function () {
|
||||
const bookDirectory =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { ShaMap } = require('../dist/shamap.js')
|
||||
const { binary, HashPrefix } = require('../dist/coretypes')
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { ShaMap } = require('../src/shamap')
|
||||
const { binary, HashPrefix } = require('../src/coretypes')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { loadFixture } = require('./utils')
|
||||
const { Buffer } = require('buffer/')
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const {
|
||||
encodeForSigning,
|
||||
encodeForSigningClaim,
|
||||
encodeForMultisigning,
|
||||
} = require('../dist')
|
||||
} = require('../src')
|
||||
|
||||
const tx_json = {
|
||||
Account: 'r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { encode, decode } = require('../dist')
|
||||
const { encode, decode } = require('../src')
|
||||
|
||||
// Notice: no Amount or Fee
|
||||
const tx_json = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { SerializedType } = require('../dist/types/serialized-type')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { SerializedType } = require('../src/types/serialized-type')
|
||||
|
||||
describe('SerializedType interfaces', () => {
|
||||
Object.entries(coreTypes).forEach(([name, Value]) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { coreTypes } = require('../dist/types')
|
||||
const { coreTypes } = require('../src/types')
|
||||
const { UInt8, UInt64 } = coreTypes
|
||||
|
||||
const { encode } = require('../dist')
|
||||
const { encode } = require('../src')
|
||||
|
||||
const binary =
|
||||
'11007222000300003700000000000000003800000000000000006280000000000000000000000000000000000000005553440000000000000000000000000000000000000000000000000166D5438D7EA4C680000000000000000000000000005553440000000000AE123A8556F3CF91154711376AFB0F894F832B3D67D5438D7EA4C680000000000000000000000000005553440000000000F51DFC2A09D62CBBA1DFBDD4691DAC96AD98B90F'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { encode, decode } = require('./../dist/index')
|
||||
const { encode, decode } = require('./../src/index')
|
||||
const fixtures = require('./fixtures/x-codec-fixtures.json')
|
||||
|
||||
let json_x1 = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"lib": [
|
||||
"es2017"
|
||||
],
|
||||
|
||||
@@ -5,7 +5,7 @@ module.exports = {
|
||||
parserOptions: {
|
||||
// Enable linting rules with type information from our tsconfig
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
project: ['./tsconfig.json', './tsconfig.eslint.json'],
|
||||
|
||||
sourceType: 'module', // Allow the use of imports / ES modules
|
||||
|
||||
@@ -19,11 +19,11 @@ module.exports = {
|
||||
browser: true, // Enable browser global variables
|
||||
node: true, // Enable node global variables & Node.js scoping
|
||||
es2020: true, // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
|
||||
mocha: true, // Add Mocha testing global variables
|
||||
jest: true, // Add Jest testing global variables
|
||||
},
|
||||
|
||||
plugins: [],
|
||||
extends: ['@xrplf/eslint-config/base', 'plugin:mocha/recommended'],
|
||||
extends: ['@xrplf/eslint-config/base'],
|
||||
|
||||
rules: {
|
||||
// ** TODO **
|
||||
@@ -56,5 +56,6 @@ module.exports = {
|
||||
'eslint-comments/require-description': 'off',
|
||||
'no-shadow': 'off',
|
||||
'multiline-comment-style': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# ripple-keypairs Release History
|
||||
|
||||
## Unreleased
|
||||
### Changed
|
||||
- All tests now use the Jest test runner and have been refactored for consistency across all packages
|
||||
|
||||
## 1.1.4 (2022-05-02)
|
||||
- `hexToBytes` now produces empty output for empty input, rather than `[0]`.
|
||||
|
||||
8
packages/ripple-keypairs/jest.config.js
Normal file
8
packages/ripple-keypairs/jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// Jest configuration for api
|
||||
const base = require('../../jest.config.base.js')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
roots: [...base.roots, '<rootDir>/test'],
|
||||
displayName: 'ripple-keypairs',
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Cryptographic key pairs for the XRP Ledger",
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"test": "tsc -b && nyc mocha",
|
||||
"test": "jest --verbose false --silent=false ./test/*.test.ts",
|
||||
"clean": "rm -rf ./dist && rm -rf tsconfig.tsbuildinfo",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"prepublish": "npm run lint && npm test"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable no-bitwise --
|
||||
* lots of bitwise operators necessary for this */
|
||||
import * as hashjs from 'hash.js'
|
||||
import * as BigNum from 'bn.js'
|
||||
import BigNum = require('bn.js')
|
||||
|
||||
export default class Sha512 {
|
||||
// TODO: type of `hash`?
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as assert from 'assert'
|
||||
import * as brorand from 'brorand'
|
||||
import brorand = require('brorand')
|
||||
import * as hashjs from 'hash.js'
|
||||
import * as elliptic from 'elliptic'
|
||||
|
||||
@@ -157,7 +157,7 @@ function deriveNodeAddress(publicKey): string {
|
||||
|
||||
const { decodeSeed } = addressCodec
|
||||
|
||||
export = {
|
||||
export {
|
||||
generateSeed,
|
||||
deriveKeypair,
|
||||
sign,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as assert from 'assert'
|
||||
import * as hashjs from 'hash.js'
|
||||
import * as BN from 'bn.js'
|
||||
import BN = require('bn.js')
|
||||
|
||||
function bytesToHex(a: Iterable<number> | ArrayLike<number>): string {
|
||||
return Array.from(a, (byteValue) => {
|
||||
|
||||
@@ -1,33 +1,36 @@
|
||||
'use strict' // eslint-disable-line strict
|
||||
import assert from 'assert'
|
||||
import fixtures from './fixtures/api.json'
|
||||
import * as api from '../src'
|
||||
|
||||
const assert = require('assert')
|
||||
const fixtures = require('./fixtures/api.json')
|
||||
const api = require('../dist')
|
||||
const decodeSeed = api.decodeSeed
|
||||
const entropy = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
|
||||
const entropy = new Uint8Array([
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
])
|
||||
|
||||
describe('api', () => {
|
||||
it('generateSeed - secp256k1', () => {
|
||||
assert.strictEqual(api.generateSeed({entropy}), fixtures.secp256k1.seed)
|
||||
assert.strictEqual(api.generateSeed({ entropy }), fixtures.secp256k1.seed)
|
||||
})
|
||||
|
||||
it('generateSeed - secp256k1, random', () => {
|
||||
const seed = api.generateSeed()
|
||||
assert(seed.charAt(0) === 's')
|
||||
const {type, bytes} = decodeSeed(seed)
|
||||
assert(seed.startsWith('s'))
|
||||
const { type, bytes } = decodeSeed(seed)
|
||||
assert(type === 'secp256k1')
|
||||
assert(bytes.length === 16)
|
||||
})
|
||||
|
||||
it('generateSeed - ed25519', () => {
|
||||
assert.strictEqual(api.generateSeed({entropy, algorithm: 'ed25519'}),
|
||||
fixtures.ed25519.seed)
|
||||
assert.strictEqual(
|
||||
api.generateSeed({ entropy, algorithm: 'ed25519' }),
|
||||
fixtures.ed25519.seed,
|
||||
)
|
||||
})
|
||||
|
||||
it('generateSeed - ed25519, random', () => {
|
||||
const seed = api.generateSeed({algorithm: 'ed25519'})
|
||||
assert(seed.slice(0, 3) === 'sEd')
|
||||
const {type, bytes} = decodeSeed(seed)
|
||||
const seed = api.generateSeed({ algorithm: 'ed25519' })
|
||||
assert(seed.startsWith('sEd'))
|
||||
const { type, bytes } = decodeSeed(seed)
|
||||
assert(type === 'ed25519')
|
||||
assert(bytes.length === 16)
|
||||
})
|
||||
@@ -43,12 +46,16 @@ describe('api', () => {
|
||||
})
|
||||
|
||||
it('deriveKeypair - secp256k1 - validator', () => {
|
||||
const keypair = api.deriveKeypair(fixtures.secp256k1.seed, {validator: true})
|
||||
const keypair = api.deriveKeypair(fixtures.secp256k1.seed, {
|
||||
validator: true,
|
||||
})
|
||||
assert.deepEqual(keypair, fixtures.secp256k1.validatorKeypair)
|
||||
})
|
||||
|
||||
it('deriveKeypair - ed25519 - validator', () => {
|
||||
const keypair = api.deriveKeypair(fixtures.ed25519.seed, {validator: true})
|
||||
const keypair = api.deriveKeypair(fixtures.ed25519.seed, {
|
||||
validator: true,
|
||||
})
|
||||
assert.deepEqual(keypair, fixtures.ed25519.validatorKeypair)
|
||||
})
|
||||
|
||||
@@ -65,7 +72,7 @@ describe('api', () => {
|
||||
it('sign - secp256k1', () => {
|
||||
const privateKey = fixtures.secp256k1.keypair.privateKey
|
||||
const message = fixtures.secp256k1.message
|
||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
||||
const messageHex = Buffer.from(message, 'utf8').toString('hex')
|
||||
const signature = api.sign(messageHex, privateKey)
|
||||
assert.strictEqual(signature, fixtures.secp256k1.signature)
|
||||
})
|
||||
@@ -74,14 +81,14 @@ describe('api', () => {
|
||||
const signature = fixtures.secp256k1.signature
|
||||
const publicKey = fixtures.secp256k1.keypair.publicKey
|
||||
const message = fixtures.secp256k1.message
|
||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
||||
const messageHex = Buffer.from(message, 'utf8').toString('hex')
|
||||
assert(api.verify(messageHex, signature, publicKey))
|
||||
})
|
||||
|
||||
it('sign - ed25519', () => {
|
||||
const privateKey = fixtures.ed25519.keypair.privateKey
|
||||
const message = fixtures.ed25519.message
|
||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
||||
const messageHex = Buffer.from(message, 'utf8').toString('hex')
|
||||
const signature = api.sign(messageHex, privateKey)
|
||||
assert.strictEqual(signature, fixtures.ed25519.signature)
|
||||
})
|
||||
@@ -90,20 +97,20 @@ describe('api', () => {
|
||||
const signature = fixtures.ed25519.signature
|
||||
const publicKey = fixtures.ed25519.keypair.publicKey
|
||||
const message = fixtures.ed25519.message
|
||||
const messageHex = (Buffer.from(message, 'utf8')).toString('hex')
|
||||
const messageHex = Buffer.from(message, 'utf8').toString('hex')
|
||||
assert(api.verify(messageHex, signature, publicKey))
|
||||
})
|
||||
|
||||
it('deriveNodeAddress', () => {
|
||||
const x = 'n9KHn8NfbBsZV5q8bLfS72XyGqwFt5mgoPbcTV4c6qKiuPTAtXYk'
|
||||
const y = 'rU7bM9ENDkybaxNrefAVjdLTyNLuue1KaJ'
|
||||
assert.strictEqual(api.deriveNodeAddress(x), y)
|
||||
const addrX = 'n9KHn8NfbBsZV5q8bLfS72XyGqwFt5mgoPbcTV4c6qKiuPTAtXYk'
|
||||
const addrY = 'rU7bM9ENDkybaxNrefAVjdLTyNLuue1KaJ'
|
||||
assert.strictEqual(api.deriveNodeAddress(addrX), addrY)
|
||||
})
|
||||
|
||||
it('Random Address', () => {
|
||||
const seed = api.generateSeed()
|
||||
const keypair = api.deriveKeypair(seed)
|
||||
const address = api.deriveAddress(keypair.publicKey)
|
||||
assert(address[0] === 'r')
|
||||
assert(address.startsWith('r'))
|
||||
})
|
||||
})
|
||||
@@ -1,9 +1,5 @@
|
||||
/* eslint-disable no-unused-expressions/no-unused-expressions */
|
||||
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const api = require('ripple-address-codec')
|
||||
import assert from 'assert'
|
||||
import * as api from 'ripple-address-codec'
|
||||
|
||||
function toHex(bytes) {
|
||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
||||
@@ -13,27 +9,31 @@ function toBytes(hex) {
|
||||
return Buffer.from(hex, 'hex').toJSON().data
|
||||
}
|
||||
|
||||
describe('ripple-address-codec', function() {
|
||||
describe('ripple-address-codec', function () {
|
||||
function makeTest(type, base58, hex) {
|
||||
it('can translate between ' + hex + ' and ' + base58 + ' (encode ' + type + ')', function() {
|
||||
const actual = api['encode' + type](toBytes(hex))
|
||||
it(`can translate between ${hex} and ${base58} (encode ${type})`, () => {
|
||||
const actual = api[`encode${type}`](toBytes(hex))
|
||||
assert.equal(actual, base58)
|
||||
})
|
||||
it('can translate between ' + base58 + ' and ' + hex + ' (decode ' + type + ')', function() {
|
||||
const buf = api['decode' + type](base58)
|
||||
it(`can translate between ${base58} and ${hex} (decode ${type})`, () => {
|
||||
const buf = api[`decode${type}`](base58)
|
||||
assert.equal(toHex(buf), hex)
|
||||
})
|
||||
}
|
||||
|
||||
makeTest('AccountID', 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072')
|
||||
makeTest(
|
||||
'AccountID',
|
||||
'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN',
|
||||
'BA8E78626EE42C41B46D46C3048DF3A1C3C87072',
|
||||
)
|
||||
|
||||
makeTest(
|
||||
'NodePublic',
|
||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828',
|
||||
)
|
||||
|
||||
it('can decode arbitrary seeds', function() {
|
||||
it('can decode arbitrary seeds', () => {
|
||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
||||
assert.equal(decoded.type, 'ed25519')
|
||||
@@ -43,11 +43,16 @@ describe('ripple-address-codec', function() {
|
||||
assert.equal(decoded2.type, 'secp256k1')
|
||||
})
|
||||
|
||||
it('can pass a type as second arg to encodeSeed', function() {
|
||||
it('can pass a type as second arg to encodeSeed', () => {
|
||||
const edSeed = 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2'
|
||||
const decoded = api.decodeSeed(edSeed)
|
||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
||||
assert.equal(decoded.type, 'ed25519')
|
||||
if (decoded.type === null) {
|
||||
assert.fail('decoded.type should not be null')
|
||||
}
|
||||
assert.equal(api.encodeSeed(decoded.bytes, decoded.type), edSeed)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
@@ -1,9 +1,7 @@
|
||||
'use strict' // eslint-disable-line strict
|
||||
import assert from 'assert'
|
||||
import * as utils from '../src/utils'
|
||||
|
||||
const assert = require('assert')
|
||||
const utils = require('../dist/utils')
|
||||
|
||||
describe('utils', () => {
|
||||
describe('utils', function () {
|
||||
it('hexToBytes - empty', () => {
|
||||
assert.deepEqual(utils.hexToBytes(''), [])
|
||||
})
|
||||
@@ -18,9 +16,14 @@ describe('utils', () => {
|
||||
|
||||
it('bytesToHex - DEADBEEF', () => {
|
||||
assert.deepEqual(utils.bytesToHex([222, 173, 190, 239]), 'DEADBEEF')
|
||||
});
|
||||
})
|
||||
|
||||
it('bytesToHex - DEADBEEF (Uint8Array)', () => {
|
||||
assert.deepEqual(utils.bytesToHex(new Uint8Array([222, 173, 190, 239])), 'DEADBEEF')
|
||||
});
|
||||
assert.deepEqual(
|
||||
utils.bytesToHex(new Uint8Array([222, 173, 190, 239])),
|
||||
'DEADBEEF',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
@@ -1,81 +0,0 @@
|
||||
/* eslint-disable no-unused-expressions/no-unused-expressions */
|
||||
|
||||
'use strict'
|
||||
|
||||
const assert = require('assert')
|
||||
const api = require('ripple-address-codec')
|
||||
|
||||
function toHex(bytes) {
|
||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
||||
}
|
||||
|
||||
function toBytes(hex) {
|
||||
return Buffer.from(hex, 'hex').toJSON().data
|
||||
}
|
||||
|
||||
describe('ripple-address-codec', function() {
|
||||
|
||||
describe('encodeSeed', function() {
|
||||
|
||||
it('encodes a secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('CF2DE378FBDD7E2EE87D486DFB5A7BFF'), 'secp256k1')
|
||||
assert.equal(result, 'sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
})
|
||||
|
||||
it('encodes low secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('00000000000000000000000000000000'), 'secp256k1')
|
||||
assert.equal(result, 'sp6JS7f14BuwFY8Mw6bTtLKWauoUs')
|
||||
})
|
||||
|
||||
it('encodes high secp256k1 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 'secp256k1')
|
||||
assert.equal(result, 'saGwBRReqUNKuWNLpUAq8i8NkXEPN')
|
||||
})
|
||||
|
||||
it('encodes an ed25519 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('4C3A1D213FBDFB14C7C28D609469B341'), 'ed25519')
|
||||
assert.equal(result, 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
})
|
||||
|
||||
it('encodes low ed25519 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('00000000000000000000000000000000'), 'ed25519')
|
||||
assert.equal(result, 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE')
|
||||
})
|
||||
|
||||
it('encodes high ed25519 seed', function() {
|
||||
const result = api.encodeSeed(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 'ed25519')
|
||||
assert.equal(result, 'sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG')
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeSeed', function() {
|
||||
|
||||
it('can decode an Ed25519 seed', function() {
|
||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
||||
assert.equal(decoded.type, 'ed25519')
|
||||
})
|
||||
|
||||
it('can decode a secp256k1 seed', function() {
|
||||
const decoded = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
assert.equal(toHex(decoded.bytes), 'CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
||||
assert.equal(decoded.type, 'secp256k1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('encodeAccountID', function() {
|
||||
|
||||
it('can encode an AccountID', function() {
|
||||
const encoded = api.encodeAccountID(toBytes('BA8E78626EE42C41B46D46C3048DF3A1C3C87072'))
|
||||
assert.equal(encoded, 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN')
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeNodePublic', function() {
|
||||
|
||||
it('can decode a NodePublic', function() {
|
||||
const decoded = api.decodeNodePublic('n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH')
|
||||
assert.equal(toHex(decoded), '0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828')
|
||||
})
|
||||
})
|
||||
})
|
||||
99
packages/ripple-keypairs/test/xrp-codec.test.ts
Normal file
99
packages/ripple-keypairs/test/xrp-codec.test.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import assert from 'assert'
|
||||
import * as api from 'ripple-address-codec'
|
||||
|
||||
function toHex(bytes: Buffer) {
|
||||
return Buffer.from(bytes).toString('hex').toUpperCase()
|
||||
}
|
||||
|
||||
function toBytes(hex: string) {
|
||||
return Buffer.from(hex, 'hex').toJSON().data
|
||||
}
|
||||
|
||||
describe('ripple-address-codec', function () {
|
||||
describe('encodeSeed', function () {
|
||||
it('encodes a secp256k1 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('CF2DE378FBDD7E2EE87D486DFB5A7BFF')),
|
||||
'secp256k1',
|
||||
)
|
||||
assert.equal(result, 'sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
})
|
||||
|
||||
it('encodes low secp256k1 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('00000000000000000000000000000000')),
|
||||
'secp256k1',
|
||||
)
|
||||
assert.equal(result, 'sp6JS7f14BuwFY8Mw6bTtLKWauoUs')
|
||||
})
|
||||
|
||||
it('encodes high secp256k1 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')),
|
||||
'secp256k1',
|
||||
)
|
||||
assert.equal(result, 'saGwBRReqUNKuWNLpUAq8i8NkXEPN')
|
||||
})
|
||||
|
||||
it('encodes an ed25519 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('4C3A1D213FBDFB14C7C28D609469B341')),
|
||||
'ed25519',
|
||||
)
|
||||
assert.equal(result, 'sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
})
|
||||
|
||||
it('encodes low ed25519 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('00000000000000000000000000000000')),
|
||||
'ed25519',
|
||||
)
|
||||
assert.equal(result, 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE')
|
||||
})
|
||||
|
||||
it('encodes high ed25519 seed', () => {
|
||||
const result = api.encodeSeed(
|
||||
Buffer.from(toBytes('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF')),
|
||||
'ed25519',
|
||||
)
|
||||
assert.equal(result, 'sEdV19BLfeQeKdEXyYA4NhjPJe6XBfG')
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeSeed', function () {
|
||||
it('can decode an Ed25519 seed', () => {
|
||||
const decoded = api.decodeSeed('sEdTM1uX8pu2do5XvTnutH6HsouMaM2')
|
||||
assert.equal(toHex(decoded.bytes), '4C3A1D213FBDFB14C7C28D609469B341')
|
||||
assert.equal(decoded.type, 'ed25519')
|
||||
})
|
||||
|
||||
it('can decode a secp256k1 seed', () => {
|
||||
const decoded = api.decodeSeed('sn259rEFXrQrWyx3Q7XneWcwV6dfL')
|
||||
assert.equal(toHex(decoded.bytes), 'CF2DE378FBDD7E2EE87D486DFB5A7BFF')
|
||||
assert.equal(decoded.type, 'secp256k1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('encodeAccountID', function () {
|
||||
it('can encode an AccountID', () => {
|
||||
const encoded = api.encodeAccountID(
|
||||
Buffer.from(toBytes('BA8E78626EE42C41B46D46C3048DF3A1C3C87072')),
|
||||
)
|
||||
assert.equal(encoded, 'rJrRMgiRgrU6hDF4pgu5DXQdWyPbY35ErN')
|
||||
})
|
||||
})
|
||||
|
||||
describe('decodeNodePublic', function () {
|
||||
it('can decode a NodePublic', () => {
|
||||
const decoded = api.decodeNodePublic(
|
||||
'n9MXXueo837zYH36DvMc13BwHcqtfAWNJY5czWVbp7uYTj7x17TH',
|
||||
)
|
||||
assert.equal(
|
||||
toHex(decoded),
|
||||
'0388E5BA87A000CB807240DF8C848EB0B5FFA5C8E5A521BC8E105C0F0A44217828',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
export {}
|
||||
4
packages/ripple-keypairs/tsconfig.eslint.json
Normal file
4
packages/ripple-keypairs/tsconfig.eslint.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"target": "es6",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "./dist",
|
||||
@@ -11,7 +11,9 @@
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"references": [{
|
||||
"path": "../ripple-address-codec/tsconfig.json"
|
||||
|
||||
@@ -6,7 +6,12 @@ module.exports = {
|
||||
parserOptions: {
|
||||
// Enable linting rules with type information from our tsconfig
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.eslint.json'],
|
||||
project: [
|
||||
'./tsconfig.eslint.json',
|
||||
'../ripple-binary-codec/tsconfig.eslint.json',
|
||||
'../ripple-address-codec/tsconfig.eslint.json',
|
||||
'../ripple-keypairs/tsconfig.eslint.json',
|
||||
],
|
||||
|
||||
// Allow the use of imports / ES modules
|
||||
sourceType: 'module',
|
||||
@@ -23,11 +28,13 @@ module.exports = {
|
||||
node: true,
|
||||
// Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
|
||||
es2020: true,
|
||||
jest: true,
|
||||
},
|
||||
|
||||
plugins: [],
|
||||
extends: ['@xrplf/eslint-config/base', 'plugin:mocha/recommended'],
|
||||
extends: ['@xrplf/eslint-config/base'],
|
||||
rules: {
|
||||
'multiline-comment-style': 'off',
|
||||
// Disabled until https://github.com/import-js/eslint-plugin-import/pull/2305 is resolved to
|
||||
// accomodate this change https://github.com/XRPLF/xrpl.js/pull/2133
|
||||
'import/no-unused-modules': 'off',
|
||||
@@ -51,7 +58,6 @@ module.exports = {
|
||||
// no-shadow has false-positives for enum, @typescript-eslint version fixes that
|
||||
'no-shadow': 'off',
|
||||
'@typescript-eslint/no-shadow': ['error'],
|
||||
'multiline-comment-style': ['error', 'starred-block'],
|
||||
'jsdoc/check-examples': 'off',
|
||||
|
||||
'tsdoc/syntax': 'off',
|
||||
@@ -74,13 +80,16 @@ module.exports = {
|
||||
'max-statements': 'off',
|
||||
// Snippets have logs on console to better understand the working.
|
||||
'no-console': 'off',
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
rules: {
|
||||
// Because this project is managed by lerna, dev dependencies are
|
||||
// hoisted and do not appear in the package.json.
|
||||
/*
|
||||
* Because this project is managed by lerna, dev dependencies are
|
||||
* hoisted and do not appear in the package.json.
|
||||
*/
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
'node/no-extraneous-import': 'off',
|
||||
|
||||
@@ -102,19 +111,12 @@ module.exports = {
|
||||
// Tests are already in 2 callbacks, so max 3 is pretty restrictive
|
||||
'max-nested-callbacks': 'off',
|
||||
|
||||
// setup/teardown client is easier to do in before/after, even if there is only one testcase
|
||||
'mocha/no-hooks-for-single-case': 'off',
|
||||
|
||||
// messes with fixtures
|
||||
'consistent-default-export-name/default-import-match-filename': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/client/*.ts'],
|
||||
rules: {
|
||||
// Rule does not work with dynamically generated tests.
|
||||
'mocha/no-setup-in-describe': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['test/models/*.ts'],
|
||||
|
||||
@@ -4,11 +4,16 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
|
||||
## Unreleased
|
||||
|
||||
### Fixed
|
||||
* Code splitting improvements for lodash
|
||||
* Fixed missing reason code in websocket implemntation on websocket disconnect
|
||||
* Fix timeout error in request manager
|
||||
* Improved typescript typing
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
### Changed
|
||||
* All tests now use the Jest test runner and have been refactored for consistency across all packages
|
||||
|
||||
### Deprecated
|
||||
Wallet.fromMmnemonic()
|
||||
|
||||
8
packages/xrpl/jest.config.js
Normal file
8
packages/xrpl/jest.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// Jest configuration for api
|
||||
const base = require('../../jest.config.base.js')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
roots: [...base.roots, '<rootDir>/test'],
|
||||
displayName: 'xrpl.js',
|
||||
}
|
||||
14
packages/xrpl/karma-setup.js
Normal file
14
packages/xrpl/karma-setup.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// the jest.fn() API
|
||||
import * as jest from 'jest-mock'
|
||||
// The matchers API
|
||||
import expect from 'expect'
|
||||
|
||||
// Add missing Jest functions
|
||||
window.test = window.it
|
||||
window.test.each = (inputs) => (testName, test) =>
|
||||
inputs.forEach((args) => window.it(testName, () => test(...args)))
|
||||
window.test.todo = function () {
|
||||
return undefined
|
||||
}
|
||||
window.jest = jest
|
||||
window.expect = expect
|
||||
34
packages/xrpl/karma.config.js
Normal file
34
packages/xrpl/karma.config.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const webpackConfig = require('./test/webpack.config')[0]()
|
||||
delete webpackConfig.entry
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
plugins: ['karma-webpack', 'karma-jasmine', 'karma-chrome-launcher'],
|
||||
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
webpack: webpackConfig,
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
'build/xrpl-latest.js',
|
||||
'test/integration/index.ts',
|
||||
'karma-setup.js',
|
||||
],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
'karma-setup.js': ['webpack'],
|
||||
// Use webpack to bundle our test files
|
||||
'test/integration/index.ts': ['webpack'],
|
||||
},
|
||||
|
||||
browsers: ['ChromeHeadless'],
|
||||
})
|
||||
}
|
||||
2214
packages/xrpl/package-lock.json
generated
2214
packages/xrpl/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -33,9 +33,19 @@
|
||||
"ws": "^8.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@geut/browser-node-core": "^2.0.13",
|
||||
"@types/node": "^14.18.36",
|
||||
"assert-browserify": "^2.0.0",
|
||||
"browserify-fs": "^1.0.0",
|
||||
"constants-browserify": "^1.0.0",
|
||||
"https-browserify": "^1.0.0",
|
||||
"karma": "^6.4.1",
|
||||
"karma-chrome-launcher": "^3.1.1",
|
||||
"karma-jasmine": "^5.1.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"node-polyfill-webpack-plugin": "^2.0.1",
|
||||
"react": "^18.2.0",
|
||||
"typedoc": "^0.23.24",
|
||||
"xrpl-local": "file:./src"
|
||||
"typedoc": "^0.23.24"
|
||||
},
|
||||
"resolutions": {
|
||||
"elliptic": "^6.5.4"
|
||||
@@ -48,13 +58,13 @@
|
||||
"build:browserTests": "webpack --config ./test/webpack.config.js",
|
||||
"analyze": "run-s build:web --analyze",
|
||||
"watch": "run-s build:lib --watch",
|
||||
"clean": "rm -rf dist",
|
||||
"clean": "rm -rf dist build coverage",
|
||||
"docgen": "tsc --build tsconfig.docs.json && typedoc && echo js.xrpl.org >> ../../docs/CNAME",
|
||||
"prepublish": "run-s clean build",
|
||||
"test": "nyc mocha --config=test/.mocharc.json --exit",
|
||||
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/integration/**/*.test.ts ./test/integration/*.test.ts",
|
||||
"test:browser": "npm run build:browserTests && TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/browser/*.ts",
|
||||
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --config=test/.mocharc.json --watch --reporter dot",
|
||||
"test": "jest --verbose false --silent=false ./test/**/*.test.ts --testPathIgnorePatterns=./test/integration --testPathIgnorePatterns=./test/fixtures",
|
||||
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json jest --verbose false --silent=false --runInBand ./test/integration/**/*.test.ts",
|
||||
"test:browser": "npm run build && npm run build:browserTests && karma start ./karma.config.js --single-run",
|
||||
"test:watch": "jest --watch --verbose false --silent=false --runInBand ./test/**/*.test.ts --testPathIgnorePatterns=./test/integration --testPathIgnorePatterns=./test/fixtures",
|
||||
"format": "prettier --write '{src,test}/**/*.ts'",
|
||||
"lint": "eslint . --ext .ts --max-warnings 0",
|
||||
"perf": "./scripts/perf_test.sh",
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
PaymentChannelCreate,
|
||||
PaymentChannelClaim,
|
||||
hashes,
|
||||
} from '../../dist/npm'
|
||||
} from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, LedgerResponse, TxResponse } from '../../dist/npm'
|
||||
import { Client, LedgerResponse, TxResponse } from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
AccountSet,
|
||||
convertStringToHex,
|
||||
SignerListSet,
|
||||
} from '../../dist/npm'
|
||||
} from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Payment, PaymentFlags, TrustSet } from '../../dist/npm'
|
||||
import { Client, Payment, PaymentFlags, TrustSet } from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Payment, RipplePathFindResponse } from '../../dist/npm'
|
||||
import { Client, Payment, RipplePathFindResponse } from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Payment } from '../../dist/npm'
|
||||
import { Client, Payment } from '../../src'
|
||||
|
||||
/**
|
||||
* When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs.
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
EscrowCreate,
|
||||
EscrowFinish,
|
||||
isoTimeToRippleTime,
|
||||
} from '../../dist/npm'
|
||||
} from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Payment, SetRegularKey } from '../../dist/npm'
|
||||
import { Client, Payment, SetRegularKey } from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src"
|
||||
"rootDir": "../../xrpl"
|
||||
},
|
||||
"include": ["./src/**/*.ts"]
|
||||
"include": ["./src/**/*.ts", "../src/**/*.ts", "../src/**/*.json"]
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Client } from '..'
|
||||
import type { Client } from '../client'
|
||||
import { XRPLFaucetError } from '../errors'
|
||||
|
||||
export interface FaucetWallet {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { request as httpsRequest, RequestOptions } from 'https'
|
||||
|
||||
import { isValidClassicAddress } from 'ripple-address-codec'
|
||||
|
||||
import type { Client } from '..'
|
||||
import type { Client } from '../client'
|
||||
import { RippledError, XRPLFaucetError } from '../errors'
|
||||
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { fromSeed } from 'bip32'
|
||||
import { mnemonicToSeedSync, validateMnemonic } from 'bip39'
|
||||
import _ from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import {
|
||||
classicAddressToXAddress,
|
||||
isValidXAddress,
|
||||
@@ -498,7 +498,7 @@ class Wallet {
|
||||
})
|
||||
/* eslint-enable @typescript-eslint/consistent-type-assertions -- Done with dynamic checking */
|
||||
|
||||
if (!_.isEqual(decoded, txCopy)) {
|
||||
if (!isEqual(decoded, txCopy)) {
|
||||
const data = {
|
||||
decoded,
|
||||
tx,
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class RequestManager {
|
||||
{
|
||||
resolve: (value: Response | PromiseLike<Response>) => void
|
||||
reject: (value: Error) => void
|
||||
timer: NodeJS.Timeout
|
||||
timer: ReturnType<typeof setTimeout>
|
||||
}
|
||||
>()
|
||||
|
||||
@@ -34,7 +34,10 @@ export default class RequestManager {
|
||||
public resolve(id: string | number, response: Response): void {
|
||||
const promise = this.promisesAwaitingResponse.get(id)
|
||||
if (promise == null) {
|
||||
throw new XrplError(`No existing promise with id ${id}`)
|
||||
throw new XrplError(`No existing promise with id ${id}`, {
|
||||
type: 'resolve',
|
||||
response,
|
||||
})
|
||||
}
|
||||
clearTimeout(promise.timer)
|
||||
promise.resolve(response)
|
||||
@@ -51,7 +54,10 @@ export default class RequestManager {
|
||||
public reject(id: string | number, error: Error): void {
|
||||
const promise = this.promisesAwaitingResponse.get(id)
|
||||
if (promise == null) {
|
||||
throw new XrplError(`No existing promise with id ${id}`)
|
||||
throw new XrplError(`No existing promise with id ${id}`, {
|
||||
type: 'reject',
|
||||
error,
|
||||
})
|
||||
}
|
||||
clearTimeout(promise.timer)
|
||||
// TODO: figure out how to have a better stack trace for an error
|
||||
@@ -93,20 +99,35 @@ export default class RequestManager {
|
||||
newId = request.id
|
||||
}
|
||||
const newRequest = JSON.stringify({ ...request, id: newId })
|
||||
const timer = setTimeout(
|
||||
() => this.reject(newId, new TimeoutError()),
|
||||
timeout,
|
||||
)
|
||||
// Typing required for Jest running in browser
|
||||
const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
|
||||
this.reject(
|
||||
newId,
|
||||
new TimeoutError(
|
||||
`Timeout for request: ${JSON.stringify(request)} with id ${newId}`,
|
||||
request,
|
||||
),
|
||||
)
|
||||
}, timeout)
|
||||
/*
|
||||
* Node.js won't exit if a timer is still running, so we tell Node to ignore.
|
||||
* (Node will still wait for the request to complete).
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Reason above.
|
||||
if (timer.unref) {
|
||||
timer.unref()
|
||||
// The following type assertions are required to get this code to pass in browser environments
|
||||
// where setTimeout has a different type
|
||||
// eslint-disable-next-line max-len -- Necessary to disable both rules.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- Reason above.
|
||||
if ((timer as unknown as any).unref) {
|
||||
// eslint-disable-next-line max-len -- Necessary to disable both rules.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call -- Reason above.
|
||||
;(timer as unknown as any).unref()
|
||||
}
|
||||
if (this.promisesAwaitingResponse.has(newId)) {
|
||||
throw new XrplError(`Response with id '${newId}' is already pending`)
|
||||
clearTimeout(timer)
|
||||
throw new XrplError(
|
||||
`Response with id '${newId}' is already pending`,
|
||||
request,
|
||||
)
|
||||
}
|
||||
const newPromise = new Promise<Response>(
|
||||
(resolve: (value: Response | PromiseLike<Response>) => void, reject) => {
|
||||
|
||||
@@ -3,13 +3,13 @@ import { EventEmitter } from 'events'
|
||||
|
||||
// Define the global WebSocket class found on the native browser
|
||||
declare class WebSocket {
|
||||
public onclose?: () => void
|
||||
public onopen?: () => void
|
||||
public onclose?: (closeEvent: CloseEvent) => void
|
||||
public onopen?: (openEvent: Event) => void
|
||||
public onerror?: (error: Error) => void
|
||||
public onmessage?: (message: MessageEvent) => void
|
||||
public readyState: number
|
||||
public constructor(url: string)
|
||||
public close(code?: number): void
|
||||
public close(code?: number, reason?: Buffer): void
|
||||
public send(message: string): void
|
||||
}
|
||||
|
||||
@@ -52,8 +52,13 @@ export default class WSWrapper extends EventEmitter {
|
||||
|
||||
this.ws = new WebSocket(url)
|
||||
|
||||
this.ws.onclose = (): void => {
|
||||
this.emit('close')
|
||||
this.ws.onclose = (closeEvent: CloseEvent): void => {
|
||||
let reason: Uint8Array | undefined
|
||||
if (closeEvent.reason) {
|
||||
const enc = new TextEncoder()
|
||||
reason = enc.encode(closeEvent.reason)
|
||||
}
|
||||
this.emit('close', closeEvent.code, reason)
|
||||
}
|
||||
|
||||
this.ws.onopen = (): void => {
|
||||
@@ -71,10 +76,13 @@ export default class WSWrapper extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Closes the websocket.
|
||||
*
|
||||
* @param code - Close code.
|
||||
* @param reason - Close reason.
|
||||
*/
|
||||
public close(): void {
|
||||
public close(code?: number, reason?: Buffer): void {
|
||||
if (this.readyState === 1) {
|
||||
this.ws.close()
|
||||
this.ws.close(code, reason)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */
|
||||
|
||||
import { EventEmitter } from 'events'
|
||||
import { Agent } from 'http'
|
||||
|
||||
import _ from 'lodash'
|
||||
import omitBy from 'lodash/omitBy'
|
||||
import WebSocket from 'ws'
|
||||
|
||||
import {
|
||||
@@ -63,7 +64,7 @@ function getAgent(url: string, config: ConnectionOptions): Agent | undefined {
|
||||
const parsedURL = new URL(url)
|
||||
const parsedProxyURL = new URL(config.proxy)
|
||||
|
||||
const proxyOptions = _.omitBy(
|
||||
const proxyOptions = omitBy(
|
||||
{
|
||||
secureEndpoint: parsedURL.protocol === 'wss:',
|
||||
secureProxy: parsedProxyURL.protocol === 'https:',
|
||||
@@ -125,7 +126,7 @@ function createWebSocket(
|
||||
Authorization: `Basic ${base64}`,
|
||||
}
|
||||
}
|
||||
const optionsOverrides = _.omitBy(
|
||||
const optionsOverrides = omitBy(
|
||||
{
|
||||
ca: config.trustedCertificates,
|
||||
key: config.key,
|
||||
@@ -175,8 +176,10 @@ async function websocketSendAsync(
|
||||
export class Connection extends EventEmitter {
|
||||
private readonly url: string | undefined
|
||||
private ws: WebSocket | null = null
|
||||
private reconnectTimeoutID: null | NodeJS.Timeout = null
|
||||
private heartbeatIntervalID: null | NodeJS.Timeout = null
|
||||
// Typing necessary for Jest tests running in browser
|
||||
private reconnectTimeoutID: null | ReturnType<typeof setTimeout> = null
|
||||
// Typing necessary for Jest tetsts running in browser
|
||||
private heartbeatIntervalID: null | ReturnType<typeof setTimeout> = null
|
||||
private readonly retryConnectionBackoff = new ExponentialBackoff({
|
||||
min: 100,
|
||||
max: SECONDS_PER_MINUTE * 1000,
|
||||
@@ -224,6 +227,7 @@ export class Connection extends EventEmitter {
|
||||
* @returns When the websocket is connected.
|
||||
* @throws ConnectionError if there is a connection error, RippleError if there is already a WebSocket in existence.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function -- Necessary
|
||||
public async connect(): Promise<void> {
|
||||
if (this.isConnected()) {
|
||||
return Promise.resolve()
|
||||
@@ -245,14 +249,17 @@ export class Connection extends EventEmitter {
|
||||
}
|
||||
|
||||
// Create the connection timeout, in case the connection hangs longer than expected.
|
||||
const connectionTimeoutID = setTimeout(() => {
|
||||
this.onConnectionFailed(
|
||||
new ConnectionError(
|
||||
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
|
||||
`rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
|
||||
),
|
||||
)
|
||||
}, this.config.connectionTimeout)
|
||||
const connectionTimeoutID: ReturnType<typeof setTimeout> = setTimeout(
|
||||
() => {
|
||||
this.onConnectionFailed(
|
||||
new ConnectionError(
|
||||
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
|
||||
`rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
|
||||
),
|
||||
)
|
||||
},
|
||||
this.config.connectionTimeout,
|
||||
)
|
||||
// Connection listeners: these stay attached only until a connection is done/open.
|
||||
this.ws = createWebSocket(this.url, this.config)
|
||||
|
||||
@@ -337,7 +344,7 @@ export class Connection extends EventEmitter {
|
||||
timeout?: number,
|
||||
): Promise<unknown> {
|
||||
if (!this.shouldBeConnected || this.ws == null) {
|
||||
throw new NotConnectedError()
|
||||
throw new NotConnectedError(JSON.stringify(request), request)
|
||||
}
|
||||
const [id, message, responsePromise] = this.requestManager.createRequest(
|
||||
request,
|
||||
@@ -429,7 +436,9 @@ export class Connection extends EventEmitter {
|
||||
* @throws Error if the websocket initialized is somehow null.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function -- Many error code conditionals to check.
|
||||
private async onceOpen(connectionTimeoutID: NodeJS.Timeout): Promise<void> {
|
||||
private async onceOpen(
|
||||
connectionTimeoutID: ReturnType<typeof setTimeout>,
|
||||
): Promise<void> {
|
||||
if (this.ws == null) {
|
||||
throw new XrplError('onceOpen: ws is null')
|
||||
}
|
||||
@@ -458,13 +467,14 @@ export class Connection extends EventEmitter {
|
||||
this.ws = null
|
||||
|
||||
if (code === undefined) {
|
||||
const reasonText = reason ? reason.toString() : 'undefined'
|
||||
// eslint-disable-next-line no-console -- The error is helpful for debugging.
|
||||
console.error(
|
||||
`Disconnected but the disconnect code was undefined (The given reason was ${reasonText}).` +
|
||||
`This could be caused by an exception being thrown during a 'connect' callback. ` +
|
||||
`Disconnecting with code 1011 to indicate an internal error has occurred.`,
|
||||
)
|
||||
// Useful to keep this code for debugging purposes.
|
||||
// const reasonText = reason ? reason.toString() : 'undefined'
|
||||
// // eslint-disable-next-line no-console -- The error is helpful for debugging.
|
||||
// console.error(
|
||||
// `Disconnected but the disconnect code was undefined (The given reason was ${reasonText}).` +
|
||||
// `This could be caused by an exception being thrown during a 'connect' callback. ` +
|
||||
// `Disconnecting with code 1011 to indicate an internal error has occurred.`,
|
||||
// )
|
||||
|
||||
/*
|
||||
* Error code 1011 represents an Internal Error according to
|
||||
|
||||
@@ -454,6 +454,10 @@ class Client extends EventEmitter {
|
||||
event: 'consensusPhase',
|
||||
listener: (phase: ConsensusStream) => void,
|
||||
): this
|
||||
public on(
|
||||
event: 'manifestReceived',
|
||||
listener: (manifest: ManifestResponse) => void,
|
||||
): this
|
||||
public on(event: 'path_find', listener: (path: PathFindStream) => void): this
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
|
||||
public on(event: 'error', listener: (...err: any[]) => void): this
|
||||
|
||||
@@ -108,6 +108,10 @@ export interface ResponseOnlyTxInfo {
|
||||
* The sequence number of the ledger that included this transaction.
|
||||
*/
|
||||
ledger_index?: number
|
||||
/**
|
||||
* @deprecated Alias for ledger_index.
|
||||
*/
|
||||
inLedger?: number
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,7 +49,7 @@ export interface LedgerDataRequest extends BaseRequest {
|
||||
|
||||
type LabeledLedgerEntry = { ledgerEntryType: string } & LedgerEntry
|
||||
|
||||
interface BinaryLedgerEntry {
|
||||
export interface BinaryLedgerEntry {
|
||||
data: string
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,14 @@ export interface JobType {
|
||||
in_progress?: number
|
||||
}
|
||||
|
||||
// The states for validating and proposing do not exist in the field state_accounting
|
||||
// See https://github.com/XRPLF/rippled/blob/develop/src/ripple/app/misc/NetworkOPs.cpp#L4545
|
||||
// https://github.com/XRPLF/rippled/blob/develop/src/ripple/app/misc/NetworkOPs.h#L66
|
||||
export type StateAccountingFinal = Record<
|
||||
Exclude<ServerState, 'validating' | 'proposing'>,
|
||||
StateAccounting
|
||||
>
|
||||
|
||||
/**
|
||||
* Response expected from a {@link ServerInfoRequest}.
|
||||
*
|
||||
@@ -158,6 +166,14 @@ export interface ServerInfoResponse extends BaseResponse {
|
||||
* cost.
|
||||
*/
|
||||
load_factor_server?: number
|
||||
/**
|
||||
* The number of peer connections which were severed.
|
||||
*/
|
||||
peer_disconnects?: string
|
||||
/**
|
||||
* The number of peer connections which were severed due to excess resource consumption.
|
||||
*/
|
||||
peer_disconnects_resources?: string
|
||||
network_ledger?: 'waiting'
|
||||
/** How many other rippled servers this one is currently connected to. */
|
||||
peers: number
|
||||
@@ -179,13 +195,13 @@ export interface ServerInfoResponse extends BaseResponse {
|
||||
* The number of consecutive microseconds the server has been in the
|
||||
* current state.
|
||||
*/
|
||||
server_state_duration_us: number
|
||||
server_state_duration_us: string
|
||||
/**
|
||||
* A map of various server states with information about the time the
|
||||
* server spends in each. This can be useful for tracking the long-term
|
||||
* health of your server's connectivity to the network.
|
||||
*/
|
||||
state_accounting: Record<ServerState, StateAccounting>
|
||||
state_accounting: StateAccountingFinal
|
||||
/** The current time in UTC, according to the server's clock. */
|
||||
time: string
|
||||
/** Number of consecutive seconds that the server has been operational. */
|
||||
@@ -227,6 +243,11 @@ export interface ServerInfoResponse extends BaseResponse {
|
||||
* static validator list.
|
||||
*/
|
||||
validator_list_expires?: string
|
||||
validator_list?: {
|
||||
count: number
|
||||
expiration: 'never' | 'unknown' | string
|
||||
status: 'active' | 'expired' | 'unknown'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseRequest, BaseResponse } from './baseMethod'
|
||||
import { JobType, ServerState, StateAccounting } from './serverInfo'
|
||||
import { JobType, ServerState, StateAccountingFinal } from './serverInfo'
|
||||
|
||||
/**
|
||||
* The `server_state` command asks the server for various machine-readable
|
||||
@@ -35,7 +35,10 @@ export interface ServerStateResponse extends BaseResponse {
|
||||
io_latency_ms: number
|
||||
jq_trans_overflow: string
|
||||
last_close: {
|
||||
converge_time_s: number
|
||||
// coverage_time_s only exists for `server_info` requests. `server_state` is a "non human" api request,
|
||||
// therefore the type is coverage_time
|
||||
// See https://github.com/XRPLF/rippled/blob/83faf43140e27e5d6d6779eaa0ffb75c33d98029/src/ripple/app/misc/NetworkOPs.cpp#L2458
|
||||
converge_time: number
|
||||
proposers: number
|
||||
}
|
||||
load?: {
|
||||
@@ -48,24 +51,27 @@ export interface ServerStateResponse extends BaseResponse {
|
||||
load_factor_fee_queue?: number
|
||||
load_factor_fee_reference?: number
|
||||
load_factor_server?: number
|
||||
peer_disconnects?: string
|
||||
peer_disconnects_resources?: string
|
||||
peers: number
|
||||
pubkey_node: string
|
||||
pubkey_validator?: string
|
||||
server_state: ServerState
|
||||
server_state_duration_us: number
|
||||
state_accounting: Record<ServerState, StateAccounting>
|
||||
server_state_duration_us: string
|
||||
state_accounting: StateAccountingFinal
|
||||
time: string
|
||||
uptime: number
|
||||
validated_ledger?: {
|
||||
age: number
|
||||
age?: number
|
||||
base_fee: number
|
||||
close_time: number
|
||||
hash: string
|
||||
reserve_base: number
|
||||
reserve_inc: number
|
||||
seq: number
|
||||
}
|
||||
validation_quorum: number
|
||||
validator_list_expires?: string
|
||||
validator_list_expires?: number
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,12 +53,14 @@ export function validateCheckCash(tx: Record<string, unknown>): void {
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
|
||||
if (tx.Amount != null && tx.Amount !== undefined && !isAmount(tx.Amount)) {
|
||||
throw new ValidationError('CheckCash: invalid Amount')
|
||||
}
|
||||
|
||||
if (
|
||||
tx.DeliverMin != null &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
|
||||
tx.DeliverMin !== undefined &&
|
||||
!isAmount(tx.DeliverMin)
|
||||
) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/* eslint-disable complexity -- verifies 19 tx types hence a lot of checks needed */
|
||||
/* eslint-disable max-lines-per-function -- need to work with a lot of Tx verifications */
|
||||
|
||||
import _ from 'lodash'
|
||||
import isEqual from 'lodash/isEqual'
|
||||
import omitBy from 'lodash/omitBy'
|
||||
import { encode, decode } from 'ripple-binary-codec'
|
||||
|
||||
import { ValidationError } from '../../errors'
|
||||
@@ -210,9 +211,9 @@ export function validate(transaction: Record<string, unknown>): void {
|
||||
}
|
||||
|
||||
if (
|
||||
!_.isEqual(
|
||||
!isEqual(
|
||||
decode(encode(tx)),
|
||||
_.omitBy(tx, (value) => value == null),
|
||||
omitBy(tx, (value) => value == null),
|
||||
)
|
||||
) {
|
||||
throw new ValidationError(`Invalid Transaction: ${tx.TransactionType}`)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import _ from 'lodash'
|
||||
import flatMap from 'lodash/flatMap'
|
||||
|
||||
import type { Client } from '..'
|
||||
import { LedgerIndex } from '../models/common'
|
||||
@@ -111,7 +111,7 @@ async function getBalances(
|
||||
// combine results
|
||||
await Promise.all([xrpPromise, linesPromise]).then(
|
||||
([xrpBalance, linesResponses]) => {
|
||||
const accountLinesBalance = _.flatMap(linesResponses, (response) =>
|
||||
const accountLinesBalance = flatMap(linesResponses, (response) =>
|
||||
formatBalances(response.result.lines),
|
||||
)
|
||||
if (xrpBalance !== '') {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable max-lines-per-function -- Needs to process orderbooks. */
|
||||
import BigNumber from 'bignumber.js'
|
||||
import _ from 'lodash'
|
||||
import flatMap from 'lodash/flatMap'
|
||||
|
||||
import type { Client } from '../client'
|
||||
import { ValidationError } from '../errors'
|
||||
import { LedgerIndex } from '../models/common'
|
||||
import { OfferFlags } from '../models/ledger/Offer'
|
||||
import {
|
||||
@@ -22,6 +23,13 @@ function sortOffers(offers: BookOffer[]): BookOffer[] {
|
||||
})
|
||||
}
|
||||
|
||||
const getOrderbookOptionsSet = new Set([
|
||||
'limit',
|
||||
'ledger_index',
|
||||
'ledger_hash',
|
||||
'taker',
|
||||
])
|
||||
|
||||
/**
|
||||
* Fetch orderbook (buy/sell orders) between two accounts.
|
||||
*
|
||||
@@ -40,7 +48,7 @@ function sortOffers(offers: BookOffer[]): BookOffer[] {
|
||||
* the order book. Defaults to 20.
|
||||
* @returns An object containing buy and sell objects.
|
||||
*/
|
||||
// eslint-disable-next-line max-params -- Once bound to Client, getOrderbook only has 3 parameters.
|
||||
// eslint-disable-next-line max-params, complexity -- Once bound to Client, getOrderbook only has 3 parameters.
|
||||
async function getOrderbook(
|
||||
this: Client,
|
||||
takerPays: TakerAmount,
|
||||
@@ -48,21 +56,60 @@ async function getOrderbook(
|
||||
options: {
|
||||
limit?: number
|
||||
ledger_index?: LedgerIndex
|
||||
ledger_hash?: string
|
||||
taker?: string
|
||||
ledger_hash?: string | null
|
||||
taker?: string | null
|
||||
} = {},
|
||||
): Promise<{
|
||||
buy: BookOffer[]
|
||||
sell: BookOffer[]
|
||||
}> {
|
||||
Object.keys(options).forEach((key) => {
|
||||
if (!getOrderbookOptionsSet.has(key)) {
|
||||
throw new ValidationError(`Unexpected option: ${key}`, options)
|
||||
}
|
||||
})
|
||||
|
||||
if (options.limit && typeof options.limit !== 'number') {
|
||||
throw new ValidationError('limit must be a number', options.limit)
|
||||
}
|
||||
|
||||
if (
|
||||
options.ledger_index &&
|
||||
!(
|
||||
typeof options.ledger_index === 'number' ||
|
||||
(typeof options.ledger_index === 'string' &&
|
||||
['validated', 'closed', 'current'].includes(options.ledger_index))
|
||||
)
|
||||
) {
|
||||
throw new ValidationError(
|
||||
'ledger_index must be a number or a string of "validated", "closed", or "current"',
|
||||
options.ledger_index,
|
||||
)
|
||||
}
|
||||
|
||||
if (
|
||||
options.ledger_hash !== undefined &&
|
||||
options.ledger_hash !== null &&
|
||||
typeof options.ledger_hash !== 'string'
|
||||
) {
|
||||
throw new ValidationError(
|
||||
'ledger_hash must be a string',
|
||||
options.ledger_hash,
|
||||
)
|
||||
}
|
||||
|
||||
if (options.taker !== undefined && typeof options.taker !== 'string') {
|
||||
throw new ValidationError('taker must be a string', options.taker)
|
||||
}
|
||||
|
||||
const request: BookOffersRequest = {
|
||||
command: 'book_offers',
|
||||
taker_pays: takerPays,
|
||||
taker_gets: takerGets,
|
||||
ledger_index: options.ledger_index ?? 'validated',
|
||||
ledger_hash: options.ledger_hash,
|
||||
ledger_hash: options.ledger_hash === null ? undefined : options.ledger_hash,
|
||||
limit: options.limit ?? DEFAULT_LIMIT,
|
||||
taker: options.taker,
|
||||
taker: options.taker ? options.taker : undefined,
|
||||
}
|
||||
// 2. Make Request
|
||||
const directOfferResults = await this.requestAll(request)
|
||||
@@ -70,11 +117,11 @@ async function getOrderbook(
|
||||
request.taker_pays = takerGets
|
||||
const reverseOfferResults = await this.requestAll(request)
|
||||
// 3. Return Formatted Response
|
||||
const directOffers = _.flatMap(
|
||||
const directOffers = flatMap(
|
||||
directOfferResults,
|
||||
(directOfferResult) => directOfferResult.result.offers,
|
||||
)
|
||||
const reverseOffers = _.flatMap(
|
||||
const reverseOffers = flatMap(
|
||||
reverseOfferResults,
|
||||
(reverseOfferResult) => reverseOfferResult.result.offers,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import _ from 'lodash'
|
||||
import flatten from 'lodash/flatten'
|
||||
import groupBy from 'lodash/groupBy'
|
||||
|
||||
import { Amount, IssuedCurrencyAmount } from '../models/common'
|
||||
import { TransactionMetadata, Node } from '../models/transactions/metadata'
|
||||
@@ -63,7 +64,7 @@ function groupByAccount(balanceChanges: BalanceChange[]): Array<{
|
||||
account: string
|
||||
balances: Balance[]
|
||||
}> {
|
||||
const grouped = _.groupBy(balanceChanges, (node) => node.account)
|
||||
const grouped = groupBy(balanceChanges, (node) => node.account)
|
||||
return Object.entries(grouped).map(([account, items]) => {
|
||||
return { account, balances: items.map((item) => item.balance) }
|
||||
})
|
||||
@@ -186,5 +187,5 @@ export default function getBalanceChanges(
|
||||
}
|
||||
return []
|
||||
})
|
||||
return groupByAccount(_.flatten(quantities))
|
||||
return groupByAccount(flatten(quantities))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import binary from 'ripple-binary-codec'
|
||||
import keypairs from 'ripple-keypairs'
|
||||
import { encodeForSigningClaim } from 'ripple-binary-codec'
|
||||
import { sign } from 'ripple-keypairs'
|
||||
|
||||
import { xrpToDrops } from './xrpConversion'
|
||||
|
||||
@@ -17,11 +17,11 @@ function signPaymentChannelClaim(
|
||||
amount: string,
|
||||
privateKey: string,
|
||||
): string {
|
||||
const signingData = binary.encodeForSigningClaim({
|
||||
const signingData = encodeForSigningClaim({
|
||||
channel,
|
||||
amount: xrpToDrops(amount),
|
||||
})
|
||||
return keypairs.sign(signingData, privateKey)
|
||||
return sign(signingData, privateKey)
|
||||
}
|
||||
|
||||
export default signPaymentChannelClaim
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import binary from 'ripple-binary-codec'
|
||||
import keypairs from 'ripple-keypairs'
|
||||
import { encodeForSigningClaim } from 'ripple-binary-codec'
|
||||
import { verify } from 'ripple-keypairs'
|
||||
|
||||
import { xrpToDrops } from './xrpConversion'
|
||||
|
||||
@@ -20,11 +20,11 @@ function verifyPaymentChannelClaim(
|
||||
signature: string,
|
||||
publicKey: string,
|
||||
): boolean {
|
||||
const signingData = binary.encodeForSigningClaim({
|
||||
const signingData = encodeForSigningClaim({
|
||||
channel,
|
||||
amount: xrpToDrops(amount),
|
||||
})
|
||||
return keypairs.verify(signingData, signature, publicKey)
|
||||
return verify(signingData, signature, publicKey)
|
||||
}
|
||||
|
||||
export default verifyPaymentChannelClaim
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { assert } from 'chai'
|
||||
import ExponentialBackoff from 'xrpl-local/client/ExponentialBackoff'
|
||||
|
||||
import ExponentialBackoff from '../src/client/ExponentialBackoff'
|
||||
|
||||
describe('ExponentialBackoff', function () {
|
||||
it('duration() return value starts with the min value', function () {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user