mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-07 14:25:49 +00:00
initial pass at linting ripple-address-codec (#1798)
* initial pass at linting ripple-address-codec * refix doc changes autofixed by eslint
This commit is contained in:
3053
package-lock.json
generated
3053
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.21",
|
||||
"@types/create-hash": "^1.2.2",
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.4.3",
|
||||
|
||||
@@ -25,22 +25,18 @@ module.exports = {
|
||||
extends: ['@xrplf/eslint-config/base'],
|
||||
|
||||
rules: {
|
||||
// This creates a lot of false positives. We should turn this off in our
|
||||
// general config.
|
||||
'jsdoc/require-description-complete-sentence': 'off',
|
||||
|
||||
// ** TODO **
|
||||
// all of the below are turned off for now during the migration to a
|
||||
// monorepo. They need to actually be addressed!
|
||||
// **
|
||||
'@typescript-eslint/no-for-in-array': 'off',
|
||||
'@typescript-eslint/consistent-type-assertions': 'off',
|
||||
'@typescript-eslint/no-unnecessary-condition': 'off',
|
||||
'@typescript-eslint/prefer-for-of': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-magic-numbers': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
@@ -49,37 +45,11 @@ module.exports = {
|
||||
'@typescript-eslint/prefer-nullish-coalescing': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/array-type': 'off',
|
||||
'@typescript-eslint/restrict-plus-operands': 'off',
|
||||
'@typescript-eslint/no-unnecessary-type-assertion': 'off',
|
||||
'jsdoc/require-returns': 'off',
|
||||
'jsdoc/check-param-names': 'off',
|
||||
'jsdoc/require-throws': 'off',
|
||||
'jsdoc/require-hyphen-before-param-description': 'off',
|
||||
'jsdoc/require-jsdoc': 'off',
|
||||
'jsdoc/require-description-complete-sentence': 'off',
|
||||
'jsdoc/require-param': 'off',
|
||||
'jsdoc/no-types': 'off',
|
||||
'tsdoc/syntax': 'off',
|
||||
'import/no-commonjs': 'off',
|
||||
'import/order': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'guard-for-in': 'off',
|
||||
'object-shorthand': 'off',
|
||||
'no-negated-condition': 'off',
|
||||
'no-loop-func': 'off',
|
||||
'id-length': 'off',
|
||||
'no-inline-comments': 'off',
|
||||
'max-lines-per-function': 'off',
|
||||
'max-len': 'off',
|
||||
'no-nested-ternary': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-bitwise': 'off',
|
||||
'multiline-comment-style': 'off',
|
||||
'id-blacklist': 'off',
|
||||
'func-names': 'off',
|
||||
'max-params': 'off',
|
||||
'prefer-template': 'off',
|
||||
'no-else-return': 'off',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
import {
|
||||
codec,
|
||||
encodeSeed,
|
||||
@@ -10,13 +12,16 @@ import {
|
||||
decodeAccountPublic,
|
||||
isValidClassicAddress,
|
||||
} from './xrp-codec'
|
||||
import * as assert from 'assert'
|
||||
|
||||
const PREFIX_BYTES = {
|
||||
MAIN: Buffer.from([0x05, 0x44]), // 5, 68
|
||||
TEST: Buffer.from([0x04, 0x93]), // 4, 147
|
||||
// 5, 68
|
||||
MAIN: Buffer.from([0x05, 0x44]),
|
||||
// 4, 147
|
||||
TEST: Buffer.from([0x04, 0x93]),
|
||||
}
|
||||
|
||||
const MAX_32_BIT_UNSIGNED_INT = 4294967295
|
||||
|
||||
function classicAddressToXAddress(
|
||||
classicAddress: string,
|
||||
tag: number | false,
|
||||
@@ -35,31 +40,36 @@ function encodeXAddress(
|
||||
// RIPEMD160 is 160 bits = 20 bytes
|
||||
throw new Error('Account ID must be 20 bytes')
|
||||
}
|
||||
const MAX_32_BIT_UNSIGNED_INT = 4294967295
|
||||
const flag = tag === false ? 0 : tag <= MAX_32_BIT_UNSIGNED_INT ? 1 : 2
|
||||
if (flag === 2) {
|
||||
if (tag > MAX_32_BIT_UNSIGNED_INT) {
|
||||
throw new Error('Invalid tag')
|
||||
}
|
||||
if (tag === false) {
|
||||
tag = 0
|
||||
}
|
||||
const theTag = tag === false ? 0 : tag
|
||||
const flag = tag === false ? 0 : 1
|
||||
/* eslint-disable no-bitwise ---
|
||||
* need to use bitwise operations here */
|
||||
const bytes = Buffer.concat([
|
||||
test ? PREFIX_BYTES.TEST : PREFIX_BYTES.MAIN,
|
||||
accountId,
|
||||
Buffer.from([
|
||||
flag, // 0x00 if no tag, 0x01 if 32-bit tag
|
||||
tag & 0xff, // first byte
|
||||
(tag >> 8) & 0xff, // second byte
|
||||
(tag >> 16) & 0xff, // third byte
|
||||
(tag >> 24) & 0xff, // fourth byte
|
||||
// 0x00 if no tag, 0x01 if 32-bit tag
|
||||
flag,
|
||||
// first byte
|
||||
theTag & 0xff,
|
||||
// second byte
|
||||
(theTag >> 8) & 0xff,
|
||||
// third byte
|
||||
(theTag >> 16) & 0xff,
|
||||
// fourth byte
|
||||
(theTag >> 24) & 0xff,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, // four zero bytes (reserved for 64-bit tags)
|
||||
// four zero bytes (reserved for 64-bit tags)
|
||||
0,
|
||||
]),
|
||||
])
|
||||
const xAddress = codec.encodeChecked(bytes)
|
||||
return xAddress
|
||||
/* eslint-enable no-bitwise */
|
||||
return codec.encodeChecked(bytes)
|
||||
}
|
||||
|
||||
function xAddressToClassicAddress(xAddress: string): {
|
||||
@@ -96,11 +106,11 @@ function isBufferForTestAddress(buf: Buffer): boolean {
|
||||
const decodedPrefix = buf.slice(0, 2)
|
||||
if (PREFIX_BYTES.MAIN.equals(decodedPrefix)) {
|
||||
return false
|
||||
} else if (PREFIX_BYTES.TEST.equals(decodedPrefix)) {
|
||||
return true
|
||||
} else {
|
||||
throw new Error('Invalid X-address: bad prefix')
|
||||
}
|
||||
if (PREFIX_BYTES.TEST.equals(decodedPrefix)) {
|
||||
return true
|
||||
}
|
||||
throw new Error('Invalid X-address: bad prefix')
|
||||
}
|
||||
|
||||
function tagFromBuffer(buf: Buffer): number | false {
|
||||
@@ -124,26 +134,41 @@ function tagFromBuffer(buf: Buffer): number | false {
|
||||
function isValidXAddress(xAddress: string): boolean {
|
||||
try {
|
||||
decodeXAddress(xAddress)
|
||||
} catch (e) {
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export {
|
||||
codec, // Codec with XRP alphabet
|
||||
encodeSeed, // Encode entropy as a "seed"
|
||||
decodeSeed, // Decode a seed into an object with its version, type, and bytes
|
||||
encodeAccountID, // Encode bytes as a classic address (r...)
|
||||
decodeAccountID, // Decode a classic address to its raw bytes
|
||||
encodeNodePublic, // Encode bytes to XRP Ledger node public key format
|
||||
decodeNodePublic, // Decode an XRP Ledger node public key into its raw bytes
|
||||
encodeAccountPublic, // Encode a public key, as for payment channels
|
||||
decodeAccountPublic, // Decode a public key, as for payment channels
|
||||
isValidClassicAddress, // Check whether a classic address (r...) is valid
|
||||
classicAddressToXAddress, // Derive X-address from classic address, tag, and network ID
|
||||
encodeXAddress, // Encode account ID, tag, and network ID to X-address
|
||||
xAddressToClassicAddress, // Decode X-address to account ID, tag, and network ID
|
||||
decodeXAddress, // Convert X-address to classic address, tag, and network ID
|
||||
isValidXAddress, // Check whether an X-address (X...) is valid
|
||||
// Codec with XRP alphabet
|
||||
codec,
|
||||
// Encode entropy as a "seed"
|
||||
encodeSeed,
|
||||
// Decode a seed into an object with its version, type, and bytes
|
||||
decodeSeed,
|
||||
// Encode bytes as a classic address (r...)
|
||||
encodeAccountID,
|
||||
// Decode a classic address to its raw bytes
|
||||
decodeAccountID,
|
||||
// Encode bytes to XRP Ledger node public key format
|
||||
encodeNodePublic,
|
||||
// Decode an XRP Ledger node public key into its raw bytes
|
||||
decodeNodePublic,
|
||||
// Encode a public key, as for payment channels
|
||||
encodeAccountPublic,
|
||||
// Decode a public key, as for payment channels
|
||||
decodeAccountPublic,
|
||||
// Check whether a classic address (r...) is valid
|
||||
isValidClassicAddress,
|
||||
// Derive X-address from classic address, tag, and network ID
|
||||
classicAddressToXAddress,
|
||||
// Encode account ID, tag, and network ID to X-address
|
||||
encodeXAddress,
|
||||
// Decode X-address to account ID, tag, and network ID
|
||||
xAddressToClassicAddress,
|
||||
// Convert X-address to classic address, tag, and network ID
|
||||
decodeXAddress,
|
||||
// Check whether an X-address (X...) is valid
|
||||
isValidXAddress,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
type Sequence = number[] | Buffer | Uint8Array
|
||||
|
||||
/**
|
||||
* Check whether two sequences (e.g. arrays of numbers) are equal.
|
||||
* Check whether two sequences (e.g. Arrays of numbers) are equal.
|
||||
*
|
||||
* @param arr1 One of the arrays to compare.
|
||||
* @param arr2 The other array to compare.
|
||||
* @param arr1 - One of the arrays to compare.
|
||||
* @param arr2 - The other array to compare.
|
||||
*/
|
||||
export function seqEqual(arr1: Sequence, arr2: Sequence): boolean {
|
||||
if (arr1.length !== arr2.length) {
|
||||
@@ -20,9 +20,9 @@ export function seqEqual(arr1: Sequence, arr2: Sequence): boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a value is a sequence (e.g. array of numbers).
|
||||
* Check whether a value is a sequence (e.g. Array of numbers).
|
||||
*
|
||||
* @param val The value to check.
|
||||
* @param val - The value to check.
|
||||
*/
|
||||
function isSequence(val: Sequence | number): val is Sequence {
|
||||
return (val as Sequence).length !== undefined
|
||||
@@ -34,17 +34,17 @@ function isSequence(val: Sequence | number): val is Sequence {
|
||||
* element retrieval via sequence[ix].
|
||||
*
|
||||
* > concatArgs(1, [2, 3], Buffer.from([4,5]), new Uint8Array([6, 7]));
|
||||
* [1,2,3,4,5,6,7]
|
||||
* [1,2,3,4,5,6,7]
|
||||
*
|
||||
* @returns {number[]} Array of concatenated arguments
|
||||
* @returns Array of concatenated arguments
|
||||
*/
|
||||
export function concatArgs(...args: (number | Sequence)[]): number[] {
|
||||
export function concatArgs(...args: Array<number | Sequence>): number[] {
|
||||
const ret: number[] = []
|
||||
|
||||
args.forEach(function (arg) {
|
||||
args.forEach((arg) => {
|
||||
if (isSequence(arg)) {
|
||||
for (let j = 0; j < arg.length; j++) {
|
||||
ret.push(arg[j])
|
||||
for (const j of arg) {
|
||||
ret.push(j)
|
||||
}
|
||||
} else {
|
||||
ret.push(arg)
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
*/
|
||||
|
||||
import * as baseCodec from 'base-x'
|
||||
import * as createHash from 'create-hash'
|
||||
|
||||
import { seqEqual, concatArgs } from './utils'
|
||||
|
||||
class Codec {
|
||||
sha256: (bytes: Uint8Array) => Buffer
|
||||
alphabet: string
|
||||
codec: any
|
||||
codec: baseCodec.BaseConverter
|
||||
base: number
|
||||
|
||||
constructor(options: {
|
||||
@@ -24,8 +26,8 @@ class Codec {
|
||||
/**
|
||||
* Encoder.
|
||||
*
|
||||
* @param bytes Buffer of data to encode.
|
||||
* @param opts Options object including the version bytes and the expected length of the data to encode.
|
||||
* @param bytes - Buffer of data to encode.
|
||||
* @param opts - Options object including the version bytes and the expected length of the data to encode.
|
||||
*/
|
||||
encode(
|
||||
bytes: Buffer,
|
||||
@@ -64,13 +66,15 @@ class Codec {
|
||||
/**
|
||||
* Decoder.
|
||||
*
|
||||
* @param base58string Base58Check-encoded string to decode.
|
||||
* @param opts Options object including the version byte(s) and the expected length of the data after decoding.
|
||||
* @param base58string - Base58Check-encoded string to decode.
|
||||
* @param opts - Options object including the version byte(s) and the expected length of the data after decoding.
|
||||
*/
|
||||
/* eslint-disable max-lines-per-function --
|
||||
* TODO refactor */
|
||||
decode(
|
||||
base58string: string,
|
||||
opts: {
|
||||
versions: (number | number[])[]
|
||||
versions: Array<number | number[]>
|
||||
expectedLength?: number
|
||||
versionTypes?: ['ed25519', 'secp256k1']
|
||||
},
|
||||
@@ -90,7 +94,7 @@ class Codec {
|
||||
)
|
||||
}
|
||||
const versionLengthGuess =
|
||||
typeof versions[0] === 'number' ? 1 : (versions[0] as number[]).length
|
||||
typeof versions[0] === 'number' ? 1 : versions[0].length
|
||||
const payloadLength =
|
||||
opts.expectedLength || withoutSum.length - versionLengthGuess
|
||||
const versionBytes = withoutSum.slice(0, -payloadLength)
|
||||
@@ -113,6 +117,7 @@ class Codec {
|
||||
'version_invalid: version bytes do not match any of the provided version(s)',
|
||||
)
|
||||
}
|
||||
/* eslint-enable max-lines-per-function */
|
||||
|
||||
decodeChecked(base58string: string): Buffer {
|
||||
const buffer = this.decodeRaw(base58string)
|
||||
@@ -140,19 +145,21 @@ class Codec {
|
||||
* XRP codec
|
||||
*/
|
||||
|
||||
// Pure JavaScript hash functions in the browser, native hash functions in Node.js
|
||||
const createHash = require('create-hash')
|
||||
|
||||
// base58 encodings: https://xrpl.org/base58-encodings.html
|
||||
const ACCOUNT_ID = 0 // Account address (20 bytes)
|
||||
const ACCOUNT_PUBLIC_KEY = 0x23 // Account public key (33 bytes)
|
||||
const FAMILY_SEED = 0x21 // 33; Seed value (for secret keys) (16 bytes)
|
||||
const NODE_PUBLIC = 0x1c // 28; Validation public key (33 bytes)
|
||||
// Account address (20 bytes)
|
||||
const ACCOUNT_ID = 0
|
||||
// Account public key (33 bytes)
|
||||
const ACCOUNT_PUBLIC_KEY = 0x23
|
||||
// 33; Seed value (for secret keys) (16 bytes)
|
||||
const FAMILY_SEED = 0x21
|
||||
// 28; Validation public key (33 bytes)
|
||||
const NODE_PUBLIC = 0x1c
|
||||
|
||||
const ED25519_SEED = [0x01, 0xe1, 0x4b] // [1, 225, 75]
|
||||
// [1, 225, 75]
|
||||
const ED25519_SEED = [0x01, 0xe1, 0x4b]
|
||||
|
||||
const codecOptions = {
|
||||
sha256: function (bytes: Uint8Array) {
|
||||
sha256(bytes: Uint8Array) {
|
||||
return createHash('sha256').update(Buffer.from(bytes)).digest()
|
||||
},
|
||||
alphabet: 'rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz',
|
||||
@@ -186,7 +193,7 @@ export function decodeSeed(
|
||||
seed: string,
|
||||
opts: {
|
||||
versionTypes: ['ed25519', 'secp256k1']
|
||||
versions: (number | number[])[]
|
||||
versions: Array<number | number[]>
|
||||
expectedLength: number
|
||||
} = {
|
||||
versionTypes: ['ed25519', 'secp256k1'],
|
||||
@@ -202,14 +209,22 @@ export function encodeAccountID(bytes: Buffer): string {
|
||||
return codecWithXrpAlphabet.encode(bytes, opts)
|
||||
}
|
||||
|
||||
/* eslint-disable import/no-unused-modules ---
|
||||
* unclear why this is aliased but we should keep it in case someone else is
|
||||
* importing it with the aliased name */
|
||||
export const encodeAddress = encodeAccountID
|
||||
/* eslint-enable import/no-unused-modules */
|
||||
|
||||
export function decodeAccountID(accountId: string): Buffer {
|
||||
const opts = { versions: [ACCOUNT_ID], expectedLength: 20 }
|
||||
return codecWithXrpAlphabet.decode(accountId, opts).bytes
|
||||
}
|
||||
|
||||
/* eslint-disable import/no-unused-modules ---
|
||||
* unclear why this is aliased but we should keep it in case someone else is
|
||||
* importing it with the aliased name */
|
||||
export const decodeAddress = decodeAccountID
|
||||
/* eslint-enable import/no-unused-modules */
|
||||
|
||||
export function decodeNodePublic(base58string: string): Buffer {
|
||||
const opts = { versions: [NODE_PUBLIC], expectedLength: 33 }
|
||||
@@ -234,7 +249,7 @@ export function decodeAccountPublic(base58string: string): Buffer {
|
||||
export function isValidClassicAddress(address: string): boolean {
|
||||
try {
|
||||
decodeAccountID(address)
|
||||
} catch (e) {
|
||||
} catch (_error) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Buffer } from 'buffer/'
|
||||
* @extends BytesList So SerializedTypes can write bytes to a Sha512Half
|
||||
*/
|
||||
class Sha512Half extends BytesList {
|
||||
private hash: createHash = createHash('sha512')
|
||||
private hash = createHash('sha512')
|
||||
|
||||
/**
|
||||
* Construct a new Sha512Hash and write bytes this.hash
|
||||
@@ -38,8 +38,7 @@ class Sha512Half extends BytesList {
|
||||
* @returns half of a SHA512 hash
|
||||
*/
|
||||
finish256(): Buffer {
|
||||
const bytes: Buffer = this.hash.digest()
|
||||
return bytes.slice(0, 32)
|
||||
return Buffer.from(this.hash.digest().slice(0, 32))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ function generateSeed(
|
||||
)
|
||||
const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16)
|
||||
const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1'
|
||||
return addressCodec.encodeSeed(entropy, type)
|
||||
return addressCodec.encodeSeed(Buffer.from(entropy), type)
|
||||
}
|
||||
|
||||
function hash(message): number[] {
|
||||
|
||||
Reference in New Issue
Block a user