Skip to content

Commit

Permalink
Merge PR #1152 from 'Vasu-08/descriptor'
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Jul 17, 2023
2 parents bb0375e + f388594 commit a4bf281
Show file tree
Hide file tree
Showing 27 changed files with 3,938 additions and 21 deletions.
15 changes: 15 additions & 0 deletions lib/bcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,23 @@ bcoin.define('Coins', './coins/coins');
bcoin.define('CoinEntry', './coins/coinentry');
bcoin.define('CoinView', './coins/coinview');

// Descriptor
bcoin.define('descriptor', './descriptor');
bcoin.define('AddressDescriptor', './descriptor/type/addr');
bcoin.define('ComboDescriptor', './descriptor/type/combo');
bcoin.define('MultisigDescriptor', './descriptor/type/multisig');
bcoin.define('PKDescriptor', './descriptor/type/pk');
bcoin.define('PKHDescriptor', './descriptor/type/pkh');
bcoin.define('RawDescriptor', './descriptor/type/raw');
bcoin.define('SHDescriptor', './descriptor/type/sh');
bcoin.define('WPKHDescriptor', './descriptor/type/wpkh');
bcoin.define('WSHDescriptor', './descriptor/type/wsh');
bcoin.define('KeyProvider', './descriptor/keyprovider');
bcoin.define('parser', './descriptor/parser');

// HD
bcoin.define('hd', './hd');
bcoin.define('KeyOriginInfo', './hd/keyorigininfo');
bcoin.define('HDPrivateKey', './hd/private');
bcoin.define('HDPublicKey', './hd/public');
bcoin.define('Mnemonic', './hd/mnemonic');
Expand Down
241 changes: 241 additions & 0 deletions lib/descriptor/abstractdescriptor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*!
* abstractdescriptor.js - abstract descriptor object for bcoin
* Copyright (c) 2023, the bcoin developers (MIT License).
* https://github.com/bcoin-org/bcoin
*/

'use strict';

const assert = require('bsert');
const common = require('./common');
const Network = require('../protocol/network');
const KeyProvider = require('./keyprovider');

/**
* Constants
*/

/**
* String type for key in a descriptor.
* stringType.PUBLIC for descriptor with public key(s)
* stringType.PRIVATE for descriptor with private key(s)
* @const {Object}
*/

const stringType = {
PUBLIC: 'PUBLIC',
PRIVATE: 'PRIVATE'
};

/**
* AbstractDescriptor
* The class which all descriptor-like objects inherit from.
* Represents an output script.
* @alias module:descriptor.AbstractDescriptor
* @see https://github.com/bitcoin/bitcoin/blob/master/doc/descriptors.md
* @property {String} type - script expression of descriptor function.
* @property {KeyProvider[]} keyProviders - parsed key arguments
* for the descriptor
* (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 {Network} network
*/

class AbstractDescriptor {
/**
* Create a abstract descriptor.
* @constructor
*/

constructor() {
this.type = null;
this.keyProviders = [];
this.subdescriptors = [];
this.network = null;
}

/**
* Inject properties from options object.
* @param {Object} options
* @returns {AbstractDescriptor}
*/

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

/**
* Inject properties from string.
* @param {String} desc
* @returns {AbstractDescriptor}
*/

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

/**
* Inject properties from options object.
* @private
* @param {Object} options
*/

parseOptions(options) {
assert(options, 'Required options for descriptor');

this.network = Network.get(options.network);

if (options.keyProviders) {
assert(Array.isArray(options.keyProviders));
for (const provider of options.keyProviders) {
assert(provider instanceof KeyProvider);
assert(provider.network === this.network);
}
this.keyProviders = options.keyProviders;
}

if (options.subdescriptors) {
assert(Array.isArray(options.subdescriptors));
for (const desc of options.subdescriptors) {
assert(desc instanceof AbstractDescriptor);
assert(desc.network === this.network);
}
this.subdescriptors = options.subdescriptors;
}

return this;
}

/**
* Test whether the descriptor contains any private key.
* @returns {Boolean}
*/

hasPrivateKeys() {
return this.keyProviders.some(key => key.hasPrivateKey())
|| this.subdescriptors.some(desc => desc.hasPrivateKeys());
}

/**
* Test whether the descriptor contains public/private keys
* in the form of HD chains
* @returns {Boolean}
*/

isRange() {
return this.keyProviders.some(key => key.isRange())
|| this.subdescriptors.some(desc => desc.isRange());
}

/**
* Whether this descriptor has all information about signing
* (ignoring private keys).
* Returns false only for `addr` and `raw` type.
* @returns {Boolean}
*/

isSolvable() {
return this.subdescriptors.every(desc => desc.isSolvable());
}

/**
* Get string form for address and raw descriptors.
* Also returns threshold string for multisig.
* Used once in toStringHelper()
* @returns {String}
*/

toStringExtra() {
return '';
}

_toStringSubScript(type) {
let res = '';
let pos = 0;

for (const subdesc of this.subdescriptors) {
if (pos++) {
res += ',';
}
res += subdesc._toString(type);
}

return res;
}

/**
* Helper function to get a descriptor in string form based on string type
* (Public, Private, Normalized)
* @param {String} type
* @returns {String}
*/

_toString(type) {
const extra = this.toStringExtra();
let pos = extra.length === 0 ? 0 : 1;
let res = this.type + '(' + extra;

for (const provider of this.keyProviders) {
if (pos++) {
res += ',';
}
switch (type) {
case stringType.PUBLIC:
res += provider.toString();
break;
case stringType.PRIVATE: {
const privkey = provider.toPrivateString();
const pubkey = provider.toString();
assert(privkey, `Private key not available for ${pubkey}`);
res += privkey;
}
}
}

const subdesc = this._toStringSubScript(type);

if (pos && subdesc.length) {
res += ',';
}

res += subdesc + ')';
return res;
}

/**
* Get a descriptor string (public keys only)
* @returns {String}
*/

toString() {
const res = this._toString(stringType.PUBLIC);
return common.addChecksum(res);
}

/**
* Get descriptor string including private keys if available
* @returns {String}
*/

toPrivateString() {
const res = this._toString(stringType.PRIVATE);
return common.addChecksum(res);
}

/**
* Test whether this descriptor will return one scriptPubKey or
* multiple (aka is or is not combo)
* @returns {Boolean}
*/

isSingleType() {
return true;
}
}

/*
* Expose
*/

module.exports = AbstractDescriptor;
Loading

0 comments on commit a4bf281

Please sign in to comment.