Bitcoinjs-lib: Signing a custom P2SH multisig

Created on 3 Sep 2017  路  7Comments  路  Source: bitcoinjs/bitcoinjs-lib

Hello everybody.

I'm trying to sign a custom P2SH Multisig but i'm having trouble.
From my understanding the "sign" method in TransactionBuilder only accepts a set of standard transactions.

This is my attempt:


    var keyPairs = [
      "cRmbQiGDNF7FGNuY7PeTS2VTG59s2kHUjVEf65dtFRWV2CJKnkzK",
      "cSwigfrwCHr2npxtRhQGUyg87uThQTjscWx6twmYSSzkZT2rB7GC"
    ].map(function(wif) {
      return bitcoin.ECPair.fromWIF(wif, testnet);
    });
    var pubKeys = keyPairs.map(function(x) {
      return x.getPublicKeyBuffer();
    });

  // 2-2 Multi-sig with an IF statement
    var witnessScript = bitcoin.script.compile(
      [].concat(ops.IF, ops.OP_RESERVED + 2, pubKeys, ops.OP_RESERVED + 2, ops.OP_CHECKMULTISIG, ops.ENDIF)
    );  

    var witnessScriptHash = bitcoin.crypto.sha256(witnessScript);
    var redeemScript = bitcoin.script.witnessScriptHash.output.encode(witnessScriptHash);
    var redeemScriptHash = bitcoin.crypto.hash160(redeemScript);
    var scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash);
    var address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet);

    testnetUtils.faucet(address, 6e4, function(err, unspent) {
      if (err) return done(err);

      var txb = new bitcoin.TransactionBuilder(testnet);
      txb.addInput(unspent.txId, unspent.vout);
      txb.addOutput(testnetUtils.RETURN_ADDRESS, 4e4);
      txb.sign(0, keyPairs[0], redeemScript, null, unspent.value, scriptPubKey);
      txb.sign(0, keyPairs[1], redeemScript, null, unspent.value, scriptPubKey);

// The redeem script should be:
//  <sig 1, sig2> 1
// However im unsure on how to do this :(

      var tx = txb.build();

      // build and broadcast to the Bitcoin Testnet network
      testnetUtils.transactions.propagate(tx.toHex(), function(err) {
        if (err) return done(err);

        testnetUtils.verify(address, tx.getId(), 4e4, done);
      });
    });

Thanks for your help

how to / question / docs

Most helpful comment

Got the following to work, slightly modified the script into an IF/ELSE.

var bitcoin = require('./src/index.js')
var bcrypto = bitcoin.crypto;
var bscript = bitcoin.script;
var ops = require('bitcoin-ops');

var testnet = bitcoin.networks.testnet;

var keyPairs = [
  "cRmbQiGDNF7FGNuY7PeTS2VTG59s2kHUjVEf65dtFRWV2CJKnkzK",
  "cSwigfrwCHr2npxtRhQGUyg87uThQTjscWx6twmYSSzkZT2rB7GC"
].map(function(wif) {
  return bitcoin.ECPair.fromWIF(wif, testnet);
});
var pubKeys = keyPairs.map(function(x) {
  return x.getPublicKeyBuffer();
});

// if 2-of-2-multisig else [alice
var scriptParts = [].concat(
  ops.OP_IF, 
    ops.OP_2, pubKeys[0], pubKeys[1], ops.OP_2, ops.OP_CHECKMULTISIG, 
  ops.OP_ELSE, 
    pubKeys[1], ops.OP_CHECKSIG, 
  ops.OP_ENDIF);

var witnessScript = bitcoin.script.compile(
  scriptParts
);
console.log(bscript.toASM(scriptParts))

var witnessHash = bcrypto.sha256(witnessScript)
var redeemScript = bscript.witnessScriptHash.output.encode(witnessHash)
var redeemScript160 = bcrypto.hash160(redeemScript)

var value = 100000000
var scriptPubKey = bscript.scriptHash.output.encode(redeemScript160)
var txId = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234";
var vout = 0

var txb = new bitcoin.Transaction();
txb.ins.push({
  hash: Buffer.from(txId, "hex").reverse(),
  index: vout,
  script: bitcoin.script.compile([redeemScript]),
  sequence: 0xffffffff
});

txb.outs.push({
  script: scriptPubKey,
  value: 90000000
});

var hash = txb.hashForWitnessV0(0, witnessScript, value, bitcoin.Transaction.SIGHASH_ALL);
var sig1 = keyPairs[0].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);
var sig2 = keyPairs[1].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);

