mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 04:05:52 +00:00
Replace TSLint with ESLint + Prettier (#71)
* Replace TSLint with ESLint + Prettier TSLint is deprecated, and ESLint is now officially supported by TypeScript. Additionally, Prettier is the industry standard for auto-formatting JS/TS code. That lets code reviews be about content rather than style. The `.eslintrc.js` file contains comments for the reasoning behind every configuration, so feel free to take a look at that as well. * Run src/ through Prettier * Add Mocha support for ESLint We do not actually lint Mocha files right now because they aren't in TS, but moving forward, this will give us better linting rules for our tests
This commit is contained in:
@@ -4,19 +4,21 @@ import * as hashjs from 'hash.js'
|
||||
import * as elliptic from 'elliptic'
|
||||
|
||||
import * as addressCodec from 'ripple-address-codec'
|
||||
import {derivePrivateKey, accountPublicFromPublicGenerator} from './secp256k1'
|
||||
import { derivePrivateKey, accountPublicFromPublicGenerator } from './secp256k1'
|
||||
import * as utils from './utils'
|
||||
|
||||
const Ed25519 = elliptic.eddsa('ed25519')
|
||||
const Secp256k1 = elliptic.ec('secp256k1')
|
||||
|
||||
const hexToBytes = utils.hexToBytes
|
||||
const bytesToHex = utils.bytesToHex
|
||||
const { hexToBytes } = utils
|
||||
const { bytesToHex } = utils
|
||||
|
||||
function generateSeed(options: {
|
||||
entropy?: Uint8Array,
|
||||
algorithm?: 'ed25519' | 'secp256k1'
|
||||
} = {}) {
|
||||
function generateSeed(
|
||||
options: {
|
||||
entropy?: Uint8Array
|
||||
algorithm?: 'ed25519' | 'secp256k1'
|
||||
} = {},
|
||||
) {
|
||||
assert(!options.entropy || options.entropy.length >= 16, 'entropy too short')
|
||||
const entropy = options.entropy ? options.entropy.slice(0, 16) : brorand(16)
|
||||
const type = options.algorithm === 'ed25519' ? 'ed25519' : 'secp256k1'
|
||||
@@ -24,51 +26,74 @@ function generateSeed(options: {
|
||||
}
|
||||
|
||||
function hash(message) {
|
||||
return hashjs.sha512().update(message).digest().slice(0, 32)
|
||||
return hashjs
|
||||
.sha512()
|
||||
.update(message)
|
||||
.digest()
|
||||
.slice(0, 32)
|
||||
}
|
||||
|
||||
const secp256k1 = {
|
||||
deriveKeypair: function(entropy, options) {
|
||||
deriveKeypair(entropy, options) {
|
||||
const prefix = '00'
|
||||
const privateKey = prefix + derivePrivateKey(entropy, options)
|
||||
.toString(16, 64).toUpperCase()
|
||||
const publicKey = bytesToHex(Secp256k1.keyFromPrivate(
|
||||
privateKey.slice(2)).getPublic().encodeCompressed())
|
||||
return {privateKey, publicKey}
|
||||
|
||||
const privateKey =
|
||||
prefix +
|
||||
derivePrivateKey(entropy, options)
|
||||
.toString(16, 64)
|
||||
.toUpperCase()
|
||||
|
||||
const publicKey = bytesToHex(
|
||||
Secp256k1.keyFromPrivate(privateKey.slice(2))
|
||||
.getPublic()
|
||||
.encodeCompressed(),
|
||||
)
|
||||
return { privateKey, publicKey }
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
return bytesToHex(Secp256k1.sign(hash(message),
|
||||
hexToBytes(privateKey), {canonical: true}).toDER())
|
||||
|
||||
sign(message, privateKey) {
|
||||
return bytesToHex(
|
||||
Secp256k1.sign(hash(message), hexToBytes(privateKey), {
|
||||
canonical: true,
|
||||
}).toDER(),
|
||||
)
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
|
||||
verify(message, signature, publicKey) {
|
||||
return Secp256k1.verify(hash(message), signature, hexToBytes(publicKey))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const ed25519 = {
|
||||
deriveKeypair: function(entropy) {
|
||||
deriveKeypair(entropy) {
|
||||
const prefix = 'ED'
|
||||
const rawPrivateKey = hash(entropy)
|
||||
const privateKey = prefix + bytesToHex(rawPrivateKey)
|
||||
const publicKey = prefix + bytesToHex(
|
||||
Ed25519.keyFromSecret(rawPrivateKey).pubBytes())
|
||||
return {privateKey, publicKey}
|
||||
const publicKey =
|
||||
prefix + bytesToHex(Ed25519.keyFromSecret(rawPrivateKey).pubBytes())
|
||||
return { privateKey, publicKey }
|
||||
},
|
||||
sign: function(message, privateKey) {
|
||||
|
||||
sign(message, privateKey) {
|
||||
// caution: Ed25519.sign interprets all strings as hex, stripping
|
||||
// any non-hex characters without warning
|
||||
assert(Array.isArray(message), 'message must be array of octets')
|
||||
return bytesToHex(Ed25519.sign(
|
||||
message, hexToBytes(privateKey).slice(1)).toBytes())
|
||||
return bytesToHex(
|
||||
Ed25519.sign(message, hexToBytes(privateKey).slice(1)).toBytes(),
|
||||
)
|
||||
},
|
||||
|
||||
verify(message, signature, publicKey) {
|
||||
return Ed25519.verify(
|
||||
message,
|
||||
hexToBytes(signature),
|
||||
hexToBytes(publicKey).slice(1),
|
||||
)
|
||||
},
|
||||
verify: function(message, signature, publicKey) {
|
||||
return Ed25519.verify(message, hexToBytes(signature),
|
||||
hexToBytes(publicKey).slice(1))
|
||||
}
|
||||
}
|
||||
|
||||
function select(algorithm) {
|
||||
const methods = {'ecdsa-secp256k1': secp256k1, ed25519}
|
||||
const methods = { 'ecdsa-secp256k1': secp256k1, ed25519 }
|
||||
return methods[algorithm]
|
||||
}
|
||||
|
||||
@@ -87,8 +112,9 @@ function deriveKeypair(seed, options) {
|
||||
|
||||
function getAlgorithmFromKey(key) {
|
||||
const bytes = hexToBytes(key)
|
||||
return (bytes.length === 33 && bytes[0] === 0xED) ?
|
||||
'ed25519' : 'ecdsa-secp256k1'
|
||||
return bytes.length === 33 && bytes[0] === 0xed
|
||||
? 'ed25519'
|
||||
: 'ecdsa-secp256k1'
|
||||
}
|
||||
|
||||
function sign(messageHex, privateKey) {
|
||||
@@ -103,7 +129,8 @@ function verify(messageHex, signature, publicKey) {
|
||||
|
||||
function deriveAddressFromBytes(publicKeyBytes: Buffer) {
|
||||
return addressCodec.encodeAccountID(
|
||||
utils.computePublicKeyHash(publicKeyBytes))
|
||||
utils.computePublicKeyHash(publicKeyBytes),
|
||||
)
|
||||
}
|
||||
|
||||
function deriveAddress(publicKey) {
|
||||
@@ -116,7 +143,7 @@ function deriveNodeAddress(publicKey) {
|
||||
return deriveAddressFromBytes(accountPublicBytes)
|
||||
}
|
||||
|
||||
const decodeSeed = addressCodec.decodeSeed
|
||||
const { decodeSeed } = addressCodec
|
||||
|
||||
module.exports = {
|
||||
generateSeed,
|
||||
@@ -125,5 +152,5 @@ module.exports = {
|
||||
verify,
|
||||
deriveAddress,
|
||||
deriveNodeAddress,
|
||||
decodeSeed
|
||||
decodeSeed,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ const secp256k1 = elliptic.ec('secp256k1')
|
||||
// TODO: type of `discrim`?
|
||||
function deriveScalar(bytes, discrim?: any) {
|
||||
const order = secp256k1.curve.n
|
||||
for (let i = 0; i <= 0xFFFFFFFF; i++) {
|
||||
for (let i = 0; i <= 0xffffffff; i++) {
|
||||
// We hash the bytes to find a 256 bit number, looping until we are sure it
|
||||
// is less than the order of the curve.
|
||||
const hasher = new Sha512().add(bytes)
|
||||
@@ -24,18 +24,21 @@ function deriveScalar(bytes, discrim?: any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} seed - bytes
|
||||
* @param {Object} [opts] - object
|
||||
* @param {Number} [opts.accountIndex=0] - the account number to generate
|
||||
* @param {Boolean} [opts.validator=false] - generate root key-pair,
|
||||
* as used by validators.
|
||||
* @return {bn.js} - 256 bit scalar value
|
||||
*
|
||||
*/
|
||||
export function derivePrivateKey(seed, opts: {
|
||||
validator?: boolean,
|
||||
accountIndex?: number
|
||||
} = {}) {
|
||||
* @param {Array} seed - bytes
|
||||
* @param {Object} [opts] - object
|
||||
* @param {Number} [opts.accountIndex=0] - the account number to generate
|
||||
* @param {Boolean} [opts.validator=false] - generate root key-pair,
|
||||
* as used by validators.
|
||||
* @return {bn.js} - 256 bit scalar value
|
||||
*
|
||||
*/
|
||||
export function derivePrivateKey(
|
||||
seed,
|
||||
opts: {
|
||||
validator?: boolean
|
||||
accountIndex?: number
|
||||
} = {},
|
||||
) {
|
||||
const root = opts.validator
|
||||
const order = secp256k1.curve.n
|
||||
|
||||
@@ -51,7 +54,8 @@ export function derivePrivateKey(seed, opts: {
|
||||
// Almost everyone just uses the first account, `0`.
|
||||
const accountIndex = opts.accountIndex || 0
|
||||
return deriveScalar(publicGen.encodeCompressed(), accountIndex)
|
||||
.add(privateGen).mod(order)
|
||||
.add(privateGen)
|
||||
.mod(order)
|
||||
}
|
||||
|
||||
export function accountPublicFromPublicGenerator(publicGenBytes) {
|
||||
|
||||
@@ -8,20 +8,29 @@ export default class Sha512 {
|
||||
constructor() {
|
||||
this.hash = hashjs.sha512()
|
||||
}
|
||||
|
||||
add(bytes) {
|
||||
this.hash.update(bytes)
|
||||
return this
|
||||
}
|
||||
|
||||
addU32(i) {
|
||||
return this.add([(i >>> 24) & 0xFF, (i >>> 16) & 0xFF,
|
||||
(i >>> 8) & 0xFF, i & 0xFF])
|
||||
return this.add([
|
||||
(i >>> 24) & 0xff,
|
||||
(i >>> 16) & 0xff,
|
||||
(i >>> 8) & 0xff,
|
||||
i & 0xff,
|
||||
])
|
||||
}
|
||||
|
||||
finish() {
|
||||
return this.hash.digest()
|
||||
}
|
||||
|
||||
first256() {
|
||||
return this.finish().slice(0, 32)
|
||||
}
|
||||
|
||||
first256BN() {
|
||||
return new BigNum(this.first256())
|
||||
}
|
||||
|
||||
@@ -3,30 +3,38 @@ import * as hashjs from 'hash.js'
|
||||
import * as BN from 'bn.js'
|
||||
|
||||
function bytesToHex(a) {
|
||||
return a.map(function(byteValue) {
|
||||
const hex = byteValue.toString(16).toUpperCase()
|
||||
return hex.length > 1 ? hex : '0' + hex
|
||||
}).join('')
|
||||
return a
|
||||
.map((byteValue) => {
|
||||
const hex = byteValue.toString(16).toUpperCase()
|
||||
return hex.length > 1 ? hex : `0${hex}`
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
||||
function hexToBytes(a) {
|
||||
assert(a.length % 2 === 0)
|
||||
return (new BN(a, 16)).toArray(null, a.length / 2)
|
||||
return new BN(a, 16).toArray(null, a.length / 2)
|
||||
}
|
||||
|
||||
function computePublicKeyHash(publicKeyBytes: Buffer): Buffer {
|
||||
const hash256 = hashjs.sha256().update(publicKeyBytes).digest()
|
||||
const hash160 = hashjs.ripemd160().update(hash256).digest()
|
||||
const hash256 = hashjs
|
||||
.sha256()
|
||||
.update(publicKeyBytes)
|
||||
.digest()
|
||||
|
||||
const hash160 = hashjs
|
||||
.ripemd160()
|
||||
.update(hash256)
|
||||
.digest()
|
||||
return Buffer.from(hash160)
|
||||
}
|
||||
|
||||
function seedFromPhrase(phrase) {
|
||||
return hashjs.sha512().update(phrase).digest().slice(0, 16)
|
||||
return hashjs
|
||||
.sha512()
|
||||
.update(phrase)
|
||||
.digest()
|
||||
.slice(0, 16)
|
||||
}
|
||||
|
||||
export {
|
||||
bytesToHex,
|
||||
hexToBytes,
|
||||
computePublicKeyHash,
|
||||
seedFromPhrase
|
||||
}
|
||||
export { bytesToHex, hexToBytes, computePublicKeyHash, seedFromPhrase }
|
||||
|
||||
Reference in New Issue
Block a user