chore(lint): apply prettier to secret-numbers (#2549)

The config was not applied when it was moved to the monorepo
This commit is contained in:
Caleb Kniffen
2023-10-25 17:35:11 -05:00
parent c5fc25efc2
commit fa4eabef0e
7 changed files with 244 additions and 247 deletions

View File

@@ -4,12 +4,12 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr
## 1.0.0 Beta 1 (2023-10-19) ## 1.0.0 Beta 1 (2023-10-19)
- Add `xrpl-secret-numbers` by @WietseWind to the mono repo. * Add `xrpl-secret-numbers` by @WietseWind to the mono repo.
- `unpkg` and `jsdelivr` support was simplified. * `unpkg` and `jsdelivr` support was simplified.
- Unit tests run in a browser and node. * Unit tests run in a browser and node.
- Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead. * Remove `brorand` as a dependency and use `@xrplf/isomorphic` instead.
### BREAKING CHANGES: ### BREAKING CHANGES:
- `xrpl-secret-numbers` is now `@xrplf/secret-numbers`. * `xrpl-secret-numbers` is now `@xrplf/secret-numbers`.
- The bundled file produced changed from `dist/browerified.js` to `build/xrplf-secret-numbers-latest.js`. * The bundled file produced changed from `dist/browerified.js` to `build/xrplf-secret-numbers-latest.js`.
- Bundle variable is `xrplf_secret_numbers` instead of using browserify's loader. * Bundle variable is `xrplf_secret_numbers` instead of using browserify's loader.

View File

@@ -32,6 +32,7 @@
"@xrplf/isomorphic": "^1.0.0-beta.0", "@xrplf/isomorphic": "^1.0.0-beta.0",
"ripple-keypairs": "^2.0.0-beta.0" "ripple-keypairs": "^2.0.0-beta.0"
}, },
"prettier": "@xrplf/prettier-config",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git" "url": "git@github.com:XRPLF/xrpl.js.git"

View File

@@ -1,8 +1,8 @@
/* Methods ==================================================================== */ /* Methods ==================================================================== */
import Account from "./schema/Account"; import Account from './schema/Account'
import * as Utils from "./utils"; import * as Utils from './utils'
/* Types ==================================================================== */ /* Types ==================================================================== */
/* Export ==================================================================== */ /* Export ==================================================================== */
export { Account, Utils }; export { Account, Utils }

View File

@@ -1,99 +1,99 @@
import * as keypairs from "ripple-keypairs"; import * as keypairs from 'ripple-keypairs'
import * as utils from "../utils"; import * as utils from '../utils'
/* Types ==================================================================== */ /* Types ==================================================================== */
// eslint-disable-next-line import/no-unused-modules -- it is returned by Account.getKeypair // eslint-disable-next-line import/no-unused-modules -- it is returned by Account.getKeypair
export interface Keypair { export interface Keypair {
publicKey: string; publicKey: string
privateKey: string; privateKey: string
} }
interface AccountData { interface AccountData {
familySeed: string; familySeed: string
address: string; address: string
keypair: Keypair; keypair: Keypair
} }
/* Class ==================================================================== */ /* Class ==================================================================== */
export default class Account { export default class Account {
private readonly _secret: string[]; private readonly _secret: string[]
private readonly _account: AccountData = { private readonly _account: AccountData = {
familySeed: "", familySeed: '',
address: "", address: '',
keypair: { keypair: {
publicKey: "", publicKey: '',
privateKey: "", privateKey: '',
}, },
}; }
constructor(secretNumbers?: string[] | string | Buffer) { constructor(secretNumbers?: string[] | string | Buffer) {
if (typeof secretNumbers === "string") { if (typeof secretNumbers === 'string') {
this._secret = utils.parseSecretString(secretNumbers); this._secret = utils.parseSecretString(secretNumbers)
} else if (Array.isArray(secretNumbers)) { } else if (Array.isArray(secretNumbers)) {
this._secret = secretNumbers; this._secret = secretNumbers
} else if (Buffer.isBuffer(secretNumbers)) { } else if (Buffer.isBuffer(secretNumbers)) {
this._secret = utils.entropyToSecret(secretNumbers); this._secret = utils.entropyToSecret(secretNumbers)
} else { } else {
this._secret = utils.randomSecret(); this._secret = utils.randomSecret()
} }
validateLengths(this._secret); validateLengths(this._secret)
this.derive(); this.derive()
} }
getSecret(): string[] { getSecret(): string[] {
return this._secret; return this._secret
} }
getSecretString(): string { getSecretString(): string {
return this._secret.join(" "); return this._secret.join(' ')
} }
getAddress(): string { getAddress(): string {
return this._account.address; return this._account.address
} }
getFamilySeed(): string { getFamilySeed(): string {
return this._account.familySeed; return this._account.familySeed
} }
getKeypair(): Keypair { getKeypair(): Keypair {
return this._account.keypair; return this._account.keypair
} }
toString(): string { toString(): string {
return this.getSecretString(); return this.getSecretString()
} }
private derive(): void { private derive(): void {
try { try {
const entropy = utils.secretToEntropy(this._secret); const entropy = utils.secretToEntropy(this._secret)
this._account.familySeed = keypairs.generateSeed({ entropy }); this._account.familySeed = keypairs.generateSeed({ entropy })
this._account.keypair = keypairs.deriveKeypair(this._account.familySeed); this._account.keypair = keypairs.deriveKeypair(this._account.familySeed)
this._account.address = keypairs.deriveAddress( this._account.address = keypairs.deriveAddress(
this._account.keypair.publicKey this._account.keypair.publicKey,
); )
} catch (error) { } catch (error) {
let message = "Unknown Error"; let message = 'Unknown Error'
if (error instanceof Error) { if (error instanceof Error) {
message = error.message; message = error.message
} }
// we'll proceed, but let's report it // we'll proceed, but let's report it
throw new Error(message); throw new Error(message)
} }
} }
} }
function validateLengths(secretNumbers: string[]): void { function validateLengths(secretNumbers: string[]): void {
if (secretNumbers.length !== 8) { if (secretNumbers.length !== 8) {
throw new Error("Secret must have 8 numbers"); throw new Error('Secret must have 8 numbers')
} }
secretNumbers.forEach((num) => { secretNumbers.forEach((num) => {
if (num.length !== 6) { if (num.length !== 6) {
throw new Error("Each secret number must be 6 digits"); throw new Error('Each secret number must be 6 digits')
} }
}); })
} }

