<p><strong>Update [2023-Nov-15]:</strong> This answer has been updated to reflect the changes in v2 of the library.</p>
<p>As the author of the <code>@bitcoinerlab/descriptors</code> library, I'd like to provide some clarification on the usage of the <code>Output</code> class and the <code>signersPubKeys</code> parameter.</p>
<p>Bitcoinerlab's descriptors module can be used for several things, all involving the creation of a <code>Output</code> class instance. From the code you provided:</p>
<pre class="lang-typescript prettyprint-override"><code>const wshOutput = new Output({
    descriptor: wshDescriptor,
    network,
    signersPubKeys
  });
</code></pre>
<p>Let's first check what <code>signersPubKeys</code> is <a href="https://bitcoinerlab.com/modules/descriptors" rel="nofollow noreferrer">in the docs</a>:</p>
<pre class="lang-typescript prettyprint-override"><code>
  signersPubKeys // (Optional): An array of the public keys used for signing
                 // the transaction when spending the output associated with
                 // this descriptor. This parameter is only used if the
                 // descriptor object is being used to finalize a transaction.
                 // It is necessary to specify the spending path when working
                 // with miniscript-based expressions that have multiple
                 // spending paths. Set this parameter to an array containing
                 // the public keys involved in the desired spending path.
                 // Leave it `undefined` if you only need to generate the
                 // `scriptPubKey` or `address` for a descriptor, or if all
                 // the public keys involved in the descriptor will sign the
                 // transaction. In the latter case, the satisfier will
                 // automatically choose the most optimal spending path in terms
                 // of tx size (if more than one path is available).
</code></pre>
<p>As per the docs, <code>signersPubKeys</code> is optional and only required when you want to spend from a specific spending branch. Let's break down the different cases for further clarity:</p>
<h2>Receiving bitcoin</h2>
<p>If you want to receive Bitcoin locked by a specific policy (specified in a miniscript), you'll need to generate an <code>address</code> or <code>scriptPubKey</code>. To do this, you don't need to pass <code>signersPubKeys</code> to the constructor:</p>
<pre class="lang-typescript prettyprint-override"><code>const wshOutput = new Output({
    descriptor: wshDescriptor,
    network
  });
