Skip to content

Commit

Permalink
Merge PR #1162 from 'Vasu-08/descriptor-3'
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Aug 5, 2023
2 parents 014a104 + 82f8c8d commit 0c18028
Show file tree
Hide file tree
Showing 19 changed files with 1,143 additions and 18 deletions.
54 changes: 54 additions & 0 deletions lib/descriptor/abstractdescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const stringType = {
* (size 1 for PK, PKH, WPKH; any size for WSH and Multisig).
* @property {AbstractDescriptor[]} subdescriptors - sub-descriptor arguments
* for the descriptor (empty for everything but SH and WSH)
* @property {String} scriptContext
* @property {Network} network
*/

Expand All @@ -52,6 +53,7 @@ class AbstractDescriptor {
this.type = null;
this.keyProviders = [];
this.subdescriptors = [];
this.scriptContext = common.scriptContext.TOP;
this.network = null;
}

Expand Down Expand Up @@ -232,6 +234,58 @@ class AbstractDescriptor {
isSingleType() {
return true;
}

/**
* Get scripts for the descriptor at a specified position.
* @param {Number} pos
* @returns {Script[]}
*/

generateScripts(pos) {
const pubkeys = [];
const subscripts = [];

for (const subdesc of this.subdescriptors) {
const outscripts = subdesc.generateScripts(pos);
assert(outscripts.length === 1);
subscripts.push(outscripts[0]);
}

for (const provider of this.keyProviders) {
const pubkey = provider.getPublicKey(pos);
pubkeys.push(pubkey);
}

return this._getScripts(pubkeys, subscripts);
}

/**
* Get the scripts (helper function).
* @returns {Script[]}
*/

_getScripts() {
throw new Error('Abstract method.');
}

/**
* Derive addresses for the descriptor at a specified position.
* @param {Number} pos
* @returns {Address[]}
*/

getAddresses(pos) {
const scripts = this.generateScripts(pos);
const addresses = [];

for (const script of scripts) {
const address = script.getAddress();
assert(address, 'Descriptor does not have a corresponding address');
addresses.push(address.toString(this.network));
}

return addresses;
}
}

/*
Expand Down
101 changes: 100 additions & 1 deletion lib/descriptor/keyprovider.js
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,24 @@ class KeyProvider {
hasPrivateKey() {
return this.ring.privateKey !== null;
}

/**
* Get the public key
* @returns {Buffer}
*/

getPublicKey() {
return this.ring.publicKey;
}

/**
* Get the private key (if available)
* @returns {Buffer}
*/

getPrivateKey() {
return this.ring.privateKey;
}
}

/**
Expand Down Expand Up @@ -407,6 +425,87 @@ class HDKeyProvider extends KeyProvider {
}
return null;
}

/**
* Derive private key at a given position..
* @returns {HDPrivateKey}
*/

getHDPrivateKey(pos) {
assert(
this.hdkey.privateKey,
'Private key not available for hardened derivation.'
);

let childkey = this.hdkey;

if (this.path.length > 0) {
const path = 'm' + HD.common.format(this.path, this.hardenedMarker);
childkey = childkey.derivePath(path);
}

if (this.type === deriveType.UNHARDENED) {
childkey = childkey.derive(pos, false);
} else if (this.type === deriveType.HARDENED) {
childkey = childkey.derive(pos, true);
}

return childkey;
}

/**
* Derive public key at a given position
* @param {Number} pos
* @returns {HDPublicKey}
*/

getHDPublicKey(pos) {
if (this.isHardened()) {
const childprivkey = this.getHDPrivateKey(pos);
return childprivkey.toPublic();
}

let childkey = this.hdkey;

if (this.hdkey instanceof HD.PrivateKey) {
childkey = this.hdkey.toPublic();
}

if (this.path.length > 0) {
const path = 'm' + HD.common.format(this.path, this.hardenedMarker);
childkey = childkey.derivePath(path);
}

if (this.type === deriveType.UNHARDENED) {
childkey = childkey.derive(pos);
}

assert(this.type !== deriveType.HARDENED);

return childkey;
}

/**
* Get public key at a given position
* @param {Number} pos
* @returns {Buffer} public key
*/

getPublicKey(pos) {
const hdkey = this.getHDPublicKey(pos);
return hdkey.publicKey;
}

/**
* Get private key at a given position
* @param {Number} pos
* @returns {Buffer} private key
*/

getPrivateKey(pos) {
const hdkey = this.getHDPrivateKey(pos);
return hdkey.privateKey;
}
}

/**
Expand All @@ -424,7 +523,7 @@ function getHardenedMarker(path) {

let hardenedMarker = null;
for (const p of path) {
const last = p[p.length - 1];
const last = p[p.length - 1];
if (last === '\'' || last === 'h') {
hardenedMarker = last;
}
Expand Down
11 changes: 11 additions & 0 deletions lib/descriptor/type/addr.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ const common = require('../common');
const {isType, strip, checkChecksum, scriptContext, types} = common;
const Address = require('../../primitives/address');
const Network = require('../../protocol/network');
const Script = require('../../script/script');

/**
* AddressDescriptor
* Represents the output script produced by the address in the descriptor.
* @see https://github.com/bitcoin/bips/blob/master/bip-0385.mediawiki#addr
* @property {String} type
* @property {Address} address
* @property {String} scriptContext
* @property {Network} network
* @extends AbstractDescriptor
*/
Expand Down Expand Up @@ -118,6 +120,15 @@ class AddressDescriptor extends AbstractDescriptor {
isSolvable() {
return false;
}

/**
* Get the scripts (helper function).
* @returns {Script[]}
*/

_getScripts() {
return [Script.fromAddress(this.address)];
}
}

