Skip to content

Commit

Permalink
feat<descriptor>: added scripts and addresses generation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasu-08 committed Jul 15, 2023
1 parent e94d8cb commit 1383e02
Show file tree
Hide file tree
Showing 17 changed files with 973 additions and 19 deletions.
50 changes: 50 additions & 0 deletions lib/descriptor/abstractdescriptor.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const assert = require('bsert');
const common = require('./common');
const Network = require('../protocol/network');
const KeyProvider = require('./keyprovider');
const Address = require('../primitives/address');

/**
* Constants
Expand Down Expand Up @@ -212,6 +213,55 @@ class AbstractDescriptor {
isSingleType() {
return true;

Check warning on line 214 in lib/descriptor/abstractdescriptor.js

View check run for this annotation

Codecov / codecov/patch

lib/descriptor/abstractdescriptor.js#L214

Added line #L214 was not covered by tests
}

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

generateScripts(pos) {
const pubkeys = [];
let subscript = null;

for (const subdesc of this.subdescriptors) {
subscript = subdesc.generateScripts(pos)[0];
}

for (const provider of this.keyProviders) {
const pubkey = provider.getDerivedPublicKey(pos);
assert(pubkey, 'Cannot derive script without private keys');
pubkeys.push(pubkey);
}

return this.getScripts(pubkeys, subscript);
}

/**
* Get the scriptsig(s).
* @returns {Scripts[]}
*/

getScripts() {
return [];

Check warning on line 246 in lib/descriptor/abstractdescriptor.js

View check run for this annotation

Codecov / codecov/patch

lib/descriptor/abstractdescriptor.js#L246

Added line #L246 was not covered by tests
}

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

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

return addresses;
}
}

/*
Expand Down
81 changes: 80 additions & 1 deletion lib/descriptor/keyprovider.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const HD = require('../hd/hd');
const KeyRing = require('../primitives/keyring');
const {scriptContext, isHex} = require('./common');
const Network = require('../protocol/network');
// const hash160 = require('bcrypto/lib/hash160');

/**
* Constants
Expand Down Expand Up @@ -272,6 +273,14 @@ class KeyProvider {
hasPrivateKey() {
return this.ring.privateKey !== null;
}

/**
* Derive the public key
*/

getPublicKey() {
return null;

Check warning on line 282 in lib/descriptor/keyprovider.js

View check run for this annotation

Codecov / codecov/patch

lib/descriptor/keyprovider.js#L282

Added line #L282 was not covered by tests
}
}

/**
Expand Down Expand Up @@ -326,6 +335,15 @@ class ConstKeyProvider extends KeyProvider {
}
return null;
}

/**
* Derive the public key.
* @param {KeyOriginInfo} info
*/

getDerivedPublicKey(pos) {
return this.ring.publicKey;
}
}

/**
Expand Down Expand Up @@ -423,6 +441,67 @@ class HDKeyProvider extends KeyProvider {
}
return null;
}

/**
* Derive tha last xpriv key.
* @returns {HDPrivateKey}
*/

getDerivedPrivateKey(pos) {
if (!this.hdkey.privateKey) {
return null;
}

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);

Check warning on line 463 in lib/descriptor/keyprovider.js

View check run for this annotation

Codecov / codecov/patch

lib/descriptor/keyprovider.js#L463

Added line #L463 was not covered by tests
} else if (this.type === deriveType.HARDENED) {
childkey = childkey.derive(pos, true);
}

return childkey;
}

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

getDerivedPublicKey(pos) {
if (this.isHardened()) {
const childprivkey = this.getDerivedPrivateKey(pos);
if (childprivkey) {
return childprivkey.publicKey;
}
return null;
}

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.publicKey;
}
}

/**
Expand All @@ -440,7 +519,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
13 changes: 13 additions & 0 deletions lib/descriptor/type/addr.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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
Expand Down Expand Up @@ -118,6 +119,18 @@ class AddressDescriptor extends AbstractDescriptor {
isSolvable() {
return false;
}

/**
* Get scriptPubkey(s) for the descriptor.
* @returns {Script[]}
*/

getScripts() {
const scripts = [];
const script = Script.fromAddress(this.address);
scripts.push(script);
return scripts;
}
}

/*
Expand Down
26 changes: 26 additions & 0 deletions lib/descriptor/type/combo.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ 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
Expand Down Expand Up @@ -112,6 +114,30 @@ class ComboDescriptor extends AbstractDescriptor {
isSolvable() {
return true;
}

/**
* Get scriptPubkey(s) for the descriptor.
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

getScripts(pubkeys) {
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
26 changes: 26 additions & 0 deletions lib/descriptor/type/multisig.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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
Expand All @@ -38,6 +39,7 @@ class MultisigDescriptor extends AbstractDescriptor {
this.type = types.MULTI;
this.isSorted = false;
this.threshold = 0;
this.scriptContext = scriptContext.TOP;

if (options) {
this.fromOptions(options);
Expand Down Expand Up @@ -168,6 +170,30 @@ class MultisigDescriptor extends AbstractDescriptor {
toStringExtra() {
return this.threshold.toString();
}

/**
* Get scriptPubkey(s) for the descriptor.
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

getScripts(pubkeys) {
const scripts = [];
const m = this.threshold;
const n = pubkeys.length;
scripts.push(Script.fromMultisig(m, n, pubkeys, this.isSorted));
return scripts;
}

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

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

/**
Expand Down
14 changes: 14 additions & 0 deletions lib/descriptor/type/pk.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ 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
Expand Down Expand Up @@ -103,6 +104,19 @@ class PKDescriptor extends AbstractDescriptor {
static fromString(str, network, context = scriptContext.TOP) {
return new this().fromString(str, network, context);
}

/**
* Get scriptPubkey(s) for the descriptor.
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

getScripts(pubkeys) {
const scripts = [];
const script = Script.fromPubkey(pubkeys[0]);
scripts.push(script);
return scripts;
}
}

/*
Expand Down
16 changes: 16 additions & 0 deletions lib/descriptor/type/pkh.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ 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
Expand Down Expand Up @@ -109,6 +111,20 @@ class PKHDescriptor extends AbstractDescriptor {
static fromString(str, network, context = scriptContext.TOP) {
return new this().fromString(str, network, context);
}

/**
* Get scriptPubkey(s) for the descriptor.
* @param {Buffer[]} pubkeys
* @returns {Script[]}
*/

getScripts(pubkeys) {
const scripts = [];
const pubkeyhash = hash160.digest(pubkeys[0]);
const script = Script.fromPubkeyhash(pubkeyhash);
scripts.push(script);
return scripts;
}
}

/*
Expand Down
9 changes: 9 additions & 0 deletions lib/descriptor/type/raw.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ class RawDescriptor extends AbstractDescriptor {
toStringExtra() {
return this.script.toJSON();
}

/**
* Get scriptPubkey(s) for the descriptor.
* @returns {Script[]}
*/

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

/*
Expand Down
15 changes: 14 additions & 1 deletion lib/descriptor/type/sh.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ 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
Expand Down Expand Up @@ -155,6 +155,19 @@ class SHDescriptor extends AbstractDescriptor {

return validSubTypes.includes(type);
}

/**
* Get scriptPubkey(s) for the descriptor.
* @param {Buffer[]} pubkeys
* @param {Script} script
* @returns {Script[]}
*/

getScripts(pubkeys, script) {
const scripts = [];
scripts.push(Script.fromScripthash(script.hash160()));
return scripts;
}
}

/*
Expand Down
Loading

0 comments on commit 1383e02

Please sign in to comment.