Compare commits

...

17 Commits
1.5 ... master

Author SHA1 Message Date
Kiyomichi Kosaka
2c8ee40a9a Make HD wallet compatible with wallets generated on https://bip32.org and bitcoin-cli. 2021-04-07 17:36:45 +02:00
OutCast3k
9a0175e18f added better error handling for when a payment fails on the #wallet page 2020-10-02 21:22:22 +00:00
OutCast3k
507b44f719 add transaction id reference to #newTransaction tab 2020-07-31 15:38:31 +00:00
OutCast3k
c868cf984e bug fix, changes to make double spending work on the transaction page from a transction id 2020-07-31 15:17:30 +00:00
root
b4fd55aebe added feature to rebuild a transaction from the txid, useful for RBF and double spending :o. Changed donation address. Released as version 1.6 2020-07-06 21:33:57 +00:00
root
f51be92a72 fixed multisig bug as described in issues/212 2020-07-05 20:21:41 +00:00
root
06cf50abb3 removed auto reload wallet feature. users must click or interact with the page. attempt to save on bandwidth. 2020-06-03 17:59:47 +00:00
root
35de4228d8 updated index.html to list link to new host 2020-04-22 20:18:08 +00:00
OutCast3k
a4ef169031 update sha1sum 2020-02-23 19:57:33 +00:00
OutCast3k
6bb5e3785a
Merge pull request #210 from junderw/patch-1
Don't add padding to P2WSH
2020-02-23 19:55:14 +00:00
OutCast3k
a199d3a55c
Merge pull request #208 from jmacxx/fix_bech32_address_decoding
fix: some bech32 addresses were not recognised
2020-02-23 19:54:46 +00:00
Jonathan Underwood
7e92530e1a
Don't add padding to P2WSH
Fixes #209
2020-02-20 10:12:57 +09:00
Wowee0
f5c2f48efc fix: some bech32 addresses were not recognised 2020-02-12 10:57:15 -06:00
OutCast3k
4f3ceeb3ea update date in sha1sum file 2019-12-28 12:10:39 +00:00
OutCast3k
73369dd253 add file hash of test.html to sha1sum 2019-12-28 12:08:51 +00:00
OutCast3k
6ed0d3e07e
Merge pull request #206 from jmacxx/tests
add some self-test routines to coinbin
2019-12-28 12:03:55 +00:00
Wowee0
84304f23ea tests added 2019-12-25 19:30:38 -06:00
6 changed files with 551 additions and 76 deletions

View File

@ -1,7 +1,7 @@
coinbin
=======
A Open Source Browser Based Bitcoin Wallet. Version 1.5 beta by OutCast3k
A Open Source Browser Based Bitcoin Wallet. Version 1.6 beta by OutCast3k
Live version available at http://coinb.in/ or http://4zpinp6gdkjfplhk.onion
@ -24,12 +24,13 @@ Coinb.in supports a number of key features such as:
- Brain wallet support.
- Compatible with bitcoin-qt
- 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
- 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
- Transaction rebuild support for RBF and double spending.
Donate to 3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN to see more development!
Donate to 33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G to see more development!

View File

