Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions modules/abstract-utxo/src/deriveKeyWithSeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createHash } from 'crypto';

import type { BIP32 } from '@bitgo/wasm-utxo';

/**
* Derive a child key from `key` using a path determined by `seed`.
*
* Mirrors `BaseCoin.deriveKeyWithSeedBip32` from sdk-core but operates on
* wasm-utxo's BIP32 class (which has the same `derivePath` semantics).
*/
export function deriveKeyWithSeed(key: BIP32, seed: string): { key: BIP32; derivationPath: string } {
const sha = (input: string | Buffer): Buffer => createHash('sha256').update(input).digest();
const derivationPathInput = sha(sha(seed)).toString('hex');
const derivationPath = `m/999999/${parseInt(derivationPathInput.slice(0, 7), 16)}/${parseInt(
derivationPathInput.slice(7, 14),
16
)}`;
return { key: key.derivePath(derivationPath), derivationPath };
}
6 changes: 2 additions & 4 deletions modules/abstract-utxo/src/impl/btc/inscriptionBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import assert from 'assert';

import {
BaseCoin,
HalfSignedUtxoTransaction,
IInscriptionBuilder,
IWallet,
Expand Down Expand Up @@ -29,8 +28,8 @@ import {
import { BIP32, fixedScriptWallet } from '@bitgo/wasm-utxo';

import { AbstractUtxoCoin } from '../../abstractUtxoCoin';
import { deriveKeyWithSeed } from '../../deriveKeyWithSeed';
import { fetchKeychains } from '../../keychains';
import { toUtxolibBIP32 } from '../../wasmUtil';

/** Key identifier for signing */
type SignerKey = 'user' | 'backup' | 'bitgo';
Expand Down Expand Up @@ -58,8 +57,7 @@ export class InscriptionBuilder implements IInscriptionBuilder {
const user = await this.wallet.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.USER] });
assert(user.pub);

const userKey = toUtxolibBIP32(BIP32.fromBase58(user.pub));
const { key: derivedKey } = BaseCoin.deriveKeyWithSeedBip32(userKey, inscriptionData.toString());
const { key: derivedKey } = deriveKeyWithSeed(BIP32.fromBase58(user.pub), inscriptionData.toString());

const result = inscriptions.createInscriptionRevealData(
derivedKey.publicKey,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { BIP32, bip32, Psbt } from '@bitgo/wasm-utxo';
import { BaseCoin } from '@bitgo/sdk-core';

import { deriveKeyWithSeed } from '../deriveKeyWithSeed';
import { UtxoCoinName } from '../names';
import { toUtxolibBIP32 } from '../wasmUtil';

import { OfflineVaultSignable } from './OfflineVaultSignable';
import { DescriptorTransaction, getHalfSignedPsbt } from './descriptor';
Expand All @@ -21,8 +20,8 @@ export function createHalfSigned(
derivationId: string,
tx: unknown
): OfflineVaultHalfSigned {
const key = typeof prv === 'string' ? BIP32.fromBase58(prv) : prv;
const derivedKey = BaseCoin.deriveKeyWithSeedBip32(toUtxolibBIP32(key), derivationId).key;
const wasmKey = typeof prv === 'string' ? BIP32.fromBase58(prv) : BIP32.fromBase58(prv.toBase58());
const derivedKey = deriveKeyWithSeed(wasmKey, derivationId).key;
if (!OfflineVaultSignable.is(tx)) {
throw new Error('unsupported transaction type');
}
Expand Down
31 changes: 31 additions & 0 deletions modules/abstract-utxo/test/unit/deriveKeyWithSeed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as assert from 'assert';

import * as utxolib from '@bitgo/utxo-lib';
import { BIP32 } from '@bitgo/wasm-utxo';
import { BaseCoin } from '@bitgo/sdk-core';

import { deriveKeyWithSeed } from '../../src/deriveKeyWithSeed';

// Deterministic test xprv — derived from a 32-byte all-ones seed.
const XPRV =
'xprv9s21ZrQH143K2QPmabzR6Q9tkNRfFxy1jy9p1PRctypZJWAjtjWBJAxxvQ3454vPpnUoLfGH8YP5KcHFX4Z5Jh7bYnFuBhxztHRy72yXmnC';

const SEEDS = ['', 'hello', 'some long seed string with spaces', '\u{1F600}'];

describe('deriveKeyWithSeed', function () {
for (const seed of SEEDS) {
it(`matches BaseCoin.deriveKeyWithSeedBip32 for seed ${JSON.stringify(seed)}`, function () {
const wasmKey = BIP32.fromBase58(XPRV);
const utxolibKey = utxolib.bip32.fromBase58(XPRV);

const wasmResult = deriveKeyWithSeed(wasmKey, seed);
const sdkCoreResult = BaseCoin.deriveKeyWithSeedBip32(utxolibKey, seed);

assert.strictEqual(wasmResult.derivationPath, sdkCoreResult.derivationPath);
assert.strictEqual(
Buffer.from(wasmResult.key.publicKey).toString('hex'),
Buffer.from(sdkCoreResult.key.publicKey).toString('hex')
);
});
}
});
Loading