bitcoinjs/bitcoinjs-lib

How to sign Psbt with P2WSH(P2MS) Script?

renz-leverfi opened this issue · 1 comments

Please help. I have a utxo with P2WSH(P2MS) script, I am having trouble to sign the transaction together while appending a signature of another party

In my understanding of P2WSH, it must have the following structure (assumed the order of signature is correct);

[
  00
  mySignature
  anotherPartySignature
  witnessScript
]

Here is the simplest form of how I tried signing the transaction

const txHex: string
const otherPartyPubkey: Buffer // compressed pubkey
const otherPartySig: Buffer // is a finalized serialized der encoded sig
const p2ms_pubkey: Buffer
const p2wsh_wp2ms_pubkey: Buffer

const tx = Transaction.fromHex(txHex)

const inputs = tx.ins.map(input => {
  return {
    hash: input.hash,
    index: input.index,
    sequence: input.sequence,
    witnessUtxo: {
      value: 1000,
      script: p2wsh_wp2ms_pubkey,
    },
    witnessScript: p2ms_pubkey,

    // This is how I thought the way to append a signature, but is incorrect
    // partialSig: [{
    //   pubkey: otherPartyPubkey,
    //   signature: otherPartySig,
    // }],
    // sighashType: 0x01,
  }
})

const psbt = new Psbt()
  .setVersion(tx.version)
  .setLocktime(tx.locktime)
  .addInputs(inputs)
  .addOutputs(tx.outs)

psbt.signInput(0, signer, 0x01)
psbt.finalizeInput(0)

I put p2ms on witnessScript; because of this checker function

bitcoinjs-lib/ts_src/psbt.ts

Lines 1433 to 1453 in 8edc5e8

function scriptCheckerFactory(
payment: any,
paymentScriptName: string,
): (idx: number, spk: Buffer, rs: Buffer, ioType: 'input' | 'output') => void {
return (
inputIndex: number,
scriptPubKey: Buffer,
redeemScript: Buffer,
ioType: 'input' | 'output',
): void => {
const redeemScriptOutput = payment({
redeem: { output: redeemScript },
}).output as Buffer;
if (!scriptPubKey.equals(redeemScriptOutput)) {
throw new Error(
`${paymentScriptName} for ${ioType} #${inputIndex} doesn't match the scriptPubKey in the prevout`,
);
}
};
}

But this become a problem when finalizing the inputs

bitcoinjs-lib/ts_src/psbt.ts

Lines 1509 to 1531 in 8edc5e8

function getFinalScripts(
inputIndex: number,
input: PsbtInput,
script: Buffer,
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean,
): {
finalScriptSig: Buffer | undefined;
finalScriptWitness: Buffer | undefined;
} {
const scriptType = classifyScript(script);
if (!canFinalize(input, script, scriptType))
throw new Error(`Can not finalize input #${inputIndex}`);
return prepareFinalScripts(
script,
scriptType,
input.partialSig!,
isSegwit,
isP2SH,
isP2WSH,
);
}

What to do here?

UTXO with prevout of P2WSH(P2MS)

https://mempool.space/testnet/tx/7ac6dde5ffda902d1741e9ffc3cd20790240da077cb7d302c25c662c26bf96aa

Here are my questions

  1. Is it possible to append another party's signature to the Witness Field when finalizing the input? How?
  2. I am totally confuse on what to use between witnessUtxo and nonWitnessUtxo because the witnessScript is considered a non-segwit script
  3. what should be the content of witnessScript, witnessUtxo.script?

Fixed it, I just insert the other partialSig later after signing, here is how I did it;

const inputs = tx.ins.map(input => {
  return {
    hash: input.hash,
    index: input.index,
    sequence: input.sequence,
    witnessUtxo: {
      value: 1000,
      script: p2wsh_wp2ms_pubkey,
    },
    witnessScript: p2ms_pubkey,
  }
})

const psbt = new Psbt()
  .setVersion(tx.version)
  .setLocktime(tx.locktime)
  .addInputs(inputs)
  .addOutputs(tx.outs)

psbt.signInput(0, signer, 0x01)

psbt.updateInput(0, {
  partialSig: [{
    pubkey: otherPartyPubkey,
    signature: otherPartySig,
  }]
})

psbt.finalizeAllInputs()

It feels like a hack, but it works, if there is proper way, please let me know.