bitcoinjs / bitcoinjs-lib

A javascript Bitcoin library for node.js and browsers.
MIT License
5.6k stars 2.08k forks source link

Problem creating transaction from Segwit multisig input #1194

Closed coinflippa closed 5 years ago

coinflippa commented 5 years ago

I followed this example to create a multisig bech32 address (my address is 2/3 though).

https://github.com/bitcoinjs/bitcoinjs-lib/blob/20551fd38070cf68d078c0644c7586a2607f57fa/test/integration/addresses.js#L76-L88

I spent to the address and then I am attempting to create a transaction, mostly based on this example: https://github.com/bitcoinjs/bitcoinjs-lib/blob/master/test/integration/transactions.js#L223-L260

const network = bitcoin.networks.testnet

const uxtos = [
  {
    "txid": "...",
    "vout": 1
  }
]

const destinations = [
  {
    "address": "...",
    "amount": "..."
  }
]

const publicKeys = [
  "03421bdf5825c26fa96fe1402a49df9901f31973dc9fab189d9ff4d8af853d0c4c",
  "022b1e2fcfe441df558097e7300e4c23a5f459f3ea45c8828962c589fc88d43543",
  "03749ba41055a85371460a7b01c5b74eab55af7c0de7c490f981cdccd5c2f0f286"
  ]

const pubkeys = publicKeys.map((pubkey) => new Buffer(pubkey, 'hex'))
const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys, network })
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network })

const wifStrings = [
  "cUfMQu8f97JsSCYMUtr3sNpKSqhFifdaoy5GAfQv94atAZH2Sg7e",
  "cW3nPhsqEhCztNF3AqGrKpNaF...",
  "cV2tBrP4VSni3nCYzAdZDfX7K..."
]

const wifs = wifStrings.map((wif) => bitcoin.ECPair.fromWIF(wif, network))
const txb = new bitcoin.TransactionBuilder(network)

utxos.map((utxo) => {
  txb.addInput(utxo.txid, utxo.vout, null, p2sh.output)
})

destinations.map((destination) => {
  txb.addOutput(destination.address, destination.amount)
})

txb.__inputs.forEach((_ele, index) => {
  wifs.map((wif) => {
    txb.sign(index, wif, p2sh.redeem.output, null, req.body.total, p2wsh.redeem.output)
   })
})

const incomplete = txb.build().toHex()

Attempting to broadcast via Electrum is giving me the following error:

error: {'code': 1, 'message': 'the transaction was rejected by network rules.\n\n64: non-mandatory-script-verify-flag (Witness requires empty scriptSig)

I'm not sure I need this because it wasn't present when making the address...

const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network })

dcousens commented 5 years ago

I'm not sure I need this because it wasn't present when making the address...

This is the red flag.

Are you dealing with P2WSH(P2MS 2/3), or are you dealing with P2SH(P2WSH(P2MS 2/3))? The example you linked is only P2WSH, no P2SH.

dabura667 commented 5 years ago

don't sign 3 times. only sign twice.

coinflippa commented 5 years ago

Are you dealing with P2WSH(P2MS 2/3), or are you dealing with P2SH(P2WSH(P2MS 2/3))

P2WSH(P2MS 2/3) but I was also trying P2SH(P2WSH(P2MS 2/3)). I have both working now.

don't sign 3 times. only sign twice.

That worked! Apologies for my stupidity.

dcousens commented 5 years ago

@junderw if we have a 2/3, and sign is called 3 times... what happens?

junderw commented 5 years ago

@dcousens pushes all three sigs on the stack. Making it invalid.

At least last time I checked. Which was a while ago.

dcousens commented 5 years ago

@junderw shouldn't the payments.p2ms explode if that happens?

dcousens commented 5 years ago

https://github.com/bitcoinjs/bitcoinjs-lib/blob/20551fd38070cf68d078c0644c7586a2607f57fa/src/payments/p2ms.js#L124-L125

junderw commented 5 years ago

@dcousens oops... forgot to pass opts.validate anywhere in transaction_builder.js... that code you linked isn't getting run at all.

Prolly need to change:

https://github.com/bitcoinjs/bitcoinjs-lib/blob/20551fd38070cf68d078c0644c7586a2607f57fa/src/transaction_builder.js#L402

From return payments.p2ms({ signatures }, { allowIncomplete }) To return payments.p2ms({ signatures }, { allowIncomplete, validate: true })

junderw commented 5 years ago

