segwit support added
This commit is contained in:
parent
d5fe5ea828
commit
6c480a1102
5 changed files with 384 additions and 72 deletions
|
@ -1,7 +1,7 @@
|
||||||
coinbin
|
coinbin
|
||||||
=======
|
=======
|
||||||
|
|
||||||
A Open Source Browser Based Bitcoin Wallet. Version 1.2 beta by OutCast3k
|
A Open Source Browser Based Bitcoin Wallet. Version 1.3 beta by OutCast3k
|
||||||
|
|
||||||
Live version available at http://coinb.in/ or http://4zpinp6gdkjfplhk.onion
|
Live version available at http://coinb.in/ or http://4zpinp6gdkjfplhk.onion
|
||||||
|
|
||||||
|
@ -28,5 +28,6 @@ Coinb.in supports a number of key features such as:
|
||||||
- HD (bip32) support
|
- HD (bip32) support
|
||||||
- Supports altcoins such as litecoin
|
- Supports altcoins such as litecoin
|
||||||
- Replace by fee (RBF) Support
|
- Replace by fee (RBF) Support
|
||||||
|
- Segwit support
|
||||||
|
|
||||||
Donate to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg to see more development!
|
Donate to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg to see more development!
|
||||||
|
|
79
index.html
79
index.html
|
@ -58,7 +58,8 @@
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-plus"></span> New<b class="caret"></b></a>
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span class="glyphicon glyphicon-plus"></span> New<b class="caret"></b></a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#newAddress" data-toggle="tab">New Address</a></li>
|
<li><a href="#newAddress" data-toggle="tab">Address</a></li>
|
||||||
|
<li><a href="#newSegWit" data-toggle="tab">SegWit Address</a></li>
|
||||||
<li><a href="#newMultiSig" data-toggle="tab">MultiSig Address</a></li>
|
<li><a href="#newMultiSig" data-toggle="tab">MultiSig Address</a></li>
|
||||||
<li><a href="#newTimeLocked" data-toggle="tab">Time Locked Address</a></li>
|
<li><a href="#newTimeLocked" data-toggle="tab">Time Locked Address</a></li>
|
||||||
<li><a href="#newHDaddress" data-toggle="tab">HD Address</a></li>
|
<li><a href="#newHDaddress" data-toggle="tab">HD Address</a></li>
|
||||||
|
@ -123,7 +124,7 @@
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<h3><span class="glyphicon glyphicon-globe"></span> Addresses</h3>
|
<h3><span class="glyphicon glyphicon-globe"></span> Addresses</h3>
|
||||||
<p>We support <a href="#newAddress">regular addresses</a> but also <a href="#newMultiSig">multisig</a> and stealth, and access to your own private keys!</p>
|
<p>We support <a href="#newAddress">regular addresses</a>, <a href="#newMultiSig">multisig</a>, <a href="#newSegWit">segwit</a> and stealth all with access to your own private keys!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
|
@ -306,6 +307,47 @@
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="tab-pane tab-content" id="newSegWit">
|
||||||
|
<h2>New SegWit Address <small> Smaller & Faster Transactions</small></h2>
|
||||||
|
|
||||||
|
<p>Any keys used you will need to manually store safely as they will be needed later to redeem the bitcoins.</p>
|
||||||
|
|
||||||
|
<label>SegWit Address (Share)</label>
|
||||||
|
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="newSegWitAddress" type="text" class="form-control address" value="" readonly>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label>RedeemScript</label>
|
||||||
|
<input id="newSegWitRedeemScript" type="text" class="form-control" readonly>
|
||||||
|
|
||||||
|
<label>Public key</label>
|
||||||
|
<input id="newSegWitPubKey" type="text" class="form-control" readonly>
|
||||||
|
|
||||||
|
<label>Private key (WIF key)</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input id="newSegWitPrivKey" type="password" class="form-control" value="" readonly>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="showKey btn btn-default" type="button">Show</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<h3>Address Options</h3>
|
||||||
|
<p>You can use the advanced options below to generate different kind of keys and addresses.</p>
|
||||||
|
|
||||||
|
<div class="checkbox">
|
||||||
|
<label><input type="checkbox" id="newSegWitBrainwallet" class="checkbox-inline"> Custom Seed or Brain Wallet</label>
|
||||||
|
<input type="text" class="form-control hidden" id="brainwalletSegWit">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="button" class="btn btn-primary" value="Generate" id="newSegWitKeysBtn">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane tab-content" id="newMultiSig">
|
<div class="tab-pane tab-content" id="newMultiSig">
|
||||||
<h2>New Multisig Address <small>Secure multisig address</small></h2>
|
<h2>New Multisig Address <small>Secure multisig address</small></h2>
|
||||||
|
|
||||||
|
@ -497,7 +539,7 @@
|
||||||
<h2>Transaction <small>Create a new transaction</small></h2>
|
<h2>Transaction <small>Create a new transaction</small></h2>
|
||||||
<p>Use this page to create a raw transaction</p>
|
<p>Use this page to create a raw transaction</p>
|
||||||
|
|
||||||
<b>Address, WIF key or Multisig Redeem Script</b>:
|
<b>Address, WIF key or Redeem Script</b>:
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-btn">
|
<span class="input-group-btn">
|
||||||
<button class="btn btn-info qrcodeScanner" type="button" data-toggle="modal" data-target="#modalQrcodeScanner" forward-result="#redeemFrom"><span class="glyphicon glyphicon-camera"></span></button>
|
<button class="btn btn-info qrcodeScanner" type="button" data-toggle="modal" data-target="#modalQrcodeScanner" forward-result="#redeemFrom"><span class="glyphicon glyphicon-camera"></span></button>
|
||||||
|
@ -679,6 +721,7 @@
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="hidden verifyData" id="verifyRsData">
|
<div class="hidden verifyData" id="verifyRsData">
|
||||||
|
|
||||||
<h4>Redeem Script</h4>
|
<h4>Redeem Script</h4>
|
||||||
<p><span style="float:right"><a href="javascript:;" target="_blank" class="verifyLink" title="Link to this page"><span class="glyphicon glyphicon-link"></span></a></span>The above redeem script has been decoded</p>
|
<p><span style="float:right"><a href="javascript:;" target="_blank" class="verifyLink" title="Link to this page"><span class="glyphicon glyphicon-link"></span></a></span>The above redeem script has been decoded</p>
|
||||||
|
|
||||||
|
@ -694,6 +737,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>Required Signatures</label>
|
<label>Required Signatures</label>
|
||||||
<p class="signaturesRequired">?</p>
|
<p class="signaturesRequired">?</p>
|
||||||
<label>Signatures Required from</label>
|
<label>Signatures Required from</label>
|
||||||
|
@ -701,9 +745,25 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<br>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="hidden" id="verifyRsDataHodl">
|
<div class="hidden verifyData" id="verifyRsDataSegWit">
|
||||||
|
<label>Segwit Address</label>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="text" class="form-control address segWitAddress" value="" readonly>
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="qrcodeBtn btn btn-default" type="button" data-toggle="modal" data-target="#modalQrcode"><span class="glyphicon glyphicon-qrcode"></span></button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden verifyData" id="verifyRsDataHodl">
|
||||||
<label>Hodl Address</label>
|
<label>Hodl Address</label>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
@ -736,8 +796,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -747,6 +805,7 @@
|
||||||
<div><b>Version</b>: <span class="transactionVersion"></span></div>
|
<div><b>Version</b>: <span class="transactionVersion"></span></div>
|
||||||
<div><b>Transaction Size</b>: <span class="transactionSize"></span></div>
|
<div><b>Transaction Size</b>: <span class="transactionSize"></span></div>
|
||||||
<div><b>Lock time</b>: <span class="transactionLockTime"></span></div>
|
<div><b>Lock time</b>: <span class="transactionLockTime"></span></div>
|
||||||
|
<div class="transactionSegWit"><b>SegWit</b>: True</div>
|
||||||
<div class="transactionRBF"><b>RBF</b>: This is a <a href="https://en.bitcoin.it/wiki/Transaction_replacement">replace by fee</a> transaction!</div>
|
<div class="transactionRBF"><b>RBF</b>: This is a <a href="https://en.bitcoin.it/wiki/Transaction_replacement">replace by fee</a> transaction!</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -774,6 +833,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="hidden verifyData" id="verifyPrivKey">
|
<div class="hidden verifyData" id="verifyPrivKey">
|
||||||
<h4>WIF key</h4>
|
<h4>WIF key</h4>
|
||||||
<p>The above wif key has been decoded</p>
|
<p>The above wif key has been decoded</p>
|
||||||
|
@ -883,6 +943,7 @@
|
||||||
|
|
||||||
<input type="button" value="Submit" class="btn btn-primary" id="verifyBtn">
|
<input type="button" value="Submit" class="btn btn-primary" id="verifyBtn">
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane tab-content" id="sign">
|
<div class="tab-pane tab-content" id="sign">
|
||||||
|
@ -975,7 +1036,7 @@
|
||||||
|
|
||||||
<div class="tab-pane tab-content" id="about">
|
<div class="tab-pane tab-content" id="about">
|
||||||
<h2>About <small>open source bitcoin wallet</small></h2>
|
<h2>About <small>open source bitcoin wallet</small></h2>
|
||||||
<p>Version 1.2</p>
|
<p>Version 1.3</p>
|
||||||
<p>Compatible with bitcoin-qt</p>
|
<p>Compatible with bitcoin-qt</p>
|
||||||
<p>Github <a href="https://github.com/OutCast3k/coinbin/">https://github.com/OutCast3k/coinbin/</a></p>
|
<p>Github <a href="https://github.com/OutCast3k/coinbin/">https://github.com/OutCast3k/coinbin/</a></p>
|
||||||
<p>TOR <a href="http://4zpinp6gdkjfplhk.onion">4zpinp6gdkjfplhk.onion</a></p>
|
<p>TOR <a href="http://4zpinp6gdkjfplhk.onion">4zpinp6gdkjfplhk.onion</a></p>
|
||||||
|
@ -987,6 +1048,8 @@
|
||||||
<p>Coinb.in is run and funded by the generosity of others in terms of <a href="https://github.com/OutCast3k/coinbin/graphs/contributors" target="_blank">development</a> and hosting.</p>
|
<p>Coinb.in is run and funded by the generosity of others in terms of <a href="https://github.com/OutCast3k/coinbin/graphs/contributors" target="_blank">development</a> and hosting.</p>
|
||||||
<h3>Privacy</h3>
|
<h3>Privacy</h3>
|
||||||
<p>Coinb.in beleives strongly in privacy, not only do we support the use of TOR, the site does not collect and store IP or transaction data via our servers nor do we store your bitcoins private key. We do route traffic via cloudflare using an SSL certificate.</p>
|
<p>Coinb.in beleives strongly in privacy, not only do we support the use of TOR, the site does not collect and store IP or transaction data via our servers nor do we store your bitcoins private key. We do route traffic via cloudflare using an SSL certificate.</p>
|
||||||
|
<h3>Support</h3>
|
||||||
|
<p>We recommend that you first check our <a href="https://status.coinb.in/" target="_blank">service status</a> page, if the problem persists you can contact us by emailing support{at}coinb.in.</p>
|
||||||
<h3>Donate</h3>
|
<h3>Donate</h3>
|
||||||
<p>Please donate to <a href="bitcoin:1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg">1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg</a> if you found this project useful or want to see more features!</p>
|
<p>Please donate to <a href="bitcoin:1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg">1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg</a> if you found this project useful or want to see more features!</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1105,7 +1168,7 @@
|
||||||
|
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div class="container text-right">
|
<div class="container text-right">
|
||||||
<p class="text-muted">Version 1.2</p>
|
<p class="text-muted">Version 1.3</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
216
js/coin.js
216
js/coin.js
|
@ -98,9 +98,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* provide a public key and return address */
|
/* 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}));
|
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 hash = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
|
||||||
var checksum = hash.slice(0, 4);
|
var checksum = hash.slice(0, 4);
|
||||||
return coinjs.base58encode(r.concat(checksum));
|
return coinjs.base58encode(r.concat(checksum));
|
||||||
|
@ -165,6 +165,19 @@
|
||||||
return {'address':address, 'redeemScript':redeemScript};
|
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)};
|
||||||
|
}
|
||||||
|
|
||||||
/* provide a privkey and return an WIF */
|
/* provide a privkey and return an WIF */
|
||||||
coinjs.privkey2wif = function(h){
|
coinjs.privkey2wif = function(h){
|
||||||
var r = Crypto.util.hexToBytes(h);
|
var r = Crypto.util.hexToBytes(h);
|
||||||
|
@ -683,6 +696,13 @@
|
||||||
var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired);
|
var multi = coinjs.pubkeys2MultisigAddress(pubkeys, r.signaturesRequired);
|
||||||
r.address = multi['address'];
|
r.address = multi['address'];
|
||||||
r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig"
|
r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig"
|
||||||
|
} 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){
|
} 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 ^
|
// ^ <unlocktime> OP_CHECKLOCKTIMEVERIFY OP_DROP <pubkey> OP_CHECKSIG ^
|
||||||
r = {}
|
r = {}
|
||||||
|
@ -772,6 +792,7 @@
|
||||||
r.lock_time = 0;
|
r.lock_time = 0;
|
||||||
r.ins = [];
|
r.ins = [];
|
||||||
r.outs = [];
|
r.outs = [];
|
||||||
|
r.witness = [];
|
||||||
r.timestamp = null;
|
r.timestamp = null;
|
||||||
r.block = null;
|
r.block = null;
|
||||||
|
|
||||||
|
@ -990,6 +1011,100 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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'};
|
||||||
|
}
|
||||||
|
|
||||||
|
var scriptcode = Crypto.util.hexToBytes(extract['script']);
|
||||||
|
if(scriptcode[0] != 0){
|
||||||
|
return {'result':0, 'fail':'scriptcode', 'response':'redeemscript is not valid'};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(extract['value'] == -1){
|
||||||
|
return {'result':0, 'fail':'value', 'response':'unable to generate a valid segwit hash without a value'};
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of redeem script check
|
||||||
|
|
||||||
|
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 */
|
/* extract the scriptSig, used in the transactionHash() function */
|
||||||
r.extractScriptKey = function(index) {
|
r.extractScriptKey = function(index) {
|
||||||
if(this.ins[index]){
|
if(this.ins[index]){
|
||||||
|
@ -1005,6 +1120,15 @@
|
||||||
} else if(this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177){//OP_CHECKLOCKTIMEVERIFY
|
} else if(this.ins[index].script.chunks.length == 5 && this.ins[index].script.chunks[1] == 177){//OP_CHECKLOCKTIMEVERIFY
|
||||||
// hodl script (not signed)
|
// hodl script (not signed)
|
||||||
return {'type':'hodl', 'signed':'false', 'signatures': 0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};
|
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){
|
||||||
|
// segwit script
|
||||||
|
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
|
} else if (this.ins[index].script.chunks[0]==0 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1][this.ins[index].script.chunks[this.ins[index].script.chunks.length-1].length-1]==174) { // OP_CHECKMULTISIG
|
||||||
// multisig script, with signature(s) included
|
// multisig script, with signature(s) included
|
||||||
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])};
|
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])};
|
||||||
|
@ -1024,7 +1148,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
/* generate a signature from a transaction hash */
|
/* generate a signature from a transaction hash */
|
||||||
r.transactionSig = function(index, wif, sigHashType){
|
r.transactionSig = function(index, wif, sigHashType, txhash){
|
||||||
|
|
||||||
function serializeSig(r, s) {
|
function serializeSig(r, s) {
|
||||||
var rBa = r.toByteArraySigned();
|
var rBa = r.toByteArraySigned();
|
||||||
|
@ -1046,7 +1170,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var shType = sigHashType || 1;
|
var shType = sigHashType || 1;
|
||||||
var hash = Crypto.util.hexToBytes(this.transactionHash(index, shType));
|
var hash = txhash || Crypto.util.hexToBytes(this.transactionHash(index, shType));
|
||||||
|
|
||||||
if(hash){
|
if(hash){
|
||||||
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
var curve = EllipticCurve.getSECCurveByName("secp256k1");
|
||||||
|
@ -1220,6 +1344,42 @@
|
||||||
return true;
|
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']);
|
||||||
|
|
||||||
|
if(segwit['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;
|
||||||
|
|
||||||
|
this.witness.push([signature, wif2['pubkey']]);
|
||||||
|
|
||||||
|
/* reorder witness data */
|
||||||
|
var witness_order = [];
|
||||||
|
for(var i = 0; i < this.ins.length; i++){
|
||||||
|
for(var y = 0; y < this.witness.length; y++){
|
||||||
|
var sw = coinjs.segwitAddress(this.witness[y][1]);
|
||||||
|
if(sw['redeemscript'] == Crypto.util.bytesToHex(this.ins[i].script.chunks[0])){
|
||||||
|
witness_order.push(this.witness[y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.witness = witness_order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* sign inputs */
|
/* sign inputs */
|
||||||
r.sign = function(wif, sigHashType){
|
r.sign = function(wif, sigHashType){
|
||||||
var shType = sigHashType || 1;
|
var shType = sigHashType || 1;
|
||||||
|
@ -1232,10 +1392,16 @@
|
||||||
|
|
||||||
if(((d['type'] == 'scriptpubkey' && d['script']==Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false"){
|
if(((d['type'] == 'scriptpubkey' && d['script']==Crypto.util.bytesToHex(pubkeyHash.buffer)) || d['type'] == 'empty') && d['signed'] == "false"){
|
||||||
this.signinput(i, wif, shType);
|
this.signinput(i, wif, shType);
|
||||||
|
|
||||||
} else if (d['type'] == 'hodl' && d['signed'] == "false") {
|
} else if (d['type'] == 'hodl' && d['signed'] == "false") {
|
||||||
this.signhodl(i, wif, shType);
|
this.signhodl(i, wif, shType);
|
||||||
|
|
||||||
} else if (d['type'] == 'multisig') {
|
} else if (d['type'] == 'multisig') {
|
||||||
this.signmultisig(i, wif, shType);
|
this.signmultisig(i, wif, shType);
|
||||||
|
|
||||||
|
} else if (d['type'] == 'segwit') {
|
||||||
|
this.signsegwit(i, wif, shType);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// could not sign
|
// could not sign
|
||||||
}
|
}
|
||||||
|
@ -1247,8 +1413,12 @@
|
||||||
r.serialize = function(){
|
r.serialize = function(){
|
||||||
var buffer = [];
|
var buffer = [];
|
||||||
buffer = buffer.concat(coinjs.numToBytes(parseInt(this.version),4));
|
buffer = buffer.concat(coinjs.numToBytes(parseInt(this.version),4));
|
||||||
buffer = buffer.concat(coinjs.numToVarInt(this.ins.length));
|
|
||||||
|
|
||||||
|
if(this.witness.length>=1){
|
||||||
|
buffer = buffer.concat([0x00, 0x01]);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer.concat(coinjs.numToVarInt(this.ins.length));
|
||||||
for (var i = 0; i < this.ins.length; i++) {
|
for (var i = 0; i < this.ins.length; i++) {
|
||||||
var txin = this.ins[i];
|
var txin = this.ins[i];
|
||||||
buffer = buffer.concat(Crypto.util.hexToBytes(txin.outpoint.hash).reverse());
|
buffer = buffer.concat(Crypto.util.hexToBytes(txin.outpoint.hash).reverse());
|
||||||
|
@ -1268,6 +1438,16 @@
|
||||||
buffer = buffer.concat(scriptBytes);
|
buffer = buffer.concat(scriptBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(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));
|
buffer = buffer.concat(coinjs.numToBytes(parseInt(this.lock_time),4));
|
||||||
return Crypto.util.bytesToHex(buffer);
|
return Crypto.util.bytesToHex(buffer);
|
||||||
}
|
}
|
||||||
|
@ -1279,6 +1459,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var pos = 0;
|
var pos = 0;
|
||||||
|
var witness = false;
|
||||||
|
|
||||||
var readAsInt = function(bytes) {
|
var readAsInt = function(bytes) {
|
||||||
if (bytes == 0) return 0;
|
if (bytes == 0) return 0;
|
||||||
pos++;
|
pos++;
|
||||||
|
@ -1304,8 +1486,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj = new coinjs.transaction();
|
var obj = new coinjs.transaction();
|
||||||
|
|
||||||
obj.version = readAsInt(4);
|
obj.version = readAsInt(4);
|
||||||
|
|
||||||
|
if(buffer[pos] == 0x00 && buffer[pos+1] == 0x01){
|
||||||
|
// segwit transaction
|
||||||
|
witness = true;
|
||||||
|
obj.witness = [];
|
||||||
|
pos += 2;
|
||||||
|
}
|
||||||
|
|
||||||
var ins = readVarInt();
|
var ins = readVarInt();
|
||||||
for (var i = 0; i < ins; i++) {
|
for (var i = 0; i < ins; i++) {
|
||||||
obj.ins.push({
|
obj.ins.push({
|
||||||
|
@ -1326,6 +1515,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);
|
obj.lock_time = readAsInt(4);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,6 +281,14 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#newSegWitBrainwallet").click(function(){
|
||||||
|
if($(this).is(":checked")){
|
||||||
|
$("#brainwalletSegWit").removeClass("hidden");
|
||||||
|
} else {
|
||||||
|
$("#brainwalletSegWit").addClass("hidden");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$("#encryptKey").click(function(){
|
$("#encryptKey").click(function(){
|
||||||
if($(this).is(":checked")){
|
if($(this).is(":checked")){
|
||||||
$("#aes256passform").removeClass("hidden");
|
$("#aes256passform").removeClass("hidden");
|
||||||
|
@ -289,6 +297,21 @@ $(document).ready(function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* new -> segwit code */
|
||||||
|
$("#newSegWitKeysBtn").click(function(){
|
||||||
|
var compressed = coinjs.compressed;
|
||||||
|
coinjs.compressed = true;
|
||||||
|
var s = ($("#newSegWitBrainwallet").is(":checked")) ? $("#brainwalletSegWit").val() : null;
|
||||||
|
var coin = coinjs.newKeys(s);
|
||||||
|
var sw = coinjs.segwitAddress(coin.pubkey);
|
||||||
|
$("#newSegWitAddress").val(sw.address);
|
||||||
|
$("#newSegWitRedeemScript").val(sw.redeemscript);
|
||||||
|
$("#newSegWitPubKey").val(coin.pubkey);
|
||||||
|
$("#newSegWitPrivKey").val(coin.wif);
|
||||||
|
coinjs.compressed = compressed;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/* new -> multisig code */
|
/* new -> multisig code */
|
||||||
|
|
||||||
$("#newMultiSigAddress").click(function(){
|
$("#newMultiSigAddress").click(function(){
|
||||||
|
@ -810,6 +833,15 @@ $(document).ready(function() {
|
||||||
$("#inputs .txId:last").val(txid);
|
$("#inputs .txId:last").val(txid);
|
||||||
$("#inputs .txIdN:last").val(n);
|
$("#inputs .txIdN:last").val(n);
|
||||||
$("#inputs .txIdAmount:last").val(amount);
|
$("#inputs .txIdAmount:last").val(amount);
|
||||||
|
|
||||||
|
if(script.match(/^00/) && script.length==44){
|
||||||
|
s = coinjs.script();
|
||||||
|
s.writeBytes(Crypto.util.hexToBytes(script));
|
||||||
|
s.writeOp(0);
|
||||||
|
s.writeBytes(coinjs.numToBytes((amount*100000000).toFixed(0), 8));
|
||||||
|
script = Crypto.util.bytesToHex(s.buffer);
|
||||||
|
}
|
||||||
|
|
||||||
$("#inputs .txIdScript:last").val(script);
|
$("#inputs .txIdScript:last").val(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1277,6 +1309,9 @@ $(document).ready(function() {
|
||||||
if(decode){
|
if(decode){
|
||||||
$("#verifyRsDataMultisig").addClass('hidden');
|
$("#verifyRsDataMultisig").addClass('hidden');
|
||||||
$("#verifyRsDataHodl").addClass('hidden');
|
$("#verifyRsDataHodl").addClass('hidden');
|
||||||
|
$("#verifyRsDataSegWit").addClass('hidden');
|
||||||
|
$("#verifyRsData").addClass("hidden");
|
||||||
|
|
||||||
|
|
||||||
if(decode.type == "multisig__") {
|
if(decode.type == "multisig__") {
|
||||||
$("#verifyRsDataMultisig .multisigAddress").val(decode['address']);
|
$("#verifyRsDataMultisig .multisigAddress").val(decode['address']);
|
||||||
|
@ -1291,6 +1326,11 @@ $(document).ready(function() {
|
||||||
$("#verifyRsDataMultisig").removeClass('hidden');
|
$("#verifyRsDataMultisig").removeClass('hidden');
|
||||||
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
||||||
return true;
|
return true;
|
||||||
|
} else if(decode.type == "segwit__"){
|
||||||
|
$("#verifyRsData").removeClass("hidden");
|
||||||
|
$("#verifyRsDataSegWit .segWitAddress").val(decode['address']);
|
||||||
|
$("#verifyRsDataSegWit").removeClass('hidden');
|
||||||
|
return true;
|
||||||
} else if(decode.type == "hodl__") {
|
} else if(decode.type == "hodl__") {
|
||||||
var d = $("#verifyRsDataHodl .date").data("DateTimePicker");
|
var d = $("#verifyRsDataHodl .date").data("DateTimePicker");
|
||||||
$("#verifyRsDataHodl .address").val(decode['address']);
|
$("#verifyRsDataHodl .address").val(decode['address']);
|
||||||
|
@ -1314,6 +1354,10 @@ $(document).ready(function() {
|
||||||
$("#verifyTransactionData .transactionSize").html(decode.size()+' <i>bytes</i>');
|
$("#verifyTransactionData .transactionSize").html(decode.size()+' <i>bytes</i>');
|
||||||
$("#verifyTransactionData .transactionLockTime").html(decode['lock_time']);
|
$("#verifyTransactionData .transactionLockTime").html(decode['lock_time']);
|
||||||
$("#verifyTransactionData .transactionRBF").hide();
|
$("#verifyTransactionData .transactionRBF").hide();
|
||||||
|
$("#verifyTransactionData .transactionSegWit").hide();
|
||||||
|
if (decode.witness.length>=1) {
|
||||||
|
$("#verifyTransactionData .transactionSegWit").show();
|
||||||
|
}
|
||||||
$("#verifyTransactionData").removeClass("hidden");
|
$("#verifyTransactionData").removeClass("hidden");
|
||||||
$("#verifyTransactionData tbody").html("");
|
$("#verifyTransactionData tbody").html("");
|
||||||
|
|
||||||
|
@ -1324,7 +1368,7 @@ $(document).ready(function() {
|
||||||
h += '<td><input class="form-control" type="text" value="'+o.outpoint.hash+'" readonly></td>';
|
h += '<td><input class="form-control" type="text" value="'+o.outpoint.hash+'" readonly></td>';
|
||||||
h += '<td class="col-xs-1">'+o.outpoint.index+'</td>';
|
h += '<td class="col-xs-1">'+o.outpoint.index+'</td>';
|
||||||
h += '<td class="col-xs-2"><input class="form-control" type="text" value="'+Crypto.util.bytesToHex(o.script.buffer)+'" readonly></td>';
|
h += '<td class="col-xs-2"><input class="form-control" type="text" value="'+Crypto.util.bytesToHex(o.script.buffer)+'" readonly></td>';
|
||||||
h += '<td class="col-xs-1"> <span class="glyphicon glyphicon-'+((s.signed=='true')?'ok':'remove')+'-circle"></span>';
|
h += '<td class="col-xs-1"> <span class="glyphicon glyphicon-'+((s.signed=='true' || (decode.witness[i] && decode.witness[i].length==2))?'ok':'remove')+'-circle"></span>';
|
||||||
if(s['type']=='multisig' && s['signatures']>=1){
|
if(s['type']=='multisig' && s['signatures']>=1){
|
||||||
h += ' '+s['signatures'];
|
h += ' '+s['signatures'];
|
||||||
}
|
}
|
||||||
|
|
10
sha1sum
10
sha1sum
|
@ -1,9 +1,9 @@
|
||||||
---- Version 1.2 2017.03.14 ----
|
---- Version 1.3 2017.09.10 ----
|
||||||
77e4519962e2f6a9fc93342137dbb31c33b76b04 ./js/aes.js
|
77e4519962e2f6a9fc93342137dbb31c33b76b04 ./js/aes.js
|
||||||
3a09a8fc0cfe828b57fc798d668234d0490ee1a6 ./js/bootstrap-datetimepicker.min.js
|
3a09a8fc0cfe828b57fc798d668234d0490ee1a6 ./js/bootstrap-datetimepicker.min.js
|
||||||
253711c6d825de55a8360552573be950da180614 ./js/bootstrap.min.js
|
253711c6d825de55a8360552573be950da180614 ./js/bootstrap.min.js
|
||||||
00aaf959783209cd348a746542b3e32bcdf333e3 ./js/coinbin.js
|
b98f718f0400fada4e0f15471031f92ce31e2b83 ./js/coinbin.js
|
||||||
f23a41e5bf56b98790c68502feae569459a60341 ./js/coin.js
|
3e7b9b1a30412f827d4709a53014d0b6f06103f0 ./js/coin.js
|
||||||
988565bc2cb402d63ed5c5fd7ff47c4278efc2c5 ./js/collapse.js
|
988565bc2cb402d63ed5c5fd7ff47c4278efc2c5 ./js/collapse.js
|
||||||
9ba5ede3d7f9d4c8fd623395f196adfdcf7e970f ./js/crypto-min.js
|
9ba5ede3d7f9d4c8fd623395f196adfdcf7e970f ./js/crypto-min.js
|
||||||
f7c09f2f5a721371e7d478050119f7e2d58e3ef9 ./js/crypto-sha256-hmac.js
|
f7c09f2f5a721371e7d478050119f7e2d58e3ef9 ./js/crypto-sha256-hmac.js
|
||||||
|
@ -29,5 +29,5 @@ ca35b697d99cae4d1b60f2d60fcd37771987eb07 ./fonts/glyphicons-halflings-regular.w
|
||||||
de51a8494180a6db074af2dee2383f0a363c5b08 ./fonts/glyphicons-halflings-regular.svg
|
de51a8494180a6db074af2dee2383f0a363c5b08 ./fonts/glyphicons-halflings-regular.svg
|
||||||
278e49a86e634da6f2a02f3b47dd9d2a8f26210f ./fonts/glyphicons-halflings-regular.woff
|
278e49a86e634da6f2a02f3b47dd9d2a8f26210f ./fonts/glyphicons-halflings-regular.woff
|
||||||
44bc1850f570972267b169ae18f1cb06b611ffa2 ./fonts/glyphicons-halflings-regular.ttf
|
44bc1850f570972267b169ae18f1cb06b611ffa2 ./fonts/glyphicons-halflings-regular.ttf
|
||||||
b4d3a33913a0877684909f7edf8b79bf9192b0a7 ./README.md
|
d8a324a13501cd5705dc26b945fc8088f00907ae ./README.md
|
||||||
5bf3ba82a7c8c798f129d9fcaffb8161095e308c ./index.html
|
da6e4cbb4a168a3583086e0997c8c678a7a80925 ./index.html
|
||||||
|
|
Loading…
Reference in a new issue