@ -597,7 +597,7 @@
<h2>Transaction <small>Create a new transaction</small></h2>
<p>Use this page to create a raw transaction</p>
<b>Address, WIF key or Redeem Script</b>:
<b>Address, WIF key, Redeem Script or Transaction ID</b>:
<div class="input-group">
<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>
@ -643,7 +643,7 @@
<label>Replace By Fee (RBF)</label>
<p class="checkbox">
<label><input type="checkbox" id="txRBF" class="checkbox-inline"> Make this a <a href="https://en.bitcoin.it/wiki/Transaction_replacement" target="_blank">RBF transaction</a>.</label>
<label><input type="checkbox" id="txRBF" class="checkbox-inline" checked> Make this a <a href="https://en.bitcoin.it/wiki/Transaction_replacement" target="_blank">RBF transaction</a>.</label>
</p>
<hr>
@ -1193,14 +1193,14 @@
<div class="row">
<div class="col-md-8">
<b>Path</b><br>
<select class="form-control" id="hdpathtype"">
<select class="form-control" id="hdpathtype">
<option value="simple">Simple: m/i</option>
<option value="custom">Custom</option>
</select>
<div id="hdpath" class="hidden" style="margin-top:4px">
<span class="input-group">
<input type="text" class="form-control" value="m/0/1"> <br>
<input type="text" class="form-control" value="m/0/1" title="WARNING: see #settings page when using hardened paths!"> <br>
<span class="input-group-addon"> / </span>
</span>
</div>
@ -1333,7 +1333,7 @@
<div class="tab-pane tab-content" id="about">
<h2>About <small>open source bitcoin wallet</small></h2>
<p>Version 1.5</p>
<p>Version 1.6</p>
<p>Compatible with bitcoin core</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>
@ -1343,12 +1343,13 @@
<h3>Information</h3>
<p>Coinb.in is a free and open source project released under the MIT license, originally by <a href="https://bitcointalk.org/index.php?action=profile;u=34834" target="_blank">OutCast3k</a> in 2013. Discussion of the project can be found at <a href="https://bitcointalk.org/index.php?topic=390046" target="_blank">bitcointalk.org</a> during its early testing stages when its primary focus was to develop a proof of concept multisig solution in javascript.</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>
<p>Coinb.in is kindly hosted by <a href="https://www.bitvps.com/" target="_blank">BitVPS.com</a>.</p>
<h3>Privacy</h3>
<p>Coinb.in believes 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 and then <a href="https://blog.coinb.in/" target="_blank">blog</a> page which has multiple <a href="https://blog.coinb.in/guides" target="_blank">guides</a>. However if the problem persists you can contact us by emailing support{at}coinb.in.</p>
<h3>Donate</h3>
<p>Please donate to <a href="bitcoin:3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN">3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN</a> if you found this project useful or want to see more features!</p>
<p>Please donate to <a href="bitcoin:33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G">33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G</a> if you found this project useful or want to see more features!</p>
</div>
<div class="tab-pane tab-content" id="settings">
@ -1455,6 +1456,19 @@
</div>
</div>
<hr>
<div class="row">
<div class="col-md-12">
<b>HD wallet hardened path derivation</b>: <br>
<p class="text-muted">The path derivation for hardened paths was calculated wrong in earlier versions of coinb.in. Please select the old path derivation to recover HD wallet keys generated with older version of coinb.in.</p>
<select class="form-control" id="coinjs_derivation">
<option value="bip32_derivation">BIP32 compliant derivation</option>
<option value="coinbin_broken">Old (broken) coinb.in path derivation</option>
</select>
</div>
</div>
<br>
<div id="statusSettings" class="hidden alert">
@ -1474,7 +1488,7 @@
<div id="footer">
<div class="container text-right">
<p class="text-muted">Version 1.5</p>
<p class="text-muted">Version 1.6</p>
</div>
</div>

View File