const address = wshOutput.getAddress();
const scriptPubKey = wshOutput.getScriptPubKey();
</code></pre>
<h2>Spending bitcoin from a miniscript-based descriptor</h2>
<p>Assuming you've received Bitcoin to a UTXO you control, you can use the library to spend it. This is where <code>signersPubKeys</code> may or may not be necessary:</p>
<h4>Single spending branch UTXO</h4>
<p>If there's only one spending branch, you don't need to pass <code>signersPubKeys</code>. The satisfier will use the provided signatures and build the script witness for the unique branch.</p>
<h4>Multiple spending branches UTXO</h4>
<p>For multiple spending paths, different possibilities arise. Let's consider the policy from your post:</p>
<pre class="lang-typescript prettyprint-override"><code>POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`;
</code></pre>
<p>If you can (and will) sign with both <code>@emergencyKey</code> and <code>@unvaultKey</code>, and the timelock has been released, you don't need to pass <code>signersPubKeys</code>. The satisfier will pick the most optimal solution in terms of tx size and discard the unused signature.</p>
<p>Now, consider a different scenario. Suppose you can only sign with <code>@emergencyKey</code>, or you can sign with both keys but have decided to use the emergency path to avoid waiting for the timelock.</p>
<p>In such cases, you need to pass <code>signersPubKeys: [emergencyPubKey]</code> and then sign with <code>@emergencyKey</code>. This ensures that the spending transaction is created with the desired unlocking script.</p>
<p>This information is indeed used in a couple of places within the code you provided:</p>
<pre class="lang-typescript prettyprint-override"><code>// `signersPubKeys` is used internally here to set the appropriate
// `nLockTime` and `nSequence` for the transaction (the psbt below):
const finalizer = wshOutput.updatePsbtAsInput({ psbt, txHex, vout: utxo[0].vout }); 

// `signersPubKeys` is used internally here to generate
// the appropriate script witness for the input:
finalizer({ psbt });
</code></pre>
<p>In summary, the <code>signersPubKeys</code> parameter is essential when working with miniscript-based descriptors that have multiple spending paths, and you want to specify a particular path. Depending on your use case, you may or may not need to include this parameter.</p>

CC BY-SA 4.0
2023-04-25T17:05:57.883Z
Now I understand all, It is a very nice explication, thanks!!
Chupitastiiks
CC BY-SA 4.0
2023-04-25T17:08:45.127Z
Thank you, this has been very helpful
Juanka
CC BY-SA 4.0
2023-04-25T16:43:03.747Z
2023-11-15T16:19:11.280Z
2023-11-15T16:19:11.280Z
landabaso
<p>What I want is to be able to spend the funds from an address, either with its private key or with the private key of another address that I designate in the policy.</p>
<p>As I understand it, it works like this:</p>
<p>When using the descriptor, it provides an address where the miniscript is stored, which tells me how the UTXOs associated with that address can be spent.</p>
<p>Then, if I want to spend the UTXOs associated with that address, I have to create a transaction with PSBT where I need to create the same descriptor that I made when generating the address that I explained earlier, and sign it with a private key, either that of the address itself or the other one designated in the policy, thus allowing me to spend the funds from the other address.</p>
<p>This is the reasoning that I have, but I think it is incorrect. Therefore, I have the following questions:</p>
<p>How do descriptors work? I know more or less what they are, but I don't know how they work programmatically, that is, how to use them to unlock an output that has a descriptor and a miniscript in it, from a new transaction that I create.</p>
<p>I am attaching a small code snippet that we are developing. Specifically in CODE 1, I don't understand how the signersPubKey parameter works:</p>
<pre><code>const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey]
  });

</code></pre>
<p>The first code is how to create an address with the spending conditions that you can see in the policy <code>POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`; </code>
The second code shows how we make a transaction that unlocks the funds from the previous address.
The CODE 2, we can see signersPubKey again. We are reaching the conclusion that in the first CODE, when generating the address, the descriptor that we create with signersPubKey, SignersPubKey doesn't really do anything, and in the second CODE, when signing the transaction, the descriptor really uses the signersPubKey parameter, but we are not sure.</p>
<p>This is what the GitHub page of the <a href="https://github.com/bitcoinerlab/descriptors" rel="nofollow noreferrer">descriptor library</a> that we are using says about SignersPubKey, but we don't fully understand it:</p>
<pre><code>      “The last part to explain from the code block above is that when a descriptor has multiple spending paths, the user needs to set which one to use. This is done by passing the public keys that will be used to sign the transaction using the variable signersPubKeys. The corresponding unlocking script (the script witness, in this case) will be computed later from this      information when finalizing the transaction. Note that in order to test different configurations, you can set EMERGENCY_RECOVERY variable to true or false back and forth.”
</code></pre>
<p>Therefore, how does the SignersPubKey field work when creating an address through the descriptor, and how does it also work when creating the transaction?</p>
<p>CODE 1, to create an address with miniscript</p>
<pre><code>import { Psbt, networks } from 'bitcoinjs-lib';
import * as ecpair from &quot;ecpair&quot;;
import * as secp from &quot;tiny-secp256k1&quot;;
const ECPair = ecpair.ECPairFactory(secp);
import * as fs from 'fs';
import * as secp256k1 from '@bitcoinerlab/secp256k1';
import * as descriptors from '@bitcoinerlab/descriptors';
import { compilePolicy } from '@bitcoinerlab/miniscript';
import { generateMnemonic, mnemonicToSeedSync } from 'bip39';

const { Descriptor, BIP32, ECPair2 } = descriptors.DescriptorsFactory(secp256k1);

let unvaultMnemonic;
const EMERGENCY_RECOVERY=true;
const network = networks.testnet;
const POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`;
const WSH_ORIGIN_PATH = `/69420'/1'/0'`; 
const WSH_KEY_PATH = `/0/0`; 

var emergencyPair = ECPair.makeRandom(); 
console.log(`emergencyPair WIF: ${emergencyPair.toWIF()}`); 

unvaultMnemonic = generateMnemonic();
console.log(`unvaultMnemonic: ${unvaultMnemonic}`); 


