mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-19 19:26:12 -07:00
135 lines
3.4 KiB
JavaScript
135 lines
3.4 KiB
JavaScript
import { encodePublicKeyAddressData } from "./address.js";
|
|
import { derivePublicKeys, parseXpub } from "./bip32.js";
|
|
import {
|
|
generateAddressesFromDescriptor,
|
|
getOutputDescriptorBranchIds,
|
|
isOutputDescriptor,
|
|
selectOutputDescriptor,
|
|
} from "./descriptor.js";
|
|
|
|
const DEFAULT_START_INDEX = 0;
|
|
const DEFAULT_ADDRESS_COUNT = 20;
|
|
const MAX_ADDRESS_COUNT = 100;
|
|
|
|
const addrTypeByScript = /** @type {const} */ ({
|
|
p2pkh: "p2pkh",
|
|
p2sh_p2wpkh: "p2sh",
|
|
v0_p2wpkh: "v0_p2wpkh",
|
|
v1_p2tr: "v1_p2tr",
|
|
v0_p2wsh_sortedmulti: "v0_p2wsh",
|
|
});
|
|
|
|
/**
|
|
* @typedef {import("./address.js").AddressScript} AddressScript
|
|
* @typedef {import("./address.js").BitcoinNetwork} BitcoinNetwork
|
|
* @typedef {(typeof addrTypeByScript)[keyof typeof addrTypeByScript]} AddressType
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} GeneratedAddress
|
|
* @property {number} index
|
|
* @property {string} address
|
|
* @property {Uint8Array} payload
|
|
* @property {Uint8Array} [publicKey]
|
|
* @property {AddressScript} script
|
|
* @property {BitcoinNetwork} network
|
|
* @property {AddressType} addrType
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} GenerateAddressesOptions
|
|
* @property {number} [start]
|
|
* @property {number} [count]
|
|
* @property {AddressScript} [script]
|
|
* @property {readonly number[]} [path]
|
|
* @property {string} [branchId]
|
|
*/
|
|
|
|
/**
|
|
* @param {number | undefined} value
|
|
*/
|
|
function readStart(value) {
|
|
if (value === undefined) return DEFAULT_START_INDEX;
|
|
if (!Number.isSafeInteger(value) || value < 0) {
|
|
throw new Error("Expected a non-negative start index");
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @param {number | undefined} value
|
|
*/
|
|
function readCount(value) {
|
|
const count = value ?? DEFAULT_ADDRESS_COUNT;
|
|
|
|
if (!Number.isSafeInteger(count) || count < 1 || count > MAX_ADDRESS_COUNT) {
|
|
throw new Error(`Expected an address count from 1 to ${MAX_ADDRESS_COUNT}`);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @param {string} source
|
|
* @param {GenerateAddressesOptions} [options]
|
|
* @returns {Promise<GeneratedAddress[]>}
|
|
*/
|
|
export async function generateAddressesFromKey(source, options = {}) {
|
|
const key = await parseXpub(source);
|
|
const start = readStart(options.start);
|
|
const count = readCount(options.count);
|
|
const script = options.script ?? key.version.script;
|
|
const addrType = addrTypeByScript[script];
|
|
const children = await derivePublicKeys(key, start, count, options.path);
|
|
const addresses = /** @type {GeneratedAddress[]} */ ([]);
|
|
|
|
for (const child of children) {
|
|
const addressData = await encodePublicKeyAddressData(
|
|
child.publicKey,
|
|
script,
|
|
key.version.network,
|
|
);
|
|
|
|
addresses.push({
|
|
index: child.index,
|
|
address: addressData.address,
|
|
payload: addressData.payload,
|
|
publicKey: child.publicKey,
|
|
script,
|
|
network: key.version.network,
|
|
addrType,
|
|
});
|
|
}
|
|
|
|
return addresses;
|
|
}
|
|
|
|
/**
|
|
* @param {string} source
|
|
* @param {GenerateAddressesOptions} [options]
|
|
* @returns {Promise<GeneratedAddress[]>}
|
|
*/
|
|
export async function generateAddressesFromWalletSource(source, options = {}) {
|
|
const start = readStart(options.start);
|
|
const count = readCount(options.count);
|
|
|
|
if (isOutputDescriptor(source)) {
|
|
return generateAddressesFromDescriptor(
|
|
selectOutputDescriptor(source, options.branchId),
|
|
{ start, count },
|
|
);
|
|
}
|
|
|
|
return generateAddressesFromKey(source, {
|
|
...options,
|
|
start,
|
|
count,
|
|
});
|
|
}
|
|
|
|
export {
|
|
getOutputDescriptorBranchIds,
|
|
isOutputDescriptor,
|
|
};
|