1/* 2 Coinjs 0.01 beta by OutCast3k{at}gmail.com 3 A bitcoin framework. 4 5 http://github.com/OutCast3k/coinjs or http://coinb.in/coinjs 6*/ 7 8(function () { 9 10 var coinjs = window.coinjs = function () { }; 11 12 /* public vars */ 13 coinjs.pub = 0x00; 14 coinjs.priv = 0x80; 15 coinjs.multisig = 0x05; 16 coinjs.hdkey = { 'prv': 0x0488ade4, 'pub': 0x0488b21e }; 17 coinjs.bech32 = { 'charset': 'qpzry9x8gf2tvdw0s3jn54khce6mua7l', 'version': 0, 'hrp': 'bc' }; 18 19 coinjs.compressed = false; 20 21 /* other vars */ 22 coinjs.developer = 'bc1q3lejwpttmjy76kp74e7cqxav8pkmtvv9spkktf'; //bitcoin 23 24 /* bit(coinb.in) api vars */ 25 coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length - 1]) == 'onion') ? '4zpinp6gdkjfplhk.onion' : 'coinb.in'; 26 coinjs.host = ('https:' == document.location.protocol ? 'https://' : 'http://') + coinjs.hostname + '/api/'; 27 coinjs.uid = '1'; 28 coinjs.key = '12345678901234567890123456789012'; 29 30 /* start of address functions */ 31 32 /* generate a private and public keypair, with address and WIF address */ 33 coinjs.newKeys = function (input) { 34 var privkey = (input) ? Crypto.SHA256(input) : this.newPrivkey(); 35 var pubkey = this.newPubkey(privkey); 36 return { 37 'privkey': privkey, 38 'pubkey': pubkey, 39 'address': this.pubkey2address(pubkey), 40 'wif': this.privkey2wif(privkey), 41 'compressed': this.compressed 42 }; 43 } 44 45 46 47 /* generate a new random private key */ 48 coinjs.newPrivkey = function () { 49 var x = window.location; 50 x += (window.screen.height * window.screen.width * window.screen.colorDepth); 51 x += coinjs.random(64); 52 x += (window.screen.availHeight * window.screen.availWidth * window.screen.pixelDepth); 53 x += navigator.language; 54 x += window.history.length; 55 x += coinjs.random(64); 56 x += navigator.userAgent; 57 x += 'coinb.in'; 58 x += (Crypto.util.randomBytes(64)).join(""); 59 x += x.length; 60 var dateObj = new Date(); 61 x += dateObj.getTimezoneOffset(); 62 x += coinjs.random(64); 63 x += (document.getElementById("entropybucket")) ? document.getElementById("entropybucket").innerHTML : ''; 64 x += x + '' + x; 65 var r = x; 66 for (i = 0; i < (x).length / 25; i++) { 67 r = Crypto.SHA256(r.concat(x)); 68 } 69 var checkrBigInt = new BigInteger(r); 70 var orderBigInt = new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); 71 while (checkrBigInt.compareTo(orderBigInt) >= 0 || checkrBigInt.equals(BigInteger.ZERO) || checkrBigInt.equals(BigInteger.ONE)) { 72 r = Crypto.SHA256(r.concat(x)); 73 checkrBigInt = new BigInteger(r); 74 } 75 return r; 76 } 77 78 /* generate a public key from a private key */ 79 coinjs.newPubkey = function (hash) { 80 var privateKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(hash)); 81 var curve = EllipticCurve.getSECCurveByName("secp256k1"); 82 83 var curvePt = curve.getG().multiply(privateKeyBigInt); 84 var x = curvePt.getX().toBigInteger(); 85 var y = curvePt.getY().toBigInteger(); 86 87 var publicKeyBytes = EllipticCurve.integerToBytes(x, 32); 88 publicKeyBytes = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32)); 89 publicKeyBytes.unshift(0x04); 90 91 if (coinjs.compressed == true) { 92 var publicKeyBytesCompressed = EllipticCurve.integerToBytes(x, 32) 93 if (y.isEven()) { 94 publicKeyBytesCompressed.unshift(0x02) 95 } else { 96 publicKeyBytesCompressed.unshift(0x03) 97 } 98 return Crypto.util.bytesToHex(publicKeyBytesCompressed); 99 } else { 100 return Crypto.util.bytesToHex(publicKeyBytes); 101 } 102 } 103 104 /* provide a public key and return address */ 105 coinjs.pubkey2address = function (h, byte) { 106 var r = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(h), { asBytes: true })); 107 r.unshift(byte || coinjs.pub); 108 var hash = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 109 var checksum = hash.slice(0, 4); 110 return coinjs.base58encode(r.concat(checksum)); 111 } 112 113 /* provide a scripthash and return address */ 114 coinjs.scripthash2address = function (h) { 115 var x = Crypto.util.hexToBytes(h); 116 x.unshift(coinjs.pub); 117 var r = x; 118 r = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 119 var checksum = r.slice(0, 4); 120 return coinjs.base58encode(x.concat(checksum)); 121 } 122 123 /* new multisig address, provide the pubkeys AND required signatures to release the funds */ 124 coinjs.pubkeys2MultisigAddress = function (pubkeys, required) { 125 var s = coinjs.script(); 126 s.writeOp(81 + (required * 1) - 1); //OP_1 127 for (var i = 0; i < pubkeys.length; ++i) { 128 s.writeBytes(Crypto.util.hexToBytes(pubkeys[i])); 129 } 130 s.writeOp(81 + pubkeys.length - 1); //OP_1 131 s.writeOp(174); //OP_CHECKMULTISIG 132 var x = ripemd160(Crypto.SHA256(s.buffer, { asBytes: true }), { asBytes: true }); 133 x.unshift(coinjs.multisig); 134 var r = x; 135 r = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 136 var checksum = r.slice(0, 4); 137 var redeemScript = Crypto.util.bytesToHex(s.buffer); 138 var address = coinjs.base58encode(x.concat(checksum)); 139 140 if (s.buffer.length > 520) { // too large
141 address = 'invalid'; 142 redeemScript = 'invalid'; 143 } 144 145 return { 'address': address, 'redeemScript': redeemScript, 'size': s.buffer.length }; 146 } 147 148 /* new time locked address, provide the pubkey and time necessary to unlock the funds. 149 when time is greater than 500000000, it should be a unix timestamp (seconds since epoch), 150 otherwise it should be the block height required before this transaction can be released. 151 152 may throw a string on failure! 153 */ 154 coinjs.simpleHodlAddress = function (pubkey, checklocktimeverify) { 155 156 if (checklocktimeverify < 0) { 157 throw "Parameter for OP_CHECKLOCKTIMEVERIFY is negative."; 158 } 159 160 var s = coinjs.script(); 161 if (checklocktimeverify <= 16 && checklocktimeverify >= 1) { 162 s.writeOp(0x50 + checklocktimeverify);//OP_1 to OP_16 for minimal encoding 163 } else { 164 s.writeBytes(coinjs.numToScriptNumBytes(checklocktimeverify)); 165 } 166 s.writeOp(177);//OP_CHECKLOCKTIMEVERIFY 167 s.writeOp(117);//OP_DROP 168 s.writeBytes(Crypto.util.hexToBytes(pubkey)); 169 s.writeOp(172);//OP_CHECKSIG 170 171 var x = ripemd160(Crypto.SHA256(s.buffer, { asBytes: true }), { asBytes: true }); 172 x.unshift(coinjs.multisig); 173 var r = x; 174 r = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 175 var checksum = r.slice(0, 4); 176 var redeemScript = Crypto.util.bytesToHex(s.buffer); 177 var address = coinjs.base58encode(x.concat(checksum)); 178 179 return { 'address': address, 'redeemScript': redeemScript }; 180 } 181 182 /* create a new segwit address */ 183 coinjs.segwitAddress = function (pubkey) { 184 var keyhash = [0x00, 0x14].concat(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), { asBytes: true }), { asBytes: true })); 185 var x = ripemd160(Crypto.SHA256(keyhash, { asBytes: true }), { asBytes: true }); 186 x.unshift(coinjs.multisig); 187 var r = x; 188 r = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 189 var checksum = r.slice(0, 4); 190 var address = coinjs.base58encode(x.concat(checksum)); 191 192 return { 'address': address, 'type': 'segwit', 'redeemscript': Crypto.util.bytesToHex(keyhash) }; 193 } 194 195 /* create a new segwit bech32 encoded address */ 196 coinjs.bech32Address = function (pubkey) { 197 var program = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), { asBytes: true }), { asBytes: true }); 198 var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true))); 199 return { 'address': address, 'type': 'bech32', 'redeemscript': Crypto.util.bytesToHex(program) }; 200 } 201 202 /* extract the redeemscript from a bech32 address */ 203 coinjs.bech32redeemscript = function (address) { 204 var r = false; 205 var decode = coinjs.bech32_decode(address); 206 if (decode) { 207 decode.data.shift(); 208 return Crypto.util.bytesToHex(coinjs.bech32_convert(decode.data, 5, 8, false)); 209 } 210 return r; 211 } 212 213 /* provide a privkey and return an WIF */ 214 coinjs.privkey2wif = function (h) { 215 var r = Crypto.util.hexToBytes(h); 216 217 if (coinjs.compressed == true) { 218 r.push(0x01); 219 } 220 221 r.unshift(coinjs.priv); 222 var hash = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true }), { asBytes: true }); 223 var checksum = hash.slice(0, 4); 224 225 return coinjs.base58encode(r.concat(checksum)); 226 } 227 228 /* convert a wif key back to a private key */ 229 coinjs.wif2privkey = function (wif) { 230 var compressed = false; 231 var decode = coinjs.base58decode(wif); 232 var key = decode.slice(0, decode.length - 4); 233 key = key.slice(1, key.length); 234 if (key.length >= 33 && key[key.length - 1] == 0x01) { 235 key = key.slice(0, key.length - 1); 236 compressed = true; 237 } 238 return { 'privkey': Crypto.util.bytesToHex(key), 'compressed': compressed }; 239 } 240 241 /* convert a wif to a pubkey */ 242 coinjs.wif2pubkey = function (wif) { 243 var compressed = coinjs.compressed; 244 var r = coinjs.wif2privkey(wif); 245 coinjs.compressed = r['compressed']; 246 var pubkey = coinjs.newPubkey(r['privkey']); 247 coinjs.compressed = compressed; 248 return { 'pubkey': pubkey, 'compressed': r['compressed'] }; 249 } 250 251 /* convert a wif to a address */ 252 coinjs.wif2address = function (wif) { 253 var r = coinjs.wif2pubkey(wif); 254 return { 'address': coinjs.pubkey2address(r['pubkey']), 'compressed': r['compressed'] }; 255 } 256 257 /* decode or validate an address and return the hash */ 258 coinjs.addressDecode = function (addr) { 259 try {
260 var bytes = coinjs.base58decode(addr); 261 var front = bytes.slice(0, bytes.length - 4); 262 var back = bytes.slice(bytes.length - 4); 263 var checksum = Crypto.SHA256(Crypto.SHA256(front, { asBytes: true }), { asBytes: true }).slice(0, 4); 264 if (checksum + "" == back + "") { 265 266 var o = {}; 267 o.bytes = front.slice(1); 268 o.version = front[0]; 269 270 if (o.version == coinjs.pub) { // standard address 271 o.type = 'standard'; 272 273 } else if (o.version == coinjs.multisig) { // multisig address 274 o.type = 'multisig'; 275 276 } else if (o.version == coinjs.priv) { // wifkey 277 o.type = 'wifkey'; 278 279 } else if (o.version == 42) { // stealth address 280 o.type = 'stealth'; 281 282 o.option = front[1]; 283 if (o.option != 0) { 284 alert("Stealth Address option other than 0 is currently not supported!"); 285 return false; 286 }; 287 288 o.scankey = Crypto.util.bytesToHex(front.slice(2, 35)); 289 o.n = front[35]; 290 291 if (o.n > 1) { 292 alert("Stealth Multisig is currently not supported!"); 293 return false; 294 }; 295 296 o.spendkey = Crypto.util.bytesToHex(front.slice(36, 69)); 297 o.m = front[69]; 298 o.prefixlen = front[70]; 299 300 if (o.prefixlen > 0) { 301 alert("Stealth Address Prefixes are currently not supported!"); 302 return false; 303 }; 304 o.prefix = front.slice(71); 305 306 } else { // everything else 307 o.type = 'other'; // address is still valid but unknown version 308 } 309 310 return o; 311 } else { 312 throw "Invalid checksum"; 313 } 314 } catch (e) { 315 bech32rs = coinjs.bech32redeemscript(addr); 316 if (bech32rs) { 317 return { 'type': 'bech32', 'redeemscript': bech32rs }; 318 } else { 319 return false; 320 } 321 } 322 } 323 324 /* Retrieve the balance from a given Bitcoin address */ 325 coinjs.addressBalance = function (address, callback) { 326 // Define the mempool.space API URL with the given address 327 const apiUrl = `https://mempool.space/api/address/${address}`; 328 329 // Perform the AJAX GET request to fetch the address balance 330 coinjs.ajax(apiUrl, function (response) { 331 try { 332 // Parse the response if it's not already in JSON format 333 const data = typeof response === "string" ? JSON.parse(response) : response; 334 335 // mempool.space returns chain_stats and mempool_stats separately 336 // funded - spent = confirmed balance; add mempool unconfirmed on top 337 const confirmed = (data.chain_stats.funded_txo_sum || 0) - (data.chain_stats.spent_txo_sum || 0); 338 const unconfirmed = (data.mempool_stats.funded_txo_sum || 0) - (data.mempool_stats.spent_txo_sum || 0); 339 const balance = confirmed + unconfirmed; 340 341 // Return the balance via the callback 342 callback(balance); 343 } catch (error) { 344 console.error("Error parsing response or fetching balance:", error); 345 callback(null); // Return null if an error occurs 346 } 347 }, "GET"); 348 }; 349 350 /* decompress an compressed public key */ 351 coinjs.pubkeydecompress = function (pubkey) { 352 if ((typeof (pubkey) == 'string') && pubkey.match(/^[a-f0-9]+$/i)) { 353 var curve = EllipticCurve.getSECCurveByName("secp256k1"); 354 try { 355 var pt = curve.curve.decodePointHex(pubkey); 356 var x = pt.getX().toBigInteger(); 357 var y = pt.getY().toBigInteger(); 358 359 var publicKeyBytes = EllipticCurve.integerToBytes(x, 32); 360 publicKeyBytes = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32)); 361 publicKeyBytes.unshift(0x04); 362 return Crypto.util.bytesToHex(publicKeyBytes); 363 } catch (e) { 364 // console.log(e); 365 return false; 366 } 367 } 368 return false; 369 } 370 371 coinjs.bech32_polymod = function (values) { 372 var chk = 1; 373 var BECH32_GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]; 374 for (var p = 0; p < values.length; ++p) { 375 var top = chk >> 25; 376 chk = (chk & 0x1ffffff) << 5 ^ values[p]; 377 for (var i = 0; i < 5; ++i) { 378 if ((top >> i) & 1) { 379 chk ^= BECH32_GENERATOR[i]; 380 } 381 } 382 } 383 return chk; 384 } 385 386 coinjs.bech32_hrpExpand = function (hrp) { 387 var ret = []; 388 var p; 389 for (p = 0; p < hrp.length; ++p) { 390 ret.push(hrp.charCodeAt(p) >> 5); 391 } 392 ret.push(0); 393 for (p = 0; p < hrp.length; ++p) { 394 ret.push(hrp.charCodeAt(p) & 31); 395 } 396 return ret; 397 } 398 399 coinjs.bech32_verifyChecksum = function (hrp, data) { 400 return coinjs.bech32_polymod(coinjs.bech32_hrpExpand(hrp).concat(data)) === 1; 401 } 402 403 coinjs.bech32_createChecksum = function (hrp, data) { 404 var values = coinjs.bech32_hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]); 405 var mod = coinjs.bech32_polymod(values) ^ 1; 406 var ret = []; 407 for (var p = 0; p < 6; ++p) {
408 ret.push((mod >> 5 * (5 - p)) & 31); 409 } 410 return ret; 411 } 412 413 coinjs.bech32_encode = function (hrp, data) { 414 var combined = data.concat(coinjs.bech32_createChecksum(hrp, data)); 415 var ret = hrp + '1'; 416 for (var p = 0; p < combined.length; ++p) { 417 ret += coinjs.bech32.charset.charAt(combined[p]); 418 } 419 return ret; 420 } 421 422 coinjs.bech32_decode = function (bechString) { 423 var p; 424 var has_lower = false; 425 var has_upper = false; 426 for (p = 0; p < bechString.length; ++p) { 427 if (bechString.charCodeAt(p) < 33 || bechString.charCodeAt(p) > 126) { 428 return null; 429 } 430 if (bechString.charCodeAt(p) >= 97 && bechString.charCodeAt(p) <= 122) { 431 has_lower = true; 432 } 433 if (bechString.charCodeAt(p) >= 65 && bechString.charCodeAt(p) <= 90) { 434 has_upper = true; 435 } 436 } 437 if (has_lower && has_upper) { 438 return null; 439 } 440 bechString = bechString.toLowerCase(); 441 var pos = bechString.lastIndexOf('1'); 442 if (pos < 1 || pos + 7 > bechString.length || bechString.length > 90) { 443 return null; 444 } 445 var hrp = bechString.substring(0, pos); 446 var data = []; 447 for (p = pos + 1; p < bechString.length; ++p) { 448 var d = coinjs.bech32.charset.indexOf(bechString.charAt(p)); 449 if (d === -1) { 450 return null; 451 } 452 data.push(d); 453 } 454 if (!coinjs.bech32_verifyChecksum(hrp, data)) { 455 return null; 456 } 457 return { 458 hrp: hrp, 459 data: data.slice(0, data.length - 6) 460 }; 461 } 462 463 coinjs.bech32_convert = function (data, inBits, outBits, pad) { 464 var value = 0; 465 var bits = 0; 466 var maxV = (1 << outBits) - 1; 467 468 var result = []; 469 for (var i = 0; i < data.length; ++i) { 470 value = (value << inBits) | data[i]; 471 bits += inBits; 472 473 while (bits >= outBits) { 474 bits -= outBits; 475 result.push((value >> bits) & maxV); 476 } 477 } 478 479 if (pad) { 480 if (bits > 0) { 481 result.push((value << (outBits - bits)) & maxV); 482 } 483 } else { 484 if (bits >= inBits) throw new Error('Excess padding'); 485 if ((value << (outBits - bits)) & maxV) throw new Error('Non-zero padding'); 486 } 487 488 return result; 489 } 490 491 coinjs.testdeterministicK = function () { 492 // https://github.com/bitpay/bitcore/blob/9a5193d8e94b0bd5b8e7f00038e7c0b935405a03/test/crypto/ecdsa.js 493 // Line 21 and 22 specify digest hash and privkey for the first 2 test vectors. 494 // Line 96-117 tells expected result. 495 496 var tx = coinjs.transaction(); 497 498 var test_vectors = [ 499 { 500 'message': 'test data', 501 'privkey': 'fee0a1f7afebf9d2a5a80c0c98a31c709681cce195cbcd06342b517970c0be1e', 502 'k_bad00': 'fcce1de7a9bcd6b2d3defade6afa1913fb9229e3b7ddf4749b55c4848b2a196e', 503 'k_bad01': '727fbcb59eb48b1d7d46f95a04991fc512eb9dbf9105628e3aec87428df28fd8', 504 'k_bad15': '398f0e2c9f79728f7b3d84d447ac3a86d8b2083c8f234a0ffa9c4043d68bd258' 505 }, 506 { 507 'message': 'Everything should be made as simple as possible, but not simpler.', 508 'privkey': '0000000000000000000000000000000000000000000000000000000000000001', 509 'k_bad00': 'ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5', 510 'k_bad01': 'df55b6d1b5c48184622b0ead41a0e02bfa5ac3ebdb4c34701454e80aabf36f56', 511 'k_bad15': 'def007a9a3c2f7c769c75da9d47f2af84075af95cadd1407393dc1e26086ef87' 512 }, 513 { 514 'message': 'Satoshi Nakamoto', 515 'privkey': '0000000000000000000000000000000000000000000000000000000000000002', 516 'k_bad00': 'd3edc1b8224e953f6ee05c8bbf7ae228f461030e47caf97cde91430b4607405e', 517 'k_bad01': 'f86d8e43c09a6a83953f0ab6d0af59fb7446b4660119902e9967067596b58374', 518 'k_bad15': '241d1f57d6cfd2f73b1ada7907b199951f95ef5ad362b13aed84009656e0254a' 519 }, 520 { 521 'message': 'Diffie Hellman', 522 'privkey': '7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f', 523 'k_bad00': 'c378a41cb17dce12340788dd3503635f54f894c306d52f6e9bc4b8f18d27afcc', 524 'k_bad01': '90756c96fef41152ac9abe08819c4e95f16da2af472880192c69a2b7bac29114', 525 'k_bad15': '7b3f53300ab0ccd0f698f4d67db87c44cf3e9e513d9df61137256652b2e94e7c' 526 }, 527 { 528 'message': 'Japan', 529 'privkey': '8080808080808080808080808080808080808080808080808080808080808080', 530 'k_bad00': 'f471e61b51d2d8db78f3dae19d973616f57cdc54caaa81c269394b8c34edcf59', 531 'k_bad01': '6819d85b9730acc876fdf59e162bf309e9f63dd35550edf20869d23c2f3e6d17', 532 'k_bad15': 'd8e8bae3ee330a198d1f5e00ad7c5f9ed7c24c357c0a004322abca5d9cd17847' 533 }, 534 { 535 'message': 'Bitcoin', 536 'privkey': 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 537 'k_bad00': '36c848ffb2cbecc5422c33a994955b807665317c1ce2a0f59c689321aaa631cc', 538 'k_bad01': '4ed8de1ec952a4f5b3bd79d1ff96446bcd45cabb00fc6ca127183e14671bcb85', 539 'k_bad15': '56b6f47babc1662c011d3b1f93aa51a6e9b5f6512e9f2e16821a238d450a31f8' 540 }, 541 { 542 'message': 'i2FLPP8WEus5WPjpoHwheXOMSobUJVaZM1JPMQZq', 543 'privkey': 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140', 544 'k_bad00': '6e9b434fcc6bbb081a0463c094356b47d62d7efae7da9c518ed7bac23f4e2ed6', 545 'k_bad01': 'ae5323ae338d6117ce8520a43b92eacd2ea1312ae514d53d8e34010154c593bb', 546 'k_bad15': '3eaa1b61d1b8ab2f1ca71219c399f2b8b3defa624719f1e96fe3957628c2c4ea' 547 }, 548 { 549 'message': 'lEE55EJNP7aLrMtjkeJKKux4Yg0E8E1SAJnWTCEh', 550 'privkey': '3881e5286abc580bb6139fe8e83d7c8271c6fe5e5c2d640c1f0ed0e1ee37edc9', 551 'k_bad00': '5b606665a16da29cc1c5411d744ab554640479dd8abd3c04ff23bd6b302e7034', 552 'k_bad01': 'f8b25263152c042807c992eacd2ac2cc5790d1e9957c394f77ea368e3d9923bd', 553 'k_bad15': 'ea624578f7e7964ac1d84adb5b5087dd14f0ee78b49072aa19051cc15dab6f33' 554 }, 555 { 556 'message': '2SaVPvhxkAPrayIVKcsoQO5DKA8Uv5X/esZFlf+y', 557 'privkey': '7259dff07922de7f9c4c5720d68c9745e230b32508c497dd24cb95ef18856631', 558 'k_bad00': '3ab6c19ab5d3aea6aa0c6da37516b1d6e28e3985019b3adb388714e8f536686b', 559 'k_bad01': '19af21b05004b0ce9cdca82458a371a9d2cf0dc35a813108c557b551c08eb52e',
560 'k_bad15': '117a32665fca1b7137a91c4739ac5719fec0cf2e146f40f8e7c21b45a07ebc6a' 561 }, 562 { 563 'message': '00A0OwO2THi7j5Z/jp0FmN6nn7N/DQd6eBnCS+/b', 564 'privkey': '0d6ea45d62b334777d6995052965c795a4f8506044b4fd7dc59c15656a28f7aa', 565 'k_bad00': '79487de0c8799158294d94c0eb92ee4b567e4dc7ca18addc86e49d31ce1d2db6', 566 'k_bad01': '9561d2401164a48a8f600882753b3105ebdd35e2358f4f808c4f549c91490009', 567 'k_bad15': 'b0d273634129ff4dbdf0df317d4062a1dbc58818f88878ffdb4ec511c77976c0' 568 } 569 ]; 570 571 var result_txt = '\n----------------------\nResults\n----------------------\n\n'; 572 573 for (i = 0; i < test_vectors.length; i++) { 574 var hash = Crypto.SHA256(test_vectors[i]['message'].split('').map(function (c) { return c.charCodeAt(0); }), { asBytes: true }); 575 var wif = coinjs.privkey2wif(test_vectors[i]['privkey']); 576 577 var KBigInt = tx.deterministicK(wif, hash); 578 var KBigInt0 = tx.deterministicK(wif, hash, 0); 579 var KBigInt1 = tx.deterministicK(wif, hash, 1); 580 var KBigInt15 = tx.deterministicK(wif, hash, 15); 581 582 var K = Crypto.util.bytesToHex(KBigInt.toByteArrayUnsigned()); 583 var K0 = Crypto.util.bytesToHex(KBigInt0.toByteArrayUnsigned()); 584 var K1 = Crypto.util.bytesToHex(KBigInt1.toByteArrayUnsigned()); 585 var K15 = Crypto.util.bytesToHex(KBigInt15.toByteArrayUnsigned()); 586 587 if (K != test_vectors[i]['k_bad00']) { 588 result_txt += 'Failed Test #' + (i + 1) + '\n K = ' + K + '\nExpected = ' + test_vectors[i]['k_bad00'] + '\n\n'; 589 } else if (K0 != test_vectors[i]['k_bad00']) { 590 result_txt += 'Failed Test #' + (i + 1) + '\n K0 = ' + K0 + '\nExpected = ' + test_vectors[i]['k_bad00'] + '\n\n'; 591 } else if (K1 != test_vectors[i]['k_bad01']) { 592 result_txt += 'Failed Test #' + (i + 1) + '\n K1 = ' + K1 + '\nExpected = ' + test_vectors[i]['k_bad01'] + '\n\n'; 593 } else if (K15 != test_vectors[i]['k_bad15']) { 594 result_txt += 'Failed Test #' + (i + 1) + '\n K15 = ' + K15 + '\nExpected = ' + test_vectors[i]['k_bad15'] + '\n\n'; 595 }; 596 }; 597 598 if (result_txt.length < 60) { 599 result_txt = 'All Tests OK!'; 600 }; 601 602 return result_txt; 603 }; 604 605 /* start of hd functions, thanks bip32.org */ 606 coinjs.hd = function (data) { 607 608 var r = {}; 609 610 /* some hd value parsing */ 611 r.parse = function () { 612 613 var bytes = []; 614 615 // some quick validation 616 if (typeof (data) == 'string') { 617 var decoded = coinjs.base58decode(data); 618 if (decoded.length == 82) { 619 var checksum = decoded.slice(78, 82); 620 var hash = Crypto.SHA256(Crypto.SHA256(decoded.slice(0, 78), { asBytes: true }), { asBytes: true }); 621 if (checksum[0] == hash[0] && checksum[1] == hash[1] && checksum[2] == hash[2] && checksum[3] == hash[3]) { 622 bytes = decoded.slice(0, 78); 623 } 624 } 625 } 626 627 // actual parsing code 628 if (bytes && bytes.length > 0) { 629 r.version = coinjs.uint(bytes.slice(0, 4), 4); 630 r.depth = coinjs.uint(bytes.slice(4, 5), 1); 631 r.parent_fingerprint = bytes.slice(5, 9); 632 r.child_index = coinjs.uint(bytes.slice(9, 13), 4); 633 r.chain_code = bytes.slice(13, 45); 634 r.key_bytes = bytes.slice(45, 78); 635 636 var c = coinjs.compressed; // get current default 637 coinjs.compressed = true; 638 639 if (r.key_bytes[0] == 0x00) { 640 r.type = 'private'; 641 var privkey = (r.key_bytes).slice(1, 33); 642 var privkeyHex = Crypto.util.bytesToHex(privkey); 643 var pubkey = coinjs.newPubkey(privkeyHex); 644 645 r.keys = { 646 'privkey': privkeyHex, 647 'pubkey': pubkey, 648 'address': coinjs.pubkey2address(pubkey), 649 'wif': coinjs.privkey2wif(privkeyHex) 650 }; 651 652 } else if (r.key_bytes[0] == 0x02 || r.key_bytes[0] == 0x03) { 653 r.type = 'public'; 654 var pubkeyHex = Crypto.util.bytesToHex(r.key_bytes); 655 656 r.keys = { 657 'pubkey': pubkeyHex, 658 'address': coinjs.pubkey2address(pubkeyHex) 659 }; 660 } else { 661 r.type = 'invalid'; 662 } 663 664 r.keys_extended = r.extend(); 665 666 coinjs.compressed = c; // reset to default 667 } 668 669 return r; 670 } 671 672 // extend prv/pub key 673 r.extend = function () { 674 var hd = coinjs.hd(); 675 return hd.make({ 676 'depth': (this.depth * 1) + 1, 677 'parent_fingerprint': this.parent_fingerprint, 678 'child_index': this.child_index, 679 'chain_code': this.chain_code, 680 'privkey': this.keys.privkey, 681 'pubkey': this.keys.pubkey 682 }); 683 } 684 685 // derive from path 686 r.derive_path = function (path) { 687 688 if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') return this; 689 690 var p = path.split('/'); 691 var hdp = coinjs.clone(this); // clone hd path 692 693 for (var i in p) { 694 695 if (((i == 0) && c != 'm') || i == 'remove') { 696 continue; 697 } 698 699 var c = p[i]; 700 701 var use_private = (c.length > 1) && (c[c.length - 1] == '\''); 702 var child_index = parseInt(use_private ? c.slice(0, c.length - 1) : c) & 0x7fffffff; 703 if (use_private) 704 child_index += 0x80000000; 705 706 hdp = hdp.derive(child_index);
707 var key = ((hdp.keys_extended.privkey) && hdp.keys_extended.privkey != '') ? hdp.keys_extended.privkey : hdp.keys_extended.pubkey; 708 hdp = coinjs.hd(key); 709 } 710 return hdp; 711 } 712 713 // derive key from index 714 r.derive = function (i) { 715 716 i = (i) ? i : 0; 717 var blob = (Crypto.util.hexToBytes(this.keys.pubkey)).concat(coinjs.numToBytes(i, 4).reverse()); 718 719 var j = new jsSHA(Crypto.util.bytesToHex(blob), 'HEX'); 720 var hash = j.getHMAC(Crypto.util.bytesToHex(r.chain_code), "HEX", "SHA-512", "HEX"); 721 722 var il = new BigInteger(hash.slice(0, 64), 16); 723 var ir = Crypto.util.hexToBytes(hash.slice(64, 128)); 724 725 var ecparams = EllipticCurve.getSECCurveByName("secp256k1"); 726 var curve = ecparams.getCurve(); 727 728 var k, key, pubkey, o; 729 730 o = coinjs.clone(this); 731 o.chain_code = ir; 732 o.child_index = i; 733 734 if (this.type == 'private') { 735 // derive key pair from from a xprv key 736 k = il.add(new BigInteger([0].concat(Crypto.util.hexToBytes(this.keys.privkey)))).mod(ecparams.getN()); 737 key = Crypto.util.bytesToHex(k.toByteArrayUnsigned()); 738 739 pubkey = coinjs.newPubkey(key); 740 741 o.keys = { 742 'privkey': key, 743 'pubkey': pubkey, 744 'wif': coinjs.privkey2wif(key), 745 'address': coinjs.pubkey2address(pubkey) 746 }; 747 748 } else if (this.type == 'public') { 749 // derive xpub key from an xpub key 750 q = ecparams.curve.decodePointHex(this.keys.pubkey); 751 var curvePt = ecparams.getG().multiply(il).add(q); 752 753 var x = curvePt.getX().toBigInteger(); 754 var y = curvePt.getY().toBigInteger(); 755 756 var publicKeyBytesCompressed = EllipticCurve.integerToBytes(x, 32) 757 if (y.isEven()) { 758 publicKeyBytesCompressed.unshift(0x02) 759 } else { 760 publicKeyBytesCompressed.unshift(0x03) 761 } 762 pubkey = Crypto.util.bytesToHex(publicKeyBytesCompressed); 763 764 o.keys = { 765 'pubkey': pubkey, 766 'address': coinjs.pubkey2address(pubkey) 767 } 768 } else { 769 // fail 770 } 771 772 o.parent_fingerprint = (ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(r.keys.pubkey), { asBytes: true }), { asBytes: true })).slice(0, 4); 773 o.keys_extended = o.extend(); 774 return o; 775 } 776 777 // make a master hd xprv/xpub 778 r.master = function (pass) { 779 var seed = (pass) ? Crypto.SHA256(pass) : coinjs.newPrivkey(); 780 var hasher = new jsSHA(seed, 'HEX'); 781 var I = hasher.getHMAC("Bitcoin seed", "TEXT", "SHA-512", "HEX"); 782 783 var privkey = Crypto.util.hexToBytes(I.slice(0, 64)); 784 var chain = Crypto.util.hexToBytes(I.slice(64, 128)); 785 786 var hd = coinjs.hd(); 787 return hd.make({ 788 'depth': 0, 789 'parent_fingerprint': [0, 0, 0, 0], 790 'child_index': 0, 791 'chain_code': chain, 792 'privkey': I.slice(0, 64), 793 'pubkey': coinjs.newPubkey(I.slice(0, 64)) 794 }); 795 } 796 797 // encode data to a base58 string 798 r.make = function (data) { // { (int) depth, (array) parent_fingerprint, (int) child_index, (byte array) chain_code, (hex str) privkey, (hex str) pubkey} 799 var k = []; 800 801 //depth 802 k.push(data.depth * 1); 803 804 //parent fingerprint 805 k = k.concat(data.parent_fingerprint); 806 807 //child index 808 k = k.concat((coinjs.numToBytes(data.child_index, 4)).reverse()); 809 810 //Chain code 811 k = k.concat(data.chain_code); 812 813 var o = {}; // results 814 815 //encode xprv key 816 if (data.privkey) { 817 var prv = (coinjs.numToBytes(coinjs.hdkey.prv, 4)).reverse(); 818 prv = prv.concat(k); 819 prv.push(0x00); 820 prv = prv.concat(Crypto.util.hexToBytes(data.privkey)); 821 var hash = Crypto.SHA256(Crypto.SHA256(prv, { asBytes: true }), { asBytes: true }); 822 var checksum = hash.slice(0, 4); 823 var ret = prv.concat(checksum); 824 o.privkey = coinjs.base58encode(ret); 825 } 826 827 //encode xpub key 828 if (data.pubkey) { 829 var pub = (coinjs.numToBytes(coinjs.hdkey.pub, 4)).reverse(); 830 pub = pub.concat(k); 831 pub = pub.concat(Crypto.util.hexToBytes(data.pubkey)); 832 var hash = Crypto.SHA256(Crypto.SHA256(pub, { asBytes: true }), { asBytes: true }); 833 var checksum = hash.slice(0, 4); 834 var ret = pub.concat(checksum); 835 o.pubkey = coinjs.base58encode(ret); 836 } 837 return o; 838 } 839 840 return r.parse(); 841 } 842 843 844 /* start of script functions */ 845 coinjs.script = function (data) { 846 var r = {}; 847 848 if (!data) { 849 r.buffer = []; 850 } else if ("string" == typeof data) { 851 r.buffer = Crypto.util.hexToBytes(data); 852 } else if (coinjs.isArray(data)) { 853 r.buffer = data; 854 } else if (data instanceof coinjs.script) { 855 r.buffer = data.buffer; 856 } else { 857 r.buffer = data; 858 } 859 860 /* parse buffer array */ 861 r.parse = function () { 862 863 var self = this; 864 r.chunks = []; 865 var i = 0; 866 867 function readChunk(n) { 868 self.chunks.push(self.buffer.slice(i, i + n)); 869 i += n; 870 }; 871 872 while (i < this.buffer.length) { 873 var opcode = this.buffer[i++]; 874 if (opcode >= 0xF0) { 875 opcode = (opcode << 8) | this.buffer[i++]; 876 } 877 878 var len; 879 if (opcode > 0 && opcode < 76) { //OP_PUSHDATA1 880 readChunk(opcode); 881 } else if (opcode == 76) { //OP_PUSHDATA1 882 len = this.buffer[i++]; 883 readChunk(len); 884 } else if (opcode == 77) { //OP_PUSHDATA2 885 len = (this.buffer[i++] << 8) | this.buffer[i++]; 886 readChunk(len); 887 } else if (opcode == 78) { //OP_PUSHDATA4 888 len = (this.buffer[i++] << 24) | (this.buffer[i++] << 16) | (this.buffer[i++] << 8) | this.buffer[i++]; 889 readChunk(len); 890 } else { 891 this.chunks.push(opcode); 892 } 893 894 if (i < 0x00) { 895 break; 896 } 897 } 898 899 return true; 900 }; 901 902 /* decode the redeemscript of a multisignature transaction */ 903 r.decodeRedeemScript = function (script) { 904 var r = false;
905 try { 906 var s = coinjs.script(Crypto.util.hexToBytes(script)); 907 if ((s.chunks.length >= 3) && s.chunks[s.chunks.length - 1] == 174) {//OP_CHECKMULTISIG 908 r = {}; 909 r.signaturesRequired = s.chunks[0] - 80; 910 var pubkeys = []; 911 for (var i = 1; i < s.chunks.length - 2; i++) { 912 pubkeys.push(Crypto.util.bytesToHex(s.chunks[i])); 913 } 914 r.pubkeys = pubkeys; 915 var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired); 916 r.address = multi['address']; 917 r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig" 918 var rs = Crypto.util.bytesToHex(s.buffer); 919 r.redeemscript = rs; 920 921 } else if ((s.chunks.length == 2) && (s.buffer[0] == 0 && s.buffer[1] == 20)) { // SEGWIT 922 r = {}; 923 r.type = "segwit__"; 924 var rs = Crypto.util.bytesToHex(s.buffer); 925 r.address = coinjs.pubkey2address(rs, coinjs.multisig); 926 r.redeemscript = rs; 927 928 } else if (s.chunks.length == 5 && s.chunks[1] == 177 && s.chunks[2] == 117 && s.chunks[4] == 172) { 929 // ^ <unlocktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG ^ 930 r = {} 931 r.pubkey = Crypto.util.bytesToHex(s.chunks[3]); 932 r.checklocktimeverify = coinjs.bytesToNum(s.chunks[0].slice()); 933 r.address = coinjs.simpleHodlAddress(r.pubkey, r.checklocktimeverify).address; 934 var rs = Crypto.util.bytesToHex(s.buffer); 935 r.redeemscript = rs; 936 r.type = "hodl__"; 937 } 938 } catch (e) { 939 // console.log(e); 940 r = false; 941 } 942 return r; 943 } 944 945 /* create output script to spend */ 946 r.spendToScript = function (address) { 947 var addr = coinjs.addressDecode(address); 948 var s = coinjs.script(); 949 if (addr.type == "bech32") { 950 s.writeOp(0); 951 s.writeBytes(Crypto.util.hexToBytes(addr.redeemscript)); 952 } else if (addr.version == coinjs.multisig) { // multisig address 953 s.writeOp(169); //OP_HASH160 954 s.writeBytes(addr.bytes); 955 s.writeOp(135); //OP_EQUAL 956 } else { // regular address 957 s.writeOp(118); //OP_DUP 958 s.writeOp(169); //OP_HASH160 959 s.writeBytes(addr.bytes); 960 s.writeOp(136); //OP_EQUALVERIFY 961 s.writeOp(172); //OP_CHECKSIG 962 } 963 return s; 964 } 965 966 /* geneate a (script) pubkey hash of the address - used for when signing */ 967 r.pubkeyHash = function (address) { 968 var addr = coinjs.addressDecode(address); 969 var s = coinjs.script(); 970 s.writeOp(118);//OP_DUP 971 s.writeOp(169);//OP_HASH160 972 s.writeBytes(addr.bytes); 973 s.writeOp(136);//OP_EQUALVERIFY 974 s.writeOp(172);//OP_CHECKSIG 975 return s; 976 } 977 978 /* write to buffer */ 979 r.writeOp = function (op) { 980 this.buffer.push(op); 981 this.chunks.push(op); 982 return true; 983 } 984 985 /* write bytes to buffer */ 986 r.writeBytes = function (data) { 987 if (data.length < 76) { //OP_PUSHDATA1 988 this.buffer.push(data.length); 989 } else if (data.length <= 0xff) { 990 this.buffer.push(76); //OP_PUSHDATA1 991 this.buffer.push(data.length); 992 } else if (data.length <= 0xffff) { 993 this.buffer.push(77); //OP_PUSHDATA2 994 this.buffer.push(data.length & 0xff); 995 this.buffer.push((data.length >>> 8) & 0xff); 996 } else { 997 this.buffer.push(78); //OP_PUSHDATA4 998 this.buffer.push(data.length & 0xff); 999 this.buffer.push((data.length >>> 8) & 0xff); 1000 this.buffer.push((data.length >>> 16) & 0xff); 1001 this.buffer.push((data.length >>> 24) & 0xff); 1002 } 1003 this.buffer = this.buffer.concat(data); 1004 this.chunks.push(data); 1005 return true; 1006 } 1007 1008 r.parse(); 1009 return r; 1010 } 1011 1012 /* start of transaction functions */ 1013 1014 /* create a new transaction object */ 1015 coinjs.transaction = function () { 1016 1017 var r = {}; 1018 r.version = 1; 1019 r.lock_time = 0; 1020 r.ins = []; 1021 r.outs = []; 1022 r.witness = false; 1023 r.timestamp = null; 1024 r.block = null; 1025 1026 /* add an input to a transaction */ 1027 r.addinput = function (txid, index, script, sequence) { 1028 var o = {}; 1029 o.outpoint = { 'hash': txid, 'index': index }; 1030 o.script = coinjs.script(script || []); 1031 o.sequence = sequence || ((r.lock_time == 0) ? 4294967295 : 0); 1032 return this.ins.push(o); 1033 } 1034 1035 /* add an output to a transaction */ 1036 r.addoutput = function (address, value) { 1037 var o = {}; 1038 o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10); 1039 var s = coinjs.script(); 1040 o.script = s.spendToScript(address); 1041 1042 return this.outs.push(o); 1043 } 1044 1045 /* add two outputs for stealth addresses to a transaction */ 1046 r.addstealth = function (stealth, value) { 1047 var ephemeralKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(coinjs.newPrivkey())); 1048 var curve = EllipticCurve.getSECCurveByName("secp256k1"); 1049 1050 var p = EllipticCurve.fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"); 1051 var a = BigInteger.ZERO; 1052 var b = EllipticCurve.fromHex("7"); 1053 var calccurve = new EllipticCurve.CurveFp(p, a, b); 1054 1055 var ephemeralPt = curve.getG().multiply(ephemeralKeyBigInt); 1056 var scanPt = calccurve.decodePointHex(stealth.scankey); 1057 var sharedPt = scanPt.multiply(ephemeralKeyBigInt); 1058 var stealthindexKeyBigInt = BigInteger.fromByteArrayUnsigned(Crypto.SHA256(sharedPt.getEncoded(true), { asBytes: true })); 1059 1060 var stealthindexPt = curve.getG().multiply(stealthindexKeyBigInt); 1061 var spendPt = calccurve.decodePointHex(stealth.spendkey); 1062 var addressPt = spendPt.add(stealthindexPt); 1063 1064 var sendaddress = coinjs.pubkey2address(Crypto.util.bytesToHex(addressPt.getEncoded(true))); 1065 1066 1067 var OPRETBytes = [6].concat(Crypto.util.randomBytes(4)).concat(ephemeralPt.getEncoded(true)); // ephemkey data 1068 var q = coinjs.script(); 1069 q.writeOp(106); // OP_RETURN 1070 q.writeBytes(OPRETBytes); 1071 v = {}; 1072 v.value = 0; 1073 v.script = q; 1074 1075 this.outs.push(v); 1076 1077 var o = {}; 1078 o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10); 1079 var s = coinjs.script(); 1080 o.script = s.spendToScript(sendaddress); 1081 1082 return this.outs.push(o); 1083 } 1084 1085 /* add data to a transaction */ 1086 r.adddata = function (data) { 1087 var r = false;
1088 if (((data.match(/^[a-f0-9]+$/gi)) && data.length < 160) && (data.length % 2) == 0) { 1089 var s = coinjs.script(); 1090 s.writeOp(106); // OP_RETURN 1091 s.writeBytes(Crypto.util.hexToBytes(data)); 1092 o = {}; 1093 o.value = 0; 1094 o.script = s; 1095 return this.outs.push(o); 1096 } 1097 return r; 1098 } 1099 1100 r.listUnspent = function (address, callback) { 1101 // mempool.space API endpoint for unspent outputs 1102 const apiUrl = `https://mempool.space/api/address/${address}/utxo`; 1103 1104 // Perform the AJAX GET request to fetch unspent outputs 1105 coinjs.ajax(apiUrl, function (response) { 1106 try { 1107 // Parse the response if it's not already in JSON format 1108 const data = typeof response === "string" ? JSON.parse(response) : response; 1109 1110 // mempool.space returns an array directly (not wrapped in unspent_outputs) 1111 // Normalize to the format expected by addUnspent: 1112 // { tx_hash, tx_output_n, value, confirmations, script } 1113 if (Array.isArray(data) && data.length > 0) { 1114 var remaining = data.length; 1115 var normalized = []; 1116 1117 data.forEach(function (utxo) { 1118 // mempool.space does not return scriptPubKey in /utxo â 1119 // fetch it from the transaction details 1120 const txUrl = `https://mempool.space/api/tx/${utxo.txid}`; 1121 coinjs.ajax(txUrl, function (txResponse) { 1122 try { 1123 const txData = typeof txResponse === "string" ? JSON.parse(txResponse)
1123: txResponse; 1124 const scriptPubKey = txData.vout[utxo.vout].scriptpubkey || ""; 1125 normalized.push({ 1126 tx_hash: utxo.txid, 1127 tx_output_n: utxo.vout, 1128 value: utxo.value, 1129 confirmations: utxo.status.confirmed ? 1 : 0, 1130 script: scriptPubKey 1131 }); 1132 } catch (e) { 1133 // Still push without script so the UTXO is not silently lost 1134 normalized.push({ 1135 tx_hash: utxo.txid, 1136 tx_output_n: utxo.vout, 1137 value: utxo.value, 1138 confirmations: utxo.status.confirmed ? 1 : 0, 1139 script: "" 1140 }); 1141 } 1142 remaining--; 1143 if (remaining === 0) { 1144 callback(normalized); 1145 } 1146 }, "GET"); 1147 }); 1148 } else { 1149 console.warn("No unspent outputs found for the address."); 1150 callback([]); 1151 } 1152 } catch (error) { 1153 console.error("Error parsing response or fetching unspent outputs:", error); 1154 callback([]); // Return an empty array if an error occurs 1155 } 1156 }, "GET"); 1157 }; 1158 1159 /* list transaction data */ 1160 r.getTransaction = function (txid, callback) { 1161 coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=bitcoin&request=gettransaction&txid=' + txid + '&r=' + Math.random(), callback, "GET"); 1162 } 1163 1164 /* add unspent to transaction */ 1165 r.addUnspent = function (address, callback, script, segwit, sequence) { 1166 const self = this; 1167 1168 // Fetch unspent transactions for the address 1169 this.listUnspent(address, function (data) { 1170 let value = 0; 1171 let total = 0; 1172 1173 // Prepare the results object 1174 const result = { 1175 unspent: [], 1176 value: 0, 1177 total: 0, 1178 result: "success", 1179 }; 1180 1181 if (data && data.length > 0) { 1182 data.forEach((u) => { 1183 // mempool.space txid is already big-endian â no reversal needed 1184 const txhash = u.tx_hash; 1185 const n = u.tx_output_n; 1186 let scr = script || u.script; // Changed from const to let 1187 1188 if (segwit) { 1189 // Include the value in the redeem script for segwit 1190 const s = coinjs.script(); 1191 s.writeBytes(Crypto.util.hexToBytes(script)); 1192 s.writeOp(0); 1193 s.writeBytes(coinjs.numToBytes(u.value, 8)); 1194 scr = Crypto.util.bytesToHex(s.buffer); // Reassigning scr 1195 } 1196 1197 const seq = sequence || false; 1198 1199 // Add input to the transaction 1200 self.addinput(txhash, n, scr, seq); 1201 value += u.value; 1202 total++; 1203 1204 // Add to the result unspent list 1205 result.unspent.push({ 1206 tx_hash: txhash, 1207 tx_output_n: n, 1208 value: u.value, 1209 script: scr, 1210 }); 1211 }); 1212 } else { 1213 result.result = "No unspent outputs"; 1214 } 1215 1216 // Update total value and count 1217 result.value = value; 1218 result.total = total; 1219 1220 // Return the result via the callback 1221 return callback(result); 1222 }); 1223 }; 1224 1225 /* add unspent and sign */ 1226 r.addUnspentAndSign = function (wif, callback) { 1227 var self = this; 1228 var address = coinjs.wif2address(wif); 1229 self.addUnspent(address['address'], function (data) { 1230 self.sign(wif); 1231 return callback(data); 1232 }); 1233 } 1234 1235 r.broadcast = async function (callback, txhex) { 1236 // The raw transaction hex string 1237 var tx = txhex || this.serialize(); 1238 1239 // mempool.space API endpoint for broadcasting a transaction 1240 const apiUrl = `https://mempool.space/api/tx`; 1241 1242 try { 1243 // Perform a POST request using fetch 1244 // mempool.space expects raw hex as plain text body (not form-encoded) 1245 const response = await fetch(apiUrl, { 1246 method: "POST", 1247 headers: { 1248 "Content-Type": "text/plain", 1249 }, 1250 body: tx, // Send the transaction hex as plain text 1251 }); 1252 1253 if (response.ok) { 1254 // If response status is 200 (OK), mempool.space returns the txid 1255 const responseText = await response.text(); 1256 callback(true); 1257 console.log("Transaction broadcast successfully. txid:", responseText); 1258 } else { 1259 // If response status is not OK 1260 const errorText = await response.text(); 1261 1262 setTimeout(function () { 1263 document.getElementById("walletSendConfirmStatus").innerText = errorText; 1264 }, 500); // 100 миллиÑекÑнд задеÑжка пеÑед изменением ÑекÑÑа 1265 console.error("Error broadcasting transaction:", errorText); 1266 callback(false); 1267 } 1268 } catch (error) { 1269 // Handle fetch or network errors 1270 document.getElementById("walletSendConfirmStatus").innerText = `Error: ${error.message}`; 1271 console.error("Network error broadcasting transaction:", error); 1272 callback(false); 1273 } 1274 }; 1275 1276 /* generate the transaction hash to sign from a transaction input */ 1277 r.transactionHash = function (index, sigHashType) { 1278 1279 var clone = coinjs.clone(this); 1280 var shType = sigHashType || 1; 1281 1282 /* black out all other ins, except this one */ 1283 for (var i = 0; i < clone.ins.length; i++) { 1284 if (index != i) { 1285 clone.ins[i].script = coinjs.script(); 1286 } 1287 } 1288 1289 var extract = this.extractScriptKey(index); 1290 clone.ins[index].script = coinjs.script(extract['script']); 1291 1292 if ((clone.ins) && clone.ins[index]) { 1293 1294 /* SIGHASH : For more info on sig hashs see https://en.bitcoin.it/wiki/OP_CHECKSIG 1295 and https://bitcoin.org/en/developer-guide#signature-hash-type */ 1296 1297 if (shType == 1) { 1298 //SIGHASH_ALL 0x01 1299 1300 } else if (shType == 2) {
1301 //SIGHASH_NONE 0x02 1302 clone.outs = []; 1303 for (var i = 0; i < clone.ins.length; i++) { 1304 if (index != i) { 1305 clone.ins[i].sequence = 0; 1306 } 1307 } 1308 1309 } else if (shType == 3) { 1310 1311 //SIGHASH_SINGLE 0x03 1312 clone.outs.length = index + 1; 1313 1314 for (var i = 0; i < index; i++) { 1315 clone.outs[i].value = -1; 1316 clone.outs[i].script.buffer = []; 1317 } 1318 1319 for (var i = 0; i < clone.ins.length; i++) { 1320 if (index != i) { 1321 clone.ins[i].sequence = 0; 1322 } 1323 } 1324 1325 } else if (shType >= 128) { 1326 //SIGHASH_ANYONECANPAY 0x80 1327 clone.ins = [clone.ins[index]]; 1328 1329 if (shType == 129) { 1330 // SIGHASH_ALL + SIGHASH_ANYONECANPAY 1331 1332 } else if (shType == 130) { 1333 // SIGHASH_NONE + SIGHASH_ANYONECANPAY 1334 clone.outs = []; 1335 1336 } else if (shType == 131) { 1337 // SIGHASH_SINGLE + SIGHASH_ANYONECANPAY 1338 clone.outs.length = index + 1; 1339 for (var i = 0; i < index; i++) { 1340 clone.outs[i].value = -1; 1341 clone.outs[i].script.buffer = []; 1342 } 1343 } 1344 } 1345 1346 var buffer = Crypto.util.hexToBytes(clone.serialize()); 1347 buffer = buffer.concat(coinjs.numToBytes(parseInt(shType), 4)); 1348 var hash = Crypto.SHA256(buffer, { asBytes: true }); 1349 var r = Crypto.util.bytesToHex(Crypto.SHA256(hash, { asBytes: true })); 1350 return r; 1351 } else { 1352 return false; 1353 } 1354 } 1355 1356 /* generate a segwit transaction hash to sign from a transaction input */ 1357 r.transactionHashSegWitV0 = function (index, sigHashType) { 1358 /* 1359 Notice: coinb.in by default, deals with segwit transactions in a non-standard way. 1360 Segwit transactions require that input values are included in the transaction hash. 1361 To save wasting resources and potentially slowing down this service, we include the amount with the 1362 redeem script to generate the transaction hash and remove it after its signed. 1363 */ 1364 1365 // start redeem script check 1366 var extract = this.extractScriptKey(index); 1367 if (extract['type'] != 'segwit') { 1368 return { 'result': 0, 'fail': 'redeemscript', 'response': 'redeemscript missing or not valid for segwit' }; 1369 } 1370 1371 if (extract['value'] == -1) { 1372 return { 'result': 0, 'fail': 'value', 'response': 'unable to generate a valid segwit hash without a value' }; 1373 } 1374 1375 var scriptcode = Crypto.util.hexToBytes(extract['script']); 1376 1377 // end of redeem script check 1378 1379 /* P2WPKH */ 1380 if (scriptcode.length == 20) { 1381 scriptcode = [0x00, 0x14].concat(scriptcode); 1382 } 1383 1384 if (scriptcode.length == 22) { 1385 scriptcode = scriptcode.slice(1); 1386 scriptcode.unshift(25, 118, 169); 1387 scriptcode.push(136, 172); 1388 } 1389 1390 var value = coinjs.numToBytes(extract['value'], 8); 1391 1392 // start 1393 1394 var zero = coinjs.numToBytes(0, 32); 1395 var version = coinjs.numToBytes(parseInt(this.version), 4); 1396 1397 var bufferTmp = []; 1398 if (!(sigHashType >= 80)) { // not sighash anyonecanpay 1399 for (var i = 0; i < this.ins.length; i++) { 1400 bufferTmp = bufferTmp.concat(Crypto.util.hexToBytes(this.ins[i].outpoint.hash).reverse()); 1401 bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].outpoint.index, 4)); 1402 } 1403 } 1404 var hashPrevouts = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, { asBytes: true }), { asBytes: true }) : zero; 1405 1406 var bufferTmp = []; 1407 if (!(sigHashType >= 80) && sigHashType != 2 && sigHashType != 3) { // not sighash anyonecanpay & single & none 1408 for (var i = 0; i < this.ins.length; i++) { 1409 bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].sequence, 4)); 1410 } 1411 } 1412 var hashSequence = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, { asBytes: true }), { asBytes: true }) : zero; 1413 1414 var outpoint = Crypto.util.hexToBytes(this.ins[index].outpoint.hash).reverse(); 1415 outpoint = outpoint.concat(coinjs.numToBytes(this.ins[index].outpoint.index, 4)); 1416 1417 var nsequence = coinjs.numToBytes(this.ins[index].sequence, 4); 1418 var hashOutputs = zero; 1419 var bufferTmp = []; 1420 if (sigHashType != 2 && sigHashType != 3) { // not sighash single & none 1421 for (var i = 0; i < this.outs.length; i++) { 1422 bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.outs[i].value, 8)); 1423 bufferTmp = bufferTmp.concat(coinjs.numToVarInt(this.outs[i].script.buffer.length)); 1424 bufferTmp = bufferTmp.concat(this.outs[i].script.buffer); 1425 } 1426 hashOutputs = Crypto.SHA256(Crypto.SHA256(bufferTmp, { asBytes: true }), { asBytes: true }); 1427 1428 } else if ((sigHashType == 2) && index < this.outs.length) { // is sighash single 1429 bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.outs[index].value, 8)); 1430 bufferTmp = bufferTmp.concat(coinjs.numToVarInt(this.outs[i].script.buffer.length)); 1431 bufferTmp = bufferTmp.concat(this.outs[index].script.buffer); 1432 hashOutputs = Crypto.SHA256(Crypto.SHA256(bufferTmp, { asBytes: true }), { asBytes: true }); 1433 } 1434 1435 var locktime = coinjs.numToBytes(this.lock_time, 4); 1436 var sighash = coinjs.numToBytes(sigHashType, 4); 1437 1438 var buffer = []; 1439 buffer = buffer.concat(version); 1440 buffer = buffer.concat(hashPrevouts); 1441 buffer = buffer.concat(hashSequence); 1442 buffer = buffer.concat(outpoint); 1443 buffer = buffer.concat(scriptcode); 1444 buffer = buffer.concat(value); 1445 buffer = buffer.concat(nsequence); 1446 buffer = buffer.concat(hashOutputs); 1447 buffer = buffer.concat(locktime); 1448 buffer = buffer.concat(sighash); 1449 1450 var hash = Crypto.SHA256(buffer, { asBytes: true }); 1451 return { 'result': 1, 'hash': Crypto.util.bytesToHex(Crypto.SHA256(hash, { asBytes: true })), 'response': 'hash generated' }; 1452 } 1453 1454 /* extract the scriptSig, used in the transactionHash() function */ 1455 r.extractScriptKey = function (index) { 1456 if (this.ins[index]) { 1457 if ((this.ins[index].script.chunks.length == 5) && this.ins[index].script.chunks[4] == 172 && coinjs.isArray(this.ins[index].script.chunks[2])) { //OP_CHECKSIG 1458 // regular scriptPubkey (not signed) 1459 return { 'type': 'scriptpubkey', 'signed': 'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1460 } else if ((this.ins[index].script.chunks.length == 2) && this.ins[index].script.chunks[0][0] == 48 && this.ins[index].script.chunks[1].length == 5 && this.ins[index].script.chunks[1][1] == 177) {//OP_CHECKLOCKTIMEVERIFY 1461 // hodl script (signed) 1462 return { 'type': 'hodl', 'signed': 'true', 'signatures': 1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1463 } else if ((this.ins[index].script.chunks.length == 2) && this.ins[index].script.chunks[0][0] == 48) { 1464 // regular scriptPubkey (probably signed) 1465 return { 'type': 'scriptpubkey', 'signed': 'true', 'signatures': 1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1466 } else if (this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177) {//OP_CHECKLOCKTIMEVERIFY 1467 // hodl script (not signed) 1468 return { 'type': 'hodl', 'signed': 'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1469 } else if ((this.ins[index].script.chunks.length <= 3 && this.ins[index].script.chunks.length > 0) && ((this.ins[index].script.chunks[0].length == 22 && this.ins[index].script.chunks[0][0] == 0) || (this.ins[index].script.chunks[0].length == 20 && this.ins[index].script.chunks[1] == 0))) { 1470 var signed = ((this.witness[index]) && this.witness[index].length == 2) ? 'true' : 'false'; 1471 var sigs = (signed == 'true') ? 1 : 0; 1472 var value = -1; // no value found 1473 if ((this.ins[index].script.chunks[2]) && this.ins[index].script.chunks[2].length == 8) { 1474 value = coinjs.bytesToNum(this.ins[index].script.chunks[2]); // value found encoded in transaction (THIS IS NON STANDARD) 1475 } 1476 return { 'type': 'segwit', 'signed': signed, 'signatures': sigs, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[0]), 'value': value }; 1477 } 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 1478 // multisig script, with signature(s) included 1479 sigcount = 0; 1480 for (i = 1; i < this.ins[index].script.chunks.length - 1; i++) { 1481 if (this.ins[index].script.chunks[i] != 0) { 1482 sigcount++; 1483 } 1484 } 1485 1486 return { 'type': 'multisig', 'signed': 'true', 'signatures': sigc
1486ount, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1]) }; 1487 } else if (this.ins[index].script.chunks[0] >= 80 && this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1] == 174) { // OP_CHECKMULTISIG 1488 // multisig script, without signature! 1489 return { 'type': 'multisig', 'signed': 'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1490 } else if (this.ins[index].script.chunks.length == 0) { 1491 // empty 1492 return { 'type': 'empty', 'signed': 'false', 'signatures': 0, 'script': '' }; 1493 } else { 1494 // something else 1495 return { 'type': 'unknown', 'signed': 'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer) }; 1496 } 1497 } else { 1498 return false; 1499 } 1500 } 1501 1502 /* generate a signature from a transaction hash */ 1503 r.transactionSig = function (index, wif, sigHashType, txhash) { 1504 1505 function serializeSig(r, s) { 1506 var rBa = r.toByteArraySigned(); 1507 var sBa = s.toByteArraySigned(); 1508 1509 var sequence = []; 1510 sequence.push(0x02); // INTEGER 1511 sequence.push(rBa.length); 1512 sequence = sequence.concat(rBa); 1513 1514 sequence.push(0x02); // INTEGER 1515 sequence.push(sBa.length); 1516 sequence = sequence.concat(sBa); 1517 1518 sequence.unshift(sequence.length); 1519 sequence.unshift(0x30); // SEQUENCE 1520 1521 return sequence; 1522 } 1523 1524 var shType = sigHashType || 1; 1525 var hash = txhash || Crypto.util.hexToBytes(this.transactionHash(index, shType)); 1526 1527 if (hash) { 1528 var curve = EllipticCurve.getSECCurveByName("secp256k1"); 1529 var key = coinjs.wif2privkey(wif); 1530 var priv = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(key['privkey'])); 1531 var n = curve.getN(); 1532 var e = BigInteger.fromByteArrayUnsigned(hash); 1533 var badrs = 0 1534 do { 1535 var k = this.deterministicK(wif, hash, badrs); 1536 var G = curve.getG(); 1537 var Q = G.multiply(k); 1538 var r = Q.getX().toBigInteger().mod(n); 1539 var s = k.modInverse(n).multiply(e.add(priv.multiply(r))).mod(n); 1540 badrs++ 1541 } while (r.compareTo(BigInteger.ZERO) <= 0 || s.compareTo(BigInteger.ZERO) <= 0); 1542 1543 // Force lower s values per BIP62 1544 var halfn = n.shiftRight(1); 1545 if (s.compareTo(halfn) > 0) { 1546 s = n.subtract(s); 1547 }; 1548 1549 var sig = serializeSig(r, s); 1550 sig.push(parseInt(shType, 10)); 1551 1552 return Crypto.util.bytesToHex(sig); 1553 } else { 1554 return false; 1555 } 1556 } 1557 1558 // https://tools.ietf.org/html/rfc6979#section-3.2 1559 r.deterministicK = function (wif, hash, badrs) { 1560 // if r or s were invalid when this function was used in signing, 1561 // we do not want to actually compute r, s here for efficiency, so, 1562 // we can increment badrs. explained at end of RFC 6979 section 3.2 1563 1564 // wif is b58check encoded wif privkey. 1565 // hash is byte array of transaction digest. 1566 // badrs is used only if the k resulted in bad r or s. 1567 1568 // some necessary things out of the way for clarity. 1569 badrs = badrs || 0; 1570 var key = coinjs.wif2privkey(wif); 1571 var x = Crypto.util.hexToBytes(key['privkey']) 1572 var curve = EllipticCurve.getSECCurveByName("secp256k1"); 1573 var N = curve.getN(); 1574 1575 // Step: a 1576 // hash is a byteArray of the message digest. so h1 == hash in our case 1577 1578 // Step: b 1579 var v = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; 1580 1581 // Step: c 1582 var k = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; 1583 1584 // Step: d 1585 k = Crypto.HMAC(Crypto.SHA256, v.concat([0]).concat(x).concat(hash), k, { asBytes: true }); 1586 1587 // Step: e 1588 v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true }); 1589 1590 // Step: f 1591 k = Crypto.HMAC(Crypto.SHA256, v.concat([1]).concat(x).concat(hash), k, { asBytes: true }); 1592 1593 // Step: g 1594 v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true }); 1595 1596 // Step: h1 1597 var T = []; 1598 1599 // Step: h2 (since we know tlen = qlen, just copy v to T.) 1600 v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true }); 1601 T = v; 1602 1603 // Step: h3 1604 var KBigInt = BigInteger.fromByteArrayUnsigned(T); 1605 1606 // loop if KBigInt is not in the range of [1, N-1] or if badrs needs incrementing. 1607 var i = 0 1608 while (KBigInt.compareTo(N) >= 0 || KBigInt.compareTo(BigInteger.ZERO) <= 0 || i < badrs) { 1609 k = Crypto.HMAC(Crypto.SHA256, v.concat([0]), k, { asBytes: true }); 1610 v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true }); 1611 v = Crypto.HMAC(Crypto.SHA256, v, k, { asBytes: true }); 1612 T = v; 1613 KBigInt = BigInteger.fromByteArrayUnsigned(T); 1614 i++ 1615 }; 1616 1617 return KBigInt; 1618 }; 1619 1620 /* sign a "standard" input */ 1621 r.signinput = function (index, wif, sigHashType) { 1622 var key = coinjs.wif2pubkey(wif); 1623 var shType = sigHashType || 1; 1624 var signature = this.transactionSig(index, wif, shType); 1625 var s = coinjs.script(); 1626 s.writeBytes(Crypto.util.hexToBytes(signature)); 1627 s.writeBytes(Crypto.util.hexToBytes(key['pubkey'])); 1628 this.ins[index].script = s; 1629 return true; 1630 } 1631 1632 /* signs a time locked / hodl input */ 1633 r.signhodl = function (index, wif, sigHashType) { 1634 var shType = sigHashType || 1; 1635 var signature = this.transactionSig(index, wif, shType); 1636 var redeemScript = this.ins[index].script.buffer 1637 var s = coinjs.script(); 1638 s.writeBytes(Crypto.util.hexToBytes(signature)); 1639 s.writeBytes(redeemScript); 1640 this.ins[index].script = s; 1641 return true; 1642 } 1643 1644 /* sign a multisig input */ 1645 r.signmultisig = function (index, wif, sigHashType) { 1646 1647 function scriptListPubkey(redeemScript) { 1648 var r = {}; 1649 for (var i = 1; i < redeemScript.chunks.length - 2; i++) { 1650 r[i] = Crypto.util.hexToBytes(coinjs.pubkeydecompress(Crypto.util.bytesToHex(redeemScript.chunks[i]))); 1651 } 1652 return r; 1653 } 1654 1655 function scriptListSigs(scriptSig) { 1656 var r = {}; 1657 var c = 0; 1658 if (scriptSig.chunks[0] == 0 && scriptSig.chunks[scriptSig.chunks.length - 1][scriptSig.chunks[scriptSig.chunks.length - 1].length - 1] == 174) { 1659 for (var i = 1; i < scriptSig.chunks.length - 1; i++) { 1660 if (scriptSig.chunks[i] != 0) { 1661 c++; 1662 r[c] = scriptSig.chunks[i]; 1663 } 1664 } 1665 } 1666 return r; 1667 } 1668 1669 var redeemScript = (this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1] == 174) ? this.ins[index].script.buffer : this.ins[index].script.chunks[this.ins[index].script.chunks.length - 1]; 1670 1671 var pubkeyList = scriptListPubkey(coinjs.script(redeemScript)); 1672 var sigsList = scriptListSigs(this.ins[index].script); 1673 1674 var shType = sigHashType || 1; 1675 var sighash = Crypto.util.hexToBytes(this.transactionHash(index, shType)); 1676 var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType)); 1677 1678 sigsList[coinjs.countObject(sigsList) + 1] = signature; 1679 1680 var s = coinjs.script(); 1681 1682 s.writeOp(0); 1683 1684 for (x in pubkeyList) { 1685 for (y in sigsList) { 1686 this.ins[index].script.buffer = redeemScript; 1687 sighash = Crypto.util.hexToBytes(this.transactionHash(index, sigsList[y].slice(-1)[0] * 1)); 1688 if (coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) { 1689 s.writeBytes(sigsList[y]); 1690 } 1691 } 1692 } 1693 1694 s.writeBytes(redeemScript); 1695 this.ins[index].script = s; 1696 return true; 1697 } 1698 1699 /* sign segwit input */ 1700 r.signsegwit = function (index, wif, sigHashType) { 1701 var shType = sigHashType || 1; 1702 1703 var wif2 = coinjs.wif2pubkey(wif); 1704 var segwit = coinjs.segwitAddress(wif2['pubkey']); 1705 var bech32 = coinjs.bech32Address(wif2['pubkey']); 1706 1707 if ((segwit['redeemscript'] == Crypto.util.bytesToHex(this.ins[index].script.chunks[0])) || (bech32['redeemscript'] == Crypto.util.bytesToHex(this.ins[index].script.chunks[0]))) { 1708 var txhash = this.transactionHashSegWitV0(index, shType); 1709 1710 if (txhash.result == 1) { 1711
1712 var segwitHash = Crypto.util.hexToBytes(txhash.hash); 1713 var signature = this.transactionSig(index, wif, shType, segwitHash); 1714 1715 // remove any non standard data we store, i.e. input value 1716 var script = coinjs.script(); 1717 script.writeBytes(this.ins[index].script.chunks[0]); 1718 this.ins[index].script = script; 1719 1720 if (!coinjs.isArray(this.witness)) { 1721 this.witness = []; 1722 } 1723 1724 this.witness.push([signature, wif2['pubkey']]); 1725 1726 /* attempt to reorder witness data as best as we can. 1727 data can't be easily validated at this stage as 1728 we dont have access to the inputs value and 1729 making a web call will be too slow. */ 1730 1731 var witness_order = []; 1732 var witness_used = []; 1733 for (var i = 0; i < this.ins.length; i++) { 1734 for (var y = 0; y < this.witness.length; y++) { 1735 if (!witness_used.includes(y)) { 1736 var sw = coinjs.segwitAddress(this.witness[y][1]); 1737 var b32 = coinjs.bech32Address(this.witness[y][1]); 1738 var rs = ''; 1739 1740 if (this.ins[i].script.chunks.length >= 1) { 1741 rs = Crypto.util.bytesToHex(this.ins[i].script.chunks[0]); 1742 } else if (this.ins[i].script.chunks.length == 0) { 1743 rs = b32['redeemscript']; 1744 } 1745 1746 if ((sw['redeemscript'] == rs) || (b32['redeemscript'] == rs)) { 1747 witness_order.push(this.witness[y]); 1748 witness_used.push(y); 1749 1750 // bech32, empty redeemscript 1751 if (b32['redeemscript'] == rs) { 1752 this.ins[index].script = coinjs.script(); 1753 } 1754 break; 1755 } 1756 } 1757 } 1758 } 1759 1760 this.witness = witness_order; 1761 } 1762 } 1763 return true; 1764 } 1765 1766 /* sign inputs */ 1767 r.sign = function (wif, sigHashType) { 1768 var shType = sigHashType || 1; 1769 for (var i = 0; i < this.ins.length; i++) { 1770 var d = this.extractScriptKey(i); 1771 1772 var w2a = coinjs.wif2address(wif); 1773 var script = coinjs.script(); 1774 var pubkeyHash = script.pubkeyHash(w2a['address']); 1775 1776 if (((d['type'] == 'scriptpubkey' && d['script'] == Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false") { 1777 this.signinput(i, wif, shType); 1778 1779 } else if (d['type'] == 'hodl' && d['signed'] == "false") { 1780 this.signhodl(i, wif, shType); 1781 1782 } else if (d['type'] == 'multisig') { 1783 this.signmultisig(i, wif, shType); 1784 1785 } else if (d['type'] == 'segwit') { 1786 this.signsegwit(i, wif, shType); 1787 1788 } else { 1789 // could not sign 1790 } 1791 } 1792 return this.serialize(); 1793 } 1794 1795 /* serialize a transaction */ 1796 r.serialize = function () { 1797 var buffer = []; 1798 buffer = buffer.concat(coinjs.numToBytes(parseInt(this.version), 4)); 1799 1800 if (coinjs.isArray(this.witness)) { 1801 buffer = buffer.concat([0x00, 0x01]); 1802 } 1803 1804 buffer = buffer.concat(coinjs.numToVarInt(this.ins.length)); 1805 for (var i = 0; i < this.ins.length; i++) { 1806 var txin = this.ins[i]; 1807 buffer = buffer.concat(Crypto.util.hexToBytes(txin.outpoint.hash).reverse()); 1808 buffer = buffer.concat(coinjs.numToBytes(parseInt(txin.outpoint.index), 4)); 1809 var scriptBytes = txin.script.buffer; 1810 buffer = buffer.concat(coinjs.numToVarInt(scriptBytes.length)); 1811 buffer = buffer.concat(scriptBytes); 1812 buffer = buffer.concat(coinjs.numToBytes(parseInt(txin.sequence), 4)); 1813 } 1814 buffer = buffer.concat(coinjs.numToVarInt(this.outs.length)); 1815 1816 for (var i = 0; i < this.outs.length; i++) { 1817 var txout = this.outs[i]; 1818 buffer = buffer.concat(coinjs.numToBytes(txout.value, 8)); 1819 var scriptBytes = txout.script.buffer; 1820 buffer = buffer.concat(coinjs.numToVarInt(scriptBytes.length)); 1821 buffer = buffer.concat(scriptBytes); 1822 } 1823 1824 if ((coinjs.isArray(this.witness)) && this.witness.length >= 1) { 1825 for (var i = 0; i < this.witness.length; i++) { 1826 buffer = buffer.concat(coinjs.numToVarInt(this.witness[i].length)); 1827 for (var x = 0; x < this.witness[i].length; x++) { 1828 buffer = buffer.concat(coinjs.numToVarInt(Crypto.util.hexToBytes(this.witness[i][x]).length)); 1829 buffer = buffer.concat(Crypto.util.hexToBytes(this.witness[i][x])); 1830 } 1831 } 1832 } 1833 1834 buffer = buffer.concat(coinjs.numToBytes(parseInt(this.lock_time), 4)); 1835 return Crypto.util.bytesToHex(buffer); 1836 } 1837 1838 /* deserialize a transaction */ 1839 r.deserialize = function (buffer) { 1840 if (typeof buffer == "string") { 1841 buffer = Crypto.util.hexToBytes(buffer) 1842 } 1843 1844 var pos = 0; 1845 var witness = false;
1846 1847 var readAsInt = function (bytes) { 1848 if (bytes == 0) return 0; 1849 pos++; 1850 return buffer[pos - 1] + readAsInt(bytes - 1) * 256; 1851 } 1852 1853 var readVarInt = function () { 1854 pos++; 1855 if (buffer[pos - 1] < 253) { 1856 return buffer[pos - 1]; 1857 } 1858 return readAsInt(buffer[pos - 1] - 251); 1859 } 1860 1861 var readBytes = function (bytes) { 1862 pos += bytes; 1863 return buffer.slice(pos - bytes, pos); 1864 } 1865 1866 var readVarString = function () { 1867 var size = readVarInt(); 1868 return readBytes(size); 1869 } 1870 1871 var obj = new coinjs.transaction(); 1872 obj.version = readAsInt(4); 1873 1874 if (buffer[pos] == 0x00 && buffer[pos + 1] == 0x01) { 1875 // segwit transaction 1876 witness = true; 1877 obj.witness = []; 1878 pos += 2; 1879 } 1880 1881 var ins = readVarInt(); 1882 for (var i = 0; i < ins; i++) { 1883 obj.ins.push({ 1884 outpoint: { 1885 hash: Crypto.util.bytesToHex(readBytes(32).reverse()), 1886 index: readAsInt(4) 1887 }, 1888 script: coinjs.script(readVarString()), 1889 sequence: readAsInt(4) 1890 }); 1891 } 1892 1893 var outs = readVarInt(); 1894 for (var i = 0; i < outs; i++) { 1895 obj.outs.push({ 1896 value: coinjs.bytesToNum(readBytes(8)), 1897 script: coinjs.script(readVarString()) 1898 }); 1899 } 1900 1901 if (witness == true) { 1902 for (i = 0; i < ins; ++i) { 1903 var count = readVarInt(); 1904 var vector = []; 1905 for (var y = 0; y < count; y++) { 1906 var slice = readVarInt(); 1907 pos += slice; 1908 if (!coinjs.isArray(obj.witness[i])) { 1909 obj.witness[i] = []; 1910 } 1911 obj.witness[i].push(Crypto.util.bytesToHex(buffer.slice(pos - slice, pos))); 1912 } 1913 } 1914 } 1915 1916 obj.lock_time = readAsInt(4); 1917 return obj; 1918 } 1919 1920 r.size = function () { 1921 return ((this.serialize()).length / 2).toFixed(0); 1922 } 1923 1924 return r; 1925 } 1926 1927 /* start of signature vertification functions */ 1928 1929 coinjs.verifySignature = function (hash, sig, pubkey) { 1930 1931 function parseSig(sig) { 1932 var cursor; 1933 if (sig[0] != 0x30) 1934 throw new Error("Signature not a valid DERSequence"); 1935 1936 cursor = 2; 1937 if (sig[cursor] != 0x02) 1938 throw new Error("First element in signature must be a DERInteger");; 1939 1940 var rBa = sig.slice(cursor + 2, cursor + 2 + sig[cursor + 1]); 1941 1942 cursor += 2 + sig[cursor + 1]; 1943 if (sig[cursor] != 0x02) 1944 throw new Error("Second element in signature must be a DERInteger"); 1945 1946 var sBa = sig.slice(cursor + 2, cursor + 2 + sig[cursor + 1]); 1947 1948 cursor += 2 + sig[cursor + 1]; 1949 1950 var r = BigInteger.fromByteArrayUnsigned(rBa); 1951 var s = BigInteger.fromByteArrayUnsigned(sBa); 1952 1953 return { r: r, s: s }; 1954 } 1955 1956 var r, s; 1957 1958 if (coinjs.isArray(sig)) { 1959 var obj = parseSig(sig); 1960 r = obj.r; 1961 s = obj.s; 1962 } else if ("object" === typeof sig && sig.r && sig.s) { 1963 r = sig.r; 1964 s = sig.s; 1965 } else { 1966 throw "Invalid value for signature"; 1967 } 1968 1969 var Q; 1970 if (coinjs.isArray(pubkey)) { 1971 var ecparams = EllipticCurve.getSECCurveByName("secp256k1"); 1972 Q = EllipticCurve.PointFp.decodeFrom(ecparams.getCurve(), pubkey); 1973 } else { 1974 throw "Invalid format for pubkey value, must be byte array"; 1975 } 1976 var e = BigInteger.fromByteArrayUnsigned(hash); 1977 1978 return coinjs.verifySignatureRaw(e, r, s, Q); 1979 } 1980 1981 coinjs.verifySignatureRaw = function (e, r, s, Q) { 1982 var ecparams = EllipticCurve.getSECCurveByName("secp256k1"); 1983 var n = ecparams.getN(); 1984 var G = ecparams.getG(); 1985 1986 if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) 1987 return false; 1988 1989 if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) 1990 return false; 1991 1992 var c = s.modInverse(n); 1993 1994 var u1 = e.multiply(c).mod(n); 1995 var u2 = r.multiply(c).mod(n); 1996 1997 var point = G.multiply(u1).add(Q.multiply(u2)); 1998 1999 var v = point.getX().toBigInteger().mod(n); 2000 2001 return v.equals(r); 2002 } 2003 2004 /* start of privates functions */ 2005 2006 /* base58 encode function */ 2007 coinjs.base58encode = function (buffer) { 2008 var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 2009 var base = BigInteger.valueOf(58); 2010 2011 var bi = BigInteger.fromByteArrayUnsigned(buffer); 2012 var chars = []; 2013 2014 while (bi.compareTo(base) >= 0) { 2015 var mod = bi.mod(base); 2016 chars.unshift(alphabet[mod.intValue()]); 2017 bi = bi.subtract(mod).divide(base); 2018 } 2019 2020 chars.unshift(alphabet[bi.intValue()]); 2021 for (var i = 0; i < buffer.length; i++) { 2022 if (buffer[i] == 0x00) { 2023 chars.unshift(alphabet[0]); 2024 } else break; 2025 } 2026 return chars.join(''); 2027 } 2028 2029 /* base58 decode function */ 2030 coinjs.base58decode = function (buffer) { 2031 var alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 2032 var base = BigInteger.valueOf(58);
2033 var validRegex = /^[1-9A-HJ-NP-Za-km-z]+$/; 2034 2035 var bi = BigInteger.valueOf(0); 2036 var leadingZerosNum = 0; 2037 for (var i = buffer.length - 1; i >= 0; i--) { 2038 var alphaIndex = alphabet.indexOf(buffer[i]); 2039 if (alphaIndex < 0) { 2040 throw "Invalid character"; 2041 } 2042 bi = bi.add(BigInteger.valueOf(alphaIndex).multiply(base.pow(buffer.length - 1 - i))); 2043 2044 if (buffer[i] == "1") leadingZerosNum++; 2045 else leadingZerosNum = 0; 2046 } 2047 2048 var bytes = bi.toByteArrayUnsigned(); 2049 while (leadingZerosNum-- > 0) bytes.unshift(0); 2050 return bytes; 2051 } 2052 2053 /* raw ajax function to avoid needing bigger frame works like jquery, mootools etc */ 2054 coinjs.ajax = function (u, f, m, a) { 2055 var x = false; 2056 try { 2057 x = new ActiveXObject('Msxml2.XMLHTTP') 2058 } catch (e) { 2059 try { 2060 x = new ActiveXObject('Microsoft.XMLHTTP') 2061 } catch (e) { 2062 x = new XMLHttpRequest() 2063 } 2064 } 2065 2066 if (x == false) { 2067 return false; 2068 } 2069 2070 x.open(m, u, true); 2071 x.onreadystatechange = function () { 2072 if ((x.readyState == 4) && f) 2073 f(x.responseText); 2074 }; 2075 2076 if (m == 'POST') { 2077 x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 2078 } 2079 2080 x.send(a); 2081 } 2082 2083 /* clone an object */ 2084 coinjs.clone = function (obj) { 2085 if (obj == null || typeof (obj) != 'object') return obj; 2086 var temp = new obj.constructor(); 2087 2088 for (var key in obj) { 2089 if (obj.hasOwnProperty(key)) { 2090 temp[key] = coinjs.clone(obj[key]); 2091 } 2092 } 2093 return temp; 2094 } 2095 2096 coinjs.numToBytes = function (num, bytes) { 2097 if (typeof bytes === "undefined") bytes = 8; 2098 if (bytes == 0) { 2099 return []; 2100 } else if (num == -1) { 2101 return Crypto.util.hexToBytes("ffffffffffffffff"); 2102 } else { 2103 return [num % 256].concat(coinjs.numToBytes(Math.floor(num / 256), bytes - 1)); 2104 } 2105 } 2106 2107 function scriptNumSize(i) { 2108 return i > 0x7fffffff ? 5 2109 : i > 0x7fffff ? 4 2110 : i > 0x7fff ? 3 2111 : i > 0x7f ? 2 2112 : i > 0x00 ? 1 2113 : 0; 2114 } 2115 2116 coinjs.numToScriptNumBytes = function (_number) { 2117 var value = Math.abs(_number); 2118 var size = scriptNumSize(value); 2119 var result = []; 2120 for (var i = 0; i < size; ++i) { 2121 result.push(0); 2122 } 2123 var negative = _number < 0; 2124 for (i = 0; i < size; ++i) { 2125 result[i] = value & 0xff; 2126 value = Math.floor(value / 256); 2127 } 2128 if (negative) { 2129 result[size - 1] |= 0x80; 2130 } 2131 return result; 2132 } 2133 2134 coinjs.numToVarInt = function (num) { 2135 if (num < 253) { 2136 return [num]; 2137 } else if (num < 65536) { 2138 return [253].concat(coinjs.numToBytes(num, 2)); 2139 } else if (num < 4294967296) { 2140 return [254].concat(coinjs.numToBytes(num, 4)); 2141 } else { 2142 return [255].concat(coinjs.numToBytes(num, 8)); 2143 } 2144 } 2145 2146 coinjs.bytesToNum = function (bytes) { 2147 if (bytes.length == 0) return 0; 2148 else return bytes[0] + 256 * coinjs.bytesToNum(bytes.slice(1)); 2149 } 2150 2151 coinjs.uint = function (f, size) { 2152 if (f.length < size) 2153 throw new Error("not enough data"); 2154 var n = 0; 2155 for (var i = 0; i < size; i++) { 2156 n *= 256; 2157 n += f[i]; 2158 } 2159 return n; 2160 } 2161 2162 coinjs.isArray = function (o) { 2163 return Object.prototype.toString.call(o) === '[object Array]'; 2164 } 2165 2166 coinjs.countObject = function (obj) { 2167 var count = 0; 2168 var i; 2169 for (i in obj) { 2170 if (obj.hasOwnProperty(i)) { 2171 count++; 2172 } 2173 } 2174 return count; 2175 } 2176 2177 coinjs.random = function (length) { 2178 var r = ""; 2179 var l = length || 25; 2180 var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; 2181 for (x = 0; x < l; x++) { 2182 r += chars.charAt(Math.floor(Math.random() * 62)); 2183 } 2184 return r; 2185 } 2186 2187})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.