/*
Expand Down
28 changes: 28 additions & 0 deletions lib/descriptor/type/combo.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ const common = require('../common');
const {isType, strip, checkChecksum, scriptContext, types} = common;
const KeyProvider = require('../keyprovider');
const Network = require('../../protocol/network');
const hash160 = require('bcrypto/lib/hash160');
const Script = require('../../script/script');

/**
* ComboDescriptor
* Represents a P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH output scripts.
* @see https://github.com/bitcoin/bips/blob/master/bip-0384.mediawiki
* @property {String} type
* @property {KeyProvider[]} keyProviders
* @property {String} scriptContext
* @property {Network} network
* @extends AbstractDescriptor
*/
Expand Down Expand Up @@ -112,6 +115,31 @@ class ComboDescriptor extends AbstractDescriptor {
isSolvable() {
return true;
}

/**
* Get the scripts (helper function).
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

_getScripts(pubkeys) {
assert(Array.isArray(pubkeys) && pubkeys.length === 1);
assert(Buffer.isBuffer(pubkeys[0]));

const scripts = [];
scripts.push(Script.fromPubkey(pubkeys[0])); // P2PK
const pubkeyhash = hash160.digest(pubkeys[0]);
scripts.push(Script.fromPubkeyhash(pubkeyhash)); // P2PKH

if (pubkeys[0].length === 33) {
const p2wpkh = Script.fromProgram(0, pubkeyhash);
scripts.push(p2wpkh); // P2WPKH
const p2sh = Script.fromScripthash(p2wpkh.hash160()); // P2SH-P2WPKH
scripts.push(p2sh);
}

return scripts;
}
}

/*
Expand Down
33 changes: 33 additions & 0 deletions lib/descriptor/type/multisig.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ const {MAX_SCRIPT_PUSH, MAX_MULTISIG_PUBKEYS} = consensus;
const KeyProvider = require('../keyprovider');
const Network = require('../../protocol/network');
const assert = require('bsert');
const Script = require('../../script/script');

/**
* MultisigDescriptor
* Represents a multisig output script.
* @see https://github.com/bitcoin/bips/blob/master/bip-0383.mediawiki
* @property {String} type
* @property {KeyProvider[]} keyProviders
* @property {String} scriptContext
* @property {Number} threshold
* @property {Boolean} isSorted - true if descriptor is sortedmulti
* @property {Network} network
Expand Down Expand Up @@ -148,6 +150,7 @@ class MultisigDescriptor extends AbstractDescriptor {
this.keyProviders = providers;
this.isSorted = isSorted;
this.network = Network.get(network);
this.scriptContext = context;

return this;
}
Expand All @@ -167,6 +170,36 @@ class MultisigDescriptor extends AbstractDescriptor {
toStringExtra() {
return this.threshold.toString();
}

/**
* Get the scripts (helper function).
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

_getScripts(pubkeys) {
assert(Array.isArray(pubkeys) && pubkeys.length > 0);

for (const pubkey of pubkeys) {
assert(Buffer.isBuffer(pubkey));
}

const m = this.threshold;
const n = pubkeys.length;
const isWitness = this.scriptContext === scriptContext.P2WSH;

return [Script.fromMultisig(m, n, pubkeys, this.isSorted, isWitness)];
}

/**
* Derive addresses from scripts.
* @param {Script[]} scripts
* @returns {Address[]}
*/

getAddresses(scripts) {
throw new Error('Descriptor does not have a corresponding address');
}
}

/**
Expand Down
16 changes: 16 additions & 0 deletions lib/descriptor/type/pk.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ const {isType, strip, checkChecksum, scriptContext, types} = common;
const assert = require('bsert');
const KeyProvider = require('../keyprovider');
const Network = require('../../protocol/network');
const Script = require('../../script/script');

/**
* PKDescriptor
* Represents a P2PK output script.
* @see https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki#pk
* @property {String} type
* @property {String} scriptContext
* @property {KeyProvider[]} keyProviders
* @property {Network} network
* @extends AbstractDescriptor
Expand Down Expand Up @@ -88,6 +90,7 @@ class PKDescriptor extends AbstractDescriptor {

this.keyProviders = [provider];
this.network = Network.get(network);
this.scriptContext = context;

return this;
}
Expand All @@ -103,6 +106,19 @@ class PKDescriptor extends AbstractDescriptor {
static fromString(str, network, context = scriptContext.TOP) {
return new this().fromString(str, network, context);
}

/**
* Get the scripts (helper function).
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

_getScripts(pubkeys) {
assert(Array.isArray(pubkeys) && pubkeys.length === 1);
assert(Buffer.isBuffer(pubkeys[0]));

return [Script.fromPubkey(pubkeys[0])];
}
}

/*
Expand Down
Loading

0 comments on commit 0c18028

Please sign in to comment.