const unvaultMasterNode = BIP32.fromSeed(mnemonicToSeedSync(unvaultMnemonic),network); 
console.log(`unvaultMasterNode WIF: ${unvaultMasterNode.toWIF()}`); 

const { miniscript, issane } = compilePolicy(POLICY);
console.log(`miniscript: ${miniscript}`);
  
const unvaultKey = unvaultMasterNode.derivePath(`m${WSH_ORIGIN_PATH}${WSH_KEY_PATH}`).publicKey; 
const unvaultKeyBase64 = unvaultKey.toString('base64');
console.log(`unvaultKey BASE 64: ${unvaultKeyBase64}`);

const wshExpression = `wsh(${miniscript
    .replace(
      '@unvaultKey',
      descriptors.keyExpressionBIP32({  
        masterNode: unvaultMasterNode,
        originPath: WSH_ORIGIN_PATH,
        keyPath: WSH_KEY_PATH
      })
    )
    .replace('@emergencyKey', emergencyPair.publicKey.toString('hex'))})`;

console.log(`wshExpression: ${wshExpression}`);

const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey]
  });

console.log(&quot;wshDescriptor: &quot;+wshDescriptor);
const wshAddress = wshDescriptor.getAddress();
console.log(`address: ${wshAddress}`)

const data = [`adress: ${wshAddress}`,`emergencyPair WIF: ${emergencyPair.toWIF()}`,`unvaultMnemonic: ${unvaultMnemonic}`,`unvaultKey: ${unvaultKeyBase64}`, `wshExpression: ${wshExpression}`];

try {
  fs.writeFileSync(&quot;info_address.txt&quot;, data.join('\n'));
  console.log(`\n//////////// Se ha guardado en el fichero los siguientes datos ////////////\n\n${data.join('\n')} `);
} catch (err) {
  console.error(err);
}

</code></pre>
<p>CODE 2, to create a transaction that spend the balance of the previous address</p>
<pre><code>import { Psbt, networks } from 'bitcoinjs-lib';
import * as ecpair from &quot;ecpair&quot;;
import * as secp from &quot;tiny-secp256k1&quot;;
const ECPair = ecpair.ECPairFactory(secp);
import * as fs from 'fs';
import * as secp256k1 from '@bitcoinerlab/secp256k1';
import * as descriptors from '@bitcoinerlab/descriptors';

const { Descriptor, BIP32, ECPair2 } = descriptors.DescriptorsFactory(secp256k1);
const toAddress='n12kEsK3LnqvYFMTdMvzAXbb8JtF6qcDSP'
const network = networks.testnet;
const EMERGENCY_RECOVERY = true;
const EXPLORER = 'https://blockstream.info/testnet';

// Leer los datos del fichero
const data = fs.readFileSync(&quot;info_address.txt&quot;, { encoding: 'utf-8' });

// Analizar los datos y extraer los valores
const lines = data.split('\n');
const wshAddress = lines[0].split(': ')[1];
const emergencyPairWIF = lines[1].split(': ')[1];
const unvaultMnemonic = lines[2].split(': ')[1];
const unvaultKeyBase64 = lines[3].split(': ')[1];
const wshExpression = lines[4].split(': ')[1];

// Convertir los valores a los tipos originales
const emergencyPair = ECPair.fromWIF(emergencyPairWIF);
const unvaultKey = Buffer.from(unvaultKeyBase64, 'base64');

console.log('wshAddress:', wshAddress);
console.log('emergencyPair:', emergencyPair);
console.log('unvaultMnemonic:', unvaultMnemonic);
console.log('unvaultKey BASE 64:', unvaultKey.toString('base64'));
console.log('wshExpression:', wshExpression);
const unvaultMasterNode = BIP32.fromSeed(mnemonicToSeedSync(unvaultMnemonic),network); 

// // Busca el utxo a gastar
const utxo = await (
  await fetch(`${EXPLORER}/api/address/${wshAddress}/utxo`)
).json();


