From fc828fe550f23c1ff4fc7893979107dc62d8e492 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Sun, 8 Oct 2023 11:04:28 +0200 Subject: [PATCH] Make all APIs synchronous --- scripts/generate-vectors.ts | 52 ++-- src/BIP44CoinTypeNode.test.ts | 365 ++++++++++++------------- src/BIP44CoinTypeNode.ts | 54 ++-- src/BIP44Node.test.ts | 188 ++++++------- src/BIP44Node.ts | 22 +- src/SLIP10Node.test.ts | 264 +++++++++--------- src/SLIP10Node.ts | 22 +- src/curves/curve.ts | 5 +- src/curves/ed25519.test.ts | 2 +- src/derivation.test.ts | 220 +++++++-------- src/derivation.ts | 18 +- src/derivers/bip32.test.ts | 28 +- src/derivers/bip32.ts | 11 +- src/derivers/bip39.test.ts | 24 +- src/derivers/bip39.ts | 14 +- src/derivers/index.ts | 2 +- src/derivers/shared.ts | 56 ++-- src/derivers/slip10.test.ts | 28 +- src/derivers/slip10.ts | 11 +- src/utils.test.ts | 10 +- test/reference-implementations.test.ts | 96 +++---- test/vectors.test.ts | 23 +- 22 files changed, 714 insertions(+), 801 deletions(-) diff --git a/scripts/generate-vectors.ts b/scripts/generate-vectors.ts index c360d22e..ba52f531 100644 --- a/scripts/generate-vectors.ts +++ b/scripts/generate-vectors.ts @@ -8,9 +8,9 @@ import { ed25519, MAX_BIP_32_INDEX, secp256k1, + createBip39KeyFromSeed, } from '../src'; import type { Curve } from '../src/curves'; -import { createBip39KeyFromSeed } from '../src/derivers/bip39'; /** * Get a random boolean value. @@ -82,13 +82,13 @@ function getRandomPath( * boolean value. * @returns A random key vector. */ -async function getRandomKeyVector( +function getRandomKeyVector( node: SLIP10Node, spec: 'bip32' | 'slip10', hardened?: boolean, ) { const path = getRandomPath(spec, undefined, hardened); - const child = await node.derive(path); + const child = node.derive(path); return { path: { @@ -114,15 +114,16 @@ async function getRandomKeyVector( * @param hardened - Whether the keys should be hardened. Defaults to a random * boolean value. * @param curve - The curve to use. Defaults to secp256k1. + * @returns The random vector. */ -async function getRandomVector( +function getRandomVector( spec: 'bip32' | 'slip10', amount = 10, hardened?: boolean, curve: Curve = secp256k1, ) { const seed = getRandomSeed(); - const node = await createBip39KeyFromSeed(seed, curve); + const node = createBip39KeyFromSeed(seed, curve); return { hexSeed: bytesToHex(seed), @@ -133,11 +134,9 @@ async function getRandomVector( masterFingerprint: node.masterFingerprint, index: node.index, depth: node.depth, - keys: await Promise.all( - new Array(amount) - .fill(0) - .map(async () => getRandomKeyVector(node, spec, hardened)), - ), + keys: new Array(amount) + .fill(0) + .map(() => getRandomKeyVector(node, spec, hardened)), }; } @@ -152,17 +151,15 @@ async function getRandomVector( * @param curve - The curve to use. Defaults to secp256k1. * @returns The random vectors. */ -async function getRandomVectors( +function getRandomVectors( spec: 'bip32' | 'slip10', amount = 10, hardened?: boolean, curve: Curve = secp256k1, ) { - return Promise.all( - new Array(amount) - .fill(0) - .map(async () => getRandomVector(spec, undefined, hardened, curve)), - ); + return new Array(amount) + .fill(0) + .map(() => getRandomVector(spec, undefined, hardened, curve)); } /** @@ -170,26 +167,23 @@ async function getRandomVectors( * * @returns The output for the vectors. */ -async function getOutput() { +function getOutput() { return { bip32: { - hardened: await getRandomVectors('bip32', 50, true), - unhardened: await getRandomVectors('bip32', 50, false), - mixed: await getRandomVectors('bip32', 50), + hardened: getRandomVectors('bip32', 50, true), + unhardened: getRandomVectors('bip32', 50, false), + mixed: getRandomVectors('bip32', 50), }, slip10: { hardened: { - secp256k1: await getRandomVectors('slip10', 50, true), - ed25519: await getRandomVectors('slip10', 50, true, ed25519), + secp256k1: getRandomVectors('slip10', 50, true), + ed25519: getRandomVectors('slip10', 50, true, ed25519), }, - unhardened: await getRandomVectors('slip10', 50, false), - mixed: await getRandomVectors('bip32', 50), + unhardened: getRandomVectors('slip10', 50, false), + mixed: getRandomVectors('bip32', 50), }, }; } -getOutput() - .then((output) => { - console.log(JSON.stringify(output, undefined, 2)); - }) - .catch(console.error); +const output = getOutput(); +console.log(JSON.stringify(output, undefined, 2)); diff --git a/src/BIP44CoinTypeNode.test.ts b/src/BIP44CoinTypeNode.test.ts index 4ffb55a7..cb1a6058 100644 --- a/src/BIP44CoinTypeNode.test.ts +++ b/src/BIP44CoinTypeNode.test.ts @@ -17,8 +17,8 @@ const defaultBip39BytesToken = mnemonicPhraseToBytes(fixtures.local.mnemonic); describe('BIP44CoinTypeNode', () => { describe('fromJSON', () => { - it('initializes a BIP44CoinTypeNode (serialized BIP44Node)', async () => { - const bip44Node = await BIP44Node.fromDerivationPath({ + it('initializes a BIP44CoinTypeNode (serialized BIP44Node)', () => { + const bip44Node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -28,10 +28,7 @@ describe('BIP44CoinTypeNode', () => { const coinType = 60; const pathString = `m / bip32:44' / bip32:${coinType}'`; - const node = await BIP44CoinTypeNode.fromJSON( - bip44Node.toJSON(), - coinType, - ); + const node = BIP44CoinTypeNode.fromJSON(bip44Node.toJSON(), coinType); expect(node.coin_type).toStrictEqual(coinType); expect(node.depth).toBe(2); @@ -52,29 +49,29 @@ describe('BIP44CoinTypeNode', () => { }); }); - it('throws if node has invalid depth', async () => { + it('throws if node has invalid depth', () => { const arbitraryCoinType = 78; - await expect( + expect(() => BIP44CoinTypeNode.fromJSON( { key: 'foo', depth: 1 } as any, arbitraryCoinType, ), - ).rejects.toThrow( + ).toThrow( `Invalid depth: Coin type nodes must be of depth ${BIP_44_COIN_TYPE_DEPTH}. Received: "1"`, ); - await expect( + expect(() => BIP44CoinTypeNode.fromJSON( { key: 'foo', depth: 3 } as any, arbitraryCoinType, ), - ).rejects.toThrow( + ).toThrow( `Invalid depth: Coin type nodes must be of depth ${BIP_44_COIN_TYPE_DEPTH}. Received: "3"`, ); }); - it('throws if node has invalid key', async () => { + it('throws if node has invalid key', () => { const arbitraryCoinType = 78; const options = { @@ -111,12 +108,12 @@ describe('BIP44CoinTypeNode', () => { ]; for (const input of inputs) { - await expect( + expect(() => BIP44CoinTypeNode.fromJSON(input as any, arbitraryCoinType), - ).rejects.toThrow('Invalid value: Must be a 32-byte byte array.'); + ).toThrow('Invalid value: Must be a 32-byte byte array.'); } - await expect( + expect(() => BIP44CoinTypeNode.fromJSON( { privateKey: 1, @@ -126,11 +123,11 @@ describe('BIP44CoinTypeNode', () => { } as any, arbitraryCoinType, ), - ).rejects.toThrow('Value must be a hexadecimal string.'); + ).toThrow('Value must be a hexadecimal string.'); }); - it('throws if coin type is invalid', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if coin type is invalid', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -142,9 +139,9 @@ describe('BIP44CoinTypeNode', () => { const inputs = ['60', 1.1, -1, {}]; for (const input of inputs) { - await expect( + expect(() => BIP44CoinTypeNode.fromJSON(jsonNode, input as any), - ).rejects.toThrow( + ).toThrow( 'Invalid coin type: The specified coin type must be a non-negative integer number.', ); } @@ -152,8 +149,8 @@ describe('BIP44CoinTypeNode', () => { }); describe('fromNode', () => { - it('initializes a BIP44CoinTypeNode (BIP44Node)', async () => { - const bip44Node = await BIP44Node.fromDerivationPath({ + it('initializes a BIP44CoinTypeNode (BIP44Node)', () => { + const bip44Node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -163,7 +160,7 @@ describe('BIP44CoinTypeNode', () => { const coinType = 60; const pathString = `m / bip32:44' / bip32:${coinType}'`; - const node = await BIP44CoinTypeNode.fromNode(bip44Node, coinType); + const node = BIP44CoinTypeNode.fromNode(bip44Node, coinType); expect(node.coin_type).toStrictEqual(coinType); expect(node.depth).toBe(2); @@ -184,17 +181,17 @@ describe('BIP44CoinTypeNode', () => { }); }); - it('throws if the node is not a BIP44Node', async () => { + it('throws if the node is not a BIP44Node', () => { // @ts-expect-error Invalid node type. - await expect(BIP44CoinTypeNode.fromNode({}, 0)).rejects.toThrow( + expect(() => BIP44CoinTypeNode.fromNode({}, 0)).toThrow( 'Invalid node: Expected an instance of BIP44Node.', ); }); }); describe('fromDerivationPath', () => { - it('initializes a BIP44CoinTypeNode (derivation path)', async () => { - const node = await BIP44CoinTypeNode.fromDerivationPath([ + it('initializes a BIP44CoinTypeNode (derivation path)', () => { + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, @@ -221,14 +218,14 @@ describe('BIP44CoinTypeNode', () => { }); }); - it('initializes a BIP44CoinTypeNode with a Uint8Array', async () => { - const node = await BIP44CoinTypeNode.fromDerivationPath([ + it('initializes a BIP44CoinTypeNode with a Uint8Array', () => { + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39BytesToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const stringNode = await BIP44CoinTypeNode.fromDerivationPath([ + const stringNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, @@ -237,24 +234,24 @@ describe('BIP44CoinTypeNode', () => { expect(node.toJSON()).toStrictEqual(stringNode.toJSON()); }); - it('throws if derivation path has invalid depth', async () => { - await expect( + it('throws if derivation path has invalid depth', () => { + expect(() => BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, ] as any), - ).rejects.toThrow( + ).toThrow( `Invalid depth: Coin type nodes must be of depth ${BIP_44_COIN_TYPE_DEPTH}. Received: "1"`, ); - await expect( + expect(() => BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, `bip32:0'`, ] as any), - ).rejects.toThrow( + ).toThrow( `Invalid depth: Coin type nodes must be of depth ${BIP_44_COIN_TYPE_DEPTH}. Received: "3"`, ); }); @@ -267,18 +264,18 @@ describe('BIP44CoinTypeNode', () => { `bip32:60'`, ] as const; - it('derives an address_index key (default inputs)', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('derives an address_index key (default inputs)', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [...coinTypePath, `bip32:0'`, `bip32:0`, `bip32:0`], }); - const coinTypeNode = await BIP44CoinTypeNode.fromDerivationPath([ + const coinTypeNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const childNode = await coinTypeNode.deriveBIP44AddressKey({ + const childNode = coinTypeNode.deriveBIP44AddressKey({ address_index: 0, }); @@ -286,18 +283,18 @@ describe('BIP44CoinTypeNode', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (default inputs, different address_index)', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('derives an address_index key (default inputs, different address_index)', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [...coinTypePath, `bip32:0'`, `bip32:0`, `bip32:99`], }); - const coinTypeNode = await BIP44CoinTypeNode.fromDerivationPath([ + const coinTypeNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const childNode = await coinTypeNode.deriveBIP44AddressKey({ + const childNode = coinTypeNode.deriveBIP44AddressKey({ address_index: 99, }); @@ -305,18 +302,18 @@ describe('BIP44CoinTypeNode', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (non-default account value)', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('derives an address_index key (non-default account value)', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [...coinTypePath, `bip32:4'`, `bip32:0`, `bip32:0`], }); - const coinTypeNode = await BIP44CoinTypeNode.fromDerivationPath([ + const coinTypeNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const childNode = await coinTypeNode.deriveBIP44AddressKey({ + const childNode = coinTypeNode.deriveBIP44AddressKey({ account: 4, address_index: 0, }); @@ -325,18 +322,18 @@ describe('BIP44CoinTypeNode', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (non-default change value)', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('derives an address_index key (non-default change value)', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [...coinTypePath, `bip32:0'`, `bip32:3`, `bip32:0`], }); - const coinTypeNode = await BIP44CoinTypeNode.fromDerivationPath([ + const coinTypeNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const childNode = await coinTypeNode.deriveBIP44AddressKey({ + const childNode = coinTypeNode.deriveBIP44AddressKey({ change: 3, address_index: 0, }); @@ -345,18 +342,18 @@ describe('BIP44CoinTypeNode', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (non-default account and change values)', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('derives an address_index key (non-default account and change values)', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [...coinTypePath, `bip32:4'`, `bip32:3`, `bip32:0`], }); - const coinTypeNode = await BIP44CoinTypeNode.fromDerivationPath([ + const coinTypeNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:60'`, ]); - const childNode = await coinTypeNode.deriveBIP44AddressKey({ + const childNode = coinTypeNode.deriveBIP44AddressKey({ account: 4, change: 3, address_index: 0, @@ -368,9 +365,9 @@ describe('BIP44CoinTypeNode', () => { }); describe('publicKey', () => { - it('returns the public key for a node', async () => { + it('returns the public key for a node', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -378,16 +375,16 @@ describe('BIP44CoinTypeNode', () => { ], }); - const parentNode = await BIP44CoinTypeNode.fromNode(node, coinType); + const parentNode = BIP44CoinTypeNode.fromNode(node, coinType); expect(parentNode.publicKey).toBe(node.publicKey); }); }); describe('compressedPublicKey', () => { - it('returns the compressed public key for a node', async () => { + it('returns the compressed public key for a node', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -395,7 +392,7 @@ describe('BIP44CoinTypeNode', () => { ], }); - const parentNode = await BIP44CoinTypeNode.fromNode(node, coinType); + const parentNode = BIP44CoinTypeNode.fromNode(node, coinType); expect(parentNode.compressedPublicKey).toStrictEqual( node.compressedPublicKey, @@ -404,9 +401,9 @@ describe('BIP44CoinTypeNode', () => { }); describe('compressedPublicKeyBytes', () => { - it('returns the compressed public key for a node', async () => { + it('returns the compressed public key for a node', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -414,7 +411,7 @@ describe('BIP44CoinTypeNode', () => { ], }); - const parentNode = await BIP44CoinTypeNode.fromNode(node, coinType); + const parentNode = BIP44CoinTypeNode.fromNode(node, coinType); expect(parentNode.compressedPublicKeyBytes).toStrictEqual( node.compressedPublicKeyBytes, @@ -423,9 +420,9 @@ describe('BIP44CoinTypeNode', () => { }); describe('address', () => { - it('returns an Ethereum address for a node', async () => { + it('returns an Ethereum address for a node', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -433,16 +430,16 @@ describe('BIP44CoinTypeNode', () => { ], }); - const parentNode = await BIP44CoinTypeNode.fromNode(node, coinType); + const parentNode = BIP44CoinTypeNode.fromNode(node, coinType); expect(parentNode.address).toBe(node.address); }); }); describe('extendedKey', () => { - it('returns the extended private key for nodes with a private key', async () => { + it('returns the extended private key for nodes with a private key', () => { const coinType = 60; - const node = await BIP44CoinTypeNode.fromDerivationPath([ + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -462,9 +459,9 @@ describe('BIP44CoinTypeNode', () => { }); describe('toJSON', () => { - it('returns a JSON-compatible representation of the node', async () => { + it('returns a JSON-compatible representation of the node', () => { const coinType = 60; - const node = await BIP44CoinTypeNode.fromDerivationPath([ + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -504,15 +501,15 @@ describe('BIP44CoinTypeNode', () => { }); describe('deriveBIP44AddressKey', () => { - it('derives a BIP-44 address key (default inputs)', async () => { + it('derives a BIP-44 address key (default inputs)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -523,7 +520,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { address_index: 0, }); @@ -531,15 +528,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (default inputs, different address_index)', async () => { + it('derives an address_index key (default inputs, different address_index)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -550,7 +547,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { address_index: 3333, }); @@ -558,15 +555,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (default inputs, object address_index)', async () => { + it('derives an address_index key (default inputs, object address_index)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -577,7 +574,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { address_index: { index: 3333, hardened: false, @@ -588,15 +585,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives an address_index key (default inputs, hardened address_index)', async () => { + it('derives an address_index key (default inputs, hardened address_index)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -607,7 +604,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { address_index: { index: 3333, hardened: true, @@ -618,15 +615,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives a BIP-44 address key (non-default account value)', async () => { + it('derives a BIP-44 address key (non-default account value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -637,7 +634,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { account: 3, address_index: 0, }); @@ -646,15 +643,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives a BIP-44 address key (non-default change value)', async () => { + it('derives a BIP-44 address key (non-default change value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -665,7 +662,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { change: 9, address_index: 0, }); @@ -674,15 +671,15 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives a BIP-44 address key (non-default account and change values)', async () => { + it('derives a BIP-44 address key (non-default account and change values)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -693,7 +690,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { account: 3, change: 9, address_index: 0, @@ -703,16 +700,16 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('derives a BIP-44 address key (JSON node)', async () => { + it('derives a BIP-44 address key (JSON node)', () => { const coinType = 60; - const node = await BIP44CoinTypeNode.fromDerivationPath([ + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const parentNode = node.toJSON(); - const expectedNode = await BIP44Node.fromDerivationPath({ + const expectedNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -723,7 +720,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(parentNode, { + const childNode = deriveBIP44AddressKey(parentNode, { address_index: 0, }); @@ -731,9 +728,9 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(expectedNode.chainCode); }); - it('derives a BIP-44 address key (extended key)', async () => { + it('derives a BIP-44 address key (extended key)', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -741,7 +738,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const expectedNode = await BIP44Node.fromDerivationPath({ + const expectedNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -752,7 +749,7 @@ describe('deriveBIP44AddressKey', () => { ], }); - const childNode = await deriveBIP44AddressKey(node.extendedKey, { + const childNode = deriveBIP44AddressKey(node.extendedKey, { address_index: 0, }); @@ -760,9 +757,9 @@ describe('deriveBIP44AddressKey', () => { expect(childNode.chainCode).toStrictEqual(expectedNode.chainCode); }); - it('throws if a node value is invalid', async () => { + it('throws if a node value is invalid', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -777,17 +774,15 @@ describe('deriveBIP44AddressKey', () => { ]; for (const input of inputs) { - await expect( - deriveBIP44AddressKey(parentNode, input as any), - ).rejects.toThrow( + expect(() => deriveBIP44AddressKey(parentNode, input as any)).toThrow( `Invalid BIP-32 index: Must be a non-negative integer.`, ); } }); - it('throws if the change or address_index value is invalid', async () => { + it('throws if the change or address_index value is invalid', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -807,9 +802,7 @@ describe('deriveBIP44AddressKey', () => { ]; for (const input of inputs) { - await expect( - deriveBIP44AddressKey(parentNode, input as any), - ).rejects.toThrow( + expect(() => deriveBIP44AddressKey(parentNode, input as any)).toThrow( `Invalid BIP-32 index: Must be an object containing the index and whether it is hardened.`, ); } @@ -817,20 +810,20 @@ describe('deriveBIP44AddressKey', () => { }); describe('getBIP44AddressKeyDeriver', () => { - it('returns the expected BIP-44 address key deriver function (default inputs)', async () => { + it('returns the expected BIP-44 address key deriver function (default inputs)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -841,26 +834,26 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function and derives a hardened index (default inputs)', async () => { + it('returns the expected BIP-44 address key deriver function and derives a hardened index (default inputs)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -871,26 +864,26 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0, true); + const childNode = deriver(0, true); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function and derives a hardened index (default inputs, different address_index)', async () => { + it('returns the expected BIP-44 address key deriver function and derives a hardened index (default inputs, different address_index)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -901,26 +894,26 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(9873, true); + const childNode = deriver(9873, true); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (different coin_type)', async () => { + it('returns the expected BIP-44 address key deriver function (different coin_type)', () => { const coinType = 8129837; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -931,26 +924,26 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (default inputs, different address_index)', async () => { + it('returns the expected BIP-44 address key deriver function (default inputs, different address_index)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -961,28 +954,28 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(9873); + const childNode = deriver(9873); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (non-default account value)', async () => { + it('returns the expected BIP-44 address key deriver function (non-default account value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:46' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode, { + const deriver = getBIP44AddressKeyDeriver(parentNode, { account: 46, }); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -993,26 +986,26 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (non-default change value)', async () => { + it('returns the expected BIP-44 address key deriver function (non-default change value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:2`; - const deriver = await getBIP44AddressKeyDeriver(parentNode, { change: 2 }); + const deriver = getBIP44AddressKeyDeriver(parentNode, { change: 2 }); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1023,22 +1016,22 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (object change value)', async () => { + it('returns the expected BIP-44 address key deriver function (object change value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:2`; - const deriver = await getBIP44AddressKeyDeriver(parentNode, { + const deriver = getBIP44AddressKeyDeriver(parentNode, { change: { index: 2, hardened: false, @@ -1047,7 +1040,7 @@ describe('getBIP44AddressKeyDeriver', () => { expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1058,22 +1051,22 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (hardened change value)', async () => { + it('returns the expected BIP-44 address key deriver function (hardened change value)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:2'`; - const deriver = await getBIP44AddressKeyDeriver(parentNode, { + const deriver = getBIP44AddressKeyDeriver(parentNode, { change: { index: 2, hardened: true, @@ -1082,7 +1075,7 @@ describe('getBIP44AddressKeyDeriver', () => { expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1093,29 +1086,29 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (non-default account and change values)', async () => { + it('returns the expected BIP-44 address key deriver function (non-default account and change values)', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, ]); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:46' / bip32:2`; - const deriver = await getBIP44AddressKeyDeriver(parentNode, { + const deriver = getBIP44AddressKeyDeriver(parentNode, { account: 46, change: 2, }); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1126,15 +1119,15 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(node.privateKey); expect(childNode.chainCode).toStrictEqual(node.chainCode); }); - it('returns the expected BIP-44 address key deriver function (JSON node)', async () => { + it('returns the expected BIP-44 address key deriver function (JSON node)', () => { const coinType = 60; - const node = await BIP44CoinTypeNode.fromDerivationPath([ + const node = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -1142,11 +1135,11 @@ describe('getBIP44AddressKeyDeriver', () => { const parentNode = node.toJSON(); const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const expectedNode = await BIP44Node.fromDerivationPath({ + const expectedNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1157,15 +1150,15 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(expectedNode.privateKey); expect(childNode.chainCode).toStrictEqual(expectedNode.chainCode); }); - it('returns the expected BIP-44 address key deriver function (extended key)', async () => { + it('returns the expected BIP-44 address key deriver function (extended key)', () => { const coinType = 60; - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1175,11 +1168,11 @@ describe('getBIP44AddressKeyDeriver', () => { const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(node.extendedKey); + const deriver = getBIP44AddressKeyDeriver(node.extendedKey); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); - const expectedNode = await BIP44Node.fromDerivationPath({ + const expectedNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -1190,15 +1183,15 @@ describe('getBIP44AddressKeyDeriver', () => { ], }); - const childNode = await deriver(0); + const childNode = deriver(0); expect(childNode.privateKey).toStrictEqual(expectedNode.privateKey); expect(childNode.chainCode).toStrictEqual(expectedNode.chainCode); }); - it('throws if a node value is invalid', async () => { + it('throws if a node value is invalid', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -1213,17 +1206,15 @@ describe('getBIP44AddressKeyDeriver', () => { ]; for (const invalidInput of inputs) { - await expect( + expect(() => getBIP44AddressKeyDeriver(parentNode, invalidInput as any), - ).rejects.toThrow( - `Invalid BIP-32 index: Must be a non-negative integer.`, - ); + ).toThrow(`Invalid BIP-32 index: Must be a non-negative integer.`); } }); - it('deriver throws if address_index value is invalid', async () => { + it('deriver throws if address_index value is invalid', () => { const coinType = 60; - const parentNode = await BIP44CoinTypeNode.fromDerivationPath([ + const parentNode = BIP44CoinTypeNode.fromDerivationPath([ defaultBip39NodeToken, BIP44PurposeNodeToken, `bip32:${coinType}'`, @@ -1231,7 +1222,7 @@ describe('getBIP44AddressKeyDeriver', () => { const expectedPath = `m / bip32:44' / bip32:${coinType}' / bip32:0' / bip32:0`; - const deriver = await getBIP44AddressKeyDeriver(parentNode); + const deriver = getBIP44AddressKeyDeriver(parentNode); expect(deriver.coin_type).toStrictEqual(coinType); expect(deriver.path).toStrictEqual(expectedPath); @@ -1245,7 +1236,7 @@ describe('getBIP44AddressKeyDeriver', () => { ]; for (const input of inputs) { - await expect(deriver(input as any)).rejects.toThrow( + expect(() => deriver(input as any)).toThrow( `Invalid BIP-32 index: Must be a non-negative integer.`, ); } diff --git a/src/BIP44CoinTypeNode.ts b/src/BIP44CoinTypeNode.ts index bb065c5c..22049103 100644 --- a/src/BIP44CoinTypeNode.ts +++ b/src/BIP44CoinTypeNode.ts @@ -75,12 +75,14 @@ export class BIP44CoinTypeNode implements BIP44CoinTypeNodeInterface { * @param json - The {@link JsonBIP44Node} for the key of this node. * @param coin_type - The coin_type index of this node. Must be a non-negative * integer. + * @returns The derived {@link BIP44CoinTypeNode} for the specified + * {@link JsonBIP44Node} and `coin_type`. */ - static async fromJSON(json: JsonBIP44Node, coin_type: number) { + static fromJSON(json: JsonBIP44Node, coin_type: number) { validateCoinType(coin_type); validateCoinTypeNodeDepth(json.depth); - const node = await BIP44Node.fromExtendedKey({ + const node = BIP44Node.fromExtendedKey({ depth: json.depth, index: json.index, parentFingerprint: json.parentFingerprint, @@ -107,11 +109,13 @@ export class BIP44CoinTypeNode implements BIP44CoinTypeNodeInterface { * `0 / 1 / 2 / 3 / 4 / 5` * * @param derivationPath - The derivation path for the key of this node. + * @returns The derived {@link BIP44CoinTypeNode} for the specified + * derivation path. */ - static async fromDerivationPath(derivationPath: CoinTypeHDPathTuple) { + static fromDerivationPath(derivationPath: CoinTypeHDPathTuple) { validateCoinTypeNodeDepth(derivationPath.length - 1); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath, }); @@ -143,8 +147,10 @@ export class BIP44CoinTypeNode implements BIP44CoinTypeNodeInterface { * @param node - The {@link BIP44Node} for the key of this node. * @param coin_type - The coin_type index of this node. Must be a non-negative * integer. + * @returns The derived {@link BIP44CoinTypeNode} for the specified + * {@link BIP44Node} and `coin_type`. */ - static async fromNode(node: BIP44Node, coin_type: number) { + static fromNode(node: BIP44Node, coin_type: number) { if (!(node instanceof BIP44Node)) { throw new Error('Invalid node: Expected an instance of BIP44Node.'); } @@ -152,8 +158,7 @@ export class BIP44CoinTypeNode implements BIP44CoinTypeNodeInterface { validateCoinType(coin_type); validateCoinTypeNodeDepth(node.depth); - // TODO: Make this function not async in a future version. - return Promise.resolve(new BIP44CoinTypeNode(node, coin_type)); + return new BIP44CoinTypeNode(node, coin_type); } readonly #node: BIP44Node; @@ -256,12 +261,12 @@ export class BIP44CoinTypeNode implements BIP44CoinTypeNodeInterface { * @param indices.address_index - The `address_index` index. * @returns The derived BIP-44 `address_index` node. */ - async deriveBIP44AddressKey({ + deriveBIP44AddressKey({ account = 0, change = 0, address_index, - }: CoinTypeToAddressIndices): Promise { - return await this.#node.derive( + }: CoinTypeToAddressIndices): BIP44Node { + return this.#node.derive( getBIP44CoinTypeToAddressPathTuple({ account, change, address_index }), ); } @@ -326,18 +331,18 @@ function validateCoinType(coin_type: unknown): asserts coin_type is number { * @param indices.address_index - The `address_index` index. * @returns The derived `address_index` key for the specified derivation path. */ -export async function deriveBIP44AddressKey( +export function deriveBIP44AddressKey( parentKeyOrNode: BIP44CoinTypeNode | JsonBIP44CoinTypeNode | string, { account = 0, change = 0, address_index }: CoinTypeToAddressIndices, -): Promise { +): BIP44Node { const path = getBIP44CoinTypeToAddressPathTuple({ account, change, address_index, }); - const node = await getNode(parentKeyOrNode); - const childNode = await deriveChildNode({ + const node = getNode(parentKeyOrNode); + const childNode = deriveChildNode({ path, node, }); @@ -352,7 +357,7 @@ export type BIP44AddressKeyDeriver = { * @returns The key corresponding to the path of this deriver and the * specified `address_index` value. */ - (address_index: number, isHardened?: boolean): Promise; + (address_index: number, isHardened?: boolean): BIP44Node; /** * A human-readable representation of the derivation path of this deriver @@ -394,22 +399,22 @@ export type BIP44AddressKeyDeriver = { * @returns The deriver function for the derivation path specified by the * `coin_type` node, `account`, and `change` indices. */ -export async function getBIP44AddressKeyDeriver( +export function getBIP44AddressKeyDeriver( node: BIP44CoinTypeNode | JsonBIP44CoinTypeNode | string, accountAndChangeIndices?: Omit, ) { const { account = 0, change = 0 } = accountAndChangeIndices ?? {}; - const actualNode = await getNode(node); + const actualNode = getNode(node); const accountNode = getHardenedBIP32NodeToken(account); const changeNode = getBIP32NodeToken(change); - const bip44AddressKeyDeriver: BIP44AddressKeyDeriver = async ( + const bip44AddressKeyDeriver: BIP44AddressKeyDeriver = ( address_index: number, isHardened = false, - ): Promise => { - const slip10Node = await deriveChildNode({ + ): BIP44Node => { + const slip10Node = deriveChildNode({ path: [ accountNode, changeNode, @@ -441,10 +446,11 @@ export async function getBIP44AddressKeyDeriver( * The depth of the node is validated to be a valid coin type node. * * @param node - A BIP-44 coin type node, JSON node or extended key. + * @returns The {@link BIP44CoinTypeNode} for the specified node. */ -async function getNode( +function getNode( node: BIP44CoinTypeNode | JsonBIP44CoinTypeNode | string, -): Promise { +): BIP44CoinTypeNode { if (node instanceof BIP44CoinTypeNode) { validateCoinTypeNodeDepth(node.depth); @@ -452,8 +458,8 @@ async function getNode( } if (typeof node === 'string') { - const bip44Node = await BIP44Node.fromExtendedKey(node); - const coinTypeNode = await BIP44CoinTypeNode.fromNode( + const bip44Node = BIP44Node.fromExtendedKey(node); + const coinTypeNode = BIP44CoinTypeNode.fromNode( bip44Node, bip44Node.index - BIP_32_HARDENED_OFFSET, ); diff --git a/src/BIP44Node.test.ts b/src/BIP44Node.test.ts index ba8f0193..b6a7c017 100644 --- a/src/BIP44Node.test.ts +++ b/src/BIP44Node.test.ts @@ -16,14 +16,14 @@ const defaultBip39BytesToken = mnemonicPhraseToBytes(fixtures.local.mnemonic); describe('BIP44Node', () => { describe('fromExtendedKey', () => { - it('initializes a new node from a private key', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new node from a private key', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); // Ethereum coin type node - const node = await BIP44Node.fromExtendedKey({ + const node = BIP44Node.fromExtendedKey({ privateKey, chainCode, depth: 2, @@ -43,14 +43,14 @@ describe('BIP44Node', () => { }); }); - it('initializes a new node from JSON', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new node from JSON', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); // Ethereum coin type node - const node = await BIP44Node.fromExtendedKey({ + const node = BIP44Node.fromExtendedKey({ privateKey, chainCode, depth: 2, @@ -58,14 +58,14 @@ describe('BIP44Node', () => { index: 0, }); - expect(await BIP44Node.fromJSON(node.toJSON())).toStrictEqual(node); + expect(BIP44Node.fromJSON(node.toJSON())).toStrictEqual(node); }); it.each(fixtures.bip32)( 'initializes a node from an extended public key', - async ({ keys }) => { + ({ keys }) => { for (const key of keys) { - const node = await BIP44Node.fromExtendedKey(key.extPubKey); + const node = BIP44Node.fromExtendedKey(key.extPubKey); expect(node.privateKey).toBeUndefined(); expect(node.publicKey).toBe(key.publicKey); } @@ -74,22 +74,22 @@ describe('BIP44Node', () => { it.each(fixtures.bip32)( 'initializes a node from an extended private key', - async ({ keys }) => { + ({ keys }) => { for (const key of keys) { - const node = await BIP44Node.fromExtendedKey(key.extPrivKey); + const node = BIP44Node.fromExtendedKey(key.extPrivKey); expect(node.privateKey).toBe(key.privateKey); expect(node.publicKey).toBe(key.publicKey); } }, ); - it('throws if the depth is invalid', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('throws if the depth is invalid', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - await expect( + expect(() => BIP44Node.fromExtendedKey({ depth: 6, privateKey, @@ -97,15 +97,15 @@ describe('BIP44Node', () => { parentFingerprint: 0, index: 0, }), - ).rejects.toThrow( + ).toThrow( `Invalid HD tree path depth: The depth must be a positive integer N such that 0 <= N <= 5. Received: "6"`, ); }); it.each(fixtures.bip32InvalidExtendedKeys)( 'throws if the extended key is invalid', - async (extendedKey) => { - await expect(BIP44Node.fromExtendedKey(extendedKey)).rejects.toThrow( + (extendedKey) => { + expect(() => BIP44Node.fromExtendedKey(extendedKey)).toThrow( /Invalid extended key: .*\./u, ); }, @@ -113,9 +113,9 @@ describe('BIP44Node', () => { }); describe('fromDerivationPath', () => { - it('initializes a new node from a derivation path', async () => { + it('initializes a new node from a derivation path', () => { // Ethereum coin type node - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -135,8 +135,8 @@ describe('BIP44Node', () => { }); }); - it('initializes a new node from a derivation path with a Uint8Array', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('initializes a new node from a derivation path with a Uint8Array', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39BytesToken, BIP44PurposeNodeToken, @@ -144,7 +144,7 @@ describe('BIP44Node', () => { ], }); - const stringNode = await BIP44Node.fromDerivationPath({ + const stringNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -155,8 +155,8 @@ describe('BIP44Node', () => { expect(node.toJSON()).toStrictEqual(stringNode.toJSON()); }); - it('throws an error if attempting to modify the fields of a node', async () => { - const node: any = await BIP44Node.fromDerivationPath({ + it('throws an error if attempting to modify the fields of a node', () => { + const node: any = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -177,26 +177,26 @@ describe('BIP44Node', () => { }); }); - it('throws if the derivation path is of depth 0 and not a single BIP-39 node', async () => { - await expect( + it('throws if the derivation path is of depth 0 and not a single BIP-39 node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [`bip32:0'`] as any }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: The "m" / seed node (depth 0) must be a BIP-39 node.', ); }); - it('throws if the depth 1 node of the derivation path is not the BIP-44 purpose node', async () => { - await expect( + it('throws if the depth 1 node of the derivation path is not the BIP-44 purpose node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, `bip32:43'`] as any, }), - ).rejects.toThrow( + ).toThrow( `Invalid derivation path: The "purpose" node (depth 1) must be the string "${BIP44PurposeNodeToken}".`, ); }); - it('throws if the depth 2 node of the derivation path is not a hardened BIP-32 node', async () => { - await expect( + it('throws if the depth 2 node of the derivation path is not a hardened BIP-32 node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, @@ -204,13 +204,13 @@ describe('BIP44Node', () => { `bip32:60`, ] as any, }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: The "coin_type" node (depth 2) must be a hardened BIP-32 node.', ); }); - it('throws if the depth 3 node of the derivation path is not a hardened BIP-32 node', async () => { - await expect( + it('throws if the depth 3 node of the derivation path is not a hardened BIP-32 node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, @@ -219,13 +219,13 @@ describe('BIP44Node', () => { `bip32:0`, ] as any, }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: The "account" node (depth 3) must be a hardened BIP-32 node.', ); }); - it('throws if the depth 4 node of the derivation path is not a BIP-32 node', async () => { - await expect( + it('throws if the depth 4 node of the derivation path is not a BIP-32 node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, @@ -235,13 +235,13 @@ describe('BIP44Node', () => { `bip32:-1`, ], }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: The "change" node (depth 4) must be a BIP-32 node.', ); }); - it('throws if the depth 5 node of the derivation path is not a BIP-32 node', async () => { - await expect( + it('throws if the depth 5 node of the derivation path is not a BIP-32 node', () => { + expect(() => BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, @@ -252,16 +252,16 @@ describe('BIP44Node', () => { `bip32:-1`, ], }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: The "address_index" node (depth 5) must be a BIP-32 node.', ); }); }); describe('derive', () => { - it('derives a child node', async () => { + it('derives a child node', () => { const coinTypeNode = `bip32:40'`; - const targetNode = await BIP44Node.fromDerivationPath({ + const targetNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -269,11 +269,11 @@ describe('BIP44Node', () => { ], }); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, BIP44PurposeNodeToken], }); - const childNode = await node.derive([coinTypeNode]); + const childNode = node.derive([coinTypeNode]); expect(childNode).toMatchObject({ depth: targetNode.depth, @@ -282,9 +282,9 @@ describe('BIP44Node', () => { }); }); - it('derives a public child node', async () => { + it('derives a public child node', () => { const coinTypeNode = `bip32:40'`; - const targetNode = await BIP44Node.fromDerivationPath({ + const targetNode = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -294,7 +294,7 @@ describe('BIP44Node', () => { ], }); - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -303,15 +303,15 @@ describe('BIP44Node', () => { ], }); - const childNode = await node.neuter().derive([`bip32:0`]); + const childNode = node.neuter().derive([`bip32:0`]); expect(childNode.privateKey).toBeUndefined(); expect(childNode.depth).toBe(targetNode.depth); expect(childNode.publicKey).toBe(targetNode.publicKey); }); - it('throws if the parent node is already a leaf node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the parent node is already a leaf node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -322,13 +322,13 @@ describe('BIP44Node', () => { ], }); - await expect(node.derive([`bip32:1`])).rejects.toThrow( + expect(() => node.derive([`bip32:1`])).toThrow( 'Illegal operation: This HD tree node is already a leaf node.', ); }); - it('throws if the child derivation path is zero', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the child derivation path is zero', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -337,33 +337,33 @@ describe('BIP44Node', () => { ], }); - await expect(node.derive([] as any)).rejects.toThrow( + expect(() => node.derive([] as any)).toThrow( 'Invalid HD tree derivation path: Deriving a path of length 0 is not defined', ); }); - it('throws if the depth 1 node of the derivation path is not the BIP-44 purpose node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the depth 1 node of the derivation path is not the BIP-44 purpose node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken], }); - await expect(node.derive([`bip32:43'`])).rejects.toThrow( + expect(() => node.derive([`bip32:43'`])).toThrow( `Invalid derivation path: The "purpose" node (depth 1) must be the string "${BIP44PurposeNodeToken}".`, ); }); - it('throws if the depth 2 node of the derivation path is not a hardened BIP-32 node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the depth 2 node of the derivation path is not a hardened BIP-32 node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, BIP44PurposeNodeToken], }); - await expect(node.derive([`bip32:60`])).rejects.toThrow( + expect(() => node.derive([`bip32:60`])).toThrow( 'Invalid derivation path: The "coin_type" node (depth 2) must be a hardened BIP-32 node.', ); }); - it('throws if the depth 3 node of the derivation path is not a hardened BIP-32 node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the depth 3 node of the derivation path is not a hardened BIP-32 node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -371,13 +371,13 @@ describe('BIP44Node', () => { ], }); - await expect(node.derive([`bip32:0`])).rejects.toThrow( + expect(() => node.derive([`bip32:0`])).toThrow( 'Invalid derivation path: The "account" node (depth 3) must be a hardened BIP-32 node.', ); }); - it('throws if the depth 4 node of the derivation path is not a BIP-32 node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the depth 4 node of the derivation path is not a BIP-32 node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -386,13 +386,13 @@ describe('BIP44Node', () => { ], }); - await expect(node.derive([`bip32:-1'`])).rejects.toThrow( + expect(() => node.derive([`bip32:-1'`])).toThrow( 'Invalid derivation path: The "change" node (depth 4) must be a BIP-32 node.', ); }); - it('throws if the depth 5 node of the derivation path is not a BIP-32 node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('throws if the depth 5 node of the derivation path is not a BIP-32 node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -402,7 +402,7 @@ describe('BIP44Node', () => { ], }); - await expect(node.derive([`bip32:-1`])).rejects.toThrow( + expect(() => node.derive([`bip32:-1`])).toThrow( 'Invalid derivation path: The "address_index" node (depth 5) must be a BIP-32 node.', ); }); @@ -414,13 +414,13 @@ describe('BIP44Node', () => { it.each(sampleAddressIndices)( 'returns the public key for an secp256k1 node', - async ({ index, publicKey }) => { - const { privateKey, chainCode } = await createBip39KeyFromSeed( + ({ index, publicKey }) => { + const { privateKey, chainCode } = createBip39KeyFromSeed( hexStringToBytes(hexSeed), secp256k1, ); - const node = await BIP44Node.fromExtendedKey({ + const node = BIP44Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -428,10 +428,7 @@ describe('BIP44Node', () => { index: 0, }); - const childNode = await node.derive([ - ...path.ours.tuple, - `bip32:${index}`, - ]); + const childNode = node.derive([...path.ours.tuple, `bip32:${index}`]); expect(childNode.publicKey).toBe(publicKey); }, @@ -444,13 +441,13 @@ describe('BIP44Node', () => { it.each(sampleAddressIndices)( 'returns the address for an secp256k1 node', - async ({ index, address }) => { - const { privateKey, chainCode } = await createBip39KeyFromSeed( + ({ index, address }) => { + const { privateKey, chainCode } = createBip39KeyFromSeed( hexStringToBytes(hexSeed), secp256k1, ); - const node = await BIP44Node.fromExtendedKey({ + const node = BIP44Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -458,10 +455,7 @@ describe('BIP44Node', () => { index: 0, }); - const childNode = await node.derive([ - ...path.ours.tuple, - `bip32:${index}`, - ]); + const childNode = node.derive([...path.ours.tuple, `bip32:${index}`]); expect(childNode.address).toBe(address); }, @@ -469,8 +463,8 @@ describe('BIP44Node', () => { }); describe('compressedPublicKey', () => { - it('returns the public key in compressed form', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns the public key in compressed form', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -486,8 +480,8 @@ describe('BIP44Node', () => { }); describe('compressedPublicKeyBytes', () => { - it('returns the public key in compressed form', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns the public key in compressed form', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -503,8 +497,8 @@ describe('BIP44Node', () => { }); describe('extendedKey', () => { - it('returns the extended private key for nodes with a private key', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns the extended private key for nodes with a private key', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -525,8 +519,8 @@ describe('BIP44Node', () => { expect(node.extendedKey).toStrictEqual(extendedKey); }); - it('returns the extended public key for nodes with a public key', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns the extended public key for nodes with a public key', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -551,8 +545,8 @@ describe('BIP44Node', () => { }); describe('neuter', () => { - it('returns a BIP-44 node without a private key', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns a BIP-44 node without a private key', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -570,8 +564,8 @@ describe('BIP44Node', () => { }); describe('toJSON', () => { - it('returns a JSON-compatible representation of the node', async () => { - const node = await BIP44Node.fromDerivationPath({ + it('returns a JSON-compatible representation of the node', () => { + const node = BIP44Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, diff --git a/src/BIP44Node.ts b/src/BIP44Node.ts index b7f93eb3..bed5b62a 100644 --- a/src/BIP44Node.ts +++ b/src/BIP44Node.ts @@ -109,8 +109,9 @@ export class BIP44Node implements BIP44NodeInterface { * for documentation. * * @param json - The JSON representation of a SLIP-10 node. + * @returns The {@link BIP44Node} corresponding to the given JSON. */ - static async fromJSON(json: JsonBIP44Node): Promise { + static fromJSON(json: JsonBIP44Node): BIP44Node { return BIP44Node.fromExtendedKey(json); } @@ -129,10 +130,10 @@ export class BIP44Node implements BIP44NodeInterface { * @param options.publicKey - The public key for the node. If a private key is * specified, this parameter is ignored. * @param options.chainCode - The chain code for the node. + * @returns The {@link BIP44Node} corresponding to the given key and chain + * code. */ - static async fromExtendedKey( - options: BIP44ExtendedKeyOptions | string, - ): Promise { + static fromExtendedKey(options: BIP44ExtendedKeyOptions | string): BIP44Node { if (typeof options === 'string') { const extendedKey = decodeExtendedKey(options); @@ -172,7 +173,7 @@ export class BIP44Node implements BIP44NodeInterface { validateBIP44Depth(depth); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, publicKey, chainCode, @@ -205,14 +206,15 @@ export class BIP44Node implements BIP44NodeInterface { * @param options - An object containing the derivation path. * @param options.derivationPath - The rooted HD tree path that will be used * to derive the key of this node. + * @returns The {@link BIP44Node} corresponding to the derived key. */ - static async fromDerivationPath({ + static fromDerivationPath({ derivationPath, - }: BIP44DerivationPathOptions): Promise { + }: BIP44DerivationPathOptions): BIP44Node { validateBIP44Depth(derivationPath.length - 1); validateBIP44DerivationPath(derivationPath, MIN_BIP_44_DEPTH); - const node = await SLIP10Node.fromDerivationPath({ + const node = SLIP10Node.fromDerivationPath({ derivationPath, curve: 'secp256k1', }); @@ -341,7 +343,7 @@ export class BIP44Node implements BIP44NodeInterface { * to derive a child key from the parent key contained within this node. * @returns The {@link BIP44Node} corresponding to the derived child key. */ - public async derive(path: PartialHDPathTuple): Promise { + public derive(path: PartialHDPathTuple): BIP44Node { if (this.depth === MAX_BIP_44_DEPTH) { throw new Error( 'Illegal operation: This HD tree node is already a leaf node.', @@ -353,7 +355,7 @@ export class BIP44Node implements BIP44NodeInterface { validateBIP44Depth(newDepth); validateBIP44DerivationPath(path, (this.depth + 1) as BIP44Depth); - const node = await this.#node.derive(path); + const node = this.#node.derive(path); return new BIP44Node(node); } diff --git a/src/SLIP10Node.test.ts b/src/SLIP10Node.test.ts index 77e9ef10..aabe4ad6 100644 --- a/src/SLIP10Node.test.ts +++ b/src/SLIP10Node.test.ts @@ -13,8 +13,8 @@ const defaultBip39BytesToken = mnemonicPhraseToBytes(fixtures.local.mnemonic); describe('SLIP10Node', () => { describe('constructor', () => { - it('throws an error when the constructor guard is not provided', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('throws an error when the constructor guard is not provided', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); @@ -38,13 +38,13 @@ describe('SLIP10Node', () => { }); describe('fromExtendedKey', () => { - it('initializes a new node from a private key', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new node from a private key', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -58,13 +58,13 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(65); }); - it('initializes a new node from a hexadecimal private key and chain code', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new node from a hexadecimal private key and chain code', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -78,13 +78,13 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(65); }); - it('initializes a new ed25519 node from a private key', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new ed25519 node from a private key', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: ed25519, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -98,8 +98,8 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(33); }); - it('initializes a new ed25519 node from a zero private key', async () => { - const node = await SLIP10Node.fromExtendedKey({ + it('initializes a new ed25519 node from a zero private key', () => { + const node = SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(0), chainCode: new Uint8Array(32).fill(1), depth: 0, @@ -113,13 +113,13 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(33); }); - it('initializes a new node from a public key', async () => { - const { publicKeyBytes, chainCodeBytes } = await deriveChildKey({ + it('initializes a new node from a public key', () => { + const { publicKeyBytes, chainCodeBytes } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ publicKey: publicKeyBytes, chainCode: chainCodeBytes, depth: 0, @@ -133,13 +133,13 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(65); }); - it('initializes a new ed25519 node from a public key', async () => { - const { publicKeyBytes, chainCodeBytes } = await deriveChildKey({ + it('initializes a new ed25519 node from a public key', () => { + const { publicKeyBytes, chainCodeBytes } = deriveChildKey({ path: fixtures.local.mnemonic, curve: ed25519, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ publicKey: publicKeyBytes, chainCode: chainCodeBytes, depth: 0, @@ -153,13 +153,13 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(33); }); - it('initializes a new node from a hexadecimal public key and chain code', async () => { - const { publicKey, chainCode } = await deriveChildKey({ + it('initializes a new node from a hexadecimal public key and chain code', () => { + const { publicKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ publicKey, chainCode, depth: 0, @@ -173,22 +173,22 @@ describe('SLIP10Node', () => { expect(node.publicKeyBytes).toHaveLength(65); }); - it('initializes a new node from JSON', async () => { - const node = await deriveChildKey({ + it('initializes a new node from JSON', () => { + const node = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - expect(await SLIP10Node.fromJSON(node.toJSON())).toStrictEqual(node); + expect(SLIP10Node.fromJSON(node.toJSON())).toStrictEqual(node); }); - it('initializes a new node from JSON with a public key', async () => { - const { privateKey, chainCode } = await deriveChildKey({ + it('initializes a new node from JSON with a public key', () => { + const { privateKey, chainCode } = deriveChildKey({ path: fixtures.local.mnemonic, curve: secp256k1, }); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, depth: 0, @@ -199,13 +199,13 @@ describe('SLIP10Node', () => { const neuteredNode = node.neuter(); - expect(await SLIP10Node.fromJSON(neuteredNode.toJSON())).toStrictEqual( + expect(SLIP10Node.fromJSON(neuteredNode.toJSON())).toStrictEqual( neuteredNode, ); }); - it('throws if no public or private key is specified', async () => { - await expect( + it('throws if no public or private key is specified', () => { + expect(() => SLIP10Node.fromExtendedKey({ chainCode: new Uint8Array(32).fill(1), depth: 0, @@ -213,12 +213,12 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid options: Must provide either a private key or a public key.', ); }); - it('throws if the depth is invalid', async () => { + it('throws if the depth is invalid', () => { const inputs = [ -1, 0.1, @@ -233,7 +233,7 @@ describe('SLIP10Node', () => { ]; for (const input of inputs) { - await expect( + expect(() => SLIP10Node.fromExtendedKey({ depth: input as any, parentFingerprint: 0, @@ -242,7 +242,7 @@ describe('SLIP10Node', () => { chainCode: new Uint8Array(32).fill(1), curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( `Invalid HD tree path depth: The depth must be a positive integer. Received: "${String( input, )}"`, @@ -250,7 +250,7 @@ describe('SLIP10Node', () => { } }); - it('throws if the parent fingerprint is invalid', async () => { + it('throws if the parent fingerprint is invalid', () => { const inputs = [ -1, 0.1, @@ -265,7 +265,7 @@ describe('SLIP10Node', () => { ]; for (const input of inputs) { - await expect( + expect(() => SLIP10Node.fromExtendedKey({ depth: 0, parentFingerprint: input as any, @@ -274,7 +274,7 @@ describe('SLIP10Node', () => { chainCode: new Uint8Array(32).fill(1), curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( `Invalid parent fingerprint: The fingerprint must be a positive integer. Received: "${String( input, )}"`, @@ -282,8 +282,8 @@ describe('SLIP10Node', () => { } }); - it('throws if the private key is invalid', async () => { - await expect( + it('throws if the private key is invalid', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: 'foo', chainCode: new Uint8Array(32).fill(1), @@ -292,11 +292,11 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow('Value must be a hexadecimal string.'); + ).toThrow('Value must be a hexadecimal string.'); }); - it('throws if the private key is not a Uint8Array or hexadecimal string', async () => { - await expect( + it('throws if the private key is not a Uint8Array or hexadecimal string', () => { + expect(() => SLIP10Node.fromExtendedKey({ // @ts-expect-error Invalid private key type. privateKey: 123, @@ -306,13 +306,13 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid value: Expected an instance of Uint8Array or hexadecimal string.', ); }); - it('throws if the private key is zero for secp256k1', async () => { - await expect( + it('throws if the private key is zero for secp256k1', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(0), chainCode: new Uint8Array(32).fill(1), @@ -321,13 +321,13 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid private key: Value is not a valid secp256k1 private key.', ); }); - it('throws if the depth is zero and the parent fingerprint is not zero', async () => { - await expect( + it('throws if the depth is zero and the parent fingerprint is not zero', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(1), chainCode: new Uint8Array(32).fill(1), @@ -336,13 +336,13 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid parent fingerprint: The fingerprint of the root node must be 0. Received: "1".', ); }); - it('throws if the depth is not zero and the parent fingerprint is zero', async () => { - await expect( + it('throws if the depth is not zero and the parent fingerprint is zero', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(1), chainCode: new Uint8Array(32).fill(1), @@ -351,13 +351,13 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid parent fingerprint: The fingerprint of a child node must not be 0. Received: "0".', ); }); - it('throws if the depth is >= 2 and the parent fingerprint is equal to the master fingerprint', async () => { - await expect( + it('throws if the depth is >= 2 and the parent fingerprint is equal to the master fingerprint', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(1), chainCode: new Uint8Array(32).fill(1), @@ -367,13 +367,13 @@ describe('SLIP10Node', () => { index: 0, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid parent fingerprint: The fingerprint of a child node cannot be equal to the master fingerprint. Received: "1".', ); }); - it('throws if the depth is zero and the index is not zero', async () => { - await expect( + it('throws if the depth is zero and the index is not zero', () => { + expect(() => SLIP10Node.fromExtendedKey({ privateKey: new Uint8Array(32).fill(1), chainCode: new Uint8Array(32).fill(1), @@ -382,16 +382,16 @@ describe('SLIP10Node', () => { index: 1, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid index: The index of the root node must be 0. Received: "1".', ); }); }); describe('fromDerivationPath', () => { - it('initializes a new node from a derivation path', async () => { + it('initializes a new node from a derivation path', () => { // Ethereum coin type node - const node = await SLIP10Node.fromDerivationPath({ + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -413,8 +413,8 @@ describe('SLIP10Node', () => { }); }); - it('initializes a new node from a derivation path with a Uint8Array', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('initializes a new node from a derivation path with a Uint8Array', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39BytesToken, BIP44PurposeNodeToken, @@ -423,7 +423,7 @@ describe('SLIP10Node', () => { curve: 'secp256k1', }); - const stringNode = await SLIP10Node.fromDerivationPath({ + const stringNode = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -435,13 +435,13 @@ describe('SLIP10Node', () => { expect(node.toJSON()).toStrictEqual(stringNode.toJSON()); }); - it('initializes a new node from a derivation path with a Uint8Array using ed25519', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('initializes a new node from a derivation path with a Uint8Array using ed25519', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39BytesToken, `slip10:44'`, `slip10:60'`], curve: 'ed25519', }); - const stringNode = await SLIP10Node.fromDerivationPath({ + const stringNode = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, `slip10:44'`, `slip10:60'`], curve: 'ed25519', }); @@ -449,39 +449,39 @@ describe('SLIP10Node', () => { expect(node.toJSON()).toStrictEqual(stringNode.toJSON()); }); - it('throws if the derivation path is empty', async () => { - await expect( + it('throws if the derivation path is empty', () => { + expect(() => SLIP10Node.fromDerivationPath({ derivationPath: [] as any, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid derivation path: May not specify an empty derivation path.', ); }); - it('throws if no derivation path is specified', async () => { - await expect( + it('throws if no derivation path is specified', () => { + expect(() => // @ts-expect-error No derivation path specified SLIP10Node.fromDerivationPath({ curve: 'secp256k1', }), - ).rejects.toThrow('Invalid options: Must provide a derivation path.'); + ).toThrow('Invalid options: Must provide a derivation path.'); }); - it('throws if the derivation path is of depth 0 and not a single BIP-39 node', async () => { - await expect( + it('throws if the derivation path is of depth 0 and not a single BIP-39 node', () => { + expect(() => SLIP10Node.fromDerivationPath({ derivationPath: [`bip32:0'`] as any, curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( 'Invalid HD path segment: The segment must consist of a single BIP-39 node for depths of 0. Received: "bip32:0\'".', ); }); - it('throws an error if attempting to modify the fields of a node', async () => { - const node: any = await SLIP10Node.fromDerivationPath({ + it('throws an error if attempting to modify the fields of a node', () => { + const node: any = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -510,30 +510,30 @@ describe('SLIP10Node', () => { ); }); - it('throws an error if no curve is specified', async () => { - await expect( + it('throws an error if no curve is specified', () => { + expect(() => // @ts-expect-error No curve specified, but required in type SLIP10Node.fromDerivationPath({}), - ).rejects.toThrow('Invalid curve: Must specify a curve.'); + ).toThrow('Invalid curve: Must specify a curve.'); }); - it('throws an error for unsupported curves', async () => { - await expect( + it('throws an error for unsupported curves', () => { + expect(() => SLIP10Node.fromDerivationPath({ // @ts-expect-error Invalid curve name for type curve: 'foo bar', specification: 'bip32', }), - ).rejects.toThrow( + ).toThrow( 'Invalid curve: Only the following curves are supported: secp256k1, ed25519.', ); }); }); describe('derive', () => { - it('derives a child node', async () => { + it('derives a child node', () => { const coinTypeNode = `bip32:40'`; - const targetNode = await SLIP10Node.fromDerivationPath({ + const targetNode = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -542,12 +542,12 @@ describe('SLIP10Node', () => { curve: 'secp256k1', }); - const node = await SLIP10Node.fromDerivationPath({ + const node = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, BIP44PurposeNodeToken], curve: 'secp256k1', }); - const childNode = await node.derive([coinTypeNode]); + const childNode = node.derive([coinTypeNode]); expect(childNode).toMatchObject({ depth: targetNode.depth, @@ -556,18 +556,18 @@ describe('SLIP10Node', () => { }); }); - it('derives a public child node', async () => { - const targetNode = await SLIP10Node.fromDerivationPath({ + it('derives a public child node', () => { + const targetNode = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, 'bip32:0'], curve: 'secp256k1', }); - const node = await SLIP10Node.fromDerivationPath({ + const node = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken], curve: 'secp256k1', - }).then((privateNode) => privateNode.neuter()); + }).neuter(); - const childNode = await node.derive(['bip32:0']); + const childNode = node.derive(['bip32:0']); expect(childNode.privateKey).toBeUndefined(); expect(childNode).toMatchObject({ @@ -576,19 +576,19 @@ describe('SLIP10Node', () => { }); }); - it('throws when trying to derive a hardened node without a private key', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws when trying to derive a hardened node without a private key', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, BIP44PurposeNodeToken], curve: 'secp256k1', }); - await expect(node.neuter().derive([`bip32:0'`])).rejects.toThrow( + expect(() => node.neuter().derive([`bip32:0'`])).toThrow( 'Invalid parameters: Cannot derive hardened child keys without a private key.', ); }); - it('throws if the child derivation path is zero', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws if the child derivation path is zero', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -598,13 +598,13 @@ describe('SLIP10Node', () => { curve: 'secp256k1', }); - await expect(node.derive([] as any)).rejects.toThrow( + expect(() => node.derive([] as any)).toThrow( 'Invalid HD tree derivation path: Deriving a path of length 0 is not defined', ); }); - it('throws when trying to derive a unhardened node with ed25519', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws when trying to derive a unhardened node with ed25519', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, `slip10:44'`, @@ -614,7 +614,7 @@ describe('SLIP10Node', () => { curve: 'ed25519', }); - await expect(node.derive(['slip10:0'])).rejects.toThrow( + expect(() => node.derive(['slip10:0'])).toThrow( 'Invalid path: Cannot derive unhardened child keys with ed25519.', ); }); @@ -628,14 +628,14 @@ describe('SLIP10Node', () => { it.each(slip10)( 'returns the public key for an ed25519 node', - async ({ hexSeed, keys }) => { - const { privateKey, chainCode } = await createBip39KeyFromSeed( + ({ hexSeed, keys }) => { + const { privateKey, chainCode } = createBip39KeyFromSeed( hexStringToBytes(hexSeed), ed25519, ); for (const { path, publicKey } of keys) { - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, curve: 'ed25519', @@ -648,7 +648,7 @@ describe('SLIP10Node', () => { continue; } - const childNode = await node.derive(path.ours.tuple); + const childNode = node.derive(path.ours.tuple); expect(childNode.publicKey).toBe(publicKey); } }, @@ -656,14 +656,14 @@ describe('SLIP10Node', () => { it.each(bip32)( 'returns the public key for an secp256k1 node', - async ({ hexSeed, keys }) => { - const { privateKey, chainCode } = await createBip39KeyFromSeed( + ({ hexSeed, keys }) => { + const { privateKey, chainCode } = createBip39KeyFromSeed( hexStringToBytes(hexSeed), secp256k1, ); for (const { path, publicKey } of keys) { - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, curve: 'secp256k1', @@ -676,7 +676,7 @@ describe('SLIP10Node', () => { continue; } - const childNode = await node.derive(path.ours.tuple); + const childNode = node.derive(path.ours.tuple); expect(childNode.publicKey).toBe(publicKey); } }, @@ -684,8 +684,8 @@ describe('SLIP10Node', () => { }); describe('compressedPublicKey', () => { - it('returns the public key in compressed form', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('returns the public key in compressed form', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -702,8 +702,8 @@ describe('SLIP10Node', () => { }); describe('compressedPublicKeyBytes', () => { - it('returns the public key in compressed form', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('returns the public key in compressed form', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -725,13 +725,13 @@ describe('SLIP10Node', () => { it.each(sampleAddressIndices)( 'returns the address for an secp256k1 node', - async ({ index, address }) => { - const { privateKey, chainCode } = await createBip39KeyFromSeed( + ({ index, address }) => { + const { privateKey, chainCode } = createBip39KeyFromSeed( hexToBytes(hexSeed), secp256k1, ); - const node = await SLIP10Node.fromExtendedKey({ + const node = SLIP10Node.fromExtendedKey({ privateKey, chainCode, curve: 'secp256k1', @@ -740,17 +740,14 @@ describe('SLIP10Node', () => { index: 0, }); - const childNode = await node.derive([ - ...path.ours.tuple, - `bip32:${index}`, - ]); + const childNode = node.derive([...path.ours.tuple, `bip32:${index}`]); expect(childNode.address).toBe(address); }, ); - it('throws an error when trying to get an address for an ed25519 node', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws an error when trying to get an address for an ed25519 node', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken, `slip10:44'`, `slip10:60'`], curve: 'ed25519', }); @@ -762,8 +759,8 @@ describe('SLIP10Node', () => { }); describe('fingerprint', () => { - it('returns the fingerprint for a public key', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('returns the fingerprint for a public key', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -777,16 +774,13 @@ describe('SLIP10Node', () => { }); describe('masterFingerprint', () => { - it('returns the master fingerprint for a node', async () => { - const masterNode = await SLIP10Node.fromDerivationPath({ + it('returns the master fingerprint for a node', () => { + const masterNode = SLIP10Node.fromDerivationPath({ derivationPath: [defaultBip39NodeToken], curve: 'secp256k1', }); - const node = await masterNode.derive([ - BIP44PurposeNodeToken, - `bip32:60'`, - ]); + const node = masterNode.derive([BIP44PurposeNodeToken, `bip32:60'`]); expect(node.masterFingerprint).toBe(3293725253); expect(node.masterFingerprint).toBe(masterNode.fingerprint); @@ -794,8 +788,8 @@ describe('SLIP10Node', () => { }); describe('neuter', () => { - it('returns a SLIP-10 node without a private key', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('returns a SLIP-10 node without a private key', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, @@ -814,8 +808,8 @@ describe('SLIP10Node', () => { }); describe('toJSON', () => { - it('returns a JSON-compatible representation of the node', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('returns a JSON-compatible representation of the node', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [ defaultBip39NodeToken, BIP44PurposeNodeToken, diff --git a/src/SLIP10Node.ts b/src/SLIP10Node.ts index a2c0cb2d..69f12ff4 100644 --- a/src/SLIP10Node.ts +++ b/src/SLIP10Node.ts @@ -118,8 +118,9 @@ export class SLIP10Node implements SLIP10NodeInterface { * for documentation. * * @param json - The JSON representation of a SLIP-10 node. + * @returns A SLIP-10 node from the given JSON. */ - static async fromJSON(json: JsonSLIP10Node): Promise { + static fromJSON(json: JsonSLIP10Node): SLIP10Node { return SLIP10Node.fromExtendedKey(json); } @@ -144,8 +145,9 @@ export class SLIP10Node implements SLIP10NodeInterface { * specified, this parameter is ignored. * @param options.chainCode - The chain code for the node. * @param options.curve - The curve used by the node. + * @returns A SLIP-10 node from the given options. */ - static async fromExtendedKey({ + static fromExtendedKey({ depth, masterFingerprint, parentFingerprint, @@ -185,7 +187,7 @@ export class SLIP10Node implements SLIP10NodeInterface { index, chainCode: chainCodeBytes, privateKey: privateKeyBytes, - publicKey: await curveObject.getPublicKey(privateKeyBytes), + publicKey: curveObject.getPublicKey(privateKeyBytes), curve, }, this.#constructorGuard, @@ -237,7 +239,7 @@ export class SLIP10Node implements SLIP10NodeInterface { * @param options.curve - The curve used by the node. * @returns A new SLIP-10 node. */ - static async fromDerivationPath({ + static fromDerivationPath({ derivationPath, curve, }: SLIP10DerivationPathOptions) { @@ -253,7 +255,7 @@ export class SLIP10Node implements SLIP10NodeInterface { ); } - return await deriveKeyFromPath({ + return deriveKeyFromPath({ path: derivationPath, depth: derivationPath.length - 1, curve, @@ -377,8 +379,8 @@ export class SLIP10Node implements SLIP10NodeInterface { * to derive a child key from the parent key contained within this node. * @returns The {@link SLIP10Node} corresponding to the derived child key. */ - public async derive(path: SLIP10PathTuple): Promise { - return await deriveChildNode({ + public derive(path: SLIP10PathTuple): SLIP10Node { + return deriveChildNode({ path, node: this, }); @@ -512,10 +514,10 @@ type DeriveChildNodeArgs = { * @param options.path - The path to the child node / key. * @returns The derived key and depth. */ -export async function deriveChildNode({ +export function deriveChildNode({ path, node, -}: DeriveChildNodeArgs): Promise { +}: DeriveChildNodeArgs): SLIP10Node { if (path.length === 0) { throw new Error( 'Invalid HD tree derivation path: Deriving a path of length 0 is not defined.', @@ -527,7 +529,7 @@ export async function deriveChildNode({ const newDepth = node.depth + path.length; validateBIP32Depth(newDepth); - return await deriveKeyFromPath({ + return deriveKeyFromPath({ path, node, depth: newDepth, diff --git a/src/curves/curve.ts b/src/curves/curve.ts index 378d1ec7..3f5ec467 100644 --- a/src/curves/curve.ts +++ b/src/curves/curve.ts @@ -16,10 +16,7 @@ export type Curve = { curve: { n: bigint; }; - getPublicKey: ( - privateKey: Uint8Array, - compressed?: boolean, - ) => Uint8Array | Promise; + getPublicKey: (privateKey: Uint8Array, compressed?: boolean) => Uint8Array; isValidPrivateKey: (privateKey: Uint8Array) => boolean; publicAdd: (publicKey: Uint8Array, tweak: Uint8Array) => Uint8Array; compressPublicKey: (publicKey: Uint8Array) => Uint8Array; diff --git a/src/curves/ed25519.test.ts b/src/curves/ed25519.test.ts index 08267687..87dbe137 100644 --- a/src/curves/ed25519.test.ts +++ b/src/curves/ed25519.test.ts @@ -36,7 +36,7 @@ describe('ed25519', () => { it.each(slip10)( 'returns the 0-padded public key for a private key', - async ({ keys }) => { + ({ keys }) => { for (const { privateKey, publicKey } of keys) { expect(bytesToHex(getPublicKey(hexToBytes(privateKey)))).toBe( publicKey, diff --git a/src/derivation.test.ts b/src/derivation.test.ts index 734fbb20..6c2c6ebf 100755 --- a/src/derivation.test.ts +++ b/src/derivation.test.ts @@ -24,33 +24,31 @@ const ethereumBip32PathParts = [ describe('derivation', () => { describe('deriveKeyFromPath', () => { - it('derives full BIP-44 paths', async () => { + it('derives full BIP-44 paths', () => { // generate keys - const keys = await Promise.all( - expectedAddresses.map(async (_, index) => { - const bip32Part = [ - ...ethereumBip32PathParts, - getUnhardenedBIP32NodeToken(index), - ] as const; - - const bip39Part = bip39MnemonicToMultipath(mnemonic); - const multipath = [bip39Part, ...bip32Part] as HDPathTuple; - - expect(multipath).toStrictEqual([ - `bip39:${mnemonic}`, - `bip32:44'`, - `bip32:60'`, - `bip32:0'`, - `bip32:0`, - `bip32:${index}`, - ]); - - return deriveKeyFromPath({ - path: multipath, - curve: 'secp256k1', - }); - }), - ); + const keys = expectedAddresses.map((_, index) => { + const bip32Part = [ + ...ethereumBip32PathParts, + getUnhardenedBIP32NodeToken(index), + ] as const; + + const bip39Part = bip39MnemonicToMultipath(mnemonic); + const multipath = [bip39Part, ...bip32Part] as HDPathTuple; + + expect(multipath).toStrictEqual([ + `bip39:${mnemonic}`, + `bip32:44'`, + `bip32:60'`, + `bip32:0'`, + `bip32:0`, + `bip32:${index}`, + ]); + + return deriveKeyFromPath({ + path: multipath, + curve: 'secp256k1', + }); + }); // validate addresses keys.forEach(({ privateKeyBytes }, index) => { @@ -59,25 +57,23 @@ describe('derivation', () => { }); }); - it('derives from Uint8Array BIP-39 nodes', async () => { - const keys = await Promise.all( - expectedAddresses.map(async (_, index) => { - const bip32Part = [ - ...ethereumBip32PathParts, - getUnhardenedBIP32NodeToken(index), - ] as const; - - const multipath = [ - mnemonicPhraseToBytes(mnemonic), - ...bip32Part, - ] as HDPathTuple; - - return deriveKeyFromPath({ - path: multipath, - curve: 'secp256k1', - }); - }), - ); + it('derives from Uint8Array BIP-39 nodes', () => { + const keys = expectedAddresses.map((_, index) => { + const bip32Part = [ + ...ethereumBip32PathParts, + getUnhardenedBIP32NodeToken(index), + ] as const; + + const multipath = [ + mnemonicPhraseToBytes(mnemonic), + ...bip32Part, + ] as HDPathTuple; + + return deriveKeyFromPath({ + path: multipath, + curve: 'secp256k1', + }); + }); // validate addresses keys.forEach(({ privateKeyBytes }, index) => { @@ -86,23 +82,21 @@ describe('derivation', () => { }); }); - it('derives the correct keys using a previously derived parent key', async () => { + it('derives the correct keys using a previously derived parent key', () => { // generate parent key const bip39Part = bip39MnemonicToMultipath(mnemonic); const multipath = [bip39Part, ...ethereumBip32PathParts] as HDPathTuple; - const node = await deriveKeyFromPath({ + const node = deriveKeyFromPath({ path: multipath, curve: 'secp256k1', }); - const keys = await Promise.all( - expectedAddresses.map(async (_, index) => { - return deriveKeyFromPath({ - path: [`bip32:${index}`], - node, - }); - }), - ); + const keys = expectedAddresses.map((_, index) => { + return deriveKeyFromPath({ + path: [`bip32:${index}`], + node, + }); + }); // validate addresses keys.forEach(({ privateKeyBytes }, index) => { @@ -111,166 +105,148 @@ describe('derivation', () => { }); }); - it('validates inputs', async () => { + it('validates inputs', () => { // generate parent key const bip39Part = bip39MnemonicToMultipath(mnemonic); const multipath = [bip39Part, ...ethereumBip32PathParts] as const; - const node = await deriveKeyFromPath({ + const node = deriveKeyFromPath({ path: multipath, curve: 'secp256k1', }); // Empty segments are forbidden - await expect(async () => - deriveKeyFromPath({ path: [], curve: 'secp256k1' }), - ).rejects.toThrow( + expect(() => deriveKeyFromPath({ path: [], curve: 'secp256k1' })).toThrow( /Invalid HD path segment: The segment must not be empty\./u, ); // Malformed multipaths are disallowed - await expect(async () => { + expect(() => { const [, ...rest] = multipath; return deriveKeyFromPath({ path: [bip39Part.replace('bip39', 'foo') as any, ...rest], curve: 'secp256k1', }); - }).rejects.toThrow( - /Invalid HD path segment: The path segment is malformed\./u, - ); + }).toThrow(/Invalid HD path segment: The path segment is malformed\./u); - await expect(async () => { + expect(() => { const [, bip32Part1, ...rest] = multipath; return deriveKeyFromPath({ path: [bip39Part, bip32Part1.replace('bip32', 'bar') as any, ...rest], curve: 'secp256k1', }); - }).rejects.toThrow( - /Invalid HD path segment: The path segment is malformed\./u, - ); + }).toThrow(/Invalid HD path segment: The path segment is malformed\./u); - await expect(async () => { + expect(() => { const [, bip32Part1, ...rest] = multipath; return deriveKeyFromPath({ path: [bip39Part, bip32Part1.replace(`44'`, 'xyz') as any, ...rest], curve: 'secp256k1', }); - }).rejects.toThrow( - /Invalid HD path segment: The path segment is malformed\./u, - ); + }).toThrow(/Invalid HD path segment: The path segment is malformed\./u); - await expect(async () => { + expect(() => { const [, bip32Part1, ...rest] = multipath; return deriveKeyFromPath({ path: [bip39Part, bip32Part1.replace(`'`, '"') as any, ...rest], curve: 'secp256k1', }); - }).rejects.toThrow( - /Invalid HD path segment: The path segment is malformed\./u, - ); + }).toThrow(/Invalid HD path segment: The path segment is malformed\./u); - await expect( + expect(() => deriveKeyFromPath({ path: [bip39Part, ethereumBip32PathParts[0]], curve: 'secp256k1', depth: 0, }), - ).rejects.toThrow( + ).toThrow( /Invalid HD path segment: The segment must consist of a single BIP-39 node for depths of 0\. Received:/u, ); // bip39 seed phrase component must be completely lowercase - await expect( + expect(() => deriveKeyFromPath({ path: [bip39Part.replace('r', 'R') as any], curve: 'secp256k1', }), - ).rejects.toThrow( - /Invalid HD path segment: The path segment is malformed\./u, - ); + ).toThrow(/Invalid HD path segment: The path segment is malformed\./u); // Multipaths that start with bip39 segment require _no_ parentKey - await expect( - deriveKeyFromPath({ path: [bip39Part], node }), - ).rejects.toThrow( + expect(() => deriveKeyFromPath({ path: [bip39Part], node })).toThrow( /Invalid derivation parameters: May not specify parent key if the path segment starts with a BIP-39 node\./u, ); // Multipaths that start with bip32 segment require parentKey - await expect( + expect(() => deriveKeyFromPath({ path: [`bip32:1'`], curve: 'secp256k1', }), - ).rejects.toThrow( + ).toThrow( /Invalid derivation parameters: Must specify parent key if the first node of the path segment is not a BIP-39 node\./u, ); }); - it('throws when no curve or node is specified', async () => { - await expect( + it('throws when no curve or node is specified', () => { + expect(() => deriveKeyFromPath({ path: [bip39MnemonicToMultipath(mnemonic)], }), - ).rejects.toThrow( + ).toThrow( 'Invalid arguments: Must specify either a parent node or curve.', ); }); - it('throws when an invalid node is provided', async () => { - await expect( + it('throws when an invalid node is provided', () => { + expect(() => deriveKeyFromPath({ // @ts-expect-error Invalid node type. node: {}, path: [bip39MnemonicToMultipath(mnemonic)], specification: 'bip32', }), - ).rejects.toThrow( + ).toThrow( 'Invalid arguments: Node must be a SLIP-10 node or a BIP-44 node when provided.', ); }); }); describe('bip32Derive', () => { - it('derives the expected keys and addresses', async () => { + it('derives the expected keys and addresses', () => { // generate parent key let node: SLIP10Node; - /* eslint-disable require-atomic-updates */ - node = await bip39Derive({ path: mnemonic, curve: secp256k1 }); - node = await bip32Derive({ + node = bip39Derive({ path: mnemonic, curve: secp256k1 }); + node = bip32Derive({ path: `44'`, node, curve: secp256k1, }); - node = await bip32Derive({ + node = bip32Derive({ path: `60'`, node, curve: secp256k1, }); - node = await bip32Derive({ + node = bip32Derive({ path: `0'`, node, curve: secp256k1, }); - node = await bip32Derive({ + node = bip32Derive({ path: `0`, node, curve: secp256k1, }); - /* eslint-enable require-atomic-updates */ - const keys = await Promise.all( - expectedAddresses.map(async (_, index) => { - return bip32Derive({ - path: `${index}`, - node, - curve: secp256k1, - }); - }), - ); + const keys = expectedAddresses.map((_, index) => { + return bip32Derive({ + path: `${index}`, + node, + curve: secp256k1, + }); + }); // validate addresses keys.forEach(({ address }, index) => { @@ -278,8 +254,8 @@ describe('derivation', () => { }); }); - it('throws for invalid inputs', async () => { - const node = await bip39Derive({ path: mnemonic, curve: secp256k1 }); + it('throws for invalid inputs', () => { + const node = bip39Derive({ path: mnemonic, curve: secp256k1 }); const inputs = [ String(-1), String(1.1), @@ -291,40 +267,38 @@ describe('derivation', () => { for (const input of inputs) { // eslint-disable-next-line no-loop-func - await expect( + expect(() => bip32Derive({ path: input, node, curve: secp256k1, }), - ).rejects.toThrow( + ).toThrow( 'Invalid path: The index must be a non-negative decimal integer less than 2147483648.', ); } }); - it('throws when no node is specified', async () => { - await expect( + it('throws when no node is specified', () => { + expect(() => bip32Derive({ path: '0', curve: secp256k1, }), - ).rejects.toThrow( - 'Invalid parameters: Must specify a node to derive from.', - ); + ).toThrow('Invalid parameters: Must specify a node to derive from.'); }); - it('throws when trying to derive from a public key node', async () => { - const node = await bip39Derive({ path: mnemonic, curve: secp256k1 }); + it('throws when trying to derive from a public key node', () => { + const node = bip39Derive({ path: mnemonic, curve: secp256k1 }); const publicNode = node.neuter(); - await expect( + expect(() => bip32Derive({ path: `0'`, node: publicNode, curve: secp256k1, }), - ).rejects.toThrow( + ).toThrow( 'Invalid parameters: Cannot derive hardened child keys without a private key.', ); }); diff --git a/src/derivation.ts b/src/derivation.ts index d21d9c06..455385ef 100755 --- a/src/derivation.ts +++ b/src/derivation.ts @@ -64,9 +64,7 @@ type DeriveKeyFromPathArgs = * @param args.depth - The depth of the segment. * @returns The derived key. */ -export async function deriveKeyFromPath( - args: DeriveKeyFromPathArgs, -): Promise { +export function deriveKeyFromPath(args: DeriveKeyFromPathArgs): SLIP10Node { const { path, depth = path.length } = args; const node = 'node' in args ? args.node : undefined; @@ -98,11 +96,9 @@ export async function deriveKeyFromPath( // Derive through each part of path. `pathSegment` needs to be cast because // `HDPathTuple.reduce()` doesn't work. Note that the first element of the // path can be a Uint8Array. - return await (path as readonly [Uint8Array | string, ...string[]]).reduce< - Promise - >(async (promise, pathNode, index) => { - const derivedNode = await promise; - + return ( + path as readonly [Uint8Array | string, ...string[]] + ).reduce((derivedNode, pathNode, index) => { if (typeof pathNode === 'string') { const [pathType, pathPart] = pathNode.split(':'); @@ -111,7 +107,7 @@ export async function deriveKeyFromPath( assert(hasDeriver(pathType), `Unknown derivation type: "${pathType}".`); const deriver = derivers[pathType] as Deriver; - return await deriver.deriveChildKey({ + return deriver.deriveChildKey({ path: pathPart, node: derivedNode, curve: getCurveByName(curve), @@ -121,12 +117,12 @@ export async function deriveKeyFromPath( // Only the first path segment can be a Uint8Array. assert(index === 0, getMalformedError()); - return await derivers.bip39.deriveChildKey({ + return derivers.bip39.deriveChildKey({ path: pathNode, node: derivedNode, curve: getCurveByName(curve), }); - }, Promise.resolve(node as SLIP10Node)); + }, node as SLIP10Node); } /** diff --git a/src/derivers/bip32.test.ts b/src/derivers/bip32.test.ts index e6b0b7ee..78722e5b 100644 --- a/src/derivers/bip32.test.ts +++ b/src/derivers/bip32.test.ts @@ -13,8 +13,8 @@ import { import { bip39MnemonicToMultipath, createBip39KeyFromSeed } from './bip39'; describe('deriveChildKey', () => { - it('handles deriving invalid private keys', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('handles deriving invalid private keys', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'secp256k1', }); @@ -22,7 +22,7 @@ describe('deriveChildKey', () => { // Simulate an invalid key once. jest.spyOn(secp256k1, 'isValidPrivateKey').mockReturnValueOnce(false); - const childNode = await deriveChildKey({ + const childNode = deriveChildKey({ node, path: `0'`, curve: secp256k1, @@ -45,8 +45,8 @@ describe('deriveChildKey', () => { it.each(fixtures.errorHandling.bip32.keys)( 'handles deriving invalid private keys (test vectors)', - async ({ path, privateKey, chainCode, index, depth }) => { - const node = await createBip39KeyFromSeed( + ({ path, privateKey, chainCode, index, depth }) => { + const node = createBip39KeyFromSeed( hexToBytes(fixtures.errorHandling.bip32.hexSeed), secp256k1, ); @@ -54,7 +54,7 @@ describe('deriveChildKey', () => { // Simulate an invalid key once. jest.spyOn(secp256k1, 'isValidPrivateKey').mockReturnValueOnce(false); - const childNode = await node.derive(path.ours.tuple); + const childNode = node.derive(path.ours.tuple); expect(childNode.privateKey).toBe(privateKey); expect(childNode.chainCode).toBe(chainCode); expect(childNode.index).toBe(index); @@ -62,18 +62,18 @@ describe('deriveChildKey', () => { }, ); - it('handles deriving invalid public keys', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('handles deriving invalid public keys', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'secp256k1', - }).then((privateNode) => privateNode.neuter()); + }).neuter(); // Simulate an invalid key once. jest.spyOn(secp256k1, 'publicAdd').mockImplementationOnce(() => { throw new Error('Invalid key.'); }); - const childNode = await deriveChildKey({ + const childNode = deriveChildKey({ node, path: `0`, curve: secp256k1, @@ -94,19 +94,19 @@ describe('deriveChildKey', () => { `); }); - it('throws an error if the curve is ed25519', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws an error if the curve is ed25519', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'secp256k1', }); - await expect( + expect(() => deriveChildKey({ node, path: `'bip32:0'`, curve: ed25519, }), - ).rejects.toThrow(`Invalid curve: Only secp256k1 is supported by BIP-32.`); + ).toThrow(`Invalid curve: Only secp256k1 is supported by BIP-32.`); }); }); diff --git a/src/derivers/bip32.ts b/src/derivers/bip32.ts index 69b5aa8d..85ce0c9a 100755 --- a/src/derivers/bip32.ts +++ b/src/derivers/bip32.ts @@ -65,9 +65,7 @@ export function publicKeyToEthAddress(key: Uint8Array) { * @param options.curve - The curve to use for derivation. * @returns The derived child key as a {@link SLIP10Node}. */ -export async function deriveChildKey( - options: DeriveChildKeyArgs, -): Promise { +export function deriveChildKey(options: DeriveChildKeyArgs): SLIP10Node { assert( options.curve.name === 'secp256k1', 'Invalid curve: Only secp256k1 is supported by BIP-32.', @@ -85,17 +83,14 @@ export async function deriveChildKey( * @returns The options for deriving a child key with the child index * incremented by one. */ -async function handleError( - _: unknown, - options: DeriveNodeArgs, -): Promise { +function handleError(_: unknown, options: DeriveNodeArgs): DeriveNodeArgs { const { childIndex, privateKey, publicKey, isHardened, curve, chainCode } = options; validateBIP32Index(childIndex + 1); if (privateKey) { - const secretExtension = await deriveSecretExtension({ + const secretExtension = deriveSecretExtension({ privateKey, childIndex: childIndex + 1, isHardened, diff --git a/src/derivers/bip39.test.ts b/src/derivers/bip39.test.ts index b2455cf8..7acc4a07 100644 --- a/src/derivers/bip39.test.ts +++ b/src/derivers/bip39.test.ts @@ -14,29 +14,23 @@ describe('createBip39KeyFromSeed', () => { '0xea82e6ee9d319c083007d0b011a37b0e480ae02417a988ac90355abd53cd04fc', ); - it('throws if the seed is less than 16 bytes', async () => { - await expect( - createBip39KeyFromSeed(new Uint8Array(15), secp256k1), - ).rejects.toThrow( + it('throws if the seed is less than 16 bytes', () => { + expect(() => createBip39KeyFromSeed(new Uint8Array(15), secp256k1)).toThrow( 'Invalid seed: The seed must be between 16 and 64 bytes long.', ); }); - it('throws if the seed is greater than 64 bytes', async () => { - await expect( - createBip39KeyFromSeed(new Uint8Array(65), secp256k1), - ).rejects.toThrow( + it('throws if the seed is greater than 64 bytes', () => { + expect(() => createBip39KeyFromSeed(new Uint8Array(65), secp256k1)).toThrow( 'Invalid seed: The seed must be between 16 and 64 bytes long.', ); }); - it('throws if the private key is zero', async () => { + it('throws if the private key is zero', () => { // Mock the hmac function to return a zero private key. jest.spyOn(hmacModule, 'hmac').mockImplementation(() => new Uint8Array(64)); - await expect( - createBip39KeyFromSeed(RANDOM_SEED, secp256k1), - ).rejects.toThrow( + expect(() => createBip39KeyFromSeed(RANDOM_SEED, secp256k1)).toThrow( 'Invalid private key: The private key must greater than 0 and less than the curve order.', ); }); @@ -46,7 +40,7 @@ describe('createBip39KeyFromSeed', () => { concatBytes([secp256k1.curve.n + BigInt(1)]), ])( 'throws if the private key is greater than or equal to the curve order', - async (privateKey) => { + (privateKey) => { // For this test to be effective, the private key must be 32 bytes. assert(privateKey.length === 32); @@ -57,9 +51,7 @@ describe('createBip39KeyFromSeed', () => { concatBytes([privateKey, new Uint8Array(32)]), ); - await expect( - createBip39KeyFromSeed(RANDOM_SEED, secp256k1), - ).rejects.toThrow( + expect(() => createBip39KeyFromSeed(RANDOM_SEED, secp256k1)).toThrow( 'Invalid private key: The private key must greater than 0 and less than the curve order.', ); }, diff --git a/src/derivers/bip39.ts b/src/derivers/bip39.ts index 57b04ddc..86bc623d 100755 --- a/src/derivers/bip39.ts +++ b/src/derivers/bip39.ts @@ -1,4 +1,4 @@ -import { mnemonicToSeed } from '@metamask/scure-bip39'; +import { mnemonicToSeedSync } from '@metamask/scure-bip39'; import { wordlist as englishWordlist } from '@metamask/scure-bip39/dist/wordlists/english'; import { assert } from '@metamask/utils'; import { hmac } from '@noble/hashes/hmac'; @@ -29,12 +29,12 @@ export function bip39MnemonicToMultipath(mnemonic: string): BIP39StringNode { * @param options.curve - The curve to use for derivation. * @returns The node. */ -export async function deriveChildKey({ +export function deriveChildKey({ path, curve, -}: DeriveChildKeyArgs): Promise { +}: DeriveChildKeyArgs): SLIP10Node { return createBip39KeyFromSeed( - await mnemonicToSeed(path, englishWordlist), + mnemonicToSeedSync(path, englishWordlist), curve, ); } @@ -47,10 +47,10 @@ export async function deriveChildKey({ * @returns An object containing the corresponding BIP-39 master key and chain * code. */ -export async function createBip39KeyFromSeed( +export function createBip39KeyFromSeed( seed: Uint8Array, curve: Curve, -): Promise { +): SLIP10Node { assert( seed.length >= 16 && seed.length <= 64, 'Invalid seed: The seed must be between 16 and 64 bytes long.', @@ -66,7 +66,7 @@ export async function createBip39KeyFromSeed( ); const masterFingerprint = getFingerprint( - await curve.getPublicKey(privateKey, true), + curve.getPublicKey(privateKey, true), ); return SLIP10Node.fromExtendedKey({ diff --git a/src/derivers/index.ts b/src/derivers/index.ts index 27133580..9a6ac48b 100644 --- a/src/derivers/index.ts +++ b/src/derivers/index.ts @@ -20,7 +20,7 @@ export type DeriveChildKeyArgs = { }; export type Deriver = { - deriveChildKey: (args: DeriveChildKeyArgs) => Promise; + deriveChildKey: (args: DeriveChildKeyArgs) => SLIP10Node; }; export const derivers = { diff --git a/src/derivers/shared.ts b/src/derivers/shared.ts index 8635da00..d6bc7057 100644 --- a/src/derivers/shared.ts +++ b/src/derivers/shared.ts @@ -14,10 +14,7 @@ import { mod } from '../curves'; import { SLIP10Node } from '../SLIP10Node'; import { isValidBytesKey, numberToUint32 } from '../utils'; -type ErrorHandler = ( - error: unknown, - options: DeriveNodeArgs, -) => Promise; +type ErrorHandler = (error: unknown, options: DeriveNodeArgs) => DeriveNodeArgs; /** * Derive a BIP-32 or SLIP-10 child key with a given path from a parent key. @@ -33,10 +30,10 @@ type ErrorHandler = ( * derivation. * @returns The derived node. */ -export async function deriveChildKey( +export function deriveChildKey( { path, node, curve }: DeriveChildKeyArgs, handleError: ErrorHandler, -) { +): SLIP10Node { validateNode(node); const { childIndex, isHardened } = getValidatedPath(path, node, curve); @@ -52,7 +49,7 @@ export async function deriveChildKey( }; if (node.privateKeyBytes) { - const secretExtension = await deriveSecretExtension({ + const secretExtension = deriveSecretExtension({ privateKey: node.privateKeyBytes, childIndex, isHardened, @@ -64,7 +61,7 @@ export async function deriveChildKey( extension: secretExtension, }); - return await deriveNode( + return deriveNode( { privateKey: node.privateKeyBytes, entropy, @@ -84,7 +81,7 @@ export async function deriveChildKey( extension: publicExtension, }); - return await deriveNode( + return deriveNode( { publicKey: node.compressedPublicKeyBytes, entropy, @@ -141,13 +138,10 @@ type DeriveSecretExtensionArgs = { * @param handleError - A function to handle errors during derivation. * @returns The derived child key as {@link SLIP10Node}. */ -async function deriveNode( +function deriveNode( options: DeriveNodeArgs, - handleError: ( - error: unknown, - args: DeriveNodeArgs, - ) => Promise, -): Promise { + handleError: ErrorHandler, +): SLIP10Node { const { privateKey, publicKey, @@ -162,7 +156,7 @@ async function deriveNode( try { if (privateKey) { - return await derivePrivateChildKey({ + return derivePrivateChildKey({ entropy, privateKey, depth, @@ -174,7 +168,7 @@ async function deriveNode( }); } - return await derivePublicChildKey({ + return derivePublicChildKey({ entropy, publicKey, depth, @@ -184,7 +178,7 @@ async function deriveNode( curve, }); } catch (error) { - return await deriveNode(await handleError(error, options), handleError); + return deriveNode(handleError(error, options), handleError); } } @@ -198,7 +192,7 @@ async function deriveNode( * @param options.curve - The curve to use for derivation. * @returns The secret extension bytes. */ -export async function deriveSecretExtension({ +export function deriveSecretExtension({ privateKey, childIndex, isHardened, @@ -214,7 +208,7 @@ export async function deriveSecretExtension({ } // Normal child - const parentPublicKey = await curve.getPublicKey(privateKey, true); + const parentPublicKey = curve.getPublicKey(privateKey, true); return derivePublicExtension({ parentPublicKey, childIndex }); } @@ -253,23 +247,23 @@ type GenerateKeyArgs = { * @param options.curve - The curve to use for derivation. * @returns The derived key. */ -async function generateKey({ +function generateKey({ privateKey, entropy, curve, -}: GenerateKeyArgs): Promise { +}: GenerateKeyArgs): DerivedKeys & { privateKey: Uint8Array } { const keyMaterial = entropy.slice(0, 32); const childChainCode = entropy.slice(32); // If curve is ed25519: The returned child key ki is parse256(IL). // https://github.com/satoshilabs/slips/blob/133ea52a8e43d338b98be208907e144277e44c0e/slip-0010.md#private-parent-key--private-child-key if (curve.name === 'ed25519') { - const publicKey = await curve.getPublicKey(keyMaterial); + const publicKey = curve.getPublicKey(keyMaterial); return { privateKey: keyMaterial, publicKey, chainCode: childChainCode }; } const childPrivateKey = privateAdd(privateKey, keyMaterial, curve); - const publicKey = await curve.getPublicKey(childPrivateKey); + const publicKey = curve.getPublicKey(childPrivateKey); return { privateKey: childPrivateKey, publicKey, chainCode: childChainCode }; } @@ -299,7 +293,7 @@ type DerivePrivateChildKeyArgs = { * @param args.curve - The curve to use for derivation. * @returns The derived {@link SLIP10Node}. */ -async function derivePrivateChildKey({ +function derivePrivateChildKey({ entropy, privateKey, depth, @@ -308,18 +302,18 @@ async function derivePrivateChildKey({ childIndex, isHardened, curve, -}: DerivePrivateChildKeyArgs): Promise { +}: DerivePrivateChildKeyArgs): SLIP10Node { const actualChildIndex = childIndex + (isHardened ? BIP_32_HARDENED_OFFSET : 0); const { privateKey: childPrivateKey, chainCode: childChainCode } = - await generateKey({ + generateKey({ privateKey, entropy, curve, }); - return await SLIP10Node.fromExtendedKey({ + return SLIP10Node.fromExtendedKey({ privateKey: childPrivateKey, chainCode: childChainCode, depth: depth + 1, @@ -385,7 +379,7 @@ type DerivePublicChildKeyArgs = { * @param args.curve - The curve to use for derivation. * @returns The derived {@link SLIP10Node}. */ -export async function derivePublicChildKey({ +export function derivePublicChildKey({ entropy, publicKey, depth, @@ -393,7 +387,7 @@ export async function derivePublicChildKey({ parentFingerprint, childIndex, curve, -}: DerivePublicChildKeyArgs): Promise { +}: DerivePublicChildKeyArgs): SLIP10Node { const { publicKey: childPublicKey, chainCode: childChainCode } = generatePublicKey({ publicKey, @@ -401,7 +395,7 @@ export async function derivePublicChildKey({ curve, }); - return await SLIP10Node.fromExtendedKey({ + return SLIP10Node.fromExtendedKey({ publicKey: childPublicKey, chainCode: childChainCode, depth: depth + 1, diff --git a/src/derivers/slip10.test.ts b/src/derivers/slip10.test.ts index ddfae7e8..55421822 100644 --- a/src/derivers/slip10.test.ts +++ b/src/derivers/slip10.test.ts @@ -8,8 +8,8 @@ import { bip39MnemonicToMultipath, createBip39KeyFromSeed } from './bip39'; import { deriveChildKey } from './slip10'; describe('deriveChildKey', () => { - it('handles deriving invalid private keys', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('handles deriving invalid private keys', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'secp256k1', }); @@ -17,7 +17,7 @@ describe('deriveChildKey', () => { // Simulate an invalid key once. jest.spyOn(secp256k1, 'isValidPrivateKey').mockReturnValueOnce(false); - const childNode = await deriveChildKey({ + const childNode = deriveChildKey({ node, path: `0'`, curve: secp256k1, @@ -40,8 +40,8 @@ describe('deriveChildKey', () => { it.each(fixtures.errorHandling.slip10.keys)( 'handles deriving invalid private keys (test vectors)', - async ({ path, privateKey, chainCode }) => { - const node = await createBip39KeyFromSeed( + ({ path, privateKey, chainCode }) => { + const node = createBip39KeyFromSeed( hexToBytes(fixtures.errorHandling.slip10.hexSeed), secp256k1, ); @@ -49,14 +49,14 @@ describe('deriveChildKey', () => { // Simulate an invalid key once. jest.spyOn(secp256k1, 'isValidPrivateKey').mockReturnValueOnce(false); - const childNode = await node.derive(path.ours.tuple); + const childNode = node.derive(path.ours.tuple); expect(childNode.privateKey).toBe(privateKey); expect(childNode.chainCode).toBe(chainCode); }, ); - it('throws the original error if the curve is ed25519', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('throws the original error if the curve is ed25519', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'ed25519', }); @@ -67,27 +67,27 @@ describe('deriveChildKey', () => { throw error; }); - await expect( + expect(() => deriveChildKey({ node, path: `0'`, curve: ed25519, }), - ).rejects.toThrow(error); + ).toThrow(error); }); - it('handles deriving invalid public keys', async () => { - const node = await SLIP10Node.fromDerivationPath({ + it('handles deriving invalid public keys', () => { + const node = SLIP10Node.fromDerivationPath({ derivationPath: [bip39MnemonicToMultipath(fixtures.local.mnemonic)], curve: 'secp256k1', - }).then((privateNode) => privateNode.neuter()); + }).neuter(); // Simulate an invalid key once. jest.spyOn(secp256k1, 'publicAdd').mockImplementationOnce(() => { throw new Error('Invalid key.'); }); - const childNode = await deriveChildKey({ + const childNode = deriveChildKey({ node, path: `0`, curve: secp256k1, diff --git a/src/derivers/slip10.ts b/src/derivers/slip10.ts index 80f3dbf0..b115e9cf 100755 --- a/src/derivers/slip10.ts +++ b/src/derivers/slip10.ts @@ -17,10 +17,8 @@ import type { DeriveNodeArgs } from './shared'; * @returns A tuple containing the derived private key, public key and chain * code. */ -export async function deriveChildKey( - options: DeriveChildKeyArgs, -): Promise { - return await sharedDeriveChildKey(options, handleError); +export function deriveChildKey(options: DeriveChildKeyArgs): SLIP10Node { + return sharedDeriveChildKey(options, handleError); } /** @@ -30,10 +28,7 @@ export async function deriveChildKey( * @param options - The options that were used for derivation. * @returns The new options to use for derivation. */ -async function handleError( - error: unknown, - options: DeriveNodeArgs, -): Promise { +function handleError(error: unknown, options: DeriveNodeArgs): DeriveNodeArgs { const { curve, isHardened, childIndex, entropy, chainCode } = options; // `ed25519` keys are always valid, so this error should never be thrown. If diff --git a/src/utils.test.ts b/src/utils.test.ts index 461c18c9..ddf6ced8 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -401,8 +401,8 @@ describe('decodeBase58Check', () => { }); describe('getFingerprint', () => { - it('returns the fingerprint for a compressed public key', async () => { - const node = await BIP44Node.fromExtendedKey( + it('returns the fingerprint for a compressed public key', () => { + const node = BIP44Node.fromExtendedKey( 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi', ); @@ -423,10 +423,10 @@ describe('getFingerprint', () => { describe('mnemonicPhraseToBytes', () => { it.each([fixtures.local.mnemonic, fixtures['eth-hd-keyring'].mnemonic])( 'converts a mnemonic phrase to a Uint8Array', - async (mnemonicPhrase) => { + (mnemonicPhrase) => { const array = mnemonicPhraseToBytes(mnemonicPhrase); - expect(await mnemonicToSeed(array, wordlist)).toStrictEqual( - await mnemonicToSeed(mnemonicPhrase, wordlist), + expect(mnemonicToSeed(array, wordlist)).toStrictEqual( + mnemonicToSeed(mnemonicPhrase, wordlist), ); }, ); diff --git a/test/reference-implementations.test.ts b/test/reference-implementations.test.ts index aa7f6fc4..6741a5cf 100644 --- a/test/reference-implementations.test.ts +++ b/test/reference-implementations.test.ts @@ -15,9 +15,9 @@ describe('reference implementation tests', () => { const mnemonicBip39Node = `bip39:${mnemonic}` as const; describe('BIP44Node', () => { - it('derives the expected keys', async () => { + it('derives the expected keys', () => { // Ethereum coin type node - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ mnemonicBip39Node, BIP44PurposeNodeToken, @@ -27,7 +27,7 @@ describe('reference implementation tests', () => { for (let index = 0; index < addresses.length; index++) { const expectedAddress = addresses[index]; - const childNode = await node.derive( + const childNode = node.derive( getBIP44CoinTypeToAddressPathTuple({ address_index: index }), ); @@ -37,16 +37,16 @@ describe('reference implementation tests', () => { }); describe('deriveKeyFromPath', () => { - it('derives the expected keys', async () => { + it('derives the expected keys', () => { // Ethereum coin type key - const node = await deriveKeyFromPath({ + const node = deriveKeyFromPath({ path: [mnemonicBip39Node, BIP44PurposeNodeToken, `bip32:60'`], curve: 'secp256k1', }); for (let index = 0; index < addresses.length; index++) { const expectedAddress = addresses[index]; - const { address } = await deriveKeyFromPath({ + const { address } = deriveKeyFromPath({ path: getBIP44CoinTypeToAddressPathTuple({ address_index: index }), node, }); @@ -62,9 +62,9 @@ describe('reference implementation tests', () => { const mnemonicBip39Node = `bip39:${mnemonic}` as const; describe('BIP44Node', () => { - it('derives the same keys as the reference implementation', async () => { + it('derives the same keys as the reference implementation', () => { // Ethereum coin type node - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ mnemonicBip39Node, BIP44PurposeNodeToken, @@ -75,17 +75,15 @@ describe('reference implementation tests', () => { const numberOfAccounts = 5; for (let i = 0; i < numberOfAccounts; i++) { const path = getBIP44CoinTypeToAddressPathTuple({ address_index: i }); - const address = await node - .derive(path) - .then((childNode) => childNode.address); + const { address } = node.derive(path); expect(address).toBe(addresses[i]); } }); - it('derives the same keys as the reference implementation using public key derivation', async () => { + it('derives the same keys as the reference implementation using public key derivation', () => { // Ethereum coin type node - const node = await BIP44Node.fromDerivationPath({ + const node = BIP44Node.fromDerivationPath({ derivationPath: [ mnemonicBip39Node, BIP44PurposeNodeToken, @@ -98,12 +96,9 @@ describe('reference implementation tests', () => { const [account, change, index] = getBIP44CoinTypeToAddressPathTuple({ address_index: i, }); - const parentNode = await node.derive([account, change]); + const parentNode = node.derive([account, change]); - const address = await parentNode - .neuter() - .derive([index]) - .then((childNode) => childNode.address); + const { address } = parentNode.neuter().derive([index]); expect(address).toBe(addresses[i]); } @@ -111,9 +106,9 @@ describe('reference implementation tests', () => { }); describe('deriveKeyFromPath', () => { - it('derives the same keys as the reference implementation', async () => { + it('derives the same keys as the reference implementation', () => { // Ethereum coin type key - const node = await deriveKeyFromPath({ + const node = deriveKeyFromPath({ path: [mnemonicBip39Node, BIP44PurposeNodeToken, `bip32:60'`], curve: 'secp256k1', }); @@ -122,10 +117,10 @@ describe('reference implementation tests', () => { const ourAccounts = []; for (let i = 0; i < numberOfAccounts; i++) { ourAccounts.push( - await deriveKeyFromPath({ + deriveKeyFromPath({ path: getBIP44CoinTypeToAddressPathTuple({ address_index: i }), node, - }).then(({ address }) => address), + }).address, ); } @@ -140,34 +135,29 @@ describe('reference implementation tests', () => { const seed = hexStringToBytes(hexSeed); describe('BIP44Node', () => { - it('derives the same keys as the reference implementation', async () => { - const parentNode = await createBip39KeyFromSeed(seed, secp256k1); - const node = await parentNode.derive(path.ours.tuple); + it('derives the same keys as the reference implementation', () => { + const parentNode = createBip39KeyFromSeed(seed, secp256k1); + const node = parentNode.derive(path.ours.tuple); expect(node.privateKey).toStrictEqual(privateKey); expect(node.address).toStrictEqual(address); for (const { index, address: theirAddress } of sampleAddressIndices) { - const ourAddress = await node - .derive([`bip32:${index}`]) - .then((childNode) => childNode.address); + const ourAddress = node.derive([`bip32:${index}`]).address; expect(ourAddress).toStrictEqual(theirAddress); } }); - it('derives the same keys as the reference implementation using public key derivation', async () => { - const parentNode = await createBip39KeyFromSeed(seed, secp256k1); - const node = await parentNode.derive(path.ours.tuple); + it('derives the same keys as the reference implementation using public key derivation', () => { + const parentNode = createBip39KeyFromSeed(seed, secp256k1); + const node = parentNode.derive(path.ours.tuple); expect(node.privateKey).toStrictEqual(privateKey); expect(node.address).toStrictEqual(address); for (const { index, address: theirAddress } of sampleAddressIndices) { - const ourAddress = await node - .neuter() - .derive([`bip32:${index}`]) - .then((childNode) => childNode.address); + const ourAddress = node.neuter().derive([`bip32:${index}`]).address; expect(ourAddress).toStrictEqual(theirAddress); } @@ -175,9 +165,9 @@ describe('reference implementation tests', () => { }); describe('deriveKeyFromPath', () => { - it('derives the same keys as the reference implementation', async () => { - const node = await createBip39KeyFromSeed(seed, secp256k1); - const childNode = await deriveKeyFromPath({ + it('derives the same keys as the reference implementation', () => { + const node = createBip39KeyFromSeed(seed, secp256k1); + const childNode = deriveKeyFromPath({ path: path.ours.tuple, node, }); @@ -186,7 +176,7 @@ describe('reference implementation tests', () => { expect(childNode.address).toStrictEqual(address); for (const { index, address: theirAddress } of sampleAddressIndices) { - const childChildNode = await deriveKeyFromPath({ + const childChildNode = deriveKeyFromPath({ path: [`bip32:${index}`], node: childNode, }); @@ -203,10 +193,10 @@ describe('reference implementation tests', () => { // We only test the BIP-32 vectors with deriveKeyFromPath, since not all // paths are BIP-44 compatible. describe('deriveKeyFromPath', () => { - it('derives the test vector keys', async () => { + it('derives the test vector keys', () => { for (const vector of vectors) { const seed = hexStringToBytes(vector.hexSeed); - const node = await createBip39KeyFromSeed(seed, secp256k1); + const node = createBip39KeyFromSeed(seed, secp256k1); for (const keyObj of vector.keys) { const { path, privateKey } = keyObj; @@ -217,7 +207,7 @@ describe('reference implementation tests', () => { if (path.ours.string === '') { targetNode = node; } else { - targetNode = await deriveKeyFromPath({ + targetNode = deriveKeyFromPath({ path: path.ours.tuple as HDPathTuple, node, }); @@ -235,9 +225,9 @@ describe('reference implementation tests', () => { const vectors = fixtures.ed25519.slip10; describe('deriveKeyFromPath', () => { - it('derives the test vector keys', async () => { + it('derives the test vector keys', () => { for (const { hexSeed, keys } of vectors) { - const node = await createBip39KeyFromSeed( + const node = createBip39KeyFromSeed( hexStringToBytes(hexSeed), ed25519, ); @@ -247,7 +237,7 @@ describe('reference implementation tests', () => { if (path.ours.string === '') { targetNode = node; } else { - targetNode = await deriveKeyFromPath({ + targetNode = deriveKeyFromPath({ path: path.ours.tuple, node, }); @@ -267,10 +257,10 @@ describe('reference implementation tests', () => { const seed = hexStringToBytes(hexSeed); describe('SLIP10Node', () => { - it('derives the same keys as the reference implementation', async () => { + it('derives the same keys as the reference implementation', () => { // Ethereum coin type node - const parentNode = await createBip39KeyFromSeed(seed, ed25519); - const node = await parentNode.derive(path.ours.tuple); + const parentNode = createBip39KeyFromSeed(seed, ed25519); + const node = parentNode.derive(path.ours.tuple); expect(node.privateKey).toStrictEqual(privateKey); @@ -279,7 +269,7 @@ describe('reference implementation tests', () => { privateKey: theirPrivateKey, publicKey: theirPublicKey, } of sampleKeyIndices) { - const childNode = await node.derive([`slip10:${index}'`]); + const childNode = node.derive([`slip10:${index}'`]); expect(childNode.privateKey).toStrictEqual(theirPrivateKey); expect(childNode.publicKey).toStrictEqual(theirPublicKey); @@ -288,10 +278,10 @@ describe('reference implementation tests', () => { }); describe('deriveKeyFromPath', () => { - it('derives the same keys as the reference implementation', async () => { + it('derives the same keys as the reference implementation', () => { // Ethereum coin type key - const node = await createBip39KeyFromSeed(seed, ed25519); - const childNode = await deriveKeyFromPath({ + const node = createBip39KeyFromSeed(seed, ed25519); + const childNode = deriveKeyFromPath({ path: [`slip10:44'`, `slip10:0'`, `slip10:0'`, `slip10:1'`], node, }); @@ -300,7 +290,7 @@ describe('reference implementation tests', () => { index, privateKey: theirPrivateKey, } of sampleKeyIndices) { - const { privateKey: ourPrivateKey } = await deriveKeyFromPath({ + const { privateKey: ourPrivateKey } = deriveKeyFromPath({ path: [`slip10:${index}'`], node: childNode, }); diff --git a/test/vectors.test.ts b/test/vectors.test.ts index 717ddb4c..ab1fe5c2 100644 --- a/test/vectors.test.ts +++ b/test/vectors.test.ts @@ -3,10 +3,9 @@ import { hexToBytes } from '@metamask/utils'; import type { SLIP10PathTuple } from '../src'; -import { secp256k1 } from '../src'; +import { secp256k1, createBip39KeyFromSeed } from '../src'; import type { Curve } from '../src/curves'; import { ed25519 } from '../src/curves'; -import { createBip39KeyFromSeed } from '../src/derivers/bip39'; import derivationVectors from './vectors/derivation.json'; type Vector = typeof derivationVectors.bip32.hardened[0]; @@ -49,8 +48,8 @@ function generateTests( { publicDerivation = false, curve = secp256k1 }: Options = {}, ) { describe(`seed: ${hexSeed}`, () => { - it('derives the correct master keys', async () => { - const node = await createBip39KeyFromSeed(hexToBytes(hexSeed), curve); + it('derives the correct master keys', () => { + const node = createBip39KeyFromSeed(hexToBytes(hexSeed), curve); expect(node.privateKey).toBe(privateKey); expect(node.compressedPublicKey).toBe(publicKey); @@ -61,13 +60,13 @@ function generateTests( expect(node.depth).toBe(depth); }); - it('derives the correct child keys', async () => { + it('derives the correct child keys', () => { expect.assertions(keys.length * 7); - const node = await createBip39KeyFromSeed(hexToBytes(hexSeed), curve); + const node = createBip39KeyFromSeed(hexToBytes(hexSeed), curve); for (const key of keys) { - const childNode = await node.derive(key.path.tuple as SLIP10PathTuple); + const childNode = node.derive(key.path.tuple as SLIP10PathTuple); expect(childNode.privateKey).toBe(key.privateKey); expect(childNode.compressedPublicKey).toBe(key.publicKey); @@ -80,18 +79,16 @@ function generateTests( }); if (publicDerivation) { - it('derives the correct public child keys', async () => { + it('derives the correct public child keys', () => { expect.assertions(keys.length * 7); - const node = await createBip39KeyFromSeed( + const node = createBip39KeyFromSeed( hexToBytes(hexSeed), curve, - ).then((privateNode) => privateNode.neuter()); + ).neuter(); for (const key of keys) { - const childNode = await node.derive( - key.path.tuple as SLIP10PathTuple, - ); + const childNode = node.derive(key.path.tuple as SLIP10PathTuple); expect(childNode.privateKey).toBeUndefined(); expect(childNode.compressedPublicKey).toBe(key.publicKey);