Version: Bitcoinjs-0.3.2 and also bitcoinjs/bitcoinjs-lib#fixes
I'm trying to integrate segwit in my walleting software, but I'm stuck in one problem. The flow is the following:
var signTxSegwit = function (txhex, pubkeys, privkey, utxos, n) {
var txb = new bitcoinjs.TransactionBuilder.fromTransaction (
bitcoinjs.Transaction.fromHex (txhex), bitcoinjs.networks.testnet);
var upair = bitcoinjs.ECPair.fromWIF(privkey, bitcoinjs.networks.testnet);
var pubkeys_raw = pubkeys.map(function (hex) { return new Buffer(hex, 'hex'); });
var witnessScript = bitcoinjs.script.multisig.output.encode (n, pubkeys_raw);
var redeemScript = bitcoinjs.script.witnessScriptHash.output.encode (bitcoinjs.crypto.sha256(witnessScript));
var scriptPubKey = bitcoinjs.script.scriptHash.output.encode (bitcoinjs.crypto.hash160(redeemScript));
var address = bitcoinjs.address.fromOutputScript(scriptPubKey, bitcoinjs.networks.testnet);
for (var j = 0; j < txb.tx.ins.length; j++) {
txb.sign (j, upair, redeemScript, null, parseInt (utxos[j].value * 100000000), witnessScript);
}
var tx = txb.build ();
return tx.toHex ();
}
The program fail at txb.sign, with this error:
Message:
Error: Expected property "2" of type Satoshi, got undefined
Stacktrace:
Error
at TfTypeError.Error (native)
at new TfTypeError (/home/dakk/Repositories/MyRepos/backend/node_modules/typeforce/errors.js:43:24)
at typeforce (/home/dakk/Repositories/MyRepos/backend/node_modules/typeforce/index.js:193:11)
at /home/dakk/Repositories/MyRepos/backend/node_modules/typeforce/index.js:152:18
at Array.every (native)
at _tuple (/home/dakk/Repositories/MyRepos/backend/node_modules/typeforce/index.js:150:20)
at typeforce (/home/dakk/Repositories/MyRepos/backend/node_modules/typeforce/index.js:191:9)
at Transaction.hashForWitnessV0 (/home/dakk/Repositories/MyRepos/backend/node_modules/bitcoinjs-lib/src/transaction.js:320:3)
at TransactionBuilder.sign (/home/dakk/Repositories/MyRepos/backend/node_modules/bitcoinjs-lib/src/transaction_builder.js:692:29)
at signTxSegwit (/home/dakk/Repositories/MyRepos/backend/tests/api/middlewares/wallet.js:79:7)
Then I jumped to bitcoinjs-lib/src/transaction_builder:692 and I found this:
signatureHash = this.tx.hashForWitnessV0(vin, input.signScript, input.value, hashType)
Which is using input.value; so I guessed prepareInput should inject this field, and I've found that it injects input.value if witnessScript and reedemScript are not undefined; but they are defined in my example, right? I can't figure out what's the problem here
Can you provide a complete test fixture? Aka, a privkey WIF and txhex w/ a UTXO?
I create a full example reproducing my problem; the script sign correctly with the first privkey, but fail trying to sign after serialize/parse (toHex -> fromHex) procedure
var bitcoin = require('bitcoinjs-lib')
// Bitcoin address : mhqo9zJRm4gBYFmBD6kCVCfZL3JS5UqLmj
var key1 = {
priv: 'cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b',
pub: '03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877'
};
// Bitcoin address : mpSAvmKXAE8B6H6bgLnGBG8iyC6tQgEy9D
var key2 = {
priv: 'cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy',
pub: '020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0'
};
// Bitcoin address : mq1HD5wiNLCNQv8u9wdwLvRH5JiMbxJHR4
var key3 = {
priv: 'cQqbmgCQroize8cD1484C5243Q7twmHq5YjN3fYFayApcfoZykcF',
pub: '02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149'
};
var keyPairs = [
'cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b',
'cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy',
'cQqbmgCQroize8cD1484C5243Q7twmHq5YjN3fYFayApcfoZykcF'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, bitcoin.networks.testnet) })
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
var witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys)
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 P2SHaddress = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)
console.log(P2SHaddress)
console.log (scriptPubKey)
// 2Mv8kEd3D7PaYciMYMgQU3zGTv5RgsYDUDy
var txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)
txb.addInput('f3e785dff9694a463f5f17570c2940aa8e65117ae69184e5fa8de69ac4ae3481', 1);
txb.addOutput (scriptPubKey, (1000000 - 10000) / 2);
txb.addOutput ("n2iptWzMeDb35222vkp3SA9ytsac3skwjU", (1000000 - 10000) / 2);
txb.sign(0, keyPairs[0], redeemScript, null, 1000000, witnessScript)
var txhex = txb.buildIncomplete ().toHex ();
var txb = new bitcoin.TransactionBuilder.fromTransaction (
bitcoin.Transaction.fromHex (txhex), bitcoin.networks.testnet);
var witnessScript = bitcoin.script.multisig.output.encode (2, pubKeys);
var redeemScript = bitcoin.script.witnessScriptHash.output.encode (bitcoin.crypto.sha256(witnessScript));
var scriptPubKey = bitcoin.script.scriptHash.output.encode (bitcoin.crypto.hash160(redeemScript));
var address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet);
txb.sign (0, keyPairs[1], redeemScript, null, 1000000, witnessScript);
var tx = txb.build ();
var txhex = tx.toHex ();
console.log (txhex);
Also, this is the previous example without toHex fromHex, and it works:
var bitcoin = require('bitcoinjs-lib')
// Bitcoin address : mhqo9zJRm4gBYFmBD6kCVCfZL3JS5UqLmj
var key1 = {
priv: 'cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b',
pub: '03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877'
};
// Bitcoin address : mpSAvmKXAE8B6H6bgLnGBG8iyC6tQgEy9D
var key2 = {
priv: 'cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy',
pub: '020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0'
};
// Bitcoin address : mq1HD5wiNLCNQv8u9wdwLvRH5JiMbxJHR4
var key3 = {
priv: 'cQqbmgCQroize8cD1484C5243Q7twmHq5YjN3fYFayApcfoZykcF',
pub: '02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149'
};
var keyPairs = [
'cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b',
'cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy',
'cQqbmgCQroize8cD1484C5243Q7twmHq5YjN3fYFayApcfoZykcF'
].map(function (wif) { return bitcoin.ECPair.fromWIF(wif, bitcoin.networks.testnet) })
var pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
var witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys)
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 P2SHaddress = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)
console.log(P2SHaddress)
console.log (scriptPubKey)
// 2Mv8kEd3D7PaYciMYMgQU3zGTv5RgsYDUDy
var txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)
txb.addInput('f3e785dff9694a463f5f17570c2940aa8e65117ae69184e5fa8de69ac4ae3481', 1);
txb.addOutput (scriptPubKey, (1000000 - 10000) / 2);
txb.addOutput ("n2iptWzMeDb35222vkp3SA9ytsac3skwjU", (1000000 - 10000) / 2);
txb.sign(0, keyPairs[0], redeemScript, null, 1000000, witnessScript)
var witnessScript = bitcoin.script.multisig.output.encode (2, pubKeys);
var redeemScript = bitcoin.script.witnessScriptHash.output.encode (bitcoin.crypto.sha256(witnessScript));
var scriptPubKey = bitcoin.script.scriptHash.output.encode (bitcoin.crypto.hash160(redeemScript));
var address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet);
txb.sign (0, keyPairs[1], redeemScript, null, 1000000, witnessScript);
var tx = txb.build ();
var txhex = tx.toHex ();
console.log (txhex);
Reproduced with
// Bitcoin address : mhqo9zJRm4gBYFmBD6kCVCfZL3JS5UqLmj
let key1 = {
priv: 'cUxccFVBdJRq6HnyxiFMd8Z15GLThXaNLcnPBgoXLEv9iX6wuV2b',
pub: '03c411cf39aca4395c81c35921dc832a0d1585d652ab1b52ccc619ff9fbbc57877'
}
// Bitcoin address : mpSAvmKXAE8B6H6bgLnGBG8iyC6tQgEy9D
let key2 = {
priv: 'cVSNe9ZdZRsRvEBL8YRR7YiZmH4cLsf5FthgERWkZezJVrGseaXy',
pub: '020636d944458a4663b75a912c37dc1cd59b11f9a00106783a65ba230d929b96b0'
}
// Bitcoin address : mq1HD5wiNLCNQv8u9wdwLvRH5JiMbxJHR4
let key3 = {
priv: 'cQqbmgCQroize8cD1484C5243Q7twmHq5YjN3fYFayApcfoZykcF',
pub: '02d1448cbf19528a1a27e5958ba73d930b5b3facdbe5c30c7094951a287fcc9149'
}
let bitcoin = require('./')
let keyPairs = [key1.priv, key2.priv, key3.priv].map(function (wif) {
return bitcoin.ECPair.fromWIF(wif, bitcoin.networks.testnet)
})
let pubKeys = keyPairs.map(function (x) { return x.getPublicKeyBuffer() })
let itxhex
{
let witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys)
let witnessScriptHash = bitcoin.crypto.sha256(witnessScript)
let redeemScript = bitcoin.script.witnessScriptHash.output.encode(witnessScriptHash)
let redeemScriptHash = bitcoin.crypto.hash160(redeemScript)
let scriptPubKey = bitcoin.script.scriptHash.output.encode(redeemScriptHash)
let P2SHaddress = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)
console.log(P2SHaddress)
console.log(scriptPubKey)
// 2Mv8kEd3D7PaYciMYMgQU3zGTv5RgsYDUDy
let txb = new bitcoin.TransactionBuilder(bitcoin.networks.testnet)
txb.addInput('f3e785dff9694a463f5f17570c2940aa8e65117ae69184e5fa8de69ac4ae3481', 1)
txb.addOutput(scriptPubKey, (1000000 - 10000) / 2)
txb.addOutput('n2iptWzMeDb35222vkp3SA9ytsac3skwjU', (1000000 - 10000) / 2)
txb.sign(0, keyPairs[0], redeemScript, null, 1000000, witnessScript)
itxhex = txb.buildIncomplete().toHex()
}
{
let txb = bitcoin.TransactionBuilder.fromTransaction(bitcoin.Transaction.fromHex(itxhex), bitcoin.networks.testnet)
let witnessScript = bitcoin.script.multisig.output.encode(2, pubKeys)
let redeemScript = bitcoin.script.witnessScriptHash.output.encode(bitcoin.crypto.sha256(witnessScript))
// let scriptPubKey = bitcoin.script.scriptHash.output.encode(bitcoin.crypto.hash160(redeemScript))
// let address = bitcoin.address.fromOutputScript(scriptPubKey, bitcoin.networks.testnet)
txb.sign(0, keyPairs[1], redeemScript, null, 1000000, witnessScript)
let tx = txb.build()
let txhex = tx.toHex()
console.log(txhex)
}
Which isolates the variables with block scoping.
Will investigate.
Closing in favour of https://github.com/bitcoinjs/bitcoinjs-lib/pull/908
This isn't over yet... the witness is malformed (missing key1 signature).
Most helpful comment
Reproduced with
Which isolates the variables with block scoping.
Will investigate.