mirror of
https://github.com/ok2/coinbin.git
synced 2026-05-09 18:15:23 +02:00
Compare commits
148 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44ba1b3d30 | |||
| 5583675390 | |||
| 56cb104ea4 | |||
| c6f70d000e | |||
| 4836b20fae | |||
| f364ae4b4a | |||
| 64cb387247 | |||
| b2a86006cf | |||
| b5ebaafed8 | |||
| d4f47fce28 | |||
| 5bd3786f94 | |||
| b77f3cf06a | |||
| b0c6c3a516 | |||
| a6f5dcf7f0 | |||
| cf2a8b01cb | |||
| 261c4ace40 | |||
| e18b634c4e | |||
| 1f3b878d63 | |||
| efc16c10fb | |||
| 477ca8890d | |||
| e73e8093b3 | |||
| 40ea3230fd | |||
| dc416c6521 | |||
| fc66bc9423 | |||
| 1c10bef510 | |||
| b22e79391f | |||
| 0be8fd9a97 | |||
| 1a364ff770 | |||
| bc0c72117e | |||
| 13c50cf4b6 | |||
| dea1d1b274 | |||
| e6c70d133b | |||
| 10aa011c6e | |||
| c7a0fda7e5 | |||
| c9cfeefc86 | |||
| cdf4aa105b | |||
| 2fc574dde0 | |||
| f088ec2e52 | |||
| 9ccd73808d | |||
| cb81362330 | |||
| fcbf25eaec | |||
| 2aca9ab28d | |||
| 5c581316fe | |||
| ebfb940dd5 | |||
| 3201550d40 | |||
| d88ec3db4b | |||
| 530453a701 | |||
| 194df37d56 | |||
| 87461b58b8 | |||
| 739aee3c0f | |||
| fd81fafdff | |||
| 9bede2aef4 | |||
| 37f4395d9f | |||
| 5a65a77bd2 | |||
| 734e6b2eeb | |||
| 90a309d4b4 | |||
| abd2191c50 | |||
| bb8abb4ded | |||
| 40d514e0f0 | |||
| c3b1a47199 | |||
| ed7d5d6411 | |||
| 10e083bd89 | |||
| 56fa7b5139 | |||
| e85276dd89 | |||
| c843685662 | |||
| 64af126bc5 | |||
| b997751791 | |||
| 9bccc5c512 | |||
| 217897285e | |||
| b1603821da | |||
| c2ef949dd0 | |||
| ecb18acb0f | |||
| 4a4f302fe6 | |||
| 4afee020b0 | |||
| 2e0422656d | |||
| 6c480a1102 | |||
| d5fe5ea828 | |||
| 52b206c7d6 | |||
| 1f9d9e97c2 | |||
| d12b9d9072 | |||
| 6c22adaa5a | |||
| 605a6df343 | |||
| 60efbb5174 | |||
| 1b111b335d | |||
| 24028a001d | |||
| f4c2b3d425 | |||
| 20547ff133 | |||
| 858774b640 | |||
| 21ecedaa48 | |||
| 3c1e957519 | |||
| 44e6f5c639 | |||
| d176899774 | |||
| 970cd756ec | |||
| b6dfa6b822 | |||
| 85c9267379 | |||
| 0c3530590f | |||
| 74a0fad4d2 | |||
| 67f9c1f926 | |||
| 67bd6e927c | |||
| 284c6361e7 | |||
| 347ccf4947 | |||
| 8ac9f4207d | |||
| 3ba79f0e3d | |||
| cad1f6e98b | |||
| e818fc738d | |||
| 0a94fb64ef | |||
| e3fff74690 | |||
| 0154514309 | |||
| 1e8773c195 | |||
| 1ea47f6ff5 | |||
| b943cbd6de | |||
| 53c48af90c | |||
| 6970a07d60 | |||
| 695947b02d | |||
| 0a8bb6f33a | |||
| 27c9bff17e | |||
| c276998687 | |||
| 4f16149a26 | |||
| 7bfacd51b1 | |||
| 1da38e46f7 | |||
| 133a5d2858 | |||
| a9cfa44c44 | |||
| cedc6dddaf | |||
| d53c236ec3 | |||
| c8577e03be | |||
| 9a669885b3 | |||
| 8652fe789d | |||
| 6b39b86447 | |||
| 902370679f | |||
| ab395a82f2 | |||
| fe141b0b8a | |||
| e33b2dc58e | |||
| a62a822b71 | |||
| 91ee89282f | |||
| 42391aba8e | |||
| 8e0742d64c | |||
| a1c3c60244 | |||
| 004de1f318 | |||
| 6f8cd22d5f | |||
| cacc30cc95 | |||
| d2f1d3a5d5 | |||
| 1c3e81bfd2 | |||
| cf71efcf59 | |||
| 92de202ed0 | |||
| 6e821cf53a | |||
| 85ec366c6e | |||
| 717f5cc7ef | |||
| da2d20f477 |
@@ -1,7 +1,7 @@
|
||||
coinbin
|
||||
=======
|
||||
|
||||
A Open Source Browser Based Bitcoin Wallet. Version 1.2 beta by OutCast3k
|
||||
A Open Source Browser Based Bitcoin Wallet. Version 1.5 beta by OutCast3k
|
||||
|
||||
Live version available at http://coinb.in/ or http://4zpinp6gdkjfplhk.onion
|
||||
|
||||
@@ -26,5 +26,10 @@ Coinb.in supports a number of key features such as:
|
||||
- An offical .onion address for tor users.
|
||||
- Offline qrcode creator and scanning tool
|
||||
- HD (bip32) support
|
||||
- Supports altcoins such as litecoin
|
||||
- Replace by fee (RBF) Support
|
||||
- Segwit Support
|
||||
- Bech32 address support
|
||||
- Fee calculator - https://coinb.in/#fees
|
||||
|
||||
Donate to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg to see more development!
|
||||
Donate to 3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN to see more development!
|
||||
|
||||
Vendored
+5
File diff suppressed because one or more lines are too long
@@ -24,3 +24,97 @@ body {
|
||||
background-color: #f5f5f5;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
overflow: hidden;
|
||||
-ms-text-overflow: ellipsis;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#fees .txi_regular {
|
||||
background: #d3d3d3;
|
||||
}
|
||||
|
||||
#fees .txi_segwit {
|
||||
background: #bae1ff;
|
||||
}
|
||||
|
||||
#fees .txi_multisig {
|
||||
background: #baffc9;
|
||||
}
|
||||
|
||||
#fees .txi_hodl {
|
||||
background: #ffdfba;
|
||||
}
|
||||
|
||||
#fees .txi_unknown {
|
||||
background: #ffb3ba;
|
||||
}
|
||||
|
||||
#fees .txo_p2pkh {
|
||||
background: #E679C8;
|
||||
}
|
||||
|
||||
#fees .txo_p2sh {
|
||||
background: #FAFE92;
|
||||
}
|
||||
|
||||
#fees .txinputs {
|
||||
}
|
||||
|
||||
#fees .txoutputs {
|
||||
}
|
||||
|
||||
.hideOverflow {
|
||||
overflow:hidden;
|
||||
white-space:nowrap;
|
||||
text-overflow:ellipsis;
|
||||
}
|
||||
|
||||
#fees .slider {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
outline: none;
|
||||
opacity: 0.7;
|
||||
-webkit-transition: .2s;
|
||||
transition: opacity .2s;
|
||||
border: 2px dotted #c3c3c3;
|
||||
}
|
||||
|
||||
#fees .sliderbtn {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
#fees .slider:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#fees .slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
background: url('https://coinb.in/images/btc32x.png');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#fees .slider::-moz-range-thumb {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 0;
|
||||
background: url('https://coinb.in/images/btc32x.png');
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#fees .total {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
+686
-33
File diff suppressed because it is too large
Load Diff
Vendored
+9
File diff suppressed because one or more lines are too long
+643
-51
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
Coinjs 0.01 beta by OutCast3k{at}gmail.com
|
||||
A bitcoin frameworkcoinjs.
|
||||
A bitcoin framework.
|
||||
|
||||
http://github.com/OutCast3k/coinjs or http://coinb.in/coinjs
|
||||
*/
|
||||
@@ -14,14 +14,16 @@
|
||||
coinjs.priv = 0x80;
|
||||
coinjs.multisig = 0x05;
|
||||
coinjs.hdkey = {'prv':0x0488ade4, 'pub':0x0488b21e};
|
||||
coinjs.bech32 = {'charset':'qpzry9x8gf2tvdw0s3jn54khce6mua7l', 'version':0, 'hrp':'bc'};
|
||||
|
||||
coinjs.compressed = false;
|
||||
|
||||
/* other vars */
|
||||
coinjs.developer = '1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg'; // bitcoin
|
||||
coinjs.developer = '3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN'; //bitcoin
|
||||
|
||||
/* bit(coinb.in) api vars */
|
||||
coinjs.host = ('https:'==document.location.protocol?'https://':'http://')+'coinb.in/api/';
|
||||
coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length-1]) == 'onion') ? '4zpinp6gdkjfplhk.onion' : 'coinb.in';
|
||||
coinjs.host = ('https:'==document.location.protocol?'https://':'http://')+coinjs.hostname+'/api/';
|
||||
coinjs.uid = '1';
|
||||
coinjs.key = '12345678901234567890123456789012';
|
||||
|
||||
@@ -98,9 +100,9 @@
|
||||
}
|
||||
|
||||
/* provide a public key and return address */
|
||||
coinjs.pubkey2address = function(h){
|
||||
coinjs.pubkey2address = function(h, byte){
|
||||
var r = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(h), {asBytes: true}));
|
||||
r.unshift(coinjs.pub);
|
||||
r.unshift(byte || coinjs.pub);
|
||||
var hash = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
|
||||
var checksum = hash.slice(0, 4);
|
||||
return coinjs.base58encode(r.concat(checksum));
|
||||
@@ -132,9 +134,80 @@
|
||||
var checksum = r.slice(0,4);
|
||||
var redeemScript = Crypto.util.bytesToHex(s.buffer);
|
||||
var address = coinjs.base58encode(x.concat(checksum));
|
||||
|
||||
if(s.buffer.length > 520){ // too large
|
||||
address = 'invalid';
|
||||
redeemScript = 'invalid';
|
||||
}
|
||||
|
||||
return {'address':address, 'redeemScript':redeemScript, 'size': s.buffer.length};
|
||||
}
|
||||
|
||||
/* new time locked address, provide the pubkey and time necessary to unlock the funds.
|
||||
when time is greater than 500000000, it should be a unix timestamp (seconds since epoch),
|
||||
otherwise it should be the block height required before this transaction can be released.
|
||||
|
||||
may throw a string on failure!
|
||||
*/
|
||||
coinjs.simpleHodlAddress = function(pubkey, checklocktimeverify) {
|
||||
|
||||
if(checklocktimeverify < 0) {
|
||||
throw "Parameter for OP_CHECKLOCKTIMEVERIFY is negative.";
|
||||
}
|
||||
|
||||
var s = coinjs.script();
|
||||
if (checklocktimeverify <= 16 && checklocktimeverify >= 1) {
|
||||
s.writeOp(0x50 + checklocktimeverify);//OP_1 to OP_16 for minimal encoding
|
||||
} else {
|
||||
s.writeBytes(coinjs.numToScriptNumBytes(checklocktimeverify));
|
||||
}
|
||||
s.writeOp(177);//OP_CHECKLOCKTIMEVERIFY
|
||||
s.writeOp(117);//OP_DROP
|
||||
s.writeBytes(Crypto.util.hexToBytes(pubkey));
|
||||
s.writeOp(172);//OP_CHECKSIG
|
||||
|
||||
var x = ripemd160(Crypto.SHA256(s.buffer, {asBytes: true}), {asBytes: true});
|
||||
x.unshift(coinjs.multisig);
|
||||
var r = x;
|
||||
r = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
|
||||
var checksum = r.slice(0,4);
|
||||
var redeemScript = Crypto.util.bytesToHex(s.buffer);
|
||||
var address = coinjs.base58encode(x.concat(checksum));
|
||||
|
||||
return {'address':address, 'redeemScript':redeemScript};
|
||||
}
|
||||
|
||||
/* create a new segwit address */
|
||||
coinjs.segwitAddress = function(pubkey){
|
||||
var keyhash = [0x00,0x14].concat(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), {asBytes: true}), {asBytes: true}));
|
||||
var x = ripemd160(Crypto.SHA256(keyhash, {asBytes: true}), {asBytes: true});
|
||||
x.unshift(coinjs.multisig);
|
||||
var r = x;
|
||||
r = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
|
||||
var checksum = r.slice(0,4);
|
||||
var address = coinjs.base58encode(x.concat(checksum));
|
||||
|
||||
return {'address':address, 'type':'segwit', 'redeemscript':Crypto.util.bytesToHex(keyhash)};
|
||||
}
|
||||
|
||||
/* create a new segwit bech32 encoded address */
|
||||
coinjs.bech32Address = function(pubkey){
|
||||
var program = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), {asBytes: true}), {asBytes: true});
|
||||
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
|
||||
return {'address':address, 'type':'bech32', 'redeemscript':Crypto.util.bytesToHex(program)};
|
||||
}
|
||||
|
||||
/* extract the redeemscript from a bech32 address */
|
||||
coinjs.bech32redeemscript = function(address){
|
||||
var r = false;
|
||||
var decode = coinjs.bech32_decode(address);
|
||||
if(decode){
|
||||
decode.data.shift();
|
||||
return Crypto.util.bytesToHex(coinjs.bech32_convert(decode.data, 5, 8, true));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* provide a privkey and return an WIF */
|
||||
coinjs.privkey2wif = function(h){
|
||||
var r = Crypto.util.hexToBytes(h);
|
||||
@@ -237,9 +310,14 @@
|
||||
return false;
|
||||
}
|
||||
} catch(e) {
|
||||
bech32rs = coinjs.bech32redeemscript(addr);
|
||||
if(bech32rs){
|
||||
return {'type':'bech32', 'redeemscript':bech32rs};
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* retreive the balance from a given address */
|
||||
coinjs.addressBalance = function(address, callback){
|
||||
@@ -248,6 +326,7 @@
|
||||
|
||||
/* decompress an compressed public key */
|
||||
coinjs.pubkeydecompress = function(pubkey) {
|
||||
if((typeof(pubkey) == 'string') && pubkey.match(/^[a-f0-9]+$/i)){
|
||||
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
try {
|
||||
var pt = curve.curve.decodePointHex(pubkey);
|
||||
@@ -263,6 +342,128 @@
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
coinjs.bech32_polymod = function(values) {
|
||||
var chk = 1;
|
||||
var BECH32_GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
|
||||
for (var p = 0; p < values.length; ++p) {
|
||||
var top = chk >> 25;
|
||||
chk = (chk & 0x1ffffff) << 5 ^ values[p];
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
if ((top >> i) & 1) {
|
||||
chk ^= BECH32_GENERATOR[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return chk;
|
||||
}
|
||||
|
||||
coinjs.bech32_hrpExpand = function(hrp) {
|
||||
var ret = [];
|
||||
var p;
|
||||
for (p = 0; p < hrp.length; ++p) {
|
||||
ret.push(hrp.charCodeAt(p) >> 5);
|
||||
}
|
||||
ret.push(0);
|
||||
for (p = 0; p < hrp.length; ++p) {
|
||||
ret.push(hrp.charCodeAt(p) & 31);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
coinjs. bech32_verifyChecksum = function(hrp, data) {
|
||||
return coinjs.bech32_polymod(coinjs.bech32_hrpExpand(hrp).concat(data)) === 1;
|
||||
}
|
||||
|
||||
coinjs.bech32_createChecksum = function(hrp, data) {
|
||||
var values = coinjs.bech32_hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
||||
var mod = coinjs.bech32_polymod(values) ^ 1;
|
||||
var ret = [];
|
||||
for (var p = 0; p < 6; ++p) {
|
||||
ret.push((mod >> 5 * (5 - p)) & 31);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
coinjs.bech32_encode = function(hrp, data) {
|
||||
var combined = data.concat(coinjs.bech32_createChecksum(hrp, data));
|
||||
var ret = hrp + '1';
|
||||
for (var p = 0; p < combined.length; ++p) {
|
||||
ret += coinjs.bech32.charset.charAt(combined[p]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
coinjs.bech32_decode = function(bechString) {
|
||||
var p;
|
||||
var has_lower = false;
|
||||
var has_upper = false;
|
||||
for (p = 0; p < bechString.length; ++p) {
|
||||
if (bechString.charCodeAt(p) < 33 || bechString.charCodeAt(p) > 126) {
|
||||
return null;
|
||||
}
|
||||
if (bechString.charCodeAt(p) >= 97 && bechString.charCodeAt(p) <= 122) {
|
||||
has_lower = true;
|
||||
}
|
||||
if (bechString.charCodeAt(p) >= 65 && bechString.charCodeAt(p) <= 90) {
|
||||
has_upper = true;
|
||||
}
|
||||
}
|
||||
if (has_lower && has_upper) {
|
||||
return null;
|
||||
}
|
||||
bechString = bechString.toLowerCase();
|
||||
var pos = bechString.lastIndexOf('1');
|
||||
if (pos < 1 || pos + 7 > bechString.length || bechString.length > 90) {
|
||||
return null;
|
||||
}
|
||||
var hrp = bechString.substring(0, pos);
|
||||
var data = [];
|
||||
for (p = pos + 1; p < bechString.length; ++p) {
|
||||
var d = coinjs.bech32.charset.indexOf(bechString.charAt(p));
|
||||
if (d === -1) {
|
||||
return null;
|
||||
}
|
||||
data.push(d);
|
||||
}
|
||||
if (!coinjs.bech32_verifyChecksum(hrp, data)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
hrp: hrp,
|
||||
data: data.slice(0, data.length - 6)
|
||||
};
|
||||
}
|
||||
|
||||
coinjs.bech32_convert = function(data, inBits, outBits, pad) {
|
||||
var value = 0;
|
||||
var bits = 0;
|
||||
var maxV = (1 << outBits) - 1;
|
||||
|
||||
var result = [];
|
||||
for (var i = 0; i < data.length; ++i) {
|
||||
value = (value << inBits) | data[i];
|
||||
bits += inBits;
|
||||
|
||||
while (bits >= outBits) {
|
||||
bits -= outBits;
|
||||
result.push((value >> bits) & maxV);
|
||||
}
|
||||
}
|
||||
|
||||
if (pad) {
|
||||
if (bits > 0) {
|
||||
result.push((value << (outBits - bits)) & maxV);
|
||||
}
|
||||
} else {
|
||||
if (bits >= inBits) throw new Error('Excess padding');
|
||||
if ((value << (outBits - bits)) & maxV) throw new Error('Non-zero padding');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
coinjs.testdeterministicK = function() {
|
||||
// https://github.com/bitpay/bitcore/blob/9a5193d8e94b0bd5b8e7f00038e7c0b935405a03/test/crypto/ecdsa.js
|
||||
@@ -437,6 +638,8 @@
|
||||
|
||||
coinjs.compressed = c; // reset to default
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
// extend prv/pub key
|
||||
@@ -450,8 +653,37 @@
|
||||
'pubkey':this.keys.pubkey});
|
||||
}
|
||||
|
||||
// derive from path
|
||||
r.derive_path = function(path) {
|
||||
|
||||
if( path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'' ) return this;
|
||||
|
||||
var p = path.split('/');
|
||||
var hdp = coinjs.clone(this); // clone hd path
|
||||
|
||||
for( var i in p ) {
|
||||
|
||||
if((( i == 0 ) && c != 'm') || i == 'remove'){
|
||||
continue;
|
||||
}
|
||||
|
||||
var c = p[i];
|
||||
|
||||
var use_private = (c.length > 1) && (c[c.length-1] == '\'');
|
||||
var child_index = parseInt(use_private ? c.slice(0, c.length - 1) : c) & 0x7fffffff;
|
||||
if(use_private)
|
||||
child_index += 0x80000000;
|
||||
|
||||
hdp = hdp.derive(child_index);
|
||||
var key = ((hdp.keys_extended.privkey) && hdp.keys_extended.privkey!='') ? hdp.keys_extended.privkey : hdp.keys_extended.pubkey;
|
||||
hdp = coinjs.hd(key);
|
||||
}
|
||||
return hdp;
|
||||
}
|
||||
|
||||
// derive key from index
|
||||
r.derive = function(i){
|
||||
|
||||
i = (i)?i:0;
|
||||
var blob = (Crypto.util.hexToBytes(this.keys.pubkey)).concat(coinjs.numToBytes(i,4).reverse());
|
||||
|
||||
@@ -506,7 +738,6 @@
|
||||
|
||||
o.parent_fingerprint = (ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(r.keys.pubkey),{asBytes:true}),{asBytes:true})).slice(0,4);
|
||||
o.keys_extended = o.extend();
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
@@ -571,8 +802,7 @@
|
||||
return o;
|
||||
}
|
||||
|
||||
r.parse();
|
||||
return r;
|
||||
return r.parse();
|
||||
}
|
||||
|
||||
|
||||
@@ -587,7 +817,7 @@
|
||||
} else if (coinjs.isArray(data)) {
|
||||
r.buffer = data;
|
||||
} else if (data instanceof coinjs.script) {
|
||||
r.buffer = r.buffer;
|
||||
r.buffer = data.buffer;
|
||||
} else {
|
||||
r.buffer = data;
|
||||
}
|
||||
@@ -649,6 +879,26 @@
|
||||
r.pubkeys = pubkeys;
|
||||
var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired);
|
||||
r.address = multi['address'];
|
||||
r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig"
|
||||
var rs = Crypto.util.bytesToHex(s.buffer);
|
||||
r.redeemscript = rs;
|
||||
|
||||
} else if((s.chunks.length==2) && (s.buffer[0] == 0 && s.buffer[1] == 20)){ // SEGWIT
|
||||
r = {};
|
||||
r.type = "segwit__";
|
||||
var rs = Crypto.util.bytesToHex(s.buffer);
|
||||
r.address = coinjs.pubkey2address(rs, coinjs.multisig);
|
||||
r.redeemscript = rs;
|
||||
|
||||
} else if(s.chunks.length == 5 && s.chunks[1] == 177 && s.chunks[2] == 117 && s.chunks[4] == 172){
|
||||
// ^ <unlocktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG ^
|
||||
r = {}
|
||||
r.pubkey = Crypto.util.bytesToHex(s.chunks[3]);
|
||||
r.checklocktimeverify = coinjs.bytesToNum(s.chunks[0].slice());
|
||||
r.address = coinjs.simpleHodlAddress(r.pubkey, r.checklocktimeverify).address;
|
||||
var rs = Crypto.util.bytesToHex(s.buffer);
|
||||
r.redeemscript = rs;
|
||||
r.type = "hodl__";
|
||||
}
|
||||
} catch(e) {
|
||||
// console.log(e);
|
||||
@@ -661,7 +911,10 @@
|
||||
r.spendToScript = function(address){
|
||||
var addr = coinjs.addressDecode(address);
|
||||
var s = coinjs.script();
|
||||
if(addr.version==5){ // multisig address
|
||||
if(addr.type == "bech32"){
|
||||
s.writeOp(0);
|
||||
s.writeBytes(Crypto.util.hexToBytes(addr.redeemscript));
|
||||
} else if(addr.version==coinjs.multisig){ // multisig address
|
||||
s.writeOp(169); //OP_HASH160
|
||||
s.writeBytes(addr.bytes);
|
||||
s.writeOp(135); //OP_EQUAL
|
||||
@@ -731,15 +984,16 @@
|
||||
r.lock_time = 0;
|
||||
r.ins = [];
|
||||
r.outs = [];
|
||||
r.witness = false;
|
||||
r.timestamp = null;
|
||||
r.block = null;
|
||||
|
||||
/* add an input to a transaction */
|
||||
r.addinput = function(txid, index, script){
|
||||
r.addinput = function(txid, index, script, sequence){
|
||||
var o = {};
|
||||
o.outpoint = {'hash':txid, 'index':index};
|
||||
o.script = coinjs.script(script||[]);
|
||||
o.sequence = (r.lock_time==0) ? 4294967295 : 0;
|
||||
o.sequence = sequence || ((r.lock_time==0) ? 4294967295 : 0);
|
||||
return this.ins.push(o);
|
||||
}
|
||||
|
||||
@@ -796,7 +1050,7 @@
|
||||
/* add data to a transaction */
|
||||
r.adddata = function(data){
|
||||
var r = false;
|
||||
if(((data.match(/^[a-f0-9]+$/gi)) && data.length<80) && (data.length%2)==0) {
|
||||
if(((data.match(/^[a-f0-9]+$/gi)) && data.length<160) && (data.length%2)==0) {
|
||||
var s = coinjs.script();
|
||||
s.writeOp(106); // OP_RETURN
|
||||
s.writeBytes(Crypto.util.hexToBytes(data));
|
||||
@@ -814,11 +1068,10 @@
|
||||
}
|
||||
|
||||
/* add unspent to transaction */
|
||||
r.addUnspent = function(address, callback){
|
||||
r.addUnspent = function(address, callback, script, segwit, sequence){
|
||||
var self = this;
|
||||
this.listUnspent(address, function(data){
|
||||
var s = coinjs.script();
|
||||
var pubkeyScript = s.pubkeyHash(address);
|
||||
var value = 0;
|
||||
var total = 0;
|
||||
var x = {};
|
||||
@@ -838,10 +1091,21 @@
|
||||
var u = xmlDoc.getElementsByTagName("unspent_"+i)[0]
|
||||
var txhash = (u.getElementsByTagName("tx_hash")[0].childNodes[0].nodeValue).match(/.{1,2}/g).reverse().join("")+'';
|
||||
var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue;
|
||||
var script = u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
|
||||
var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
|
||||
|
||||
self.addinput(txhash, n, script);
|
||||
if(segwit){
|
||||
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
|
||||
It is not standard and removed during the signing procedure. */
|
||||
|
||||
s = coinjs.script();
|
||||
s.writeBytes(Crypto.util.hexToBytes(script));
|
||||
s.writeOp(0);
|
||||
s.writeBytes(coinjs.numToBytes(u.getElementsByTagName("value")[0].childNodes[0].nodeValue*1, 8));
|
||||
scr = Crypto.util.bytesToHex(s.buffer);
|
||||
}
|
||||
|
||||
var seq = sequence || false;
|
||||
self.addinput(txhash, n, scr, seq);
|
||||
value += u.getElementsByTagName("value")[0].childNodes[0].nodeValue*1;
|
||||
total++;
|
||||
}
|
||||
@@ -870,9 +1134,12 @@
|
||||
}
|
||||
|
||||
/* generate the transaction hash to sign from a transaction input */
|
||||
r.transactionHash = function(index) {
|
||||
r.transactionHash = function(index, sigHashType) {
|
||||
|
||||
var clone = coinjs.clone(this);
|
||||
if((clone.ins) && clone.ins[index]){
|
||||
var shType = sigHashType || 1;
|
||||
|
||||
/* black out all other ins, except this one */
|
||||
for (var i = 0; i < clone.ins.length; i++) {
|
||||
if(index!=i){
|
||||
clone.ins[i].script = coinjs.script();
|
||||
@@ -882,8 +1149,62 @@
|
||||
var extract = this.extractScriptKey(index);
|
||||
clone.ins[index].script = coinjs.script(extract['script']);
|
||||
|
||||
if((clone.ins) && clone.ins[index]){
|
||||
|
||||
/* SIGHASH : For more info on sig hashs see https://en.bitcoin.it/wiki/OP_CHECKSIG
|
||||
and https://bitcoin.org/en/developer-guide#signature-hash-type */
|
||||
|
||||
if(shType == 1){
|
||||
//SIGHASH_ALL 0x01
|
||||
|
||||
} else if(shType == 2){
|
||||
//SIGHASH_NONE 0x02
|
||||
clone.outs = [];
|
||||
for (var i = 0; i < clone.ins.length; i++) {
|
||||
if(index!=i){
|
||||
clone.ins[i].sequence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if(shType == 3){
|
||||
|
||||
//SIGHASH_SINGLE 0x03
|
||||
clone.outs.length = index + 1;
|
||||
|
||||
for(var i = 0; i < index; i++){
|
||||
clone.outs[i].value = -1;
|
||||
clone.outs[i].script.buffer = [];
|
||||
}
|
||||
|
||||
for (var i = 0; i < clone.ins.length; i++) {
|
||||
if(index!=i){
|
||||
clone.ins[i].sequence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (shType >= 128){
|
||||
//SIGHASH_ANYONECANPAY 0x80
|
||||
clone.ins = [clone.ins[index]];
|
||||
|
||||
if(shType==129){
|
||||
// SIGHASH_ALL + SIGHASH_ANYONECANPAY
|
||||
|
||||
} else if(shType==130){
|
||||
// SIGHASH_NONE + SIGHASH_ANYONECANPAY
|
||||
clone.outs = [];
|
||||
|
||||
} else if(shType==131){
|
||||
// SIGHASH_SINGLE + SIGHASH_ANYONECANPAY
|
||||
clone.outs.length = index + 1;
|
||||
for(var i = 0; i < index; i++){
|
||||
clone.outs[i].value = -1;
|
||||
clone.outs[i].script.buffer = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = Crypto.util.hexToBytes(clone.serialize());
|
||||
buffer = buffer.concat(coinjs.numToBytes(parseInt(1),4));
|
||||
buffer = buffer.concat(coinjs.numToBytes(parseInt(shType), 4));
|
||||
var hash = Crypto.SHA256(buffer, {asBytes: true});
|
||||
var r = Crypto.util.bytesToHex(Crypto.SHA256(hash, {asBytes: true}));
|
||||
return r;
|
||||
@@ -892,15 +1213,127 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a segwit transaction hash to sign from a transaction input */
|
||||
r.transactionHashSegWitV0 = function(index, sigHashType){
|
||||
/*
|
||||
Notice: coinb.in by default, deals with segwit transactions in a non-standard way.
|
||||
Segwit transactions require that input values are included in the transaction hash.
|
||||
To save wasting resources and potentially slowing down this service, we include the amount with the
|
||||
redeem script to generate the transaction hash and remove it after its signed.
|
||||
*/
|
||||
|
||||
// start redeem script check
|
||||
var extract = this.extractScriptKey(index);
|
||||
if(extract['type'] != 'segwit'){
|
||||
return {'result':0, 'fail':'redeemscript', 'response':'redeemscript missing or not valid for segwit'};
|
||||
}
|
||||
|
||||
if(extract['value'] == -1){
|
||||
return {'result':0, 'fail':'value', 'response':'unable to generate a valid segwit hash without a value'};
|
||||
}
|
||||
|
||||
var scriptcode = Crypto.util.hexToBytes(extract['script']);
|
||||
|
||||
// end of redeem script check
|
||||
|
||||
/* P2WPKH */
|
||||
if(scriptcode.length == 20){
|
||||
scriptcode = [0x00,0x14].concat(scriptcode);
|
||||
}
|
||||
|
||||
if(scriptcode.length == 22){
|
||||
scriptcode = scriptcode.slice(1);
|
||||
scriptcode.unshift(25, 118, 169);
|
||||
scriptcode.push(136, 172);
|
||||
}
|
||||
|
||||
var value = coinjs.numToBytes(extract['value'], 8);
|
||||
|
||||
// start
|
||||
|
||||
var zero = coinjs.numToBytes(0, 32);
|
||||
var version = coinjs.numToBytes(parseInt(this.version), 4);
|
||||
|
||||
var bufferTmp = [];
|
||||
if(!(sigHashType >= 80)){ // not sighash anyonecanpay
|
||||
for(var i = 0; i < this.ins.length; i++){
|
||||
bufferTmp = bufferTmp.concat(Crypto.util.hexToBytes(this.ins[i].outpoint.hash).reverse());
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].outpoint.index, 4));
|
||||
}
|
||||
}
|
||||
var hashPrevouts = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;
|
||||
|
||||
var bufferTmp = [];
|
||||
if(!(sigHashType >= 80) && sigHashType != 2 && sigHashType != 3){ // not sighash anyonecanpay & single & none
|
||||
for(var i = 0; i < this.ins.length; i++){
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.ins[i].sequence, 4));
|
||||
}
|
||||
}
|
||||
var hashSequence = bufferTmp.length >= 1 ? Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true}) : zero;
|
||||
|
||||
var outpoint = Crypto.util.hexToBytes(this.ins[index].outpoint.hash).reverse();
|
||||
outpoint = outpoint.concat(coinjs.numToBytes(this.ins[index].outpoint.index, 4));
|
||||
|
||||
var nsequence = coinjs.numToBytes(this.ins[index].sequence, 4);
|
||||
var hashOutputs = zero;
|
||||
var bufferTmp = [];
|
||||
if(sigHashType != 2 && sigHashType != 3){ // not sighash single & none
|
||||
for(var i = 0; i < this.outs.length; i++ ){
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.outs[i].value, 8));
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToVarInt(this.outs[i].script.buffer.length));
|
||||
bufferTmp = bufferTmp.concat(this.outs[i].script.buffer);
|
||||
}
|
||||
hashOutputs = Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true});
|
||||
|
||||
} else if ((sigHashType == 2) && index < this.outs.length){ // is sighash single
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToBytes(this.outs[index].value, 8));
|
||||
bufferTmp = bufferTmp.concat(coinjs.numToVarInt(this.outs[i].script.buffer.length));
|
||||
bufferTmp = bufferTmp.concat(this.outs[index].script.buffer);
|
||||
hashOutputs = Crypto.SHA256(Crypto.SHA256(bufferTmp, {asBytes: true}), {asBytes: true});
|
||||
}
|
||||
|
||||
var locktime = coinjs.numToBytes(this.lock_time, 4);
|
||||
var sighash = coinjs.numToBytes(sigHashType, 4);
|
||||
|
||||
var buffer = [];
|
||||
buffer = buffer.concat(version);
|
||||
buffer = buffer.concat(hashPrevouts);
|
||||
buffer = buffer.concat(hashSequence);
|
||||
buffer = buffer.concat(outpoint);
|
||||
buffer = buffer.concat(scriptcode);
|
||||
buffer = buffer.concat(value);
|
||||
buffer = buffer.concat(nsequence);
|
||||
buffer = buffer.concat(hashOutputs);
|
||||
buffer = buffer.concat(locktime);
|
||||
buffer = buffer.concat(sighash);
|
||||
|
||||
var hash = Crypto.SHA256(buffer, {asBytes: true});
|
||||
return {'result':1,'hash':Crypto.util.bytesToHex(Crypto.SHA256(hash, {asBytes: true})), 'response':'hash generated'};
|
||||
}
|
||||
|
||||
/* extract the scriptSig, used in the transactionHash() function */
|
||||
r.extractScriptKey = function(index) {
|
||||
if(this.ins[index]){
|
||||
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
|
||||
// regular scriptPubkey (not signed)
|
||||
return {'type':'scriptpubkey', 'signed':'false', 'signatures':0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
|
||||
} 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
|
||||
// hodl script (signed)
|
||||
return {'type':'hodl', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
|
||||
} else if((this.ins[index].script.chunks.length==2) && this.ins[index].script.chunks[0][0]==48){
|
||||
// regular scriptPubkey (probably signed)
|
||||
return {'type':'scriptpubkey', 'signed':'true', 'signatures':1, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
|
||||
} else if(this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177){//OP_CHECKLOCKTIMEVERIFY
|
||||
// hodl script (not signed)
|
||||
return {'type':'hodl', 'signed':'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
|
||||
} 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))){
|
||||
var signed = ((this.witness[index]) && this.witness[index].length==2) ? 'true' : 'false';
|
||||
var sigs = (signed == 'true') ? 1 : 0;
|
||||
var value = -1; // no value found
|
||||
if((this.ins[index].script.chunks[2]) && this.ins[index].script.chunks[2].length==8){
|
||||
value = coinjs.bytesToNum(this.ins[index].script.chunks[2]); // value found encoded in transaction (THIS IS NON STANDARD)
|
||||
}
|
||||
return {'type':'segwit', 'signed':signed, 'signatures': sigs, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[0]), 'value': value};
|
||||
} else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174) { // OP_CHECKMULTISIG
|
||||
// multisig script, with signature(s) included
|
||||
return {'type':'multisig', 'signed':'true', 'signatures':this.ins[index].script.chunks.length-2, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1])};
|
||||
@@ -920,7 +1353,7 @@
|
||||
}
|
||||
|
||||
/* generate a signature from a transaction hash */
|
||||
r.transactionSig = function(index, wif){
|
||||
r.transactionSig = function(index, wif, sigHashType, txhash){
|
||||
|
||||
function serializeSig(r, s) {
|
||||
var rBa = r.toByteArraySigned();
|
||||
@@ -941,7 +1374,8 @@
|
||||
return sequence;
|
||||
}
|
||||
|
||||
var hash = Crypto.util.hexToBytes(this.transactionHash(index));
|
||||
var shType = sigHashType || 1;
|
||||
var hash = txhash || Crypto.util.hexToBytes(this.transactionHash(index, shType));
|
||||
|
||||
if(hash){
|
||||
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
||||
@@ -966,7 +1400,7 @@
|
||||
};
|
||||
|
||||
var sig = serializeSig(r, s);
|
||||
sig.push(parseInt(1, 10));
|
||||
sig.push(parseInt(shType, 10));
|
||||
|
||||
return Crypto.util.bytesToHex(sig);
|
||||
} else {
|
||||
@@ -995,12 +1429,10 @@
|
||||
// hash is a byteArray of the message digest. so h1 == hash in our case
|
||||
|
||||
// Step: b
|
||||
var v = new Uint8Array(32);
|
||||
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];
|
||||
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];
|
||||
|
||||
// Step: c
|
||||
var k = new Uint8Array(32);
|
||||
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];
|
||||
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];
|
||||
|
||||
// Step: d
|
||||
k = Crypto.HMAC(Crypto.SHA256, v.concat([0]).concat(x).concat(hash), k, { asBytes: true });
|
||||
@@ -1039,9 +1471,10 @@
|
||||
};
|
||||
|
||||
/* sign a "standard" input */
|
||||
r.signinput = function(index, wif){
|
||||
r.signinput = function(index, wif, sigHashType){
|
||||
var key = coinjs.wif2pubkey(wif);
|
||||
var signature = this.transactionSig(index, wif);
|
||||
var shType = sigHashType || 1;
|
||||
var signature = this.transactionSig(index, wif, shType);
|
||||
var s = coinjs.script();
|
||||
s.writeBytes(Crypto.util.hexToBytes(signature));
|
||||
s.writeBytes(Crypto.util.hexToBytes(key['pubkey']));
|
||||
@@ -1049,8 +1482,20 @@
|
||||
return true;
|
||||
}
|
||||
|
||||
/* signs a time locked / hodl input */
|
||||
r.signhodl = function(index, wif, sigHashType){
|
||||
var shType = sigHashType || 1;
|
||||
var signature = this.transactionSig(index, wif, shType);
|
||||
var redeemScript = this.ins[index].script.buffer
|
||||
var s = coinjs.script();
|
||||
s.writeBytes(Crypto.util.hexToBytes(signature));
|
||||
s.writeBytes(redeemScript);
|
||||
this.ins[index].script = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* sign a multisig input */
|
||||
r.signmultisig = function(index, wif){
|
||||
r.signmultisig = function(index, wif, sigHashType){
|
||||
|
||||
function scriptListPubkey(redeemScript){
|
||||
var r = {};
|
||||
@@ -1062,46 +1507,118 @@
|
||||
|
||||
function scriptListSigs(scriptSig){
|
||||
var r = {};
|
||||
var c = 0;
|
||||
if (scriptSig.chunks[0]==0 && scriptSig.chunks[scriptSig.chunks.length-1][scriptSig.chunks[scriptSig.chunks.length-1].length-1]==174){
|
||||
for(var i=1;i<scriptSig.chunks.length-1;i++){
|
||||
r[i] = scriptSig.chunks[i];
|
||||
if (scriptSig.chunks[i] != 0){
|
||||
c++;
|
||||
r[c] = scriptSig.chunks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
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];
|
||||
var sighash = Crypto.util.hexToBytes(this.transactionHash(index));
|
||||
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif));
|
||||
|
||||
var pubkeyList = scriptListPubkey(coinjs.script(redeemScript));
|
||||
var sigsList = scriptListSigs(this.ins[index].script);
|
||||
|
||||
var shType = sigHashType || 1;
|
||||
var sighash = Crypto.util.hexToBytes(this.transactionHash(index, shType));
|
||||
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType));
|
||||
|
||||
sigsList[coinjs.countObject(sigsList)+1] = signature;
|
||||
|
||||
var s = coinjs.script();
|
||||
|
||||
s.writeOp(0);
|
||||
|
||||
if(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1]==174){
|
||||
s.writeBytes(signature);
|
||||
|
||||
} 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){
|
||||
var pubkeyList = scriptListPubkey(coinjs.script(redeemScript));
|
||||
var sigsList = scriptListSigs(this.ins[index].script);
|
||||
sigsList[coinjs.countObject(sigsList)+1] = signature;
|
||||
|
||||
for(x in pubkeyList){
|
||||
for(y in sigsList){
|
||||
this.ins[index].script.buffer = redeemScript;
|
||||
sighash = Crypto.util.hexToBytes(this.transactionHash(index, sigsList[y].slice(-1)[0]*1));
|
||||
if(coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])){
|
||||
s.writeBytes(sigsList[y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
s.writeBytes(redeemScript);
|
||||
this.ins[index].script = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* sign segwit input */
|
||||
r.signsegwit = function(index, wif, sigHashType){
|
||||
var shType = sigHashType || 1;
|
||||
|
||||
var wif2 = coinjs.wif2pubkey(wif);
|
||||
var segwit = coinjs.segwitAddress(wif2['pubkey']);
|
||||
var bech32 = coinjs.bech32Address(wif2['pubkey']);
|
||||
|
||||
if((segwit['redeemscript'] == Crypto.util.bytesToHex(this.ins[index].script.chunks[0])) || (bech32['redeemscript'] == Crypto.util.bytesToHex(this.ins[index].script.chunks[0]))){
|
||||
var txhash = this.transactionHashSegWitV0(index, shType);
|
||||
|
||||
if(txhash.result == 1){
|
||||
|
||||
var segwitHash = Crypto.util.hexToBytes(txhash.hash);
|
||||
var signature = this.transactionSig(index, wif, shType, segwitHash);
|
||||
|
||||
// remove any non standard data we store, i.e. input value
|
||||
var script = coinjs.script();
|
||||
script.writeBytes(this.ins[index].script.chunks[0]);
|
||||
this.ins[index].script = script;
|
||||
|
||||
if(!coinjs.isArray(this.witness)){
|
||||
this.witness = [];
|
||||
}
|
||||
|
||||
this.witness.push([signature, wif2['pubkey']]);
|
||||
|
||||
/* attempt to reorder witness data as best as we can.
|
||||
data can't be easily validated at this stage as
|
||||
we dont have access to the inputs value and
|
||||
making a web call will be too slow. */
|
||||
|
||||
var witness_order = [];
|
||||
var witness_used = [];
|
||||
for(var i = 0; i < this.ins.length; i++){
|
||||
for(var y = 0; y < this.witness.length; y++){
|
||||
if(!witness_used.includes(y)){
|
||||
var sw = coinjs.segwitAddress(this.witness[y][1]);
|
||||
var b32 = coinjs.bech32Address(this.witness[y][1]);
|
||||
var rs = '';
|
||||
|
||||
if(this.ins[i].script.chunks.length>=1){
|
||||
rs = Crypto.util.bytesToHex(this.ins[i].script.chunks[0]);
|
||||
} else if (this.ins[i].script.chunks.length==0){
|
||||
rs = b32['redeemscript'];
|
||||
}
|
||||
|
||||
if((sw['redeemscript'] == rs) || (b32['redeemscript'] == rs)){
|
||||
witness_order.push(this.witness[y]);
|
||||
witness_used.push(y);
|
||||
|
||||
// bech32, empty redeemscript
|
||||
if(b32['redeemscript'] == rs){
|
||||
this.ins[index].script = coinjs.script();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.witness = witness_order;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* sign inputs */
|
||||
r.sign = function(wif){
|
||||
r.sign = function(wif, sigHashType){
|
||||
var shType = sigHashType || 1;
|
||||
for (var i = 0; i < this.ins.length; i++) {
|
||||
var d = this.extractScriptKey(i);
|
||||
|
||||
@@ -1110,9 +1627,17 @@
|
||||
var pubkeyHash = script.pubkeyHash(w2a['address']);
|
||||
|
||||
if(((d['type'] == 'scriptpubkey' && d['script']==Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false"){
|
||||
this.signinput(i, wif);
|
||||
this.signinput(i, wif, shType);
|
||||
|
||||
} else if (d['type'] == 'hodl' && d['signed'] == "false") {
|
||||
this.signhodl(i, wif, shType);
|
||||
|
||||
} else if (d['type'] == 'multisig') {
|
||||
this.signmultisig(i, wif);
|
||||
this.signmultisig(i, wif, shType);
|
||||
|
||||
} else if (d['type'] == 'segwit') {
|
||||
this.signsegwit(i, wif, shType);
|
||||
|
||||
} else {
|
||||
// could not sign
|
||||
}
|
||||
@@ -1124,8 +1649,12 @@
|
||||
r.serialize = function(){
|
||||
var buffer = [];
|
||||
buffer = buffer.concat(coinjs.numToBytes(parseInt(this.version),4));
|
||||
buffer = buffer.concat(coinjs.numToVarInt(this.ins.length));
|
||||
|
||||
if(coinjs.isArray(this.witness)){
|
||||
buffer = buffer.concat([0x00, 0x01]);
|
||||
}
|
||||
|
||||
buffer = buffer.concat(coinjs.numToVarInt(this.ins.length));
|
||||
for (var i = 0; i < this.ins.length; i++) {
|
||||
var txin = this.ins[i];
|
||||
buffer = buffer.concat(Crypto.util.hexToBytes(txin.outpoint.hash).reverse());
|
||||
@@ -1145,6 +1674,16 @@
|
||||
buffer = buffer.concat(scriptBytes);
|
||||
}
|
||||
|
||||
if((coinjs.isArray(this.witness)) && this.witness.length>=1){
|
||||
for(var i = 0; i < this.witness.length; i++){
|
||||
buffer = buffer.concat(coinjs.numToVarInt(this.witness[i].length));
|
||||
for(var x = 0; x < this.witness[i].length; x++){
|
||||
buffer = buffer.concat(coinjs.numToVarInt(Crypto.util.hexToBytes(this.witness[i][x]).length));
|
||||
buffer = buffer.concat(Crypto.util.hexToBytes(this.witness[i][x]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer = buffer.concat(coinjs.numToBytes(parseInt(this.lock_time),4));
|
||||
return Crypto.util.bytesToHex(buffer);
|
||||
}
|
||||
@@ -1156,6 +1695,8 @@
|
||||
}
|
||||
|
||||
var pos = 0;
|
||||
var witness = false;
|
||||
|
||||
var readAsInt = function(bytes) {
|
||||
if (bytes == 0) return 0;
|
||||
pos++;
|
||||
@@ -1181,8 +1722,15 @@
|
||||
}
|
||||
|
||||
var obj = new coinjs.transaction();
|
||||
|
||||
obj.version = readAsInt(4);
|
||||
|
||||
if(buffer[pos] == 0x00 && buffer[pos+1] == 0x01){
|
||||
// segwit transaction
|
||||
witness = true;
|
||||
obj.witness = [];
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
var ins = readVarInt();
|
||||
for (var i = 0; i < ins; i++) {
|
||||
obj.ins.push({
|
||||
@@ -1203,6 +1751,21 @@
|
||||
});
|
||||
}
|
||||
|
||||
if(witness == true){
|
||||
for (i = 0; i < ins; ++i) {
|
||||
var count = readVarInt();
|
||||
var vector = [];
|
||||
for(var y = 0; y < count; y++){
|
||||
var slice = readVarInt();
|
||||
pos += slice;
|
||||
if(!coinjs.isArray(obj.witness[i])){
|
||||
obj.witness[i] = [];
|
||||
}
|
||||
obj.witness[i].push(Crypto.util.bytesToHex(buffer.slice(pos - slice, pos)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
obj.lock_time = readAsInt(4);
|
||||
return obj;
|
||||
}
|
||||
@@ -1373,7 +1936,7 @@
|
||||
/* clone an object */
|
||||
coinjs.clone = function(obj) {
|
||||
if(obj == null || typeof(obj) != 'object') return obj;
|
||||
var temp = obj.constructor();
|
||||
var temp = new obj.constructor();
|
||||
|
||||
for(var key in obj) {
|
||||
if(obj.hasOwnProperty(key)) {
|
||||
@@ -1384,14 +1947,43 @@
|
||||
}
|
||||
|
||||
coinjs.numToBytes = function(num,bytes) {
|
||||
if (typeof bytes === undefined) bytes = 8;
|
||||
if (typeof bytes === "undefined") bytes = 8;
|
||||
if (bytes == 0) {
|
||||
return [];
|
||||
} else if (num == -1){
|
||||
return Crypto.util.hexToBytes("ffffffffffffffff");
|
||||
} else {
|
||||
return [num % 256].concat(coinjs.numToBytes(Math.floor(num / 256),bytes-1));
|
||||
}
|
||||
}
|
||||
|
||||
function scriptNumSize(i) {
|
||||
return i > 0x7fffffff ? 5
|
||||
: i > 0x7fffff ? 4
|
||||
: i > 0x7fff ? 3
|
||||
: i > 0x7f ? 2
|
||||
: i > 0x00 ? 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
coinjs.numToScriptNumBytes = function(_number) {
|
||||
var value = Math.abs(_number);
|
||||
var size = scriptNumSize(value);
|
||||
var result = [];
|
||||
for (var i = 0; i < size; ++i) {
|
||||
result.push(0);
|
||||
}
|
||||
var negative = _number < 0;
|
||||
for (i = 0; i < size; ++i) {
|
||||
result[i] = value & 0xff;
|
||||
value = Math.floor(value / 256);
|
||||
}
|
||||
if (negative) {
|
||||
result[size - 1] |= 0x80;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
coinjs.numToVarInt = function(num) {
|
||||
if (num < 253) {
|
||||
return [num];
|
||||
@@ -1400,7 +1992,7 @@
|
||||
} else if (num < 4294967296) {
|
||||
return [254].concat(coinjs.numToBytes(num,4));
|
||||
} else {
|
||||
return [253].concat(coinjs.numToBytes(num,8));
|
||||
return [255].concat(coinjs.numToBytes(num,8));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1161
-112
File diff suppressed because it is too large
Load Diff
+211
@@ -0,0 +1,211 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: collapse.js v3.3.4
|
||||
* http://getbootstrap.com/javascript/#collapse
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// COLLAPSE PUBLIC CLASS DEFINITION
|
||||
// ================================
|
||||
|
||||
var Collapse = function (element, options) {
|
||||
this.$element = $(element)
|
||||
this.options = $.extend({}, Collapse.DEFAULTS, options)
|
||||
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
|
||||
'[data-toggle="collapse"][data-target="#' + element.id + '"]')
|
||||
this.transitioning = null
|
||||
|
||||
if (this.options.parent) {
|
||||
this.$parent = this.getParent()
|
||||
} else {
|
||||
this.addAriaAndCollapsedClass(this.$element, this.$trigger)
|
||||
}
|
||||
|
||||
if (this.options.toggle) this.toggle()
|
||||
}
|
||||
|
||||
Collapse.VERSION = '3.3.4'
|
||||
|
||||
Collapse.TRANSITION_DURATION = 350
|
||||
|
||||
Collapse.DEFAULTS = {
|
||||
toggle: true
|
||||
}
|
||||
|
||||
Collapse.prototype.dimension = function () {
|
||||
var hasWidth = this.$element.hasClass('width')
|
||||
return hasWidth ? 'width' : 'height'
|
||||
}
|
||||
|
||||
Collapse.prototype.show = function () {
|
||||
if (this.transitioning || this.$element.hasClass('in')) return
|
||||
|
||||
var activesData
|
||||
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing')
|
||||
|
||||
if (actives && actives.length) {
|
||||
activesData = actives.data('bs.collapse')
|
||||
if (activesData && activesData.transitioning) return
|
||||
}
|
||||
|
||||
var startEvent = $.Event('show.bs.collapse')
|
||||
this.$element.trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) return
|
||||
|
||||
if (actives && actives.length) {
|
||||
Plugin.call(actives, 'hide')
|
||||
activesData || actives.data('bs.collapse', null)
|
||||
}
|
||||
|
||||
var dimension = this.dimension()
|
||||
|
||||
this.$element
|
||||
.removeClass('collapse')
|
||||
.addClass('collapsing')[dimension](0)
|
||||
.attr('aria-expanded', true)
|
||||
|
||||
this.$trigger
|
||||
.removeClass('collapsed')
|
||||
.attr('aria-expanded', true)
|
||||
|
||||
this.transitioning = 1
|
||||
|
||||
var complete = function () {
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse in')[dimension]('')
|
||||
this.transitioning = 0
|
||||
this.$element
|
||||
.trigger('shown.bs.collapse')
|
||||
}
|
||||
|
||||
if (!$.support.transition) return complete.call(this)
|
||||
|
||||
var scrollSize = $.camelCase(['scroll', dimension].join('-'))
|
||||
|
||||
this.$element
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize])
|
||||
}
|
||||
|
||||
Collapse.prototype.hide = function () {
|
||||
if (this.transitioning || !this.$element.hasClass('in')) return
|
||||
|
||||
var startEvent = $.Event('hide.bs.collapse')
|
||||
this.$element.trigger(startEvent)
|
||||
if (startEvent.isDefaultPrevented()) return
|
||||
|
||||
var dimension = this.dimension()
|
||||
|
||||
this.$element[dimension](this.$element[dimension]())[0].offsetHeight
|
||||
|
||||
this.$element
|
||||
.addClass('collapsing')
|
||||
.removeClass('collapse in')
|
||||
.attr('aria-expanded', false)
|
||||
|
||||
this.$trigger
|
||||
.addClass('collapsed')
|
||||
.attr('aria-expanded', false)
|
||||
|
||||
this.transitioning = 1
|
||||
|
||||
var complete = function () {
|
||||
this.transitioning = 0
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse')
|
||||
.trigger('hidden.bs.collapse')
|
||||
}
|
||||
|
||||
if (!$.support.transition) return complete.call(this)
|
||||
|
||||
this.$element
|
||||
[dimension](0)
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)
|
||||
}
|
||||
|
||||
Collapse.prototype.toggle = function () {
|
||||
this[this.$element.hasClass('in') ? 'hide' : 'show']()
|
||||
}
|
||||
|
||||
Collapse.prototype.getParent = function () {
|
||||
return $(this.options.parent)
|
||||
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
|
||||
.each($.proxy(function (i, element) {
|
||||
var $element = $(element)
|
||||
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element)
|
||||
}, this))
|
||||
.end()
|
||||
}
|
||||
|
||||
Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) {
|
||||
var isOpen = $element.hasClass('in')
|
||||
|
||||
$element.attr('aria-expanded', isOpen)
|
||||
$trigger
|
||||
.toggleClass('collapsed', !isOpen)
|
||||
.attr('aria-expanded', isOpen)
|
||||
}
|
||||
|
||||
function getTargetFromTrigger($trigger) {
|
||||
var href
|
||||
var target = $trigger.attr('data-target')
|
||||
|| (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7
|
||||
|
||||
return $(target)
|
||||
}
|
||||
|
||||
|
||||
// COLLAPSE PLUGIN DEFINITION
|
||||
// ==========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function () {
|
||||
var $this = $(this)
|
||||
var data = $this.data('bs.collapse')
|
||||
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
|
||||
|
||||
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false
|
||||
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
|
||||
if (typeof option == 'string') data[option]()
|
||||
})
|
||||
}
|
||||
|
||||
var old = $.fn.collapse
|
||||
|
||||
$.fn.collapse = Plugin
|
||||
$.fn.collapse.Constructor = Collapse
|
||||
|
||||
|
||||
// COLLAPSE NO CONFLICT
|
||||
// ====================
|
||||
|
||||
$.fn.collapse.noConflict = function () {
|
||||
$.fn.collapse = old
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
// COLLAPSE DATA-API
|
||||
// =================
|
||||
|
||||
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
|
||||
var $this = $(this)
|
||||
|
||||
if (!$this.attr('data-target')) e.preventDefault()
|
||||
|
||||
var $target = getTargetFromTrigger($this)
|
||||
var data = $target.data('bs.collapse')
|
||||
var option = data ? 'toggle' : $this.data()
|
||||
|
||||
Plugin.call($target, option)
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
Vendored
+7
File diff suppressed because one or more lines are too long
@@ -0,0 +1,59 @@
|
||||
/* ========================================================================
|
||||
* Bootstrap: transition.js v3.3.4
|
||||
* http://getbootstrap.com/javascript/#transitions
|
||||
* ========================================================================
|
||||
* Copyright 2011-2015 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
|
||||
+function ($) {
|
||||
'use strict';
|
||||
|
||||
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
|
||||
// ============================================================
|
||||
|
||||
function transitionEnd() {
|
||||
var el = document.createElement('bootstrap')
|
||||
|
||||
var transEndEventNames = {
|
||||
WebkitTransition : 'webkitTransitionEnd',
|
||||
MozTransition : 'transitionend',
|
||||
OTransition : 'oTransitionEnd otransitionend',
|
||||
transition : 'transitionend'
|
||||
}
|
||||
|
||||
for (var name in transEndEventNames) {
|
||||
if (el.style[name] !== undefined) {
|
||||
return { end: transEndEventNames[name] }
|
||||
}
|
||||
}
|
||||
|
||||
return false // explicit for ie8 ( ._.)
|
||||
}
|
||||
|
||||
// http://blog.alexmaccaw.com/css-transitions
|
||||
$.fn.emulateTransitionEnd = function (duration) {
|
||||
var called = false
|
||||
var $el = this
|
||||
$(this).one('bsTransitionEnd', function () { called = true })
|
||||
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
|
||||
setTimeout(callback, duration)
|
||||
return this
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$.support.transition = transitionEnd()
|
||||
|
||||
if (!$.support.transition) return
|
||||
|
||||
$.event.special.bsTransitionEnd = {
|
||||
bindType: $.support.transition.end,
|
||||
delegateType: $.support.transition.end,
|
||||
handle: function (e) {
|
||||
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}(jQuery);
|
||||
@@ -1,22 +1,28 @@
|
||||
---- Version 1.2 2015.07.23 ----
|
||||
e6810907c901e6bd34a28735a68850936f0823b8 ./js/ellipticcurve.js
|
||||
9ba5ede3d7f9d4c8fd623395f196adfdcf7e970f ./js/crypto-min.js
|
||||
---- Version 1.4 2019.12.25 ----
|
||||
77e4519962e2f6a9fc93342137dbb31c33b76b04 ./js/aes.js
|
||||
0ce26da5ef686d4ece91acd6cb6506559e11ab07 ./js/qcode-decoder.min.js
|
||||
be17ca7c834204bff711f582e41f76c06d472bac ./js/jsbn.js
|
||||
0700fc9ad2e39adeca0b50614bb3d327fb49f609 ./js/crypto-sha256.js
|
||||
3a09a8fc0cfe828b57fc798d668234d0490ee1a6 ./js/bootstrap-datetimepicker.min.js
|
||||
253711c6d825de55a8360552573be950da180614 ./js/bootstrap.min.js
|
||||
17cc6a56ee601fbe3858563f26232f64ce12abcb ./js/coinbin.js
|
||||
dc83017470f3ced2b0d522781b3ccded0d3a8d28 ./js/coin.js
|
||||
988565bc2cb402d63ed5c5fd7ff47c4278efc2c5 ./js/collapse.js
|
||||
9ba5ede3d7f9d4c8fd623395f196adfdcf7e970f ./js/crypto-min.js
|
||||
f7c09f2f5a721371e7d478050119f7e2d58e3ef9 ./js/crypto-sha256-hmac.js
|
||||
0700fc9ad2e39adeca0b50614bb3d327fb49f609 ./js/crypto-sha256.js
|
||||
e6810907c901e6bd34a28735a68850936f0823b8 ./js/ellipticcurve.js
|
||||
ae49e56999d82802727455f0ba83b63acd90a22b ./js/jquery-1.9.1.min.js
|
||||
be17ca7c834204bff711f582e41f76c06d472bac ./js/jsbn.js
|
||||
ce4fa351a2e62accf7fad77110fa4ddb09a324bf ./js/moment.min.js
|
||||
0ce26da5ef686d4ece91acd6cb6506559e11ab07 ./js/qcode-decoder.min.js
|
||||
ad038e1f39646b68ae666324ed4c2882a8c42474 ./js/qrcode.js
|
||||
64eb4ea5c882f8bce3e1885bf00728455f1c2f4c ./js/ripemd160.js
|
||||
114089ef2a3feb6d4db4f9cabcb186d7750d5884 ./js/sha512.js
|
||||
3ff26f7ca616b01742a25f9aa304bdb653ce4a4d ./js/coin.js
|
||||
cb26a4ae2216754f3089daaf7605e3ac41410126 ./js/coinbin.js
|
||||
ae49e56999d82802727455f0ba83b63acd90a22b ./js/jquery-1.9.1.min.js
|
||||
5f570018ed044eafd464f7e0ab1783b966224055 ./LICENCE
|
||||
506c40035e0d22560478629434d0fea27643b77a ./js/transition.js
|
||||
5f570018ed044eafd464f7e0ab1783b966224055 ./LICENSE
|
||||
255c58c17e63eb54adb3cd02b5c06224c67fc364 ./css/bootstrap-datetimepicker.min.css
|
||||
ed29315e0ffb3f14382431f2724235bf67f44eb3 ./css/bootstrap.min.css
|
||||
fc6b4268fbd57ad95d2b41a1d4d6866f222fbdb2 ./css/bootstrap-theme.min.css
|
||||
8297b8f4d686ec6c65981077514975e06ce41812 ./css/style.css
|
||||
eb54f374256b75a17f274847b4ca9985fd046f9f ./css/style.css
|
||||
2e3217a3f3b7c2fb30562ab9a4ef9a407ae81897 ./images/btc32x.png
|
||||
8ac24915d59cef71c542e7cb7d7e153f560cba1f ./images/coinbin.gif
|
||||
f2af060f1cadbc9065c8c465c648dc01be67cc12 ./images/loader.gif
|
||||
86b6f62b7853e67d3e635f6512a5a5efc58ea3c3 ./fonts/glyphicons-halflings-regular.eot
|
||||
@@ -24,5 +30,5 @@ ca35b697d99cae4d1b60f2d60fcd37771987eb07 ./fonts/glyphicons-halflings-regular.w
|
||||
de51a8494180a6db074af2dee2383f0a363c5b08 ./fonts/glyphicons-halflings-regular.svg
|
||||
278e49a86e634da6f2a02f3b47dd9d2a8f26210f ./fonts/glyphicons-halflings-regular.woff
|
||||
44bc1850f570972267b169ae18f1cb06b611ffa2 ./fonts/glyphicons-halflings-regular.ttf
|
||||
fe8d57914bb036ab94e86ec35b2671eeb6d20d0d ./README.md
|
||||
f4803ce0396b7e65d2f9dd4a5ae232b32403ef19 ./index.html
|
||||
4665ee4d8ca96db25954f6f3587ac367386eb9e8 ./README.md
|
||||
9cf7084c331b7c536f5f6e34533300c3d9d3e038 ./index.html
|
||||
|
||||
Reference in New Issue
Block a user