PageSourceSearch

https://bitcoin-wallet.org/ru/js/coin.js

js bitcoin-wallet.org collected 2026-09-27 01:10:45 UTC 70,991 bytes, 2,187 lines download raw bytes

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.