Skip to content

Commit

Permalink
feat<descriptor>: added functions for generating scripts and addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasu-08 committed Aug 4, 2023
1 parent f3184af commit 32e5940
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 3 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
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
18 changes: 18 additions & 0 deletions lib/descriptor/type/pkh.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ const {isType, strip, checkChecksum, scriptContext, types} = common;
const assert = require('bsert');
const KeyProvider = require('../keyprovider');
const Network = require('../../protocol/network');
const hash160 = require('bcrypto/lib/hash160');
const Script = require('../../script/script');

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

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

return this;
}
Expand All @@ -110,6 +114,20 @@ class PKHDescriptor 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]));

const pubkeyhash = hash160.digest(pubkeys[0]);
return [Script.fromPubkeyhash(pubkeyhash)];
}
}

/*
Expand Down
10 changes: 10 additions & 0 deletions lib/descriptor/type/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const assert = require('bsert');
* Represents the script represented by HEX in input.
* @see https://github.com/bitcoin/bips/blob/master/bip-0385.mediawiki#raw
* @property {String} type
* @property {String} scriptContext
* @property {Script} script
* @property {Network} network
* @extends AbstractDescriptor
Expand Down Expand Up @@ -115,6 +116,15 @@ class RawDescriptor extends AbstractDescriptor {
toStringExtra() {
return this.script.toJSON();
}

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

_getScripts() {
return [this.script];
}
}

/*
Expand Down
19 changes: 18 additions & 1 deletion lib/descriptor/type/sh.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ const WSHDescriptor = require('./wsh');
const assert = require('bsert');
const common = require('../common');
const {isType, strip, getType, scriptContext, checkChecksum, types} = common;

const Network = require('../../protocol/network');
const Script = require('../../script/script');

/**
* SHDescriptor
* Represents a P2SH output script.
* @see https://github.com/bitcoin/bips/blob/master/bip-0381.mediawiki#sh
* @property {String} type
* @property {String} scriptContext
* @property {Descriptor[]} subdescriptors
* @property {Network} network
* @extends AbstractDescriptor
Expand Down Expand Up @@ -63,6 +64,7 @@ class SHDescriptor extends AbstractDescriptor {
);

const subdesc = options.subdescriptors[0];
subdesc.scriptContext = scriptContext.P2SH;
const isValid = this.isValidSubdescriptor(subdesc.type);

assert(isValid, `Can not have ${subdesc.type}() inside sh()`);
Expand Down Expand Up @@ -155,6 +157,21 @@ class SHDescriptor extends AbstractDescriptor {

return validSubTypes.includes(type);
}

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

_getScripts(pubkeys, subscripts) {
assert(Array.isArray(pubkeys) && pubkeys.length === 0);
assert(Array.isArray(subscripts) && subscripts.length === 1);
assert(subscripts[0] instanceof Script);

return [Script.fromScripthash(subscripts[0].hash160())];
}
}

/*
Expand Down
Loading

0 comments on commit 32e5940

Please sign in to comment.