var witness1 = [Buffer.from("", "hex"), sig1, sig2, Buffer.from("01", "hex"), witnessScript];
txb.ins[0].witness = witness1;

var hex = txb.toHex()
console.log(hex)

var witness2 = [sig2, Buffer.from("", "hex"), witnessScript];
txb.ins[0].witness = witness2;

var hex = txb.toHex()
console.log(hex)

All 7 comments

TransactionBuilder only supports specific transaction types.

If you'd like to know how to sign, look at the sign function, and create a similar function within your app.

Thanks for you help :)
I'm using the Transaction class to create a raw transaction.

When I broadcast the transaction I get the following error:
Error: 64: non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation)
I believe i'm not generating the sig correctly?


      var txb = new bitcoin.Transaction();
      txb.ins.push({
        hash: Buffer.from(unspent.txId, "hex").reverse(),
        index: 0,
        script: bitcoin.script.compile([redeemScript]), 
        sequence: 0xffffffff
      });
      const hash = txb.hashForWitnessV0(0, witnessScript, unspent.value, bitcoin.Transaction.SIGHASH_ALL);

      const sig1 = keyPairs[0].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);
      const sig2 = keyPairs[1].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);
      const witness = [sig2, sig1, Buffer.from("01", "hex"), witnessScript];
      txb.ins[0].witness = witness;

      txb.outs.push({
        script: scriptPubKey, 
        value: 4e4
      });

txb.toHex()

you can't add an output after generating the sighash...

Also, be sure that the sig order is correct. Otherwise it will fail.

looks like the null byte for the CHECKMULTISIG is missing also?

Got the following to work, slightly modified the script into an IF/ELSE.

var bitcoin = require('./src/index.js')
var bcrypto = bitcoin.crypto;
var bscript = bitcoin.script;
var ops = require('bitcoin-ops');

var testnet = bitcoin.networks.testnet;

var keyPairs = [
  "cRmbQiGDNF7FGNuY7PeTS2VTG59s2kHUjVEf65dtFRWV2CJKnkzK",
  "cSwigfrwCHr2npxtRhQGUyg87uThQTjscWx6twmYSSzkZT2rB7GC"
].map(function(wif) {
  return bitcoin.ECPair.fromWIF(wif, testnet);
});
var pubKeys = keyPairs.map(function(x) {
  return x.getPublicKeyBuffer();
});

// if 2-of-2-multisig else [alice
var scriptParts = [].concat(
  ops.OP_IF, 
    ops.OP_2, pubKeys[0], pubKeys[1], ops.OP_2, ops.OP_CHECKMULTISIG, 
  ops.OP_ELSE, 
    pubKeys[1], ops.OP_CHECKSIG, 
  ops.OP_ENDIF);

var witnessScript = bitcoin.script.compile(
  scriptParts
);
console.log(bscript.toASM(scriptParts))

var witnessHash = bcrypto.sha256(witnessScript)
var redeemScript = bscript.witnessScriptHash.output.encode(witnessHash)
var redeemScript160 = bcrypto.hash160(redeemScript)

var value = 100000000
var scriptPubKey = bscript.scriptHash.output.encode(redeemScript160)
var txId = "abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234";
var vout = 0

var txb = new bitcoin.Transaction();
txb.ins.push({
  hash: Buffer.from(txId, "hex").reverse(),
  index: vout,
  script: bitcoin.script.compile([redeemScript]),
  sequence: 0xffffffff
});

txb.outs.push({
  script: scriptPubKey,
  value: 90000000
});

var hash = txb.hashForWitnessV0(0, witnessScript, value, bitcoin.Transaction.SIGHASH_ALL);
var sig1 = keyPairs[0].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);
var sig2 = keyPairs[1].sign(hash).toScriptSignature(bitcoin.Transaction.SIGHASH_ALL);

var witness1 = [Buffer.from("", "hex"), sig1, sig2, Buffer.from("01", "hex"), witnessScript];
txb.ins[0].witness = witness1;

var hex = txb.toHex()
console.log(hex)

var witness2 = [sig2, Buffer.from("", "hex"), witnessScript];
txb.ins[0].witness = witness2;

var hex = txb.toHex()
console.log(hex)

Thank you so much that worked perfectly:
Hopefully I can start contributing soon. :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tuyennvtb picture tuyennvtb  路  3Comments

panpan2 picture panpan2  路  3Comments

dcousens picture dcousens  路  3Comments

LeonYanghaha picture LeonYanghaha  路  3Comments

namnv04 picture namnv04  路  3Comments