Adding multisig bech32 signing

This commit is contained in:
sairajzero 2022-11-21 00:04:51 +05:30
parent 9dd075757e
commit 6f9f44c82e

View File

@ -299,11 +299,12 @@
/* other vars */ /* other vars */
coinjs.developer = '33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G'; //bitcoin coinjs.developer = '33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G'; //bitcoin
/* bit(coinb.in) api vars */ /* bit(coinb.in) api vars
coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length - 1]) == 'onion') ? 'coinbin3ravkwb24f7rmxx6w3snkjw45jhs5lxbh3yfeg3vpt6janwqd.onion' : 'coinb.in'; coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length - 1]) == 'onion') ? 'coinbin3ravkwb24f7rmxx6w3snkjw45jhs5lxbh3yfeg3vpt6janwqd.onion' : 'coinb.in';
coinjs.host = ('https:' == document.location.protocol ? 'https://' : 'http://') + coinjs.hostname + '/api/'; coinjs.host = ('https:' == document.location.protocol ? 'https://' : 'http://') + coinjs.hostname + '/api/';
coinjs.uid = '1'; coinjs.uid = '1';
coinjs.key = '12345678901234567890123456789012'; coinjs.key = '12345678901234567890123456789012';
*/
/* start of address functions */ /* start of address functions */
@ -339,7 +340,7 @@
x += (document.getElementById("entropybucket")) ? document.getElementById("entropybucket").innerHTML : ''; x += (document.getElementById("entropybucket")) ? document.getElementById("entropybucket").innerHTML : '';
x += x + '' + x; x += x + '' + x;
var r = x; var r = x;
for (i = 0; i < (x).length / 25; i++) { for (let i = 0; i < (x).length / 25; i++) {
r = Crypto.SHA256(r.concat(x)); r = Crypto.SHA256(r.concat(x));
} }
var checkrBigInt = new BigInteger(r); var checkrBigInt = new BigInteger(r);
@ -444,18 +445,19 @@
} }
//Return a Bech32 address for the multisig. Format is same as above //Return a Bech32 address for the multisig. Format is same as above
coinjs.pubkeys2MultisigAddressBech32 = function (pubkeys, required){ coinjs.pubkeys2MultisigAddressBech32 = function (pubkeys, required) {
var t1,t2,t3; var r = coinjs.pubkeys2MultisigAddress(pubkeys, required);
t1 = coinjs.pubkeys2MultisigAddress(pubkeys, required); var program = Crypto.SHA256(Crypto.util.hexToBytes(r.redeemScript), {
t2 = Crypto.SHA256(Crypto.util.hexToBytes(t1.redeemScript)); asBytes: true
t3 = btc_api.encodeBech32(t2,0,"bc"); });
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return { return {
'address': t3, 'address': address,
'redeemScript': t1.redeemScript, 'redeemScript': r.redeemScript,
'size': t1.size 'size': r.size
}; };
} }
/* new time locked address, provide the pubkey and time necessary to unlock the funds. /* new time locked address, provide the pubkey and time necessary to unlock the funds.
when time is greater than 500000000, it should be a unix timestamp (seconds since epoch), when time is greater than 500000000, it should be a unix timestamp (seconds since epoch),
otherwise it should be the block height required before this transaction can be released. otherwise it should be the block height required before this transaction can be released.
@ -544,20 +546,19 @@
'redeemscript': Crypto.util.bytesToHex(program) 'redeemscript': Crypto.util.bytesToHex(program)
}; };
} }
coinjs.multisigBech32Address = function (raw_redeemscript) { coinjs.multisigBech32Address = function (raw_redeemscript) {
var t1,t2; var program = Crypto.SHA256(Crypto.util.hexToBytes(raw_redeemscript), {
asBytes: true
t1 = Crypto.SHA256(Crypto.util.hexToBytes(raw_redeemscript)); });
t2 = btc_api.encodeBech32(t1,0,"bc"); var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return { return {
'address': t2, 'address': address,
'type': 'multisigBech32', 'type': 'multisigBech32',
'redeemscript': t1 'redeemscript': Crypto.util.bytesToHex(program)
}; };
} }
/* extract the redeemscript from a bech32 address */ /* extract the redeemscript from a bech32 address */
coinjs.bech32redeemscript = function (address) { coinjs.bech32redeemscript = function (address) {
var r = false; var r = false;
@ -568,7 +569,7 @@
} }
return r; return r;
} }
/* provide a privkey and return an WIF */ /* provide a privkey and return an WIF */
coinjs.privkey2wif = function (h) { coinjs.privkey2wif = function (h) {
var r = Crypto.util.hexToBytes(h); var r = Crypto.util.hexToBytes(h);
@ -626,6 +627,7 @@
}; };
} }
/* decode or validate an address and return the hash */
coinjs.addressDecode = function (addr) { coinjs.addressDecode = function (addr) {
try { try {
var bytes = coinjs.base58decode(addr); var bytes = coinjs.base58decode(addr);
@ -647,9 +649,9 @@
} else if (o.version == coinjs.multisig) { // multisig address } else if (o.version == coinjs.multisig) { // multisig address
o.type = 'multisig'; o.type = 'multisig';
} else if (o.version == coinjs.multisigBech32) { // multisigBech32 added } else if (o.version == coinjs.multisigBech32) { // multisigBech32 added
o.type = 'multisigBech32'; o.type = 'multisigBech32';
} else if (o.version == coinjs.priv) { // wifkey } else if (o.version == coinjs.priv) { // wifkey
o.type = 'wifkey'; o.type = 'wifkey';
@ -690,13 +692,13 @@
throw "Invalid checksum"; throw "Invalid checksum";
} }
} catch (e) { } catch (e) {
bech32rs = coinjs.bech32redeemscript(addr); let bech32rs = coinjs.bech32redeemscript(addr);
if (bech32rs.length == 40) { if (bech32rs && bech32rs.length == 40) {
return { return {
'type': 'bech32', 'type': 'bech32',
'redeemscript': bech32rs 'redeemscript': bech32rs
}; };
} else if (bech32rs.length == 64 ) { } else if (bech32rs && bech32rs.length == 64) {
return { return {
'type': 'multisigBech32', 'type': 'multisigBech32',
'redeemscript': bech32rs 'redeemscript': bech32rs
@ -1345,7 +1347,7 @@
var rs = Crypto.util.bytesToHex(s.buffer); var rs = Crypto.util.bytesToHex(s.buffer);
r.redeemscript = rs; r.redeemscript = rs;
} }
} catch (e) { } catch (e) {
// console.log(e); // console.log(e);
@ -1353,12 +1355,12 @@
} }
return r; return r;
} }
/* create output script to spend */ /* create output script to spend */
r.spendToScript = function (address) { r.spendToScript = function (address) {
var addr = coinjs.addressDecode(address); var addr = coinjs.addressDecode(address);
var s = coinjs.script(); var s = coinjs.script();
if (addr.type == "bech32") { if (addr.type == "bech32" || addr.type == "multisigBech32") {
s.writeOp(0); s.writeOp(0);
s.writeBytes(Crypto.util.hexToBytes(addr.redeemscript)); s.writeBytes(Crypto.util.hexToBytes(addr.redeemscript));
} else if (addr.version == coinjs.multisig) { // multisig address } else if (addr.version == coinjs.multisig) { // multisig address
@ -1690,7 +1692,7 @@
// start redeem script check // start redeem script check
var extract = this.extractScriptKey(index); var extract = this.extractScriptKey(index);
if (extract['type'] != 'segwit') { if (extract['type'] != 'segwit' && extract['type'] != 'multisig_bech32') {
return { return {
'result': 0, 'result': 0,
'fail': 'redeemscript', 'fail': 'redeemscript',
@ -1861,7 +1863,7 @@
} else if (this.ins[index].script.chunks[0] == 0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1][this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1].length - 1] == 174) { // OP_CHECKMULTISIG } else if (this.ins[index].script.chunks[0] == 0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1][this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1].length - 1] == 174) { // OP_CHECKMULTISIG
// multisig script, with signature(s) included // multisig script, with signature(s) included
var sigcount = 0; var sigcount = 0;
for (i = 1; i < this.ins[index].script.chunks.length - 1; i++) { for (let i = 1; i < this.ins[index].script.chunks.length - 1; i++) {
if (this.ins[index].script.chunks[i] != 0) { if (this.ins[index].script.chunks[i] != 0) {
sigcount++; sigcount++;
} }
@ -1881,6 +1883,25 @@
'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
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 sigcount, req_sigs = this.ins[index].script.chunks[0] - 80;
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)
}
if (!this.witness[index])
sigcount = 0;
else
sigcount = this.witness[index].length - 2;
return {
'type': 'multisig_bech32',
'signed': sigcount >= req_sigs ? 'true' : 'false',
'signatures': sigcount,
'script': rs,
'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
@ -2064,6 +2085,36 @@
return true; return true;
} }
r.signmultisig_bech32 = function (index, wif, sigHashType, redeemScript) {
var shType = sigHashType || 1;
var wif2 = coinjs.wif2pubkey(wif);
let decode_rs = coinjs.script().decodeRedeemScriptBech32(redeemScript);
var txhash = this.transactionHashSegWitV0(index, shType);
console.debug(txhash, decode_rs.pubkeys,wif2['pubkey'])
if (txhash.result == 1 && decode_rs.pubkeys.includes(wif2['pubkey'])) {
var segwitHash = Crypto.util.hexToBytes(txhash.hash);
var signature = this.transactionSig(index, wif, shType, segwitHash);
if (!coinjs.isArray(this.witness)) {
this.witness = new Array(this.ins.length);
this.witness.fill([]);
}
if (!coinjs.isArray(this.witness[index]) || !this.witness[index].length)
this.witness[index] = [0, redeemScript];
console.debug(this.witness[index])
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
}
}
}
/* sign a multisig input */ /* sign a multisig input */
r.signmultisig = function (index, wif, sigHashType) { r.signmultisig = function (index, wif, sigHashType) {
@ -2104,8 +2155,8 @@
s.writeOp(0); s.writeOp(0);
for (x in pubkeyList) { for (let x in pubkeyList) {
for (y in sigsList) { for (let y in sigsList) {
this.ins[index].script.buffer = redeemScript; this.ins[index].script.buffer = redeemScript;
sighash = Crypto.util.hexToBytes(this.transactionHash(index, sigsList[y].slice(-1)[0] * 1)); sighash = Crypto.util.hexToBytes(this.transactionHash(index, sigsList[y].slice(-1)[0] * 1));
if (coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) { if (coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) {
@ -2203,6 +2254,7 @@
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);
@ -2213,6 +2265,9 @@
} else if (d['type'] == 'multisig') { } else if (d['type'] == 'multisig') {
this.signmultisig(i, wif, shType); this.signmultisig(i, wif, shType);
} else if (d['type'] == 'multisig_bech32' && d['signed'] == "false") {
this.signmultisig_bech32(i, wif, shType, d['script']);
} else if (d['type'] == 'segwit') { } else if (d['type'] == 'segwit') {
this.signsegwit(i, wif, shType); this.signsegwit(i, wif, shType);
@ -2676,14 +2731,14 @@
return result.join(''); return result.join('');
} }
coinjs.getTransactionHash = function (transaction_in_hex,changeOutputEndianess) { coinjs.getTransactionHash = function (transaction_in_hex, changeOutputEndianess) {
var x1, x2, x3, x4, x5; var x1, x2, x3, x4, x5;
x1 = Crypto.util.hexToBytes(transaction_in_hex); x1 = Crypto.util.hexToBytes(transaction_in_hex);
x2 = Crypto.SHA256(x1); x2 = Crypto.SHA256(x1);
x3 = Crypto.util.hexToBytes(x2); x3 = Crypto.util.hexToBytes(x2);
x4 = Crypto.SHA256(x3); x4 = Crypto.SHA256(x3);
x5 = coinjs.changeEndianness(x4); x5 = coinjs.changeEndianness(x4);
if (changeOutputEndianess == true) { x5 = x5 } else if ((typeof changeOutputEndianess == 'undefined') || (changeOutputEndianess == false) ){ x5 = x4 }; if (changeOutputEndianess == true) { x5 = x5 } else if ((typeof changeOutputEndianess == 'undefined') || (changeOutputEndianess == false)) { x5 = x4 };
return x5; return x5;
} }
@ -2713,72 +2768,74 @@
return coinjs.verifySignature(h1, s1, p2); return coinjs.verifySignature(h1, s1, p2);
} }
coinjs.generateBitcoinSignature = function (private_key,hash,sighash_type_int = 1){ coinjs.generateBitcoinSignature = function (private_key, hash, sighash_type_int = 1) {
var wif, tx1; var wif, tx1;
if (private_key.length < 60) { wif = private_key } else { wif = coinjs.privkey2wif(private_key) }; if (private_key.length < 60) { wif = private_key } else { wif = coinjs.privkey2wif(private_key) };
tx1 = coinjs.transaction(); tx1 = coinjs.transaction();
return tx1.transactionSigNoIndex(wif,sighash_type_int,hash); return tx1.transactionSigNoIndex(wif, sighash_type_int, hash);
} }
coinjs.dSHA256 = function(data){ coinjs.dSHA256 = function (data) {
var t1,t2,t3 ; var t1, t2, t3;
t1= Crypto.SHA256(Crypto.util.hexToBytes(data)); t1 = Crypto.SHA256(Crypto.util.hexToBytes(data));
t2= Crypto.util.hexToBytes(t1); t2 = Crypto.util.hexToBytes(t1);
t3= Crypto.SHA256(t2); t3 = Crypto.SHA256(t2);
return t3; return t3;
} }
coinjs.fromBitcoinAmountFormat = function (data){ coinjs.fromBitcoinAmountFormat = function (data) {
var x1,x2,x3; var x1, x2, x3;
x1 = coinjs.changeEndianness(data); x1 = coinjs.changeEndianness(data);
x2 = parseInt(x1,16); x2 = parseInt(x1, 16);
x3 = x2/(10**8); x3 = x2 / (10 ** 8);
return x3; return x3;
} }
coinjs.toBitcoinAmountFormat = function (countBitcoin) { coinjs.toBitcoinAmountFormat = function (countBitcoin) {
var t2,t3,t4,t5; var t2, t3, t4, t5;
t2 = countBitcoin*10**8; t2 = countBitcoin * 10 ** 8;
t3 = t2.toString(16); t3 = t2.toString(16);
t4 = coinjs.changeEndianness(t3); t4 = coinjs.changeEndianness(t3);
t5 = t4.padEnd(16,"0"); t5 = t4.padEnd(16, "0");
return t5; return t5;
} }
coinjs.scriptcodeCreatorBasic = function (scriptpubkey){ coinjs.scriptcodeCreatorBasic = function (scriptpubkey) {
var t1,t2,t3,t4; var t1, t2, t3, t4;
if (scriptpubkey.substr(0,4) == "0014"){ if (scriptpubkey.substr(0, 4) == "0014") {
//Scriptpubkey case //Scriptpubkey case
t1 = scriptpubkey.slice(2); t1 = scriptpubkey.slice(2);
t2 = "1976a9" + t1 + "88ac"; } else { t2 = "1976a9" + t1 + "88ac";
//Redeemscript case } else {
t3 = (scriptpubkey.length)/2; //Redeemscript case
t4 = t3.toString(16); t3 = (scriptpubkey.length) / 2;
t2 = t4 + scriptpubkey; t4 = t3.toString(16);
t2 = t4 + scriptpubkey;
} }
return t2; return t2;
} }
coinjs.ripemd160sha256 = function (data) {
var t1,t2;
t1 = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(data), {asBytes: true}), {asBytes: true}); coinjs.ripemd160sha256 = function (data) {
var t1, t2;
t1 = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(data), { asBytes: true }), { asBytes: true });
t2 = Crypto.util.bytesToHex(t1) t2 = Crypto.util.bytesToHex(t1)
return t2; return t2;
} }
coinjs.random = function (length) { coinjs.random = function (length) {
var r = ""; var r = "";
var l = length || 25; var l = length || 25;
var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
for (x = 0; x < l; x++) { for (let x = 0; x < l; x++) {
r += chars.charAt(Math.floor(Math.random() * 62)); r += chars.charAt(Math.floor(securedMathRandom() * 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