View File

@@ -1,82 +1,82 @@
import { randomBytes } from "@xrplf/isomorphic/utils"; import { randomBytes } from '@xrplf/isomorphic/utils'
function randomEntropy(): Buffer { function randomEntropy(): Buffer {
return Buffer.from(randomBytes(16)); return Buffer.from(randomBytes(16))
} }
function calculateChecksum(position: number, value: number): number { function calculateChecksum(position: number, value: number): number {
return (value * (position * 2 + 1)) % 9; return (value * (position * 2 + 1)) % 9
} }
function checkChecksum( function checkChecksum(
position: number, position: number,
value: number | string, value: number | string,
checksum?: number checksum?: number,
): boolean { ): boolean {
let normalizedChecksum: number; let normalizedChecksum: number
let normalizedValue: number; let normalizedValue: number
if (typeof value === "string") { if (typeof value === 'string') {
if (value.length !== 6) { if (value.length !== 6) {
throw new Error("value must have a length of 6"); throw new Error('value must have a length of 6')
} }
normalizedChecksum = parseInt(value.slice(5), 10); normalizedChecksum = parseInt(value.slice(5), 10)
normalizedValue = parseInt(value.slice(0, 5), 10); normalizedValue = parseInt(value.slice(0, 5), 10)
} else { } else {
if (typeof checksum !== "number") { if (typeof checksum !== 'number') {
throw new Error("checksum must be a number when value is a number"); throw new Error('checksum must be a number when value is a number')
} }
normalizedChecksum = checksum; normalizedChecksum = checksum
normalizedValue = value; normalizedValue = value
} }
return (normalizedValue * (position * 2 + 1)) % 9 === normalizedChecksum; return (normalizedValue * (position * 2 + 1)) % 9 === normalizedChecksum
} }
function entropyToSecret(entropy: Buffer): string[] { function entropyToSecret(entropy: Buffer): string[] {
const len = new Array(Math.ceil(entropy.length / 2)); const len = new Array(Math.ceil(entropy.length / 2))
const chunks = Array.from(len, (_a, chunk) => { const chunks = Array.from(len, (_a, chunk) => {
const buffChunk = entropy.slice(chunk * 2, (chunk + 1) * 2); const buffChunk = entropy.slice(chunk * 2, (chunk + 1) * 2)
const no = parseInt(buffChunk.toString("hex"), 16); const no = parseInt(buffChunk.toString('hex'), 16)
const fill = "0".repeat(5 - String(no).length); const fill = '0'.repeat(5 - String(no).length)
return fill + String(no) + String(calculateChecksum(chunk, no)); return fill + String(no) + String(calculateChecksum(chunk, no))
}); })
if (chunks.length !== 8) { if (chunks.length !== 8) {
throw new Error("Chucks must have 8 digits"); throw new Error('Chucks must have 8 digits')
} }
return chunks; return chunks
} }
function randomSecret(): string[] { function randomSecret(): string[] {
return entropyToSecret(randomEntropy()); return entropyToSecret(randomEntropy())
} }
function secretToEntropy(secret: string[]): Buffer { function secretToEntropy(secret: string[]): Buffer {
return Buffer.concat( return Buffer.concat(
secret.map((chunk, i) => { secret.map((chunk, i) => {
const no = Number(chunk.slice(0, 5)); const no = Number(chunk.slice(0, 5))
const checksum = Number(chunk.slice(5)); const checksum = Number(chunk.slice(5))
if (chunk.length !== 6) { if (chunk.length !== 6) {
throw new Error("Invalid secret: number invalid"); throw new Error('Invalid secret: number invalid')
} }
if (!checkChecksum(i, no, checksum)) { if (!checkChecksum(i, no, checksum)) {
throw new Error("Invalid secret part: checksum invalid"); throw new Error('Invalid secret part: checksum invalid')
} }
const hex = `0000${no.toString(16)}`.slice(-4); const hex = `0000${no.toString(16)}`.slice(-4)
return Buffer.from(hex, "hex"); return Buffer.from(hex, 'hex')
}) }),
); )
} }
function parseSecretString(secret: string): string[] { function parseSecretString(secret: string): string[] {
const normalizedSecret = secret.replace(/[^0-9]/gu, ""); const normalizedSecret = secret.replace(/[^0-9]/gu, '')
if (normalizedSecret.length !== 48) { if (normalizedSecret.length !== 48) {
throw new Error( throw new Error(
"Invalid secret string (should contain 8 blocks of 6 digits" 'Invalid secret string (should contain 8 blocks of 6 digits',
); )
} }
return Array.from(new Array(8), (_a, index) => { return Array.from(new Array(8), (_a, index) => {
return normalizedSecret.slice(index * 6, (index + 1) * 6); return normalizedSecret.slice(index * 6, (index + 1) * 6)
}); })
} }
export { export {
@@ -87,4 +87,4 @@ export {
calculateChecksum, calculateChecksum,
checkChecksum, checkChecksum,
parseSecretString, parseSecretString,
}; }

View File

@@ -1,99 +1,95 @@
import * as keypairs from "ripple-keypairs"; import * as keypairs from 'ripple-keypairs'
import { Account, Utils } from "../src"; import { Account, Utils } from '../src'
describe("API: XRPL Secret Numbers", () => { describe('API: XRPL Secret Numbers', () => {
describe("Generate new account", () => { describe('Generate new account', () => {
const account = new Account(); const account = new Account()
it("Output sanity checks", () => { it('Output sanity checks', () => {
expect(account.getAddress()).toMatch(/^r[a-zA-Z0-9]{19,}$/u); expect(account.getAddress()).toMatch(/^r[a-zA-Z0-9]{19,}$/u)
const entropy = Utils.secretToEntropy(`${account.toString()}`.split(" ")); const entropy = Utils.secretToEntropy(`${account.toString()}`.split(' '))
const familySeed = keypairs.generateSeed({ entropy }); const familySeed = keypairs.generateSeed({ entropy })
const keypair = keypairs.deriveKeypair(familySeed); const keypair = keypairs.deriveKeypair(familySeed)
const address = keypairs.deriveAddress(keypair.publicKey); const address = keypairs.deriveAddress(keypair.publicKey)
expect(address).toEqual(account.getAddress()); expect(address).toEqual(account.getAddress())
expect(familySeed).toEqual(account.getFamilySeed()); expect(familySeed).toEqual(account.getFamilySeed())
}); })
}); })
describe("Account based on entropy", () => { describe('Account based on entropy', () => {
const entropy = Buffer.from("0123456789ABCDEF0123456789ABCDEF", "hex"); const entropy = Buffer.from('0123456789ABCDEF0123456789ABCDEF', 'hex')
const account = new Account(entropy); const account = new Account(entropy)
it("familySeed as expected", () => { it('familySeed as expected', () => {
expect(account.getFamilySeed()).toEqual("sp5DmDCut79BpgumfHhvRzdxXYQyU"); expect(account.getFamilySeed()).toEqual('sp5DmDCut79BpgumfHhvRzdxXYQyU')
}); })
it("address as expected", () => { it('address as expected', () => {
expect(account.getAddress()).toEqual( expect(account.getAddress()).toEqual('rMCcybKHfwCSkDHd3M36PAeUniEoygwjR3')
"rMCcybKHfwCSkDHd3M36PAeUniEoygwjR3" })
); it('Account object to string as expected', () => {
});
it("Account object to string as expected", () => {
const accountAsStr = const accountAsStr =
"002913 177673 352434 527196 002910 177672 352435 527190"; '002913 177673 352434 527196 002910 177672 352435 527190'
expect(`${account.toString()}`).toEqual(accountAsStr); expect(`${account.toString()}`).toEqual(accountAsStr)
}); })
}); })
describe("Account based on existing secret", () => { describe('Account based on existing secret', () => {
const secret = [ const secret = [
"084677", '084677',
"005323", '005323',
"580272", '580272',
"282388", '282388',
"626800", '626800',
"105300", '105300',
"560913", '560913',
"071783", '071783',
]; ]
const account = new Account(secret); const account = new Account(secret)
it("familySeed as expected", () => { it('familySeed as expected', () => {
expect(account.getFamilySeed()).toEqual("sswpWwri7Y11dNCSmXdphgcoPZk3y"); expect(account.getFamilySeed()).toEqual('sswpWwri7Y11dNCSmXdphgcoPZk3y')
}); })
it("publicKey as expected", () => { it('publicKey as expected', () => {
const pubkey = const pubkey =
"020526A0EDC9123F7FBB7588402518B80FCD2C8D8AB4C45F5A68A2F220098EA06F"; '020526A0EDC9123F7FBB7588402518B80FCD2C8D8AB4C45F5A68A2F220098EA06F'
expect(account.getKeypair().publicKey).toEqual(pubkey); expect(account.getKeypair().publicKey).toEqual(pubkey)
}); })
it("privateKey as expected", () => { it('privateKey as expected', () => {
const privkey = const privkey =
"005122B2127B4635FEE7D242FA6EC9B02B611C04494D0D7D49764374D90C8BC8D3"; '005122B2127B4635FEE7D242FA6EC9B02B611C04494D0D7D49764374D90C8BC8D3'
expect(account.getKeypair().privateKey).toEqual(privkey); expect(account.getKeypair().privateKey).toEqual(privkey)
}); })
it("address as expected", () => { it('address as expected', () => {
expect(account.getAddress()).toEqual( expect(account.getAddress()).toEqual('rfqJsRLLmr7wdWnEzW1mP6AVaPSdzmso9Z')
"rfqJsRLLmr7wdWnEzW1mP6AVaPSdzmso9Z" })
); it('Account object to string as expected', () => {
});
it("Account object to string as expected", () => {
const accountAsStr = const accountAsStr =
"084677 005323 580272 282388 626800 105300 560913 071783"; '084677 005323 580272 282388 626800 105300 560913 071783'
expect(`${account.toString()}`).toEqual(accountAsStr); expect(`${account.toString()}`).toEqual(accountAsStr)
}); })
}); })
describe("Checksum error", () => { describe('Checksum error', () => {
const secret = [ const secret = [
"084677", '084677',
"005324", '005324',
"580272", '580272',
"626800", '626800',
"282388", '282388',
"105300", '105300',
"560913", '560913',
"071783", '071783',
]; ]
it("Should throw an Checksum Error", () => { it('Should throw an Checksum Error', () => {
expect(() => { expect(() => {
// eslint-disable-next-line no-new -- Don't want unused variable // eslint-disable-next-line no-new -- Don't want unused variable
new Account(secret); new Account(secret)
}) })
// TODO: Remove if jest is removed. // TODO: Remove if jest is removed.
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- Jest and Jasmine have two different signatures. // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- Jest and Jasmine have two different signatures.
// @ts-expect-error // @ts-expect-error
.toThrowError(Error, "Invalid secret part: checksum invalid"); .toThrowError(Error, 'Invalid secret part: checksum invalid')
}); })
}); })
}); })

View File

@@ -1,107 +1,107 @@
import * as utils from "../src/utils"; import * as utils from '../src/utils'
describe("Utils", () => { describe('Utils', () => {
it("randomEntropy: valid output", () => { it('randomEntropy: valid output', () => {
const data = utils.randomEntropy(); const data = utils.randomEntropy()
expect(typeof data).toEqual("object"); expect(typeof data).toEqual('object')
expect(data instanceof Buffer).toBeTruthy(); expect(data instanceof Buffer).toBeTruthy()
expect(data.length).toEqual(16); expect(data.length).toEqual(16)
expect(data.toString("hex").length).toEqual(32); expect(data.toString('hex').length).toEqual(32)
expect(data.toString("hex")).toMatch(/^[a-f0-9]+$/u); expect(data.toString('hex')).toMatch(/^[a-f0-9]+$/u)
}); })
it("calculateChecksum: 1st position", () => { it('calculateChecksum: 1st position', () => {
expect(utils.calculateChecksum(0, 55988)).toEqual(8); expect(utils.calculateChecksum(0, 55988)).toEqual(8)
}); })
it("calculateChecksum: 8th position", () => { it('calculateChecksum: 8th position', () => {
expect(utils.calculateChecksum(7, 49962)).toEqual(0); expect(utils.calculateChecksum(7, 49962)).toEqual(0)
}); })
it("checkChecksum: 2nd position, split numbers", () => { it('checkChecksum: 2nd position, split numbers', () => {
expect(utils.checkChecksum(1, 55450, 3)).toBeTruthy(); expect(utils.checkChecksum(1, 55450, 3)).toBeTruthy()
}); })
it("checkChecksum: 7th position, split numbers", () => { it('checkChecksum: 7th position, split numbers', () => {
expect(utils.checkChecksum(6, 18373, 7)).toBeTruthy(); expect(utils.checkChecksum(6, 18373, 7)).toBeTruthy()
}); })
it("checkChecksum: 4th position, as string", () => { it('checkChecksum: 4th position, as string', () => {
expect(utils.checkChecksum(3, "391566")).toBeTruthy(); expect(utils.checkChecksum(3, '391566')).toBeTruthy()
}); })
it("randomSecret: valid checksums", () => { it('randomSecret: valid checksums', () => {
utils.randomSecret(); utils.randomSecret()
expect(0).toEqual(0); expect(0).toEqual(0)
}); })
it("randomSecret: valid output", () => { it('randomSecret: valid output', () => {
const data = utils.randomSecret(); const data = utils.randomSecret()
expect(Array.isArray(data)).toBeTruthy(); expect(Array.isArray(data)).toBeTruthy()
expect(data.length).toEqual(8); expect(data.length).toEqual(8)
expect(typeof data[0]).toEqual("string"); expect(typeof data[0]).toEqual('string')
expect(data[0].length).toEqual(6); expect(data[0].length).toEqual(6)
expect(data[7].length).toEqual(6); expect(data[7].length).toEqual(6)
}); })
it("entropyToSecret", () => { it('entropyToSecret', () => {
const entropy = Buffer.from("76ebb2d06879b45b7568fb9c1ded097c", "hex"); const entropy = Buffer.from('76ebb2d06879b45b7568fb9c1ded097c', 'hex')
const secret = [ const secret = [
"304435", '304435',
"457766", '457766',
"267453", '267453',
"461717", '461717',
"300560", '300560',
"644127", '644127',
"076618", '076618',
"024286", '024286',
]; ]
expect(utils.entropyToSecret(entropy)).toEqual(secret); expect(utils.entropyToSecret(entropy)).toEqual(secret)
}); })
it("secretToEntropy", () => { it('secretToEntropy', () => {
const secret = [ const secret = [
"304435", '304435',
"457766", '457766',
"267453", '267453',
"461717", '461717',
"300560", '300560',
"644127", '644127',
"076618", '076618',
"024286", '024286',
]; ]
const entropy = Buffer.from("76ebb2d06879b45b7568fb9c1ded097c", "hex"); const entropy = Buffer.from('76ebb2d06879b45b7568fb9c1ded097c', 'hex')
expect(utils.secretToEntropy(secret)).toEqual(entropy); expect(utils.secretToEntropy(secret)).toEqual(entropy)
}); })
it("parseSecretString with spaces valid", () => { it('parseSecretString with spaces valid', () => {
const secret = [ const secret = [
"304435", '304435',
"457766", '457766',
"267453", '267453',
"461717", '461717',
"300560", '300560',
"644127", '644127',
"076618", '076618',
"024286", '024286',
]; ]
expect( expect(
utils.parseSecretString( utils.parseSecretString(
"304435 457766 267453 461717 300560 644127 076618 024286" '304435 457766 267453 461717 300560 644127 076618 024286',
) ),
).toEqual(secret); ).toEqual(secret)
expect( expect(
utils.parseSecretString( utils.parseSecretString(
"304435457766267453461717300560644127076618024286" '304435457766267453461717300560644127076618024286',
) ),
).toEqual(secret); ).toEqual(secret)
expect( expect(
utils.parseSecretString(` utils.parseSecretString(`
304435 457766 304435 457766
267453 461717 267453 461717
300560 644127 300560 644127
076618 024286 076618 024286
`) `),
).toEqual(secret); ).toEqual(secret)
}); })
}); })