if (utxo?.[0]) {
    const txHex = await (
    await fetch(`${EXPLORER}/api/tx/${utxo?.[0].txid}/hex`)
  ).text();
  const inputValue = utxo[0].value;

  const psbt = new Psbt({ network });
  
  const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey] 
  });
  
  wshDescriptor.updatePsbt({ psbt, txHex, vout: utxo[0].vout });

  psbt.addOutput({
    address: EMERGENCY_RECOVERY
      ?  toAddress
      : 'tb1q4280xax2lt0u5a5s9hd4easuvzalm8v9ege9ge',
    value: inputValue - 1000
  });

  //Now sign the PSBT
  if (EMERGENCY_RECOVERY)
    descriptors.signers.signECPair({ psbt, ecpair: emergencyPair });
  else descriptors.signers.signBIP32({ psbt, masterNode: unvaultMasterNode });
  
  //Finalize the tx (compute &amp; add the scriptWitness) &amp; push to the blockchain
  wshDescriptor.finalizePsbtInput({ index: 0, psbt });
  const spendTx = psbt.extractTransaction();
  const spendTxPushResult = await (
    await fetch(`${EXPLORER}/api/tx`, {
      method: 'POST',
      body: spendTx.toHex()
    })
  ).text();
  console.log(`Pushing: ${spendTx.toHex()}`);
  console.log(`Tx pushed with result: ${spendTxPushResult}`);

}

</code></pre>

CC BY-SA 4.0
2023-04-25T10:56:37.220Z
Hey, i'd be glad to answer this question. But "how do descriptors work" is too broad. It looks like you are stuck at using the satisfier? Could you break up your question about how descriptors work into several more focused ones? Also please copy code snippets instead of pasting screenshots.
Antoine Poinsot
CC BY-SA 4.0
2023-04-25T11:34:35.087Z
I`ve already corrected, thanks
Chupitastiiks
CC BY-SA 4.0
2023-04-25T12:42:57.860Z
Author of the library mentioned in the OP here. I've updated the signersPubKeys parameter documentation on GitHub & site for better clarity. Check out the updated docs. If you had issues understanding just that parameter, let me summarize: signersPubKeys is an array of the public keys used for signing the transaction when spending the output associated with a descriptor. This parameter is only required if the descriptor object is being used to finalize a transaction. It is necessary to specify the spending path when working with miniscript-based expressions that have multiple spending paths.
landabaso
CC BY-SA 4.0
2023-04-25T13:45:59.987Z
@landabaso In the last case that you mentioned, I dont understand : Leave it `undefined` if you only need to generate the
            `scriptPubKey` or `address` for a descriptor, or if all
               the public keys involved in the descriptor will sign the
                transaction. In the latter case, the satisfier will
                automatically choose the most optimal spending path in terms
                of tx size (if more than one path is available).
Chupitastiiks
<javascript><output-script-descriptors><miniscript>
How do descriptors work with miniscript in the library bitcoinerLab?
Chupitastiiks
# How do descriptors work with miniscript in the library bitcoinerLab?

(Asked by: username_0 on 2023-04-25)

What I want is to be able to spend the funds from an address, either with its private key or with the private key of another address that I designate in the policy.

As I understand it, it works like this:

When using the descriptor, it provides an address where the miniscript is stored, which tells me how the UTXOs associated with that address can be spent.

Then, if I want to spend the UTXOs associated with that address, I have to create a transaction with PSBT where I need to create the same descriptor that I made when generating the address that I explained earlier, and sign it with a private key, either that of the address itself or the other one designated in the policy, thus allowing me to spend the funds from the other address.

This is the reasoning that I have, but I think it is incorrect. Therefore, I have the following questions:

How do descriptors work? I know more or less what they are, but I don't know how they work programmatically, that is, how to use them to unlock an output that has a descriptor and a miniscript in it, from a new transaction that I create.

I am attaching a small code snippet that we are developing. Specifically in CODE 1, I don't understand how the signersPubKey parameter works:

```
const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey]
  });

```

The first code is how to create an address with the spending conditions that you can see in the policy `POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`;` 
The second code shows how we make a transaction that unlocks the funds from the previous address.
The CODE 2, we can see signersPubKey again. We are reaching the conclusion that in the first CODE, when generating the address, the descriptor that we create with signersPubKey, SignersPubKey doesn't really do anything, and in the second CODE, when signing the transaction, the descriptor really uses the signersPubKey parameter, but we are not sure.

