mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-19 19:26:12 -07:00
125 lines
2.7 KiB
JavaScript
125 lines
2.7 KiB
JavaScript
import { decodeBase58Check } from "./base58.js";
|
|
import { readUint32 } from "./bytes.js";
|
|
|
|
const EXTENDED_PUBLIC_KEY_LENGTH = 78;
|
|
const PUBLIC_KEY_LENGTH = 33;
|
|
const CHAIN_CODE_LENGTH = 32;
|
|
|
|
const extendedPublicKeyVersions = /** @type {const} */ ([
|
|
{
|
|
version: 0x0488b21e,
|
|
prefix: "xpub",
|
|
network: "mainnet",
|
|
script: "p2pkh",
|
|
addrType: "p2pkh",
|
|
},
|
|
{
|
|
version: 0x049d7cb2,
|
|
prefix: "ypub",
|
|
network: "mainnet",
|
|
script: "p2sh_p2wpkh",
|
|
addrType: "p2sh",
|
|
},
|
|
{
|
|
version: 0x04b24746,
|
|
prefix: "zpub",
|
|
network: "mainnet",
|
|
script: "v0_p2wpkh",
|
|
addrType: "v0_p2wpkh",
|
|
},
|
|
{
|
|
version: 0x043587cf,
|
|
prefix: "tpub",
|
|
network: "testnet",
|
|
script: "p2pkh",
|
|
addrType: "p2pkh",
|
|
},
|
|
{
|
|
version: 0x044a5262,
|
|
prefix: "upub",
|
|
network: "testnet",
|
|
script: "p2sh_p2wpkh",
|
|
addrType: "p2sh",
|
|
},
|
|
{
|
|
version: 0x045f1cf6,
|
|
prefix: "vpub",
|
|
network: "testnet",
|
|
script: "v0_p2wpkh",
|
|
addrType: "v0_p2wpkh",
|
|
},
|
|
]);
|
|
|
|
/**
|
|
* @typedef {typeof extendedPublicKeyVersions[number]} ExtendedPublicKeyVersion
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} ExtendedPublicKey
|
|
* @property {string} text
|
|
* @property {number} depth
|
|
* @property {number} childNumber
|
|
* @property {Uint8Array} parentFingerprint
|
|
* @property {Uint8Array} chainCode
|
|
* @property {Uint8Array} publicKey
|
|
* @property {ExtendedPublicKeyVersion} version
|
|
*/
|
|
|
|
/**
|
|
* @param {number} version
|
|
* @returns {ExtendedPublicKeyVersion}
|
|
*/
|
|
function findExtendedPublicKeyVersion(version) {
|
|
const metadata = extendedPublicKeyVersions.find((item) => {
|
|
return item.version === version;
|
|
});
|
|
|
|
if (!metadata) {
|
|
throw new Error(`Unsupported extended public key version: ${version}`);
|
|
}
|
|
|
|
return metadata;
|
|
}
|
|
|
|
/**
|
|
* @param {Uint8Array} publicKey
|
|
*/
|
|
function validateCompressedPublicKey(publicKey) {
|
|
if (
|
|
publicKey.length !== PUBLIC_KEY_LENGTH ||
|
|
(publicKey[0] !== 0x02 && publicKey[0] !== 0x03)
|
|
) {
|
|
throw new Error("Expected a compressed public key");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} text
|
|
* @returns {Promise<ExtendedPublicKey>}
|
|
*/
|
|
export async function parseExtendedPublicKey(text) {
|
|
const value = text.trim();
|
|
const bytes = await decodeBase58Check(value);
|
|
|
|
if (bytes.length !== EXTENDED_PUBLIC_KEY_LENGTH) {
|
|
throw new Error("Invalid extended public key length");
|
|
}
|
|
|
|
const version = findExtendedPublicKeyVersion(readUint32(bytes, 0));
|
|
const parentFingerprint = bytes.slice(5, 9);
|
|
const chainCode = bytes.slice(13, 13 + CHAIN_CODE_LENGTH);
|
|
const publicKey = bytes.slice(45);
|
|
|
|
validateCompressedPublicKey(publicKey);
|
|
|
|
return {
|
|
text: value,
|
|
depth: bytes[4],
|
|
childNumber: readUint32(bytes, 9),
|
|
parentFingerprint,
|
|
chainCode,
|
|
publicKey,
|
|
version,
|
|
};
|
|
}
|