I am trying to build a raw transaction that will send funds from a 2 of 3 multisig address to an output address. While the below code works, it does not include the redeem script anywhere in the raw transaction. Sites like coinb.in when creating a raw transaction include it and can tell that 2 of 3 signatures are needed, but using the below code it is not seen as a 2 of 3 multisig needing 2 signature. While this isnt a big issue by itself, it also causes some clients like coinb.in to not actually sign the transaction again.
Basically I am not sure how I can include the redeem script in the raw transaction that gets created, any help is appreciated.
const bitcoin = require('bitcoinjs-lib');
const tx = new bitcoin.TransactionBuilder();
tx.addInput('INPUT HERE AS STRING', 0);
tx.addOutput('OUTPUT ADDRESS HERE', 0.01);
const tx2 = tx.buildIncomplete();
console.log(tx2.toHex());
You have to do something like this
var txb = new bitcoin.Transaction()
utxo.forEach(inn => {
txb.ins.push({
hash: inn.hash,
index: inn.index,
script: redeemScript, // You may need to compile it : bitcoin.script.compile(redeemScript)
sequence: 0xffffffff,
value: inn.value
})
})
It’s simple.
addInput has a prevoutScript arg (iirc it’s the 4th arg, you can pass null for 3rd)
@dabura667 thoughts on options argument there for 4.0.0?
bitcore-lib uses opts objects as the last arg all the time.
It only works because they have a comment above each function explaining what possibilities the opts could contain.
I like easy to understand args in the function declaration personally, but if we write a comment list of all expected parameters, OR standardize it so that all possible parameters are somehow checked at the top of the function, it would be fine.
Bitgo-JS is an example of how NOT to do opts.
(Every function uses opts objects, and while some functions have possible attributes for itself, it doesn’t document the attributes that affect subroutines.)
(ie. sendCoins basically just creates an array with one item and calls sendMany, but the only opts explained are the one specific to sendCoins, so I had to check the function for all instances of opts being shuffled off to another function and jump around the codebase to find what my options for the opts object were... ughhh.)
So I would call sendCoins with minConf: 1, even though the explanation of minConf is in getUnspents, which is like layer 15 of the callstack when I call sendCoins... it’s horrid.
(btw Bitgo uses us... so if any of them see this, no offense intended.)
In my opinion it gets annoying when a function has 3+ arguments, it makes the code less readable.
@dabura667 in the above, the prevOutputScript would not be the redeem script, it'd be the P2SH script... you can only provide a redeemScript in .sign?
ahh yes.
@starsoccer
const bitcoin = require('bitcoinjs-lib');
const txb = new bitcoin.TransactionBuilder();
// prevOutScript is a script (Buffer) created with :
// bitcoin.script.scriptHash.output.encode(hashBufferOfRedeemScript)
txb.addInput('INPUT HERE AS STRING', 0, null, prevOutScript);
txb.addOutput('OUTPUT ADDRESS HERE', 0.01);
const tx = txb.buildIncomplete();
console.log(tx.toHex());
Then when you sign it:
const bitcoin = require('bitcoinjs-lib');
// txhex is the output from the previous function
const txb = bitcoin.TransactionBuilder.fromTransaction(bitcoin.Transaction.fromHex(txhex));
// keyPair is the ECPair object with your private key
// redeemScript is the raw redeemScript as a Buffer. (including all opcodes)
// Currently bitcoinjs-lib only supports multi-sig with TransactionBuilders, so
// bitcoin.script.multisig.output.encode(m, pubKeys)
// where m is the m in m-of-n, and pubKeys is an Array of Buffers representing 33 or 65 byte DER pubkeys
txb.sign(0, keyPair, redeemScript);
// if the last signature:
// const tx = txb.build();
const tx = txb.buildIncomplete();
console.log(tx.toHex());
If you have any further questions let me know.
Thanks.
@junderw yes I am getting an error when trying with the code you mentioned.
Basically I used the same code you posted but before the txb.addInput I put the following line,
const prevOutScript = bitcoin.script.scriptHash.output.encode("RedeemScriptAsStringHere");
Then an error is thrown saying, TypeError: Cannot read property 'output' of undefined.
Okay fixed that and converted it to a buffer. Can you clarify how I can convert the redeemscript to a hash or hashBuffer as it was called before? As I am assuming thats why I am getting the following error now,
Expected Buffer(Length: 20), got Buffer(Length: 338)
bitcoin.crypto.hash160(redeemScriptBuffer) gives you a Buffer that is the ripemd160-sha256 hash.
Thanks, its working now, but it doesnt seem to have any impact on the resulting raw transaction. Below is my code, but the resulting tx2.toHex() returns the same raw transaction as the code I first posted returned.
```
const bitcoin = require('bitcoinjs-lib');
const tx = new bitcoin.TransactionBuilder();
const prevOutScript = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(new Buffer("RedeemScriptAsStringHere")));
tx.addInput('INPUT HERE AS STRING', 0, null, prevOutScript);
tx.addOutput('OUTPUT ADDRESS HERE', 0.01);
const tx2 = tx.buildIncomplete();
console.log(tx2.toHex());
````
yeah, it doesn't affect anything if you are doing buildIncomplete().
Okay..... well are there any other options? I need to be able to build a raw transaction that has the redeemscript without needing any signatures.
a raw transaction that has the redeemscript without needing any signatures.
you need to do that yourself. Send both transaction and redeemscript separately, or separate them with a comma and do split(',')
Sorry, maybe I wasnt really clear with what I meant. If you go for example and create a raw transaction on coinb.in the previous script is included. Building the same raw transaction with bitcoinjs does not. I am just looking for a way to have the previous script included in the raw transaction when its built.
I dont fully understand the difference between the previous script and redeem script, but basically all I am trying to accomplish is to have a raw transaction that can be signed multiple times on coinb.in. The current raw transaction generated can only be signed once.
use coinb.in.
The only site that includes the redeemscript in the raw transaction is coinbin, so use them.
Hmm okay, I didnt realize that. Is their any alternative web tool that can be used? I am looking for a place where I can direct users who do not have software to sign it.
Also I am not really clear on how I would even go about signing the raw transaction once it has been signed once as an error is thrown saying PrevOutScript must be P2SH.
var tx = bitcoin.Transaction.fromHex('RAW TRANSACTION STRING')
var txb = bitcoin.TransactionBuilder.fromTransaction(tx)
var keyPair = bitcoin.ECPair.fromWIF('PRIVATE KEY As String')
txb.sign(0, keyPair, redeemscript);
console.log(txb.toHex());
@dabura667 Just for reference, http://ms-brainwallet.org creates raw transactions(with one signature) with the redeemscript built in and is compatible with coinb.in
Doing some more testing, other wallet software like electrum also will not sign the raw transaction created
bump
So the goal is:
Output transactions in coinb.in’s format.
Correct?
If so, give us an example.
Like maybe a 3 of 3 that only has 2 signatures or something.
@dabura667
Sorry for the delay, so below is a signed raw transaction for a 2 of 3 multisig. The problem with the below signed raw transaction is that it can not actually be signed again. If I attempt to sign it using coinb.in, ms-brainwallet.org, or even electrum it just returns the same signed raw transaction.
0100000001a4e6cf9016d8661a38043b0734ba3ad482e0e4a18e11f21e2e33d85d454f3986000000006b48304502210089b462cc210319f36666a8663a973653e2619fb18b903b14de797ca09495414a02201ff2cfa795ede1da6be7afa65c70e26aead68c8ad520f41fe53c443171d34ac6012103452e35b56f95e3a6934860869eed9d91d734da2754857a2b9c671b93198b5456ffffffff01301b0f00000000001976a91477584f3c2289b777e09ad921d7b011fc27eb7e5388ac00000000
That’s weird........ there’s no redeemscript in there...
Yes, thats exactly the issue. That raw transaction was created using the code in the first post. Then it was signed once and cant be signed again.
That’s not what I mean.
I want you to create a transaction with coinbin then sign once with coinbin, and paste the result.
We can’t tell how to match coinbin if we don’t know what their transactions are like.
Sorry, I didnt realize thats what you meant. My point was just that I cant get any wallet software to actually sign that raw transaction 2 times.
Here is the raw transaction created by coinb.in(no signatures)
0100000001a4e6cf9016d8661a38043b0734ba3ad482e0e4a18e11f21e2e33d85d454f398600000000a9524104452e35b56f95e3a6934860869eed9d91d734da2754857a2b9c671b93198b54562f48486f4ec017ab67a01766a3a20cd13d71addf9bce3d90ed88570dff18cb73410410926d6f05a2fdd2b68a145463ae3bea7a0cebd8299fe05219243c148293365ced26d149b7732f5d251e93cb091e0c8c2d33f759664ad98becdcb99e2b6ceccc210349c9709ddeb1c302e5e619af798c5e34162f719371ca191ab46604aea6c1951753aeffffffff01301b0f00000000001976a91477584f3c2289b777e09ad921d7b011fc27eb7e5388ac00000000
Here is the raw transaction signed once on coinb.in
0100000001a4e6cf9016d8661a38043b0734ba3ad482e0e4a18e11f21e2e33d85d454f398600000000f500483045022100de3baaa9c97461b4ab47cfe0947dad6cfa685a0f8e0f2e4b58e78018c01200af02204f87d3a03217058bf1ddeac360a05316daf5ef73be6c47d891bb10ad60cf1003014ca9524104452e35b56f95e3a6934860869eed9d91d734da2754857a2b9c671b93198b54562f48486f4ec017ab67a01766a3a20cd13d71addf9bce3d90ed88570dff18cb73410410926d6f05a2fdd2b68a145463ae3bea7a0cebd8299fe05219243c148293365ced26d149b7732f5d251e93cb091e0c8c2d33f759664ad98becdcb99e2b6ceccc210349c9709ddeb1c302e5e619af798c5e34162f719371ca191ab46604aea6c1951753aeffffffff01301b0f00000000001976a91477584f3c2289b777e09ad921d7b011fc27eb7e5388ac00000000
@dabura667 ^ in case you didnt see it
const bitcoin = require('bitcoinjs-lib');
const txb = new bitcoin.TransactionBuilder();
txb.addInput('INPUT HERE AS STRING', 0);
txb.addOutput('OUTPUT ADDRESS HERE', 0.01);
const tx = txb.buildIncomplete();
for (var i = 0; i < tx.ins.length; i++) {
tx.ins[i].script = bufferOfRedeemScript // in your case: <Buffer 52 41 04 45 2e 35 b5 6f 95 e3 a6 93 48 60 86 9e ed 9d 91 d7 34 da 27 54 85 7a 2b 9c 67 1b 93 19 8b 54 56 2f 48 48 6f 4e c0 17 ab 67 a0 17 66 a3 a2 0c d1 3d 71 ad df 9b ce 3d 90 ed 88 57 0d ff 18 cb 73 41 04 10 92 6d 6f 05 a2 fd d2 b6 8a 14 54 63 ae 3b ea 7a 0c eb d8 29 9f e0 52 19 24 3c 14 82 93 36 5c ed 26 d1 49 b7 73 2f 5d 25 1e 93 cb 09 1e 0c 8c 2d 33 f7 59 66 4a d9 8b ec dc b9 9e 2b 6c ec cc 21 03 49 c9 70 9d de b1 c3 02 e5 e6 19 af 79 8c 5e 34 16 2f 71 93 71 ca 19 1a b4 66 04 ae a6 c1 95 17 53 ae >
}
console.log(tx.toHex());
This will get you the unsigned transaction exactly like coinbin.
As for the signed transaction It should be the same imo.
tx.ins[0].script.toString('hex')
// 00483045022100de3baaa9c97461b4ab47cfe0947dad6cfa685a0f8e0f2e4b58e78018c01200af02204f87d3a03217058bf1ddeac360a05316daf5ef73be6c47d891bb10ad60cf1003014ca9524104452e35b56f95e3a6934860869eed9d91d734da2754857a2b9c671b93198b54562f48486f4ec017ab67a01766a3a20cd13d71addf9bce3d90ed88570dff18cb73410410926d6f05a2fdd2b68a145463ae3bea7a0cebd8299fe05219243c148293365ced26d149b7732f5d251e93cb091e0c8c2d33f759664ad98becdcb99e2b6ceccc210349c9709ddeb1c302e5e619af798c5e34162f719371ca191ab46604aea6c1951753ae
If you want to get it out using bitcoinjs-lib:
const bitcoin = require('bitcoinjs-lib');
const tx = bitcoin.Transaction.fromHex(txhex)
let redeemScripts = []
for (var i = 0; i < tx.ins.length; i++) {
redeemScripts.push(tx.ins[i].script)
}
const txb = bitcoin.TransactionBuilder.fromTransaction(tx);
// ...
@junderw Thanks, I tried the code you mentioned, but do not get the same exact raw transaction.
Code used
var bitcoin = require('bitcoinjs-lib');
const redeemscript = new Buffer("524104452e35b56f95e3a6934860869eed9d91d734da275" +
"4857a2b9c671b93198b54562f48486f4ec017ab67a01766a3a20cd13d71addf9bce3d90" +
"ed88570dff18cb73410410926d6f05a2fdd2b68a145463ae3bea7a0cebd8299fe" +
"05219243c148293365ced26d149b7732f5d251e93cb091e0c8c2d33f759664ad" +
"98becdcb99e2b6ceccc210349c9709ddeb1c302e5e619af798c5e34162f719371" +
"ca191ab46604aea6c1951753ae", "hex");
const txb = new bitcoin.TransactionBuilder();
txb.addInput('86394f455dd8332e1ef2118ea1e4e082d43aba34073b04381a66d81690cfe6a4', 0);
txb.addOutput(
'1Bt3CgTKG859a7tK9mPxzQxTbRikP7i1kL',
parseInt(((0.01 - 0.0001) * 100000000).toFixed(0))
);
const tx = txb.buildIncomplete();
for (var i = 0; i < tx.ins.length; i++) {
tx.ins[i].script = redeemscript;
}
console.log(tx.toHex());
Using the above code (Admin Edit: Fixed above code to get proper results) I get the following raw transaction
0100000001a4e6cf9016d8661a38043b0734ba3ad482e0e4a18e11f21e2e33d85d454f398600000000fd52013532343130343435326533356235366639356533613639333438363038363965656439643931643733346461323735343835376132623963363731623933313938623534353632663438343836663465633031376162363761303137363661336132306364313364373161646466396263653364393065643838353730646666313863623733343130343130393236643666303561326664643262363861313435343633616533626561376130636562643832393966653035323139323433633134383239333336356365643236643134396237373332663564323531653933636230393165306338633264333366373539363634616439386265636463623939653262366365636363323130333439633937303964646562316333303265356536313961663739386335653334313632663731393337316361313931616234363630346165613663313935313735336165ffffffff01301b0f00000000001976a91477584f3c2289b777e09ad921d7b011fc27eb7e5388ac00000000
The above raw transaction is not the sign. Despite this I did try using it. When signing it in coinbin I get the exact same output like no signature was even made. I have not tried signing it with other software but I am assuming I will get the same result.
........................ Your buffer is utf8... you need to use hex.
new Buffer(“55”)
Will give you the byte for the ASCII text “5” twice. Two bytes.
new Buffer(“55”, “hex”) will give you one byte... 0x55
Sorry, didnt realize. Glad it was such a simple fix.
Thank you both for your help. I hope this thread helps others in the same situation as me.