This is what the GitHub page of the [descriptor library](https://github.com/bitcoinerlab/descriptors) that we are using says about SignersPubKey, but we don't fully understand it:

```
      “The last part to explain from the code block above is that when a descriptor has multiple spending paths, the user needs to set which one to use. This is done by passing the public keys that will be used to sign the transaction using the variable signersPubKeys. The corresponding unlocking script (the script witness, in this case) will be computed later from this      information when finalizing the transaction. Note that in order to test different configurations, you can set EMERGENCY_RECOVERY variable to true or false back and forth.”

```

Therefore, how does the SignersPubKey field work when creating an address through the descriptor, and how does it also work when creating the transaction?

CODE 1, to create an address with miniscript

```
import { Psbt, networks } from 'bitcoinjs-lib';
import * as ecpair from "ecpair";
import * as secp from "tiny-secp256k1";
const ECPair = ecpair.ECPairFactory(secp);
import * as fs from 'fs';
import * as secp256k1 from '@bitcoinerlab/secp256k1';
import * as descriptors from '@bitcoinerlab/descriptors';
import { compilePolicy } from '@bitcoinerlab/miniscript';
import { generateMnemonic, mnemonicToSeedSync } from 'bip39';

const { Descriptor, BIP32, ECPair2 } = descriptors.DescriptorsFactory(secp256k1);

let unvaultMnemonic;
const EMERGENCY_RECOVERY=true;
const network = networks.testnet;
const POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`;
const WSH_ORIGIN_PATH = `/69420'/1'/0'`; 
const WSH_KEY_PATH = `/0/0`; 

var emergencyPair = ECPair.makeRandom(); 
console.log(`emergencyPair WIF: ${emergencyPair.toWIF()}`); 

unvaultMnemonic = generateMnemonic();
console.log(`unvaultMnemonic: ${unvaultMnemonic}`); 

const unvaultMasterNode = BIP32.fromSeed(mnemonicToSeedSync(unvaultMnemonic),network); 
console.log(`unvaultMasterNode WIF: ${unvaultMasterNode.toWIF()}`); 

const { miniscript, issane } = compilePolicy(POLICY);
console.log(`miniscript: ${miniscript}`);
  
const unvaultKey = unvaultMasterNode.derivePath(`m${WSH_ORIGIN_PATH}${WSH_KEY_PATH}`).publicKey; 
const unvaultKeyBase64 = unvaultKey.toString('base64');
console.log(`unvaultKey BASE 64: ${unvaultKeyBase64}`);

const wshExpression = `wsh(${miniscript
    .replace(
      '@unvaultKey',
      descriptors.keyExpressionBIP32({  
        masterNode: unvaultMasterNode,
        originPath: WSH_ORIGIN_PATH,
        keyPath: WSH_KEY_PATH
      })
    )
    .replace('@emergencyKey', emergencyPair.publicKey.toString('hex'))})`;

console.log(`wshExpression: ${wshExpression}`);

const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey]
  });

console.log("wshDescriptor: "+wshDescriptor);
const wshAddress = wshDescriptor.getAddress();
console.log(`address: ${wshAddress}`)

const data = [`adress: ${wshAddress}`,`emergencyPair WIF: ${emergencyPair.toWIF()}`,`unvaultMnemonic: ${unvaultMnemonic}`,`unvaultKey: ${unvaultKeyBase64}`, `wshExpression: ${wshExpression}`];

try {
  fs.writeFileSync("info_address.txt", data.join('\n'));
  console.log(`\n//////////// Se ha guardado en el fichero los siguientes datos ////////////\n\n${data.join('\n')} `);
} catch (err) {
  console.error(err);
}

```

CODE 2, to create a transaction that spend the balance of the previous address

```
import { Psbt, networks } from 'bitcoinjs-lib';
import * as ecpair from "ecpair";
import * as secp from "tiny-secp256k1";
const ECPair = ecpair.ECPairFactory(secp);
import * as fs from 'fs';
import * as secp256k1 from '@bitcoinerlab/secp256k1';
import * as descriptors from '@bitcoinerlab/descriptors';

