@ecash/lib
Version:
Library for eCash transaction building
148 lines • 4.89 kB
JavaScript
;
// Copyright (c) 2025 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
Object.defineProperty(exports, "__esModule", { value: true });
exports.HdNode = void 0;
const hmac_js_1 = require("./hmac.js");
const hash_js_1 = require("./hash.js");
const bytes_js_1 = require("./io/bytes.js");
const str_js_1 = require("./io/str.js");
const writerbytes_js_1 = require("./io/writerbytes.js");
const HIGHEST_BIT = 0x80000000;
class HdNode {
constructor(params) {
this._ecc = params.ecc;
this._seckey = params.seckey;
this._pubkey = params.pubkey;
this._chainCode = params.chainCode;
this._depth = params.depth;
this._index = params.index;
this._parentFingerprint = params.parentFingerprint;
}
seckey() {
return this._seckey;
}
pubkey() {
return this._pubkey;
}
pkh() {
return (0, hash_js_1.shaRmd160)(this._pubkey);
}
fingerprint() {
return this.pkh().slice(0, 4);
}
index() {
return this._index;
}
depth() {
return this._depth;
}
parentFingerprint() {
return this._parentFingerprint;
}
chainCode() {
return this._chainCode;
}
derive(index) {
const isHardened = index >= HIGHEST_BIT;
const data = new writerbytes_js_1.WriterBytes(1 + 32 + 4);
if (isHardened) {
if (this._seckey === undefined) {
throw new Error('Missing private key for hardened child key');
}
data.putU8(0);
data.putBytes(this._seckey);
}
else {
data.putBytes(this._pubkey);
}
data.putU32(index, 'BE');
const hashed = (0, hmac_js_1.hmacSha512)(this._chainCode, data.data);
const hashedLeft = hashed.slice(0, 32);
const hashedRight = hashed.slice(32);
// In case the secret key doesn't lie on the curve, we proceed with the
// next index. This is astronomically unlikely but part of the specification.
if (!this._ecc.isValidSeckey(hashedLeft)) {
return this.derive(index + 1);
}
let seckey;
let pubkey;
if (this._seckey !== undefined) {
try {
seckey = this._ecc.seckeyAdd(this._seckey, hashedLeft);
}
catch (ex) {
console.log('Skipping index', index, ':', ex);
return this.derive(index + 1);
}
pubkey = this._ecc.derivePubkey(seckey);
}
else {
try {
pubkey = this._ecc.pubkeyAdd(this._pubkey, hashedLeft);
}
catch (ex) {
console.log('Skipping index', index, ':', ex);
return this.derive(index + 1);
}
seckey = undefined;
}
return new HdNode({
ecc: this._ecc,
seckey: seckey,
pubkey: pubkey,
chainCode: hashedRight,
depth: this._depth + 1,
index,
parentFingerprint: new bytes_js_1.Bytes(this.fingerprint()).readU32('BE'),
});
}
deriveHardened(index) {
if (index < 0 || index >= HIGHEST_BIT) {
throw new TypeError(`index must be between 0 and ${HIGHEST_BIT}, got ${index}`);
}
return this.derive(index + HIGHEST_BIT);
}
derivePath(path) {
let splitPath = path.split('/');
if (splitPath[0] === 'm') {
if (this._parentFingerprint) {
throw new TypeError('Expected master, got child');
}
splitPath = splitPath.slice(1);
}
let hd = this;
for (const step of splitPath) {
if (step.slice(-1) === `'`) {
hd = hd.deriveHardened(parseInt(step.slice(0, -1), 10));
}
else {
hd = hd.derive(parseInt(step, 10));
}
}
return hd;
}
static fromPrivateKey(ecc, seckey, chainCode) {
return new HdNode({
ecc,
seckey: seckey,
pubkey: ecc.derivePubkey(seckey),
chainCode,
depth: 0,
index: 0,
parentFingerprint: 0,
});
}
static fromSeed(ecc, seed) {
if (seed.length < 16 || seed.length > 64) {
throw new TypeError('Seed must be between 16 and 64 bytes long');
}
const hashed = (0, hmac_js_1.hmacSha512)((0, str_js_1.strToBytes)('Bitcoin seed'), seed);
const hashedLeft = hashed.slice(0, 32);
const hashedRight = hashed.slice(32);
return HdNode.fromPrivateKey(ecc, hashedLeft, hashedRight);
}
}
exports.HdNode = HdNode;
//# sourceMappingURL=hdwallet.js.map