@ -18,8 +18,10 @@
coinjs.compressed = false;
coinjs.hd_derivation = "bip32_derivation";
/* other vars */
coinjs.developer = '3K1oFZMks41C7qDYBsr72SYjapLqDuSYuN'; //bitcoin
coinjs.developer = '33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G'; //bitcoin
/* bit(coinb.in) api vars */
coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length-1]) == 'onion') ? '4zpinp6gdkjfplhk.onion' : 'coinb.in';
@ -203,7 +205,7 @@
var decode = coinjs.bech32_decode(address);
if(decode){
decode.data.shift();
return Crypto.util.bytesToHex(coinjs.bech32_convert(decode.data, 5, 8, true));
return Crypto.util.bytesToHex(coinjs.bech32_convert(decode.data, 5, 8, false));
}
return r;
}
@ -307,7 +309,7 @@
return o;
} else {
return false;
throw "Invalid checksum";
}
} catch(e) {
bech32rs = coinjs.bech32redeemscript(addr);
@ -685,7 +687,11 @@
r.derive = function(i){
i = (i)?i:0;
var blob = (Crypto.util.hexToBytes(this.keys.pubkey)).concat(coinjs.numToBytes(i,4).reverse());
if ((i >= 0x80000000) && (coinjs.hd_derivation == "bip32_derivation")) {
var blob = (Crypto.util.hexToBytes("00").concat(Crypto.util.hexToBytes(this.keys.privkey)).concat(coinjs.numToBytes(i,4).reverse()));
} else {
var blob = (Crypto.util.hexToBytes(this.keys.pubkey)).concat(coinjs.numToBytes(i,4).reverse());
}
var j = new jsSHA(Crypto.util.bytesToHex(blob), 'HEX');
var hash = j.getHMAC(Crypto.util.bytesToHex(r.chain_code), "HEX", "SHA-512", "HEX");
@ -1067,6 +1073,11 @@
coinjs.ajax(coinjs.host+'?uid='+coinjs.uid+'&key='+coinjs.key+'&setmodule=addresses&request=unspent&address='+address+'&r='+Math.random(), callback, "GET");
}
/* list transaction data */
r.getTransaction = function(txid, callback) {
coinjs.ajax(coinjs.host+'?uid='+coinjs.uid+'&key='+coinjs.key+'&setmodule=bitcoin&request=gettransaction&txid='+txid+'&r='+Math.random(), callback, "GET");
}
/* add unspent to transaction */
r.addUnspent = function(address, callback, script, segwit, sequence){
var self = this;
@ -1087,32 +1098,37 @@
var unspent = xmlDoc.getElementsByTagName("unspent")[0];
for(i=1;i<=unspent.childElementCount;i++){
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 scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
if(unspent){
for(i=1;i<=unspent.childElementCount;i++){
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 scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
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. */
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);
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++;
}
var seq = sequence || false;
self.addinput(txhash, n, scr, seq);
value += u.getElementsByTagName("value")[0].childNodes[0].nodeValue*1;
total++;
}
x.unspent = $(xmlDoc).find("unspent");
x.result = xmlDoc.getElementsByTagName("result")[0].childNodes[0].nodeValue;
x.unspent = unspent;
x.value = value;
x.total = total;
x.response = xmlDoc.getElementsByTagName("response")[0].childNodes[0].nodeValue;
return callback(x);
});
}
@ -1336,7 +1352,14 @@
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])};
sigcount = 0;
for(i=1; i<this.ins[index].script.chunks.length-1;i++){
if(this.ins[index].script.chunks[i]!=0){
sigcount++;
}
}
return {'type':'multisig', 'signed':'true', 'signatures':sigcount, 'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[this.ins[index].script.chunks.length-1])};
} else if (this.ins[index].script.chunks[0]>=80 && this.ins[index].script.chunks[this.ins[index].script.chunks.length-1]==174) { // OP_CHECKMULTISIG
// multisig script, without signature!
return {'type':'multisig', 'signed':'false', 'signatures':0, 'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)};

View File

@ -65,7 +65,6 @@ $(document).ready(function() {
$("#openWallet").removeClass("hidden").show();
walletBalance();
checkBalanceLoop();
} else {
$("#openLoginStatus").html("Your passwords do not match!").removeClass("hidden").fadeOut().fadeIn();
}
@ -133,7 +132,7 @@ $(document).ready(function() {
$("#walletSpend").removeClass("hidden").addClass("hidden");
});
$("#walletBalance").click(function(){
$("#walletBalance, #walletAddress, #walletQrCode").click(function(){
walletBalance();
});
@ -297,28 +296,20 @@ $(document).ready(function() {
});
function walletBalance(){
var tx = coinjs.transaction();
$("#walletLoader").removeClass("hidden");
coinjs.addressBalance($("#walletAddress").html(),function(data){
if($(data).find("result").text()==1){
var v = $(data).find("balance").text()/100000000;
$("#walletBalance").html(v+" BTC").attr('rel',v).fadeOut().fadeIn();
} else {
if($("#walletLoader").hasClass("hidden")){
var tx = coinjs.transaction();
$("#walletLoader").removeClass("hidden");
coinjs.addressBalance($("#walletAddress").html(),function(data){
if($(data).find("result").text()==1){
var v = $(data).find("balance").text()/100000000;
$("#walletBalance").html(v+" BTC").attr('rel',v).fadeOut().fadeIn();
} else {
$("#walletBalance").html("0.00 BTC").attr('rel',v).fadeOut().fadeIn();
}
}
$("#walletLoader").addClass("hidden");
});
}
function checkBalanceLoop(){
clearTimeout(wallet_timer);
wallet_timer = setTimeout(function(){
if($("#walletLoader").hasClass("hidden")){
walletBalance();
}
checkBalanceLoop();
},45000);
$("#walletLoader").addClass("hidden");
});
}
}
/* new -> address code */
@ -890,7 +881,7 @@ $(document).ready(function() {
$("#redeemFromStatus, #redeemFromAddress").addClass('hidden');
if(redeem.from=='multisigAddress'){
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> You should use the redeem script, not the multisig address!');
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> You should use the redeem script, not its address!');
return false;
}
@ -982,9 +973,15 @@ $(document).ready(function() {
r.redeemscript = true;
r.decodescript = decodeRs;
} else { // something else
r.addr = '';
r.from = 'other';
r.redeemscript = false;
if(string.match(/^[a-f0-9]{64}$/i)){
r.addr = string;
r.from = 'txid';
r.redeemscript = false;
} else {
r.addr = '';
r.from = 'other';
r.redeemscript = false;
}
}
}
return r;
@ -1051,9 +1048,59 @@ $(document).ready(function() {
}
}
/* global function to add inputs to page */
function addInput(address, amount) {
if($("#recipients .recipient:last .address:last").val() != ""){
$("#recipients .addressAddTo:first").click();
};
$("#recipients .address:last").val(address);
$("#recipients .amount:last").val(amount);
}
/* default function to retreive unspent outputs*/
function listUnspentDefault(redeem){
var tx = coinjs.transaction();
// unspent from transaction; double spend and RBF.
if(redeem.from == 'txid'){
tx.getTransaction(redeem.addr, function(data){
$("#redeemFromAddress").removeClass('hidden').html('<span class="glyphicon glyphicon-info-sign"></span> Attempted to rebuild transaction id <a href="'+explorer_tx+redeem.addr+'" target="_blank">'+redeem.addr+'</a>');
$.each($(data).find("inputs").children(), function(i,o){
var tx = $(o).find("txid").text();
var n = $(o).find("output_no").text();
var amount = (($(o).find("value").text()*1)).toFixed(8);
var scr = $(o).find("script").text();
addOutput(tx, n, scr, amount);
});
$("#recipients .addressRemoveTo").click();
$("#recipients .address").val("");
$("#recipients .amount").val("");
$.each($(data).find("outputs").children(), function(i,o){
addInput($(o).find("address").text(), $(o).find("value").text());
});
$("#redeemFromBtn").html("Load").attr('disabled',false);
totalInputAmount();
validateOutputAmount();
});
return;
}
// unspent from address
tx.listUnspent(redeem.addr, function(data){
if(redeem.addr) {
$("#redeemFromAddress").removeClass('hidden').html('<span class="glyphicon glyphicon-info-sign"></span> Retrieved unspent inputs from address <a href="'+explorer_addr+redeem.addr+'" target="_blank">'+redeem.addr+'</a>');
@ -1087,10 +1134,9 @@ $(document).ready(function() {
},
success: function(data) {
if (data.address) { // address field will always be present, txrefs is only present if there are UTXOs
$("#redeemFromAddress").removeClass('hidden').html(
'<span class="glyphicon glyphicon-info-sign"></span> Retrieved unspent inputs from address <a href="'+explorer_addr+redeem.addr+'" target="_blank">'+redeem.addr+'</a>');
$("#redeemFromAddress").removeClass('hidden').html('<span class="glyphicon glyphicon-info-sign"></span> Retrieved unspent inputs from address <a href="'+explorer_addr+redeem.addr+'" target="_blank">'+redeem.addr+'</a>');
for(var i in data.txrefs){
var o = data.txrefs[i]
var o = data.txrefs[i];
var tx = ((""+o.tx_hash).match(/.{1,2}/g).reverse()).join("")+'';
if(tx.match(/^[a-f0-9]+$/)){
var n = o.tx_output_n;
@ -1655,15 +1701,23 @@ $(document).ready(function() {
function deriveHDaddress() {
var hd = coinjs.hd($("#verifyHDaddress .hdKey").html());
var index_start = $("#verifyHDaddress .derivation_index_start").val()*1;
var index_end = $("#verifyHDaddress .derivation_index_end").val()*1;
var index_start = $("#verifyHDaddress .derivation_index_start").val();
if ((index_start.length > 1) && (index_start[index_start.length - 1] == '\'')) {
var use_private_index = '\'';
index_start = index_start.replace(/[']/, "") * 1;
} else {
var use_private_index = '';
index_start = index_start.replace(/[']/, "") * 1;
}
var index_end = $("#verifyHDaddress .derivation_index_end").val().replace(/[']/, "") * 1;
$("#verifyHDaddress .derivation_index_end").val(index_end + use_private_index);
var html = '';
$("#verifyHDaddress .derived_data table tbody").html("");
for(var i=index_start;i<=index_end;i++){
if($("#hdpathtype option:selected").val()=='simple'){
var derived = hd.derive(i);
} else {
var derived = hd.derive_path(($("#hdpath input").val().replace(/\/+$/, ""))+'/'+i);
var derived = hd.derive_path(($("#hdpath input").val().replace(/\/+$/, ""))+'/'+i+use_private_index);
}
html += '<tr>';
html += '<td>'+i+'</td>';
@ -1868,6 +1922,8 @@ $(document).ready(function() {
coinjs.hdkey.pub = $("#coinjs_hdpub").val()*1;
coinjs.hdkey.prv = $("#coinjs_hdprv").val()*1;
coinjs.hd_derivation = $("#coinjs_derivation").val();
configureBroadcast();
configureGetUnspentTx();
@ -1913,6 +1969,7 @@ $(document).ready(function() {
$("#coinjs_multisig").val(o[2]);
$("#coinjs_hdpub").val(o[3]);
$("#coinjs_hdprv").val(o[4]);
$("#coinjs_derivation").val(o[7]);
// hide/show custom screen
if($("option:selected",this).val()=="custom"){

11
sha1sum
View File

@ -1,9 +1,9 @@
---- Version 1.4 2019.12.25 ----
---- Version 1.6 2020.10.02 ----
77e4519962e2f6a9fc93342137dbb31c33b76b04 ./js/aes.js
3a09a8fc0cfe828b57fc798d668234d0490ee1a6 ./js/bootstrap-datetimepicker.min.js
253711c6d825de55a8360552573be950da180614 ./js/bootstrap.min.js
17cc6a56ee601fbe3858563f26232f64ce12abcb ./js/coinbin.js
dc83017470f3ced2b0d522781b3ccded0d3a8d28 ./js/coin.js
cb5717166bf1c99abbd75a06013067c4a5897d5a ./js/coinbin.js
911fb7bdba24269029f27659e4040cd991b2307c ./js/coin.js
988565bc2cb402d63ed5c5fd7ff47c4278efc2c5 ./js/collapse.js
9ba5ede3d7f9d4c8fd623395f196adfdcf7e970f ./js/crypto-min.js
f7c09f2f5a721371e7d478050119f7e2d58e3ef9 ./js/crypto-sha256-hmac.js
@ -30,5 +30,6 @@ ca35b697d99cae4d1b60f2d60fcd37771987eb07 ./fonts/glyphicons-halflings-regular.w
de51a8494180a6db074af2dee2383f0a363c5b08 ./fonts/glyphicons-halflings-regular.svg
278e49a86e634da6f2a02f3b47dd9d2a8f26210f ./fonts/glyphicons-halflings-regular.woff
44bc1850f570972267b169ae18f1cb06b611ffa2 ./fonts/glyphicons-halflings-regular.ttf
4665ee4d8ca96db25954f6f3587ac367386eb9e8 ./README.md
9cf7084c331b7c536f5f6e34533300c3d9d3e038 ./index.html
ee481606d8d48f402d152fa632ba9e5f9da7f169 ./README.md
a706e95f6436ee4a31dc26b17a9ac8ab1f0d3750 ./index.html
7130b64e3ef4cf6f2f1550e902f081c58dc053de ./test.html

379
test.html Normal file
View File

@ -0,0 +1,379 @@
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<!-- =================================================================== -->
<head>
<title>TESTING COINBIN</title>
<link rel="stylesheet" href="css/bootstrap.min.css" media="screen">
<link rel="stylesheet" href="css/bootstrap-datetimepicker.min.css">
<link rel="stylesheet" href="css/style.css" media="screen">
<script type="text/javascript" src="js/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="js/moment.min.js"></script>
<script type="text/javascript" src="js/transition.js"></script>
<script type="text/javascript" src="js/collapse.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script type="text/javascript" src="js/bootstrap-datetimepicker.min.js"></script>
<script type="text/javascript" src="js/crypto-min.js"></script>
<script type="text/javascript" src="js/crypto-sha256.js"></script>
<script type="text/javascript" src="js/crypto-sha256-hmac.js"></script>
<script type="text/javascript" src="js/sha512.js"></script>
<script type="text/javascript" src="js/ripemd160.js"></script>
<script type="text/javascript" src="js/aes.js"></script>
<script type="text/javascript" src="js/jsbn.js"></script>
<script type="text/javascript" src="js/ellipticcurve.js"></script>
<script type="text/javascript" src="js/coin.js"></script>
</head>
<!-- =================================================================== -->
<body>
<!-- =================================================================== -->
<div id="fwrap">
<!-- Fixed navbar -->
<div id="header" class="navbar navbar-default " role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="#home" class="navbar-brand" id="homeBtn"><img src="images/coinbin.gif" style="height:25px;margin-top:-5px"></a>
</div>
</div>
</div>
<div id="content" class="container">
<div class="tab-content">
<div class="tab-pane tab-content active" id="home">
<br />
<button id="test1Btn" class="btn btn-primary" type="submit">Run Coinbin Test Suite</button>
<br />
<textarea rows=20 cols=86 id="testResults"></textarea>
</div>
</div> <!-- content -->
</div> <!-- wrap -->
</div> <!-- navbar -->
</div> <!-- container -->
</body>
<script type="text/javascript">
$(document).ready(function() {
}); // end of document.ready
$("#test1Btn").click(function(){
{
var testName = "hex private key to compressed address";
var testInput = "0000000000000000000000000000000000000000000000000000000000000001";
var testExpected = "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH";
coinjs.compressed = true;
var pubkeyHex = coinjs.newPubkey(testInput);
var testOutput = coinjs.pubkey2address(pubkeyHex, coinjs.pub);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "hex private key to uncompressed address";
var testInput = "0000000000000000000000000000000000000000000000000000000000000001";
var testExpected = "1EHNa6Q4Jz2uvNExL497mE43ikXhwF6kZm";
coinjs.compressed = false;
var pubkeyHex = coinjs.newPubkey(testInput);
var testOutput = coinjs.pubkey2address(pubkeyHex, coinjs.pub);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "WIF uncompressed private key to address";
var testInput = "5J1LYLWqNxJBTwdGAmzYnpkqqSuFu48fsHv8jgojFMV2Z8exk9L";
var testExpected ="16SK7HnxBMRxSpLhhdf8RYcqv8MPJiSF6Q";
coinjs.compressed = false;
var testOutput = coinjs.wif2address(testInput).address;
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "raw private key to uncompressed base58check WIF private key";
var testInput = "62A87AD3272B41E67108FEA10C57BA6ED609F2F7A2264A83B690CD45707090D1";
var testExpected = "5JZjfs5wJv1gNkJXCmYpyj6VxciqPkwmK4yHW8zMmPN1PW7Hk7F";
coinjs.compressed = false;
var testOutput = coinjs.privkey2wif(testInput);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "raw private key to compressed base58check WIF private key";
var testInput = "62A87AD3272B41E67108FEA10C57BA6ED609F2F7A2264A83B690CD45707090D1";
var testExpected = "KzXVLY4ni4yznz8LJwdUmNoGpUfebSxiakXRqcGAeuhihzaVe3Rz";
coinjs.compressed = true;
var testOutput = coinjs.privkey2wif(testInput);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "hex ripemd160 hash of public key, to base58check address";
var testInput = "62E907B15CBF27D5425399EBF6F0FB50EBB88F18";
var testExpected = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa";
var testOutput = coinjs.scripthash2address(testInput);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "base58check address, to hex ripemd160 hash of public key";
var testInput = "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa";
var testExpected = "62e907b15cbf27d5425399ebf6f0fb50ebb88f18";
var bytes = coinjs.base58decode(testInput);
var front = bytes.slice(1, bytes.length-4);
var testOutput = Crypto.util.bytesToHex(front);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "convert 'Hash 160' to address";
var testInput = "119b098e2e980a229e139a9ed01a469e518e6f26";
var testExpected = "12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX";
var testOutput = coinjs.scripthash2address(testInput);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "convert 'SHA256' to address";
var testInput = "904b8a01c68095a9e825d28082c04b75b1f56277648256985717620e8913b79b";
var testExpected = "1JNC98D5LZbrGHFR8shDwiqLPGfpg15BUM";
var r = ripemd160(Crypto.util.hexToBytes(testInput));
r.unshift(coinjs.pub);
var hash = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
var checksum = hash.slice(0, 4);
var testOutput = coinjs.base58encode(r.concat(checksum));
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "convert WIF private key to address bech32";
var testInput = "L3GzRAGwCqfSNFr6g1NQm7edn29DgAKZJ6owUBqYELpP6Kbim5kM";
var testExpected = "bc1qhmc0vk4xzr37ayv7tlyhns7x4dk04tyvflk8ey";
var pubkey = coinjs.wif2pubkey(testInput);
var testOutput = coinjs.bech32Address(pubkey.pubkey).address;
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "bech32 address, to hex ripemd160 hash of public key";
var testInput = "bc1qhmc0vk4xzr37ayv7tlyhns7x4dk04tyvflk8ey";
var testExpected = "bef0f65aa610e3ee919e5fc979c3c6ab6cfaac8c";
var testOutput = coinjs.bech32redeemscript(testInput);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "prefix1 - WIF compressed private key to address (bitcoin)";
var testInput = "Kx4VFK8gXu4qBv73x9b1KFnWYqKekkprYyfX9QhFUMQhrTUooXKc";
var testExpected = "1NFeCVtA3zuCUAmYheRvfyABnSZCHfrR3j";
var testOutput = coinjs.wif2address(testInput).address;
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "prefix2 - WIF compressed private key to address (bitcoin-testnet)";
var testInput = "92Wn1EBgiwDNT8SC7WMZfcSk2y3mQkLUPAQtwMNYZQGAzCFUTdu";
var testExpected = "mxToLbBqPcSNnqPCSnrYjFv172TFPLjVNf";
var saved = pushNetworkVars("btc-testnet");
var testOutput = coinjs.wif2address(testInput).address;
popNetworkVars(saved);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "prefix3 - WIF compressed private key to address (litecoin)";
var testInput = "6vVAeKejJRV5wgrAqtqi7eQsS4Zf79nkw8xuYntU3JwHCiexYaJ";
var testExpected = "LMzBLYQG2opHvMBihMQgJBboxunoj5pssC";
var saved = pushNetworkVars("ltc-mainnet");
var testOutput = coinjs.wif2address(testInput).address;
popNetworkVars(saved);
addTestOutput(testName, testOutput, testExpected);
}
{
var testName = "prefix4 - WIF compressed private key to address (dogecoin)";
var testInput = "6KayMYAEQfFACQhZUzbBpFhvGzDWSmRtaY9NrPQGig9qVzRCzQf";
var testExpected = "DHEPGdnS46dHT79tkfm5DyhGAbQj4Xi8Ni";
var saved = pushNetworkVars("doge-mainnet");
var testOutput = coinjs.wif2address(testInput).address;
popNetworkVars(saved);
addTestOutput(testName, testOutput, testExpected);
}
{
// this test comes from https://bitcoindev.network/guides/bitcoinjs-lib/bitcoin-script-puzzles/
var testName = "P2SH redeem script to address (bitcoin testnet)";
var testInput = "935587";
var testExpected = "2N7WfHK1ftrTdhWej8rnFNR7guhvhfGWwFR";
var saved = pushNetworkVars("btc-testnet");
var hash = Crypto.SHA256(Crypto.util.hexToBytes(testInput), {asBytes: true});
var r = ripemd160(hash);
r.unshift(coinjs.multisig);
var hash = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
var checksum = hash.slice(0, 4);
var testOutput = coinjs.base58encode(r.concat(checksum));
addTestOutput(testName, testOutput, testExpected);
popNetworkVars(saved);
}
{
// this test comes from https://bitcoindev.network/guides/bitcoinjs-lib/bitcoin-script-puzzles/
var testName = "P2WSH redeem script to address (bitcoin testnet)";
var testInput = "935587";
var testExpected = "bcrt1qpt7c23c0wep9e8up4ywn070w3tqz3828ngy34aj8slsfxrh08ddq2d2pyu";
var hash = Crypto.SHA256(Crypto.util.hexToBytes(testInput), {asBytes: true});
var testOutput = coinjs.bech32_encode(/*coinjs.bech32.hrp*/"bcrt", [coinjs.bech32.version].concat(coinjs.bech32_convert(hash, 8, 5, true)));
addTestOutput(testName, testOutput, testExpected);
}
{
// data from https://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx (runeks)
var testName = "basic transaction building bitcoin";
var testExpectedUnsigned = "0100000001eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2010000001976a914010966776006953d5567439e5e39f86a0d273bee88acffffffff01605af405000000001976a914097072524438d003d23a2f23edb65aae1bb3e46988ac00000000";
var testExpectedSigned = "0100000001eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2010000008a4730440220299fffaf20745458111e7826e5c2cca3b78dd27c97e0a513aab807f0d724103402203247498cfb019bbbd3d629814c8703e974f177478f6fde53503a9b1088852fad01410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6ffffffff01605af405000000001976a914097072524438d003d23a2f23edb65aae1bb3e46988ac00000000";
var privKeyHex = "18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725";
var inputTx = "f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec";
var inputScript = "76a914010966776006953d5567439e5e39f86a0d273bee88ac";
var inputN = 1;
var address0 = "097072524438d003d23a2f23edb65aae1bb3e469";
var amount = 0.999;
coinjs.compressed = false;
var r = coinjs.transaction();
var wif = coinjs.privkey2wif(privKeyHex);
var address1 = coinjs.scripthash2address(address0);
r.addinput(inputTx, inputN, inputScript, 0xffffffff/*sequence*/);
r.addoutput(address1, amount);
var testOutputUnsigned = r.serialize();
debugger;
r.sign(wif, 1/*sighashtype*/);
var testOutputSigned = r.serialize();
addTestOutput(testName+" (unsigned)", testOutputUnsigned, testExpectedUnsigned);
addTestOutput(testName+" (signed)", testOutputSigned, testExpectedSigned);
}
{
// bitcoin testnet transaction https://tbtc.bitaps.com/04bbae5806d2b8fb17ed9339f42c6f6d731191a974b975d2e1df8e7601e90f6f
var saved = pushNetworkVars("btc-testnet");
var testName = "basic transaction building bitcoin-testnet";
var testExpectedUnsigned = "0100000001c72eabf9f208cacc908538e2609bbe665ffda680e2a6c39475941389dd5b14de000000001976a914b9e16a03bbf40ebb78cbc35e22d72a695f27624088acffffffff01703a0f00000000001976a914a447681601eef322926c0b3de5dfbb4157bbe40988ac00000000";
var testExpectedSigned = "0100000001c72eabf9f208cacc908538e2609bbe665ffda680e2a6c39475941389dd5b14de000000008b483045022100d909d4d3d2b540891c102d06fc8eaf1e9b914b93ea28626990666554a75b369102205a73b38071eab5b0acb8381c1454e7d998c80cd6d229645231b6bc1fb024d1d70141046fad107ba21fae3f047096152d0298291168bc0cb6b834f7cc77510dcb41839206b936649623988f7ca58c6104a22105c5b398912ded514685ebd0d8ac4011c2ffffffff01703a0f00000000001976a914a447681601eef322926c0b3de5dfbb4157bbe40988ac00000000";
var wif = "92Wn1EBgiwDNT8SC7WMZfcSk2y3mQkLUPAQtwMNYZQGAzCFUTdu";
var inputTx = "de145bdd8913947594c3a6e280a6fd5f66be9b60e2388590ccca08f2f9ab2ec7";
var inputScript = "76a914b9e16a03bbf40ebb78cbc35e22d72a695f27624088ac";
var inputN = 0;
var address1 = "mvVaevwNK2SdNj9kcugh29HbSLPhv7xszY";
var amount = 0.00998;
coinjs.compressed = true;
var r = coinjs.transaction();
r.addinput(inputTx, inputN, inputScript, 0xffffffff/*sequence*/);
r.addoutput(address1, amount);
var warnings = new Array;
var testOutputUnsigned = r.serialize();
r.sign(wif, 1/*sighashtype*/, warnings);
var testOutputSigned = r.serialize();
popNetworkVars(saved);
addTestOutput(testName+" (unsigned)", testOutputUnsigned, testExpectedUnsigned);
addTestOutput(testName+" (signed)", testOutputSigned, testExpectedSigned);
}
// create a lot of timelock scripts, compare them to known ones created using bitcoinjs-lib
// focus on edge cases like described in https://github.com/OutCast3k/coinbin/issues/201
// ranges 80-ff, 8000-ffff, 800000-ffffff, 80000000-ffffffff.
{
var testExpected = "HODL_depositAddress: 2NAx7Sx9B6epdUGyPeUEAU2tJiPectEym4F HODL_redeemScript: 050000008000b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2N5VZkAjtGerFUrc3bKjuK3whVEyaoKQceg HODL_redeemScript: 0400000008b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2MyN366sJLwXTMVMsMTHxi1bSJBwZwmNsQ4 HODL_redeemScript: 0400008000b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2NFjqkVBLKXjFLGPDpSoBTbhVsccUrqhRLW HODL_redeemScript: 03000008b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2N555b2vUzCJ5t8DryLYTw6vggH87SrK14b HODL_redeemScript: 03008000b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2N31s67tdRuaVfQipgkozEXd9jAt4saniH5 HODL_redeemScript: 020008b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2N9vLAD9f1WqFiJXinC9oCxDhypz36ZzaT7 HODL_redeemScript: 028000b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n" +
"HODL_depositAddress: 2MxRBGmDkNK44wCw2NMNq12UKxuPmkN8Wrx HODL_redeemScript: 58b175210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ac\n";
var testOutput = "";
var saved = pushNetworkVars("btc-testnet");
var testName = "timelocks";
var timeLock = 0x80000000;
while (timeLock > 0) {
//var timeLock = Math.pow(2,n)-1;
//var timeLock = 16777215;
var wif = "cMahea7zqjxrtgAbB7LSGbcQUr1uX1ojuat9jZodMN87JcbXMTcA"; // TESTNET pubKey="0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", addr="mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r"
var pubkey = coinjs.wif2pubkey(wif);
var myHodl = coinjs.simpleHodlAddress(pubkey.pubkey, timeLock);
testOutput += 'HODL_depositAddress: ' + myHodl.address + ' ';
testOutput += 'HODL_redeemScript: ' + myHodl.redeemScript + '\n';
timeLock = (timeLock >>> 4);
}
popNetworkVars(saved);
addTestOutput(testName, testOutput, testExpected);
}
});
function addTestOutput(testName, testOutput, testExpected) {
var testResult = "Fail ❌";
if (testOutput == testExpected) { testResult = "Pass ✓"; }
document.getElementById('testResults').value += testName + " : " + testResult + "\n";
}
function popNetworkVars(saved) {
coinjs.pub = saved.pub;
coinjs.priv = saved.priv;
coinjs.multisig = saved.multisig;
}
function pushNetworkVars(network) {
var savedParams = {
'pub':coinjs.pub,
'priv':coinjs.priv,
'multisig':coinjs.multisig
};
if (network == "btc-mainnet") {
coinjs.pub = 0x00;
coinjs.priv = 0x80;
coinjs.multisig = 0x05;
}
if (network == "btc-testnet") {
coinjs.pub = 0x6f;
coinjs.priv = 0xef;
coinjs.multisig = 0xc4;
}
if (network == "ltc-mainnet") {
coinjs.pub = 0x30;
coinjs.priv = 0xb0;
coinjs.multisig = 0x32;
}
if (network == "ltc-testnet") {
coinjs.pub = 0x6f;
coinjs.priv = 0xef;
coinjs.multisig = 0x3a4;
}
if (network == "doge-mainnet") {
coinjs.pub = 0x1e;
coinjs.priv = 0x9e;
coinjs.multisig = 0x16;
}
return savedParams;
}
</script>
</html>