I am trying to create and sign a psbt as demonstrated P2SH(P2WSH(P2MS(3 of 4))) (SegWit multisig) input . I created the address from this example. My code for address creation is as follows:
'use strict'
const bitcoin = require('bitcoinjs-lib')
const wifList = require('./wif')
const NETWORKS = require('./networks')
const testnet = NETWORKS.testnet
exports.genAddress = function (m) {
m = 2
const network = testnet
const eccArray = getECPairFromWifArray(wifList.wifList, network)
let pubkeys = []
for (let i = 0; i < eccArray.length; i++) {
pubkeys.push(eccArray[i].publicKey)
}
const info = bitcoin.payments.p2sh({ network: network,
redeem: bitcoin.payments.p2wsh({ network: network,
redeem: bitcoin.payments.p2ms({ m: m, pubkeys: pubkeys, network: network })
})
})
return info.address
}
function getECPairFromWifArray (wifArray, network) {
let eccPairArray = []
for (let i = 0; i < wifArray.length; i++) {
eccPairArray.push(bitcoin.ECPair.fromWIF(wifArray[i], network))
}
return eccPairArray
}
// wif.js
'use strict'
exports.wifList = [
'cU5YmqypsqeMokL3faxKLsW5uMEwpfmYq8bRQjWhAHS718o3XRX2',
'cS5MyJDQcvmQm9M4dHBFzWduFJLNWDmPubPgme3kTsvAaT1ZLTfD'
]
I sent some coins to 2MyG6iPpbbH5G7Fh7kcQpevLte3jhSy5Yke
on the testnet as shown in this transaction.
The psbt code that I am trying to get working is here:
//psbt.js
const bitcoin = require('bitcoinjs-lib')
const NETWORKS = require('./networks')
const wifList = require('./wif')
const regtest = NETWORKS.regtest
const testnet = NETWORKS.testnet
let psbt = async function () {
const network = testnet
const eccArray = getECPairFromWifArray(wifList.wifList, network)
const p2sh = createPayment('p2sh-p2wsh-p2ms(2 of 2)', eccArray, network)
const inputData = await getInputData(p2sh.payment, 'p2sh-p2wsh')
const spendable = 10000
const fees = spendable - 5000
const totalToSend = spendable - fees
const psbt = new bitcoin.Psbt({ network: network })
.addInput(inputData)
.addOutput({
address: '2NGZrVvZG92qGYqzTLjCAewvPZ7JE8S8VxE',
value: totalToSend
})
.signInput(0, p2sh.keys[0])
.signInput(0, p2sh.keys[1])
psbt.finalizeAllInputs()
const tx = psbt.extractTransaction()
return tx
}
function getECPairFromWifArray (wifArray, network) {
let eccPairArray = []
for (let i = 0; i < wifArray.length; i++) {
eccPairArray.push(bitcoin.ECPair.fromWIF(wifArray[i], network))
}
return eccPairArray
}
function createPayment (_type, myKeys, network) {
network = network || regtest
const splitType = _type.split('-').reverse()
const isMultisig = splitType[0].slice(0, 4) === 'p2ms'
const keys = myKeys || []
let m
if (isMultisig) {
const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/)
m = parseInt(match[1])
let n = parseInt(match[2])
if (keys.length > 0 && keys.length !== n) {
throw new Error('Need n keys for multisig')
}
// eslint-disable-next-line no-unmodified-loop-condition
while (!myKeys && n > 1) {
keys.push(bitcoin.ECPair.makeRandom({ network }))
n--
}
}
if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network }))
let payment
splitType.forEach(type => {
if (type.slice(0, 4) === 'p2ms') {
payment = bitcoin.payments.p2ms({
m,
pubkeys: keys.map(key => key.publicKey).sort(),
network
})
} else if (['p2sh', 'p2wsh'].indexOf(type) > -1) {
payment = bitcoin.payments[type]({
redeem: payment,
network
})
} else {
payment = bitcoin.payments[type]({
pubkey: keys[0].publicKey,
network
})
}
})
return {
payment,
keys
}
}
async function getInputData (payment, redeemType) {
const amount = 10000
const hash = '9c72e8d196e13f643331e4d26c942640655128c1773a5f6c56cb70efb43f0628'
const index = 1
const script = Buffer.from(
'a91441fb19425667dba47df50802d046f35520732c5487', 'hex'
)
const witnessUtxo = {
script: script,
value: amount }
const mixin = { witnessUtxo }
const mixin2 = {}
switch (redeemType) {
case 'p2sh-p2wsh':
mixin2.witnessScript = payment.redeem.redeem.output
mixin2.redeemScript = payment.redeem.output
break
}
return {
hash: hash,
index: index,
...mixin,
...mixin2
}
}
but when I try and run the psbt function I get this error:
Error: Redeem script for input #0 doesn't match the scriptPubKey in the prevout.
I am not using regtest network but I think I have the correct info for the witnessUtxo as shown above. I looked at the scriptpubkey for the input and I got 2MyG6iPpbbH5G7Fh7kcQpevLte3jhSy5Yke. I am unsure what the redeem script should read for ths kind of transaction, but I get 00208815ac1e5cfb61858546b2df9676fd4fced4de9b5a043a04834bd562513ad0c2 when I log the mixin2.redeemscript. Any help would be appreciated.
try sorting your pubkeys for p2ms creation.
let me know if that helps.
Psbt should support any pubkey order, but if it works sorted than maybe that's where the problem lies
ahh, ok. yeah.
genAddress is not sorting pubkeys but createPayment is.
it should work if you remove .sort() from createPayment.
but just FYI sorting pubkeys just before creating the script is best practice.
Thank You so much. It's a pleasure to work with this library.
Most helpful comment
Thank You so much. It's a pleasure to work with this library.