0

i experimented a lot with p2wsh multisig configurations with bitcoinjs-lib. but when i tried to sign a standard native segwit p2wpkh to p2wpkh transaction everything goes ok except for bitcoin-cli sendrawtransaction. it says

non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation)

js code (./btc is here, the rest can be npm installed):

const { btc, send, listunspent, getnewaddress, bech32toScriptPubKey } = require('./btc')();
const bitcoin = require('bitcoinjs-lib');
const network = bitcoin.networks.testnet;
const hashtype = bitcoin.Transaction.SIGHASH_ALL;
const ECPair = require('ecpair').ECPairFactory(require('tiny-secp256k1'));
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.on('close', () => process.exit(0));

const input = q => new Promise(r => rl.question(q, r));

main();

async function main() {
    const u = (await listunspent(0, 0, true)).filter(x => x.solvable && x.spendable && x.desc.startsWith('wpkh'));

    console.log(u);

    const v = await input('Which input to use? ');

    const utxo = u[parseInt(v)];

    if (!utxo) {
        console.log('error');
        process.exit(1);
    }

    var out = await input('Output address (leave empty to generate a new one) ');

    if (out.length == 0) {
        out = await getnewaddress();
    }

    if (out.length != 42) {
        console.log('p2wpkh addresses are 42 chars long');
        process.exit(1);
    }

    const ecpair = ECPair.fromWIF(await btc('dumpprivkey', utxo.address), network);

    const tx = new bitcoin.Transaction(network);

    tx.addInput(Buffer.from(utxo.txid, 'hex').reverse(), utxo.vout);

    const fee = 110;

    tx.addOutput(bech32toScriptPubKey(out), utxo.amount - fee);

    const sighash = tx.hashForWitnessV0(
        0, Buffer.from(utxo.scriptPubKey, 'hex'), utxo.amount, hashtype
    );

    tx.setWitness(0, [
        bitcoin.script.signature.encode(ecpair.sign(sighash), hashtype),
        ecpair.publicKey
    ]);

    console.log(tx.toHex());
    console.log(await send(tx.toHex()));
}

TX hex

01000000000101c690a8157bb45506b15173a3175bd3504182cf8c91f916235522522a3d1a28280100000000ffffffff01d2410f00000000001600149b959b78f09b7f7976391fd20aad3835b738e0c3024730440220120a950d96210c48a6831e7ee7f746e6119183d8512b10a6b14c60509556bba3022042359f1699e9cfb90637647ecb085e262736dc5ad9c076666cb44c29c20baafc0121027b612527ff9e157b80dd2ddac0e11555afcd517a7a425c27e5c7afbad889528e00000000

i think i am making a mistake in what must be included in the sighash. What scriptPubKey is used in the sighash of a p2wpkh?

1

1 Answer 1

2

i have fixed the problem by using a P2PKH output script for the sighash

const sighash = tx.hashForWitnessV0(
    0, Buffer.from(utxo.scriptPubKey, 'hex'), utxo.amount, hashtype
);

has been changed to

const pkh = bitcoin.script.decompile(Buffer.from(utxo.scriptPubKey, 'hex'))[1];

const sighash = tx.hashForWitnessV0(
    0, bitcoin.script.compile([
        bitcoin.opcodes.OP_DUP,
        bitcoin.opcodes.OP_HASH160,
        pkh,
        bitcoin.opcodes.OP_EQUALVERIFY,
        bitcoin.opcodes.OP_CHECKSIG
    ]), utxo.amount, hashtype
);

my script now works and added it to my Bitcoin stuff repo
https://github.com/antonilol/btc_stuff
https://github.com/antonilol/btc_stuff/blob/master/p2wpkh.js

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.