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:
ledhed2222
2021-11-30 04:18:35 -05:00
committed by GitHub
parent 89b8833610
commit ff9bdbdc4c
8 changed files with 144 additions and 3127 deletions

3053
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -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",

View File

@@ -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',
},
}

View File

@@ -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,
}

View File

@@ -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)

View File

@@ -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

View File

@@ -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))
}
/**

View File

@@ -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[] {