const { Descriptor, BIP32, ECPair2 } = descriptors.DescriptorsFactory(secp256k1);
const toAddress='n12kEsK3LnqvYFMTdMvzAXbb8JtF6qcDSP'
const network = networks.testnet;
const EMERGENCY_RECOVERY = true;
const EXPLORER = 'https://blockstream.info/testnet';

// Leer los datos del fichero
const data = fs.readFileSync("info_address.txt", { encoding: 'utf-8' });

// Analizar los datos y extraer los valores
const lines = data.split('\n');
const wshAddress = lines[0].split(': ')[1];
const emergencyPairWIF = lines[1].split(': ')[1];
const unvaultMnemonic = lines[2].split(': ')[1];
const unvaultKeyBase64 = lines[3].split(': ')[1];
const wshExpression = lines[4].split(': ')[1];

// Convertir los valores a los tipos originales
const emergencyPair = ECPair.fromWIF(emergencyPairWIF);
const unvaultKey = Buffer.from(unvaultKeyBase64, 'base64');

console.log('wshAddress:', wshAddress);
console.log('emergencyPair:', emergencyPair);
console.log('unvaultMnemonic:', unvaultMnemonic);
console.log('unvaultKey BASE 64:', unvaultKey.toString('base64'));
console.log('wshExpression:', wshExpression);
const unvaultMasterNode = BIP32.fromSeed(mnemonicToSeedSync(unvaultMnemonic),network); 

// // Busca el utxo a gastar
const utxo = await (
  await fetch(`${EXPLORER}/api/address/${wshAddress}/utxo`)
).json();

if (utxo?.[0]) {
    const txHex = await (
    await fetch(`${EXPLORER}/api/tx/${utxo?.[0].txid}/hex`)
  ).text();
  const inputValue = utxo[0].value;

  const psbt = new Psbt({ network });
  
  const wshDescriptor = new Descriptor({
    expression: wshExpression,
    network,
    signersPubKeys: [EMERGENCY_RECOVERY ? emergencyPair.publicKey : unvaultKey] 
  });
  
  wshDescriptor.updatePsbt({ psbt, txHex, vout: utxo[0].vout });

  psbt.addOutput({
    address: EMERGENCY_RECOVERY
      ?  toAddress
      : 'tb1q4280xax2lt0u5a5s9hd4easuvzalm8v9ege9ge',
    value: inputValue - 1000
  });

  //Now sign the PSBT
  if (EMERGENCY_RECOVERY)
    descriptors.signers.signECPair({ psbt, ecpair: emergencyPair });
  else descriptors.signers.signBIP32({ psbt, masterNode: unvaultMasterNode });
  
  //Finalize the tx (compute & add the scriptWitness) & push to the blockchain
  wshDescriptor.finalizePsbtInput({ index: 0, psbt });
  const spendTx = psbt.extractTransaction();
  const spendTxPushResult = await (
    await fetch(`${EXPLORER}/api/tx`, {
      method: 'POST',
      body: spendTx.toHex()
    })
  ).text();
  console.log(`Pushing: ${spendTx.toHex()}`);
  console.log(`Tx pushed with result: ${spendTxPushResult}`);

}

```

**Tags:** javascript, output-script-descriptors, miniscript

> **Comments:**
> **username_1**: Hey, i'd be glad to answer this question. But "how do descriptors work" is too broad. It looks like you are stuck at using the satisfier? Could you break up your question about how descriptors work into several more focused ones? Also please copy code snippets instead of pasting screenshots.  
> **username_0**: I`ve already corrected, thanks  
> **username_2**: Author of the library mentioned in the OP here. I've updated the signersPubKeys parameter documentation on GitHub & site for better clarity. Check out the updated docs. If you had issues understanding just that parameter, let me summarize: signersPubKeys is an array of the public keys used for signing the transaction when spending the output associated with a descriptor. This parameter is only required if the descriptor object is being used to finalize a transaction. It is necessary to specify the spending path when working with miniscript-based expressions that have multiple spending paths.  
> **username_0**: @username_2 In the last case that you mentioned, I dont understand : Leave it `undefined` if you only need to generate the             `scriptPubKey` or `address` for a descriptor, or if all                the public keys involved in the descriptor will sign the                 transaction. In the latter case, the satisfier will                 automatically choose the most optimal spending path in terms                 of tx size (if more than one path is available).  

