Fix: signing for multisig bech32 (p2wsh)

This commit is contained in:
sairajzero 2022-11-24 02:57:48 +05:30
parent 6f9f44c82e
commit 9a487f1605

View File

@ -1553,7 +1553,7 @@
var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue; var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue;
var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue; var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
if (segwit) { if (segwit) { //also for MULTISIG_BECH32 (p2wsh-multisig)(script = raw_redeemscript; for p2wsh-multisig)
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother. /* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
It is not standard and removed during the signing procedure. */ It is not standard and removed during the signing procedure. */
@ -1721,6 +1721,8 @@
scriptcode = scriptcode.slice(1); scriptcode = scriptcode.slice(1);
scriptcode.unshift(25, 118, 169); scriptcode.unshift(25, 118, 169);
scriptcode.push(136, 172); scriptcode.push(136, 172);
} else if (scriptcode[0] > 80) {
scriptcode.unshift(scriptcode.length)
} }
var value = coinjs.numToBytes(extract['value'], 8); var value = coinjs.numToBytes(extract['value'], 8);
@ -1883,30 +1885,26 @@
'signatures': 0, 'signatures': 0,
'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)
}; };
} else if (this.ins[index].script.chunks[0] >= 80 && this.ins[index].script.chunks[this.ins[index].script.chunks.length - 3] == 174 && this.ins[index].script.chunks[this.ins[index].script.chunks.length - 2] == 0) { //OP_CHECKMULTISIG_BECH32 } else if (this.ins[index].script.chunks.length == 3 && this.ins[index].script.chunks[0][0] >= 80 && this.ins[index].script.chunks[0][this.ins[index].script.chunks[0].length - 1] == 174 && this.ins[index].script.chunks[1] == 0) { //OP_CHECKMULTISIG_BECH32
// multisig bech32 script
let last_index = this.ins[index].script.chunks.length - 1; let last_index = this.ins[index].script.chunks.length - 1;
let rs = Crypto.util.bytesToHex(this.ins[index].script.buffer.slice(0, this.ins[index].script.buffer.length - 2 - this.ins[index].script.chunks[last_index].length)) var value = -1;
var sigcount, req_sigs = this.ins[index].script.chunks[0] - 80; if (last_index >= 2 && this.ins[index].script.chunks[last_index].length == 8) {
var value = - 1;
if ((this.ins[index].script.chunks[last_index]) && this.ins[index].script.chunks[last_index].length == 8) {
value = coinjs.bytesToNum(this.ins[index].script.chunks[last_index]); // value found encoded in transaction (THIS IS NON STANDARD) value = coinjs.bytesToNum(this.ins[index].script.chunks[last_index]); // value found encoded in transaction (THIS IS NON STANDARD)
} }
if (!this.witness[index]) var sigcount = (!this.witness[index]) ? 0 : this.witness[index].length - 2;
sigcount = 0;
else
sigcount = this.witness[index].length - 2;
return { return {
'type': 'multisig_bech32', 'type': 'multisig_bech32',
'signed': sigcount >= req_sigs ? 'true' : 'false', 'signed': 'false',
'signatures': sigcount, 'signatures': sigcount,
'script': rs, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[0]),
'value': value 'value': value
}; };
} else if (this.ins[index].script.chunks.length == 0) { } else if (this.ins[index].script.chunks.length == 0) {
// empty // empty
//bech32 witness check //bech32 witness check
var signed = ((this.witness[index]) && this.witness[index].length == 2) ? 'true' : 'false'; var signed = ((this.witness[index]) && this.witness[index].length >= 2) ? 'true' : 'false';
var sigs = (signed == 'true') ? 1 : 0; var sigs = (signed == 'true') ? (!this.witness[index][0] ? this.witness[index].length - 2 : 1) : 0;
return { return {
'type': 'empty', 'type': 'empty',
'signed': signed, 'signed': signed,
@ -2085,33 +2083,68 @@
return true; return true;
} }
r.signmultisig_bech32 = function (index, wif, sigHashType, redeemScript) { r.signmultisig_bech32 = function (index, wif, sigHashType) {
var shType = sigHashType || 1;
var wif2 = coinjs.wif2pubkey(wif);
function scriptListPubkey(redeemScript) {
var r = {};
for (var i = 1; i < redeemScript.chunks.length - 2; i++) {
r[i] = Crypto.util.hexToBytes(coinjs.pubkeydecompress(Crypto.util.bytesToHex(redeemScript.chunks[i])));
}
return r;
}
function scriptListSigs(sigList) {
let r = {};
var c = 0;
if (Array.isArray(sigList)) {
for (let i = 1; i < sigList.length - 1; i++) {
c++;
r[c] = Crypto.util.hexToBytes(sigList[i]);
}
}
return r;
}
var redeemScript = Crypto.util.bytesToHex(this.ins[index].script.chunks[0]); //redeemScript
if (!coinjs.isArray(this.witness)) {
this.witness = new Array(this.ins.length);
this.witness.fill([]);
}
var pubkeyList = scriptListPubkey(coinjs.script(redeemScript));
var sigsList = scriptListSigs(this.witness[index]);
let decode_rs = coinjs.script().decodeRedeemScriptBech32(redeemScript); let decode_rs = coinjs.script().decodeRedeemScriptBech32(redeemScript);
var shType = sigHashType || 1;
var txhash = this.transactionHashSegWitV0(index, shType); var txhash = this.transactionHashSegWitV0(index, shType);
console.debug(txhash, decode_rs.pubkeys,wif2['pubkey'])
if (txhash.result == 1 && decode_rs.pubkeys.includes(wif2['pubkey'])) { if (txhash.result == 1 && decode_rs.pubkeys.includes(coinjs.wif2pubkey(wif)['pubkey'])) {
var segwitHash = Crypto.util.hexToBytes(txhash.hash); var segwitHash = Crypto.util.hexToBytes(txhash.hash);
var signature = this.transactionSig(index, wif, shType, segwitHash); var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType, segwitHash)); //CHECK THIS
if (!coinjs.isArray(this.witness)) { sigsList[coinjs.countObject(sigsList) + 1] = signature;
this.witness = new Array(this.ins.length);
this.witness.fill([]); var w = [];
for (let x in pubkeyList) {
for (let y in sigsList) {
var sighash = this.transactionHashSegWitV0(index, sigsList[y].slice(-1)[0] * 1).hash
sighash = Crypto.util.hexToBytes(sighash);
if (coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) {
w.push((Crypto.util.bytesToHex(sigsList[y])))
}
}
} }
if (!coinjs.isArray(this.witness[index]) || !this.witness[index].length) // when enough signatures collected, remove any non standard data we store, i.e. input value
this.witness[index] = [0, redeemScript]; if (w.length >= decode_rs.signaturesRequired) {
console.debug(this.witness[index]) this.ins[index].script = coinjs.script();
this.witness[index].splice(this.witness[index].length - 1, 0, signature);
console.debug(this.witness[index])
if (this.witness[index].length - 2 >= decode_rs.signaturesRequired) { // TODO Case:FULLY_SIGNED
// remove any non standard data we store, i.e. input value
var script = coinjs.script();
this.ins[index].script = script; //TODO: check if hash of redeemScript or something else is required instead
} }
w.unshift(0);
w.push(redeemScript);
this.witness[index] = w;
} }
} }
@ -2254,7 +2287,6 @@
var w2a = coinjs.wif2address(wif); var w2a = coinjs.wif2address(wif);
var script = coinjs.script(); var script = coinjs.script();
var pubkeyHash = script.pubkeyHash(w2a['address']); var pubkeyHash = script.pubkeyHash(w2a['address']);
console.debug(d);
if (((d['type'] == 'scriptpubkey' && d['script'] == Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false") { if (((d['type'] == 'scriptpubkey' && d['script'] == Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false") {
this.signinput(i, wif, shType); this.signinput(i, wif, shType);
@ -2266,7 +2298,7 @@
this.signmultisig(i, wif, shType); this.signmultisig(i, wif, shType);
} else if (d['type'] == 'multisig_bech32' && d['signed'] == "false") { } else if (d['type'] == 'multisig_bech32' && d['signed'] == "false") {
this.signmultisig_bech32(i, wif, shType, d['script']); this.signmultisig_bech32(i, wif, shType);
} else if (d['type'] == 'segwit') { } else if (d['type'] == 'segwit') {
this.signsegwit(i, wif, shType); this.signsegwit(i, wif, shType);
@ -2829,13 +2861,13 @@
var l = length || 25; var l = length || 25;
var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
for (let x = 0; x < l; x++) { for (let x = 0; x < l; x++) {
r += chars.charAt(Math.floor(securedMathRandom() * 62)); r += chars.charAt(Math.floor(Math.random() * 62));
} }
return r; return r;
} }
})(); })();
})(typeof global !== "undefined" ? global : window); })(typeof global !== "undefined" ? global : window);
(function (EXPORTS) { //btc_api v1.0.6 (function (EXPORTS) { //btc_api v1.0.6