https://github.com/bitcoinjs/bitcoinjs-lib/pull/1195

Made a PR.

junderw commented 5 years ago

Perhaps opts = opts || { validate: true } at the top should be changed to opts = Object.assign({ validate: true }, opts || {})

This will overwrite the default validate value with what it is in opts if the key is contained in opts... otherwise, validate will be added to opts with the value true... if opts is undefined, this just returns the object { validate: true }

I think this is closer to the original intention... so I added it to the PR.

vineettyagi28 commented 4 years ago

I followed the code given in this thread and build a transaction given below:- 02000000000101752b4df0ea8a9c84acca4ab338bffac59d7af90e93e139d00a4f5803452ba3c00100000023220020291a9f40a238fac33175191109344b9a86773ae7a1ace069718757d258df515bffffffff021d290000000000001976a91421328c7223c37b02118f7b1cf3f80260366d8d1b88acf3080f000000000017a914329f8db458c594b45bcaf9b54e0bd387e733e6f587040047304402206bb3be1f627df40010330a330689e403ebdcb3065ae905012786d476040c71a702202600cc05ec996bd3da2c2448fafd4027d4dd11169540ab39a4ddd53a9ea5a310014730440220615a4de8d3524a678592c68082e18f912e8a2939faa25a71144c9fd8e3b7dc19022038ab2448c7ebc98288759173ae083d242af4ced9ad48e759545aa3136308fcb801695221030c275a40cd25c05cd705b2202a96709f77571868462bebcb65ecf01f0166134221020a4ccc55d5d058c5fd46ba93fefbddb145018fbc3d9b928a02d6fb0642ddc6e821033eab4e7446ea2032427f95337f49f15e589ad1d9d83c928fd8dfbafc80a3bc5d53ae00000000

But I am getting following error:-

16: bad-txns-vin-empty. Code:-26>

Here is the code I am using:-

`

var pubkBuf1 = new Buffer(pubKey1, 'hex');
var pubkBuf2 = new Buffer(pubKey2, 'hex');
var pubkBuf3 = new Buffer(pubKey3, 'hex');

let pubKeyBufs = [pubkBuf1, pubkBuf2, pubkBuf3];
//let redeemScript = bitcoin.script.multisig.output.encode(2, pubKeyBufs);

const network = bitcoin.networks.testnet

const p2ms = bitcoin.payments.p2ms({ m: 2, pubkeys: pubKeyBufs, network })
const p2wsh = bitcoin.payments.p2wsh({ redeem: p2ms, network })
const p2sh = bitcoin.payments.p2sh({ redeem: p2wsh, network })

let txb = new bitcoin.TransactionBuilder(network);

const utxos = await utxoProvider(multiSigAddr);

const amtSatoshi = Math.floor(value * BITCOIN_SAT_MULT);

let ninputs = 0;
let availableSat = 0;
for (let i = 0; i < utxos.length; i++) {
    const utxo = utxos[i];
    //TODO chnage to minimum confirmation instead of 1
    if (Math.abs(utxo.confirmations) >= 1) {
        txb.addInput(utxo.txid, utxo.vout, null, p2sh.output);
        availableSat += utxo.satoshis;
        ninputs++;

        if (availableSat >= amtSatoshi) break;
    }
}

//txb.addOutput(destAdr, Math.floor(value * BITCOIN_SAT_MULT)); // fee 500000 satoshi

const change = availableSat - amtSatoshi;
const fee = getTransactionSize(ninputs, change > 0 ? 2 : 1) * feesPerByte;
alert(fee);
alert(amtSatoshi);
if (fee > amtSatoshi) throw "BitCoin amount must be larger than the fee. (Ideally it should be MUCH larger)";
if (availableSat < (amtSatoshi + fee)) throw "You do not have enough in your wallet to send that much.";
txb.addOutput(destAdr, amtSatoshi - fee);
if (change > 0) txb.addOutput(multiSigAddr, change);
for (let i = 0; i < ninputs; i++) {
    txb.sign(i, sellerKey, p2sh.redeem.output, null, availableSat, p2wsh.redeem.output);
    txb.sign(i, buyerKey, p2sh.redeem.output, null, availableSat, p2wsh.redeem.output);
    //txb.sign(i, buyerKey, p2sh.redeem.output);
}

let tx1 = txb.build();
console.log(tx1);
console.log(tx1.toHex());
return pushtxProvider(tx1.toHex());}