---

## 1 answers

### Answer by username_2 on 2023-04-25. Score: 5 | Accepted answer

**Update [2023-Nov-15]:** This answer has been updated to reflect the changes in v2 of the library.

As the author of the `@bitcoinerlab/descriptors` library, I'd like to provide some clarification on the usage of the `Output` class and the `signersPubKeys` parameter.

Bitcoinerlab's descriptors module can be used for several things, all involving the creation of a `Output` class instance. From the code you provided:

```typescript
const wshOutput = new Output({
    descriptor: wshDescriptor,
    network,
    signersPubKeys
  });

```

Let's first check what `signersPubKeys` is [in the docs](https://bitcoinerlab.com/modules/descriptors):

```typescript

  signersPubKeys // (Optional): An array of the public keys used for signing
                 // the transaction when spending the output associated with
                 // this descriptor. This parameter is only used if the
                 // descriptor object is being used to finalize a transaction.
                 // It is necessary to specify the spending path when working
                 // with miniscript-based expressions that have multiple
                 // spending paths. Set this parameter to an array containing
                 // the public keys involved in the desired spending path.
                 // Leave it `undefined` if you only need to generate the
                 // `scriptPubKey` or `address` for a descriptor, or if all
                 // the public keys involved in the descriptor will sign the
                 // transaction. In the latter case, the satisfier will
                 // automatically choose the most optimal spending path in terms
                 // of tx size (if more than one path is available).

```

As per the docs, `signersPubKeys` is optional and only required when you want to spend from a specific spending branch. Let's break down the different cases for further clarity:

## Receiving bitcoin

If you want to receive Bitcoin locked by a specific policy (specified in a miniscript), you'll need to generate an `address` or `scriptPubKey`. To do this, you don't need to pass `signersPubKeys` to the constructor:

```typescript
const wshOutput = new Output({
    descriptor: wshDescriptor,
    network
  });
const address = wshOutput.getAddress();
const scriptPubKey = wshOutput.getScriptPubKey();

```

## Spending bitcoin from a miniscript-based descriptor

Assuming you've received Bitcoin to a UTXO you control, you can use the library to spend it. This is where `signersPubKeys` may or may not be necessary:

#### Single spending branch UTXO

If there's only one spending branch, you don't need to pass `signersPubKeys`. The satisfier will use the provided signatures and build the script witness for the unique branch.

#### Multiple spending branches UTXO

For multiple spending paths, different possibilities arise. Let's consider the policy from your post:

```typescript
POLICY =`or(pk(@emergencyKey),and(pk(@unvaultKey),after(20)))`;

```

If you can (and will) sign with both `@emergencyKey` and `@unvaultKey`, and the timelock has been released, you don't need to pass `signersPubKeys`. The satisfier will pick the most optimal solution in terms of tx size and discard the unused signature.

Now, consider a different scenario. Suppose you can only sign with `@emergencyKey`, or you can sign with both keys but have decided to use the emergency path to avoid waiting for the timelock.

In such cases, you need to pass `signersPubKeys: [emergencyPubKey]` and then sign with `@emergencyKey`. This ensures that the spending transaction is created with the desired unlocking script.

This information is indeed used in a couple of places within the code you provided:

```typescript
// `signersPubKeys` is used internally here to set the appropriate
// `nLockTime` and `nSequence` for the transaction (the psbt below):
const finalizer = wshOutput.updatePsbtAsInput({ psbt, txHex, vout: utxo[0].vout }); 

// `signersPubKeys` is used internally here to generate
// the appropriate script witness for the input:
finalizer({ psbt });

```

In summary, the `signersPubKeys` parameter is essential when working with miniscript-based descriptors that have multiple spending paths, and you want to specify a particular path. Depending on your use case, you may or may not need to include this parameter.

> **Comments:**
> **username_0**: Now I understand all, It is a very nice explication, thanks!!  
> **username_3**: Thank you, this has been very helpful  

---

