1022 lines
33 KiB
JavaScript
1022 lines
33 KiB
JavaScript
$(document).ready(function() {
|
|
|
|
/* open wallet code */
|
|
|
|
$("#openBtn").click(function(){
|
|
var email = $("#openEmail").val().toLowerCase();
|
|
if(email.match(/[\s\w\d]+@[\s\w\d]+/g)){
|
|
if($("#openPass").val().length>=10){
|
|
if($("#openPass").val()==$("#openPassConfirm").val()){
|
|
var email = $("#openEmail").val().toLowerCase();
|
|
var pass = $("#openPass").val();
|
|
var s = email;
|
|
s += '|'+pass+'|';
|
|
s += s.length+'|!@'+((pass.length*7)+email.length)*7;
|
|
var regchars = (pass.match(/[a-z]+/g)) ? pass.match(/[a-z]+/g).length : 1;
|
|
var regupchars = (pass.match(/[A-Z]+/g)) ? pass.match(/[A-Z]+/g).length : 1;
|
|
var regnums = (pass.match(/[0-9]+/g)) ? pass.match(/[0-9]+/g).length : 1;
|
|
s += ((regnums+regchars)+regupchars)*pass.length+'3571';
|
|
s += (s+''+s);
|
|
|
|
for(i=0;i<=50;i++){
|
|
s = Crypto.SHA256(s);
|
|
}
|
|
|
|
coinjs.compressed = true;
|
|
var keys = coinjs.newKeys(s);
|
|
|
|
$("#walletAddress").html(keys.address);
|
|
$("#walletHistory").attr('href','https://btc.blockr.io/address/info/'+keys.address);
|
|
|
|
$("#walletQrCode").html("");
|
|
var qrcode = new QRCode("walletQrCode");
|
|
qrcode.makeCode("bitcoin:"+keys.address);
|
|
|
|
$("#walletKeys .privkey").val(keys.wif);
|
|
$("#walletKeys .pubkey").val(keys.pubkey);
|
|
$("#walletKeys .privkeyaes").val(CryptoJS.AES.encrypt(keys.wif, pass));
|
|
|
|
$("#openLogin").hide();
|
|
$("#openWallet").removeClass("hidden").show();
|
|
|
|
walletBalance();
|
|
checkBalanceLoop();
|
|
} else {
|
|
$("#openLoginStatus").html("Your passwords do not match!").removeClass("hidden").fadeOut().fadeIn();
|
|
}
|
|
} else {
|
|
$("#openLoginStatus").html("Your password must be at least 10 chars long").removeClass("hidden").fadeOut().fadeIn();
|
|
}
|
|
} else {
|
|
$("#openLoginStatus").html("Your email address doesn't appear to be valid").removeClass("hidden").fadeOut().fadeIn();
|
|
}
|
|
|
|
$("#openLoginStatus").prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ');
|
|
});
|
|
|
|
$("#walletLogout").click(function(){
|
|
$("#openEmail").val("");
|
|
$("#openPass").val("");
|
|
$("#openPassConfirm").val("");
|
|
|
|
$("#openLogin").show();
|
|
$("#openWallet").addClass("hidden").show();
|
|
|
|
$("#walletAddress").html("");
|
|
$("#walletHistory").attr('href','https://btc.blockr.io/address/info/');
|
|
|
|
$("#walletQrCode").html("");
|
|
var qrcode = new QRCode("walletQrCode");
|
|
qrcode.makeCode("bitcoin:");
|
|
|
|
$("#walletKeys .privkey").val("");
|
|
$("#walletKeys .pubkey").val("");
|
|
|
|
});
|
|
|
|
$("#walletShowKeys").click(function(){
|
|
$("#walletKeys").removeClass("hidden");
|
|
$("#walletSpend").removeClass("hidden").addClass("hidden");
|
|
});
|
|
|
|
$("#walletBalance").click(function(){
|
|
walletBalance();
|
|
});
|
|
|
|
$("#walletConfirmSend").click(function(){
|
|
var thisbtn = $(this);
|
|
var tx = coinjs.transaction();
|
|
var txfee = $("#txFee");
|
|
var devaddr = coinjs.developer;
|
|
var devamount = $("#developerDonation");
|
|
|
|
if((devamount.val()*1)>0){
|
|
tx.addoutput(devaddr, devamount.val()*1);
|
|
}
|
|
|
|
var total = (devamount.val()*1) + (txfee.val()*1);
|
|
|
|
$.each($("#walletSpendTo .output"), function(i,o){
|
|
var addr = $('.addressTo',o);
|
|
var amount = $('.amount',o);
|
|
total += amount.val()*1;
|
|
tx.addoutput(addr.val(), amount.val()*1);
|
|
});
|
|
|
|
thisbtn.attr('disabled',true);
|
|
|
|
tx.addUnspent($("#walletAddress").html(), function(data){
|
|
var dvalue = data.value/100000000
|
|
if(dvalue>=total){
|
|
var change = dvalue-total;
|
|
if(change>0){
|
|
tx.addoutput($("#walletAddress").html(), change);
|
|
}
|
|
|
|
// clone the transaction with out using coinjs.clone() function as it gives us trouble
|
|
var tx2 = coinjs.transaction();
|
|
var txunspent = tx2.deserialize(tx.serialize());
|
|
|
|
// then sign
|
|
var signed = txunspent.sign($("#walletKeys .privkey").val());
|
|
|
|
// and finally broadcast!
|
|
tx2.broadcast(function(data){
|
|
if($(data).find("result").text()=="1"){
|
|
$("#walletSendConfirmStatus").removeClass('hidden').addClass('alert-success').html("txid: "+$(data).find("txid").text());
|
|
} else {
|
|
$("#walletSendConfirmStatus").removeClass('hidden').addClass('alert-danger').html(unescape($(data).find("response").text()).replace(/\+/g,' '));
|
|
}
|
|
|
|
// update wallet balance
|
|
walletBalance();
|
|
|
|
}, signed);
|
|
} else {
|
|
$("#walletSendConfirmStatus").removeClass("hidden").addClass('alert-danger').html("You have a confirmed balance of "+data.value+" BTC unable to send "+total+" BTC").fadeOut().fadeIn();
|
|
}
|
|
|
|
thisbtn.attr('disabled',false);
|
|
$("#walletLoader").addClass("hidden");
|
|
});
|
|
});
|
|
|
|
$("#walletSendBtn").click(function(){
|
|
|
|
$("#walletSendStatus").addClass("hidden").html("");
|
|
|
|
var thisbtn = $(this);
|
|
var txfee = $("#txFee");
|
|
var devamount = $("#developerDonation");
|
|
|
|
if((!isNaN(devamount.val())) && devamount.val()>=0){
|
|
$(devamount).parent().removeClass('has-error');
|
|
} else {
|
|
$(devamount).parent().addClass('has-error')
|
|
}
|
|
|
|
if((!isNaN(txfee.val())) && txfee.val()>=0){
|
|
$(txfee).parent().removeClass('has-error');
|
|
} else {
|
|
$(txfee).parent().addClass('has-error');
|
|
}
|
|
|
|
var total = (devamount.val()*1) + (txfee.val()*1);
|
|
|
|
$.each($("#walletSpendTo .output"), function(i,o){
|
|
var amount = $('.amount',o);
|
|
var address = $('.addressTo',o);
|
|
|
|
total += amount.val()*1;
|
|
|
|
if((!isNaN($(amount).val())) && $(amount).val()>0){
|
|
$(amount).parent().removeClass('has-error');
|
|
} else {
|
|
$(amount).parent().addClass('has-error');
|
|
}
|
|
|
|
if(coinjs.addressDecode($(address).val())){
|
|
$(address).parent().removeClass('has-error');
|
|
} else {
|
|
$(address).parent().addClass('has-error');
|
|
}
|
|
});
|
|
|
|
total = total.toFixed(8);
|
|
|
|
if($("#walletSpend .has-error").length==0){
|
|
var balance = ($("#walletBalance").html()).replace(/[^0-9\.]+/g,'')*1;
|
|
if(total<=balance){
|
|
$("#walletSendConfirmStatus").addClass("hidden").removeClass('alert-success').removeClass('alert-danger').html("");
|
|
$("#spendAmount").html(total);
|
|
$("#modalWalletConfirm").modal("show");
|
|
$("#walletConfirmSend").attr('disabled',false);
|
|
} else {
|
|
$("#walletSendStatus").removeClass("hidden").html("You are trying to spend "+total+' but have a balance of '+balance);
|
|
}
|
|
} else {
|
|
$("#walletSpend .has-error").fadeOut().fadeIn();
|
|
$("#walletSendStatus").removeClass("hidden").html('<span class="glyphicon glyphicon-exclamation-sign"></span> One or more input has an error');
|
|
}
|
|
});
|
|
|
|
$("#walletShowSpend").click(function(){
|
|
$("#walletSpend").removeClass("hidden");
|
|
$("#walletKeys").removeClass("hidden").addClass("hidden");
|
|
});
|
|
|
|
$("#walletSpendTo .addressAdd").click(function(){
|
|
var clone = '<div class="form-horizontal output">'+$(this).parent().html()+'</div>';
|
|
$("#walletSpendTo").append(clone);
|
|
$("#walletSpendTo .glyphicon-plus:last").removeClass('glyphicon-plus').addClass('glyphicon-minus');
|
|
$("#walletSpendTo .glyphicon-minus:last").parent().removeClass('addressAdd').addClass('addressRemove');
|
|
$("#walletSpendTo .addressRemove").unbind("");
|
|
$("#walletSpendTo .addressRemove").click(function(){
|
|
$(this).parent().fadeOut().remove();
|
|
});
|
|
});
|
|
|
|
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 {
|
|
$("#walletBalance").html("0.00 BTC").attr('rel',v).fadeOut().fadeIn();
|
|
}
|
|
|
|
$("#walletLoader").addClass("hidden");
|
|
});
|
|
}
|
|
|
|
function checkBalanceLoop(){
|
|
setTimeout(function(){
|
|
walletBalance();
|
|
checkBalanceLoop();
|
|
},45000);
|
|
}
|
|
|
|
/* new -> address code */
|
|
|
|
$("#newKeysBtn").click(function(){
|
|
coinjs.compressed = false;
|
|
if($("#newCompressed").is(":checked")){
|
|
coinjs.compressed = true;
|
|
}
|
|
var s = ($("#newBrainwallet").is(":checked")) ? $("#brainwallet").val() : null;
|
|
var coin = coinjs.newKeys(s);
|
|
$("#newBitcoinAddress").val(coin.address);
|
|
$("#newPubKey").val(coin.pubkey);
|
|
$("#newPrivKey").val(coin.wif);
|
|
|
|
/* encrypted key code */
|
|
if((!$("#encryptKey").is(":checked")) || $("#aes256pass").val()==$("#aes256pass_confirm").val()){
|
|
$("#aes256passStatus").addClass("hidden");
|
|
if($("#encryptKey").is(":checked")){
|
|
$("#aes256wifkey").removeClass("hidden");
|
|
}
|
|
} else {
|
|
$("#aes256passStatus").removeClass("hidden");
|
|
}
|
|
$("#newPrivKeyEnc").val(CryptoJS.AES.encrypt(coin.wif, $("#aes256pass").val())+'');
|
|
|
|
});
|
|
|
|
$("#newBrainwallet").click(function(){
|
|
if($(this).is(":checked")){
|
|
$("#brainwallet").removeClass("hidden");
|
|
} else {
|
|
$("#brainwallet").addClass("hidden");
|
|
}
|
|
});
|
|
|
|
$("#encryptKey").click(function(){
|
|
if($(this).is(":checked")){
|
|
$("#aes256passform").removeClass("hidden");
|
|
} else {
|
|
$("#aes256wifkey, #aes256passform, #aes256passStatus").addClass("hidden");
|
|
}
|
|
});
|
|
|
|
/* new -> multisig code */
|
|
|
|
$("#newMultiSigAddress").click(function(){
|
|
|
|
$("#multiSigData").removeClass('show').addClass('hidden').fadeOut();
|
|
$("#multisigPubKeys .pubkey").parent().removeClass('has-error');
|
|
$("#releaseCoins").parent().removeClass('has-error');
|
|
$("#multiSigErrorMsg").hide();
|
|
|
|
if((isNaN($("#releaseCoins option:selected").html())) || ((!isNaN($("#releaseCoins option:selected").html())) && ($("#releaseCoins option:selected").html()>$("#multisigPubKeys .pubkey").length || $("#releaseCoins option:selected").html()*1<=0 || $("#releaseCoins option:selected").html()*1>8))){
|
|
$("#releaseCoins").parent().addClass('has-error');
|
|
$("#multiSigErrorMsg").html('<span class="glyphicon glyphicon-exclamation-sign"></span> Minimum signatures required is greater than the amount of public keys provided').fadeIn();
|
|
return false;
|
|
}
|
|
|
|
var keys = [];
|
|
$.each($("#multisigPubKeys .pubkey"), function(i,o){
|
|
if(coinjs.pubkeydecompress($(o).val())){
|
|
keys.push($(o).val());
|
|
$(o).parent().removeClass('has-error');
|
|
} else {
|
|
$(o).parent().addClass('has-error');
|
|
}
|
|
});
|
|
|
|
if(($("#multisigPubKeys .pubkey").parent().hasClass('has-error')==false) && $("#releaseCoins").parent().hasClass('has-error')==false){
|
|
var sigsNeeded = $("#releaseCoins option:selected").html();
|
|
var multisig = coinjs.pubkeys2MultisigAddress(keys, sigsNeeded);
|
|
$("#multiSigData .address").val(multisig['address']);
|
|
$("#multiSigData .script").val(multisig['redeemScript']);
|
|
$("#multiSigData .scriptUrl").val(document.location.origin+''+document.location.pathname+'?verify='+multisig['redeemScript']+'#verify');
|
|
$("#multiSigData").removeClass('hidden').addClass('show').fadeIn();
|
|
$("#releaseCoins").removeClass('has-error');
|
|
} else {
|
|
$("#multiSigErrorMsg").html('<span class="glyphicon glyphicon-exclamation-sign"></span> One or more public key is invalid!').fadeIn();
|
|
}
|
|
});
|
|
|
|
$("#multisigPubKeys .pubkeyAdd").click(function(){
|
|
if($("#multisigPubKeys .pubkeyRemove").length<14){
|
|
var clone = '<div class="form-horizontal">'+$(this).parent().html()+'</div>';
|
|
$("#multisigPubKeys").append(clone);
|
|
$("#multisigPubKeys .glyphicon-plus:last").removeClass('glyphicon-plus').addClass('glyphicon-minus');
|
|
$("#multisigPubKeys .glyphicon-minus:last").parent().removeClass('pubkeyAdd').addClass('pubkeyRemove');
|
|
$("#multisigPubKeys .pubkeyRemove").unbind("");
|
|
$("#multisigPubKeys .pubkeyRemove").click(function(){
|
|
$(this).parent().fadeOut().remove();
|
|
});
|
|
}
|
|
});
|
|
|
|
/* new -> Hd address code */
|
|
|
|
$(".deriveHDbtn").click(function(){
|
|
$("#verifyScript").val($("input[type='text']",$(this).parent().parent()).val());
|
|
window.location = "#verify";
|
|
$("#verifyBtn").click();
|
|
});
|
|
|
|
$("#newHDKeysBtn").click(function(){
|
|
coinjs.compressed = true;
|
|
var s = ($("#newHDBrainwallet").is(":checked")) ? $("#HDBrainwallet").val() : null;
|
|
var hd = coinjs.hd();
|
|
var pair = hd.master(s);
|
|
$("#newHDxpub").val(pair.pubkey);
|
|
$("#newHDxprv").val(pair.privkey);
|
|
|
|
});
|
|
|
|
$("#newHDBrainwallet").click(function(){
|
|
if($(this).is(":checked")){
|
|
$("#HDBrainwallet").removeClass("hidden");
|
|
} else {
|
|
$("#HDBrainwallet").addClass("hidden");
|
|
}
|
|
});
|
|
|
|
/* new -> transaction code */
|
|
|
|
$("#recipients .addressAddTo").click(function(){
|
|
if($("#recipients .addressRemoveTo").length<19){
|
|
var clone = '<div class="row recipient"><br>'+$(this).parent().parent().html()+'</div>';
|
|
$("#recipients").append(clone);
|
|
$("#recipients .glyphicon-plus:last").removeClass('glyphicon-plus').addClass('glyphicon-minus');
|
|
$("#recipients .glyphicon-minus:last").parent().removeClass('addressAdd').addClass('addressRemoveTo');
|
|
$("#recipients .addressRemoveTo").unbind("");
|
|
$("#recipients .addressRemoveTo").click(function(){
|
|
$(this).parent().parent().fadeOut().remove();
|
|
validateOutputAmount();
|
|
});
|
|
validateOutputAmount();
|
|
}
|
|
});
|
|
|
|
$("#inputs .txidAdd").click(function(){
|
|
var clone = '<div class="row inputs"><br>'+$(this).parent().parent().html()+'</div>';
|
|
$("#inputs").append(clone);
|
|
$("#inputs .txidClear:last").remove();
|
|
$("#inputs .glyphicon-plus:last").removeClass('glyphicon-plus').addClass('glyphicon-minus');
|
|
$("#inputs .glyphicon-minus:last").parent().removeClass('txidAdd').addClass('txidRemove');
|
|
$("#inputs .txidRemove").unbind("");
|
|
$("#inputs .txidRemove").click(function(){
|
|
$(this).parent().parent().fadeOut().remove();
|
|
totalInputAmount();
|
|
});
|
|
$("#inputs .row:last input").attr('disabled',false);
|
|
|
|
$("#inputs .txIdAmount").unbind("").change(function(){
|
|
totalInputAmount();
|
|
}).keyup(function(){
|
|
totalInputAmount();
|
|
});
|
|
|
|
});
|
|
|
|
$("#transactionBtn").click(function(){
|
|
var tx = coinjs.transaction();
|
|
|
|
if(($("#nLockTime").val()).match(/^[0-9]+$/g)){
|
|
tx.lock_time = $("#nLockTime").val()*1;
|
|
}
|
|
|
|
$.each($("#inputs .row"), function(i,o){
|
|
if($(".txId",o).val()!="" && $(".txIdN",o).val()!=""){
|
|
tx.addinput($(".txId",o).val(), $(".txIdN",o).val(), $(".txIdScript",o).val());
|
|
}
|
|
});
|
|
|
|
$("#recipients .row").removeClass('has-error');
|
|
|
|
$.each($("#recipients .row"), function(i,o){
|
|
var a = ($(".address",o).val());
|
|
var ad = coinjs.addressDecode(a)
|
|
if(((a!="") && (ad.version === 0 || ad.version === 5)) && $(".amount",o).val()!=""){ // address
|
|
tx.addoutput(a, $(".amount",o).val());
|
|
} else if (((a!="") && ad.version === 42) && $(".amount",o).val()!=""){ // stealth address
|
|
tx.addstealth(ad, $(".amount",o).val());
|
|
} else if (((($("#opReturn").is(":checked")) && a.match(/^[a-f0-9]+$/ig)) && a.length<80) && (a.length%2)==0) { // data
|
|
tx.adddata(a);
|
|
} else { // neither address nor data
|
|
$(o).addClass('has-error');
|
|
}
|
|
});
|
|
|
|
$("#transactionCreate textarea").val(tx.serialize());
|
|
$("#transactionCreate .txSize").html(tx.size());
|
|
|
|
$("#transactionCreate").removeClass("hidden");
|
|
});
|
|
|
|
$(".txidClear").click(function(){
|
|
$("#inputs .row:first input").attr('disabled',false);
|
|
$("#inputs .row:first input").val("");
|
|
totalInputAmount();
|
|
});
|
|
|
|
$("#inputs .txIdAmount").unbind("").change(function(){
|
|
totalInputAmount();
|
|
}).keyup(function(){
|
|
totalInputAmount();
|
|
});
|
|
|
|
/* code for the qr code scanner */
|
|
|
|
$(".qrcodeScanner").click(function(){
|
|
if ((typeof MediaStreamTrack === 'function') && typeof MediaStreamTrack.getSources === 'function'){
|
|
MediaStreamTrack.getSources(function(sourceInfos){
|
|
var f = 0;
|
|
$("select#videoSource").html("");
|
|
for (var i = 0; i !== sourceInfos.length; ++i) {
|
|
var sourceInfo = sourceInfos[i];
|
|
var option = document.createElement('option');
|
|
option.value = sourceInfo.id;
|
|
if (sourceInfo.kind === 'video') {
|
|
option.text = sourceInfo.label || 'camera ' + ($("select#videoSource options").length + 1);
|
|
$(option).appendTo("select#videoSource");
|
|
}
|
|
}
|
|
});
|
|
|
|
$("#videoSource").unbind("change").change(function(){
|
|
scannerStart()
|
|
});
|
|
|
|
} else {
|
|
$("#videoSource").addClass("hidden");
|
|
}
|
|
scannerStart();
|
|
$("#qrcode-scanner-callback-to").html($(this).attr('forward-result'));
|
|
});
|
|
|
|
function scannerStart(){
|
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || false;
|
|
if(navigator.getUserMedia){
|
|
if (!!window.stream) {
|
|
$("video").attr('src',null);
|
|
window.stream.stop();
|
|
}
|
|
|
|
var videoSource = $("select#videoSource").val();
|
|
var constraints = {
|
|
video: {
|
|
optional: [{sourceId: videoSource}]
|
|
}
|
|
};
|
|
|
|
navigator.getUserMedia(constraints, function(stream){
|
|
window.stream = stream; // make stream available to console
|
|
var videoElement = document.querySelector('video');
|
|
videoElement.src = window.URL.createObjectURL(stream);
|
|
videoElement.play();
|
|
}, function(error){ });
|
|
|
|
QCodeDecoder().decodeFromCamera(document.getElementById('videoReader'), function(er,data){
|
|
if(!er){
|
|
var match = data.match(/^bitcoin\:([13][a-z0-9]{26,33})/i);
|
|
var result = match ? match[1] : data;
|
|
$(""+$("#qrcode-scanner-callback-to").html()).val(result);
|
|
$("#qrScanClose").click();
|
|
}
|
|
});
|
|
} else {
|
|
$("#videoReaderError").removeClass("hidden");
|
|
$("#videoReader, #videoSource").addClass("hidden");
|
|
}
|
|
}
|
|
|
|
$("#redeemFromBtn").click(function(){
|
|
var thisbtn = this;
|
|
var addr = '';
|
|
var isMultiSig = false;
|
|
var s = $("#redeemFrom").val();
|
|
|
|
$("#redeemFromStatus, #redeemFromAddress").addClass('hidden');
|
|
$(thisbtn).html("Please wait, loading...").attr('disabled',true);
|
|
|
|
var decode = coinjs.addressDecode(s);
|
|
|
|
if(decode.version == coinjs.pub){
|
|
addr = s;
|
|
} else if (decode.version == coinjs.priv){
|
|
var a = coinjs.wif2address(s);
|
|
addr = a['address'];
|
|
} else if (decode.version == coinjs.multisig){
|
|
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> You should use the redeem script, not the multisig address!');
|
|
} else {
|
|
var script = coinjs.script();
|
|
var decodeRs = script.decodeRedeemScript(s);
|
|
if(decodeRs){
|
|
addr = decodeRs['address'];
|
|
isMultiSig = true;
|
|
} else {
|
|
// input is neither a regular address or redeem script
|
|
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> The address or multisig redeem script you have entered is invalid');
|
|
}
|
|
}
|
|
|
|
var tx = coinjs.transaction();
|
|
tx.listUnspent(addr, function(data){
|
|
if(addr) {
|
|
if($("#clearInputsOnLoad").is(":checked")){
|
|
$("#inputs .txidRemove, #inputs .txidClear").click();
|
|
}
|
|
|
|
$("#redeemFromAddress").removeClass('hidden').html('<span class="glyphicon glyphicon-info-sign"></span> Retrieved unspent inputs from address <a href="https://btc.blockr.io/address/info/'+addr+'" target="_blank">'+addr+'</a>');
|
|
|
|
$.each($(data).find("unspent").children(), function(i,o){
|
|
|
|
if($("#inputs .txId:last").val()!=""){
|
|
$("#inputs .txidAdd").click();
|
|
}
|
|
|
|
$("#inputs .row:last input").attr('disabled',true);
|
|
|
|
var val = (($(o).find("value").text()*1)/100000000);
|
|
var txid = (($(o).find("tx_hash").text()).match(/.{1,2}/g).reverse()).join("")+'';
|
|
|
|
$("#inputs .txId:last").val(txid);
|
|
$("#inputs .txIdN:last").val($(o).find("tx_output_n").text());
|
|
$("#inputs .txIdAmount:last").val(val.toFixed(8));
|
|
if(isMultiSig==true){
|
|
$("#inputs .txIdScript:last").val(s);
|
|
} else {
|
|
$("#inputs .txIdScript:last").val($(o).find("script").text());
|
|
}
|
|
});
|
|
}
|
|
|
|
$(thisbtn).html("Load").attr('disabled',false);
|
|
totalInputAmount();
|
|
});
|
|
});
|
|
|
|
function totalInputAmount(){
|
|
$("#totalInput").html('0.00');
|
|
$.each($("#inputs .txIdAmount"), function(i,o){
|
|
if(isNaN($(o).val())){
|
|
$(o).parent().addClass('has-error');
|
|
} else {
|
|
$(o).parent().removeClass('has-error');
|
|
var f = 0;
|
|
if(!isNaN($(o).val())){
|
|
f += $(o).val()*1;
|
|
}
|
|
$("#totalInput").html((($("#totalInput").html()*1) + (f*1)).toFixed(8));
|
|
}
|
|
});
|
|
totalFee();
|
|
}
|
|
|
|
function validateOutputAmount(){
|
|
$("#recipients .amount").unbind('');
|
|
$("#recipients .amount").keyup(function(){
|
|
if(isNaN($(this).val())){
|
|
$(this).parent().addClass('has-error');
|
|
} else {
|
|
$(this).parent().removeClass('has-error');
|
|
var f = 0;
|
|
$.each($("#recipients .amount"),function(i,o){
|
|
if(!isNaN($(o).val())){
|
|
f += $(o).val()*1;
|
|
}
|
|
});
|
|
$("#totalOutput").html((f).toFixed(8));
|
|
}
|
|
totalFee();
|
|
}).keyup();
|
|
}
|
|
|
|
function totalFee(){
|
|
var fee = (($("#totalInput").html()*1) - ($("#totalOutput").html()*1)).toFixed(8);
|
|
$("#transactionFee").val((fee>0)?fee:'0.00');
|
|
}
|
|
|
|
$("#optionsCollapse").click(function(){
|
|
if($("#optionsAdvanced").hasClass('hidden')){
|
|
$("#glyphcollapse").removeClass('glyphicon-collapse-down').addClass('glyphicon-collapse-up');
|
|
$("#optionsAdvanced").removeClass("hidden");
|
|
} else {
|
|
$("#glyphcollapse").removeClass('glyphicon-collapse-up').addClass('glyphicon-collapse-down');
|
|
$("#optionsAdvanced").addClass("hidden");
|
|
}
|
|
});
|
|
|
|
/* broadcast a transaction */
|
|
|
|
$("#rawSubmitBtn").click(function(){
|
|
var thisbtn = this;
|
|
var tx = coinjs.transaction();
|
|
$(thisbtn).val('Please wait, loading...').attr('disabled',true);
|
|
tx.broadcast(function(data){
|
|
$("#rawTransactionStatus").html(unescape($(data).find("response").text()).replace(/\+/g,' ')).removeClass('hidden');
|
|
if($(data).find("result").text()==1){
|
|
$("#rawTransactionStatus").addClass('alert-success').removeClass('alert-danger');
|
|
$("#rawTransactionStatus").html('txid: '+$(data).find("txid").text());
|
|
} else {
|
|
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ');
|
|
}
|
|
$("#rawTransactionStatus").fadeOut().fadeIn();
|
|
$(thisbtn).val('Submit').attr('disabled',false);
|
|
}, $("#rawTransaction").val());
|
|
});
|
|
|
|
/* verify script code */
|
|
|
|
$("#verifyBtn").click(function(){
|
|
$(".verifyData").addClass("hidden");
|
|
$("#verifyStatus").hide();
|
|
if(!decodeRedeemScript()){
|
|
if(!decodeTransactionScript()){
|
|
if(!decodePrivKey()){
|
|
if(!decodePubKey()){
|
|
if(!decodeHDaddress()){
|
|
$("#verifyStatus").removeClass('hidden').fadeOut().fadeIn();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
function decodeRedeemScript(){
|
|
var script = coinjs.script();
|
|
var decode = script.decodeRedeemScript($("#verifyScript").val());
|
|
if(decode){
|
|
$("#verifyRsData .multisigAddress").val(decode['address']);
|
|
$("#verifyRsData .signaturesRequired").html(decode['signaturesRequired']);
|
|
$("#verifyRsData table tbody").html("");
|
|
for(var i=0;i<decode.pubkeys.length;i++){
|
|
$('<tr><td><input type="text" class="form-control" value="'+decode.pubkeys[i]+'" readonly></td></tr>').appendTo("#verifyRsData table tbody");
|
|
}
|
|
$("#verifyRsData").removeClass("hidden");
|
|
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function decodeTransactionScript(){
|
|
var tx = coinjs.transaction();
|
|
try {
|
|
var decode = tx.deserialize($("#verifyScript").val());
|
|
// console.log(decode);
|
|
$("#verifyTransactionData .transactionVersion").html(decode['version']);
|
|
$("#verifyTransactionData .transactionSize").html(decode.size()+' <i>bytes</i>');
|
|
$("#verifyTransactionData .transactionLockTime").html(decode['lock_time']);
|
|
$("#verifyTransactionData").removeClass("hidden");
|
|
$("#verifyTransactionData tbody").html("");
|
|
|
|
var h = '';
|
|
$.each(decode.ins, function(i,o){
|
|
var s = decode.extractScriptKey(i);
|
|
h += '<tr>';
|
|
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-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>';
|
|
if(s['type']=='multisig' && s['signatures']>=1){
|
|
h += ' '+s['signatures'];
|
|
}
|
|
h += '</td>';
|
|
h += '<td class="col-xs-1">';
|
|
if(s['type']=='multisig'){
|
|
var script = coinjs.script();
|
|
var rs = script.decodeRedeemScript(s.script);
|
|
h += rs['signaturesRequired']+' of '+rs['pubkeys'].length;
|
|
} else {
|
|
h += '<span class="glyphicon glyphicon-remove-circle"></span>';
|
|
}
|
|
h += '</td>';
|
|
h += '</tr>';
|
|
});
|
|
|
|
$(h).appendTo("#verifyTransactionData .ins tbody");
|
|
|
|
h = '';
|
|
$.each(decode.outs, function(i,o){
|
|
|
|
if(o.script.chunks.length==2 && o.script.chunks[0]==106){ // OP_RETURN
|
|
|
|
var data = Crypto.util.bytesToHex(o.script.chunks[1]);
|
|
var dataascii = hex2ascii(data);
|
|
|
|
if(dataascii.match(/^[\s\d\w]+$/ig)){
|
|
data = dataascii;
|
|
}
|
|
|
|
h += '<tr>';
|
|
h += '<td><input type="text" class="form-control" value="(OP_RETURN) '+data+'" readonly></td>';
|
|
h += '<td class="col-xs-1">'+(o.value/100000000).toFixed(8)+'</td>';
|
|
h += '<td class="col-xs-2"><input class="form-control" type="text" value="'+Crypto.util.bytesToHex(o.script.buffer)+'" readonly></td>';
|
|
h += '</tr>';
|
|
} else {
|
|
|
|
var addr = '';
|
|
if(o.script.chunks.length==5){
|
|
addr = coinjs.scripthash2address(Crypto.util.bytesToHex(o.script.chunks[2]));
|
|
} else {
|
|
var pub = coinjs.pub;
|
|
coinjs.pub = coinjs.multisig;
|
|
addr = coinjs.scripthash2address(Crypto.util.bytesToHex(o.script.chunks[1]));
|
|
coinjs.pub = pub;
|
|
}
|
|
|
|
h += '<tr>';
|
|
h += '<td><input class="form-control" type="text" value="'+addr+'" readonly></td>';
|
|
h += '<td class="col-xs-1">'+(o.value/100000000).toFixed(8)+'</td>';
|
|
h += '<td class="col-xs-2"><input class="form-control" type="text" value="'+Crypto.util.bytesToHex(o.script.buffer)+'" readonly></td>';
|
|
h += '</tr>';
|
|
}
|
|
});
|
|
$(h).appendTo("#verifyTransactionData .outs tbody");
|
|
|
|
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
|
return true;
|
|
} catch(e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function hex2ascii(hex) {
|
|
var str = '';
|
|
for (var i = 0; i < hex.length; i += 2)
|
|
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
|
return str;
|
|
}
|
|
|
|
function decodePrivKey(){
|
|
var wif = $("#verifyScript").val();
|
|
if(wif.length==51 || wif.length==52){
|
|
try {
|
|
var w2address = coinjs.wif2address(wif);
|
|
var w2pubkey = coinjs.wif2pubkey(wif);
|
|
var w2privkey = coinjs.wif2privkey(wif);
|
|
|
|
$("#verifyPrivKey .address").val(w2address['address']);
|
|
$("#verifyPrivKey .pubkey").val(w2pubkey['pubkey']);
|
|
$("#verifyPrivKey .privkey").val(w2privkey['privkey']);
|
|
$("#verifyPrivKey .iscompressed").html(w2address['compressed']?'true':'false');
|
|
|
|
$("#verifyPrivKey").removeClass("hidden");
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function decodePubKey(){
|
|
var pubkey = $("#verifyScript").val();
|
|
if(pubkey.length==66 || pubkey.length==130){
|
|
try {
|
|
$("#verifyPubKey .address").val(coinjs.pubkey2address(pubkey));
|
|
$("#verifyPubKey").removeClass("hidden");
|
|
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
|
return true;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function decodeHDaddress(){
|
|
var s = $("#verifyScript").val();
|
|
try {
|
|
var hex = Crypto.util.bytesToHex((coinjs.base58decode(s)).slice(0,4));
|
|
var hex_cmp_prv = Crypto.util.bytesToHex((coinjs.numToBytes(coinjs.hdkey.prv,4)).reverse());
|
|
var hex_cmp_pub = Crypto.util.bytesToHex((coinjs.numToBytes(coinjs.hdkey.pub,4)).reverse());
|
|
if(hex == hex_cmp_prv || hex == hex_cmp_pub){
|
|
var hd = coinjs.hd(s);
|
|
$("#verifyHDaddress .hdKey").html(s);
|
|
$("#verifyHDaddress .chain_code").val(Crypto.util.bytesToHex(hd.chain_code));
|
|
$("#verifyHDaddress .depth").val(hd.depth);
|
|
$("#verifyHDaddress .version").val('0x'+(hd.version).toString(16));
|
|
$("#verifyHDaddress .child_index").val(hd.child_index);
|
|
$("#verifyHDaddress .hdwifkey").val((hd.keys.wif)?hd.keys.wif:'');
|
|
$("#verifyHDaddress .key_type").html((((hd.depth==0 && hd.child_index==0)?'Master':'Derived')+' '+hd.type).toLowerCase());
|
|
$("#verifyHDaddress .parent_fingerprint").val(Crypto.util.bytesToHex(hd.parent_fingerprint));
|
|
$("#verifyHDaddress .derived_data table tbody").html("");
|
|
deriveHDaddress();
|
|
$(".verifyLink").attr('href','?verify='+$("#verifyScript").val());
|
|
$("#verifyHDaddress").removeClass("hidden");
|
|
return true;
|
|
}
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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 html = '';
|
|
$("#verifyHDaddress .derived_data table tbody").html("");
|
|
for(var i=index_start;i<=index_end;i++){
|
|
var derived = hd.derive(i);
|
|
html += '<tr>';
|
|
html += '<td>'+i+'</td>';
|
|
html += '<td><input type="text" class="form-control" value="'+derived.keys.address+'" readonly></td>';
|
|
html += '<td><input type="text" class="form-control" value="'+((derived.keys.wif)?derived.keys.wif:'')+'" readonly></td>';
|
|
html += '<td><input type="text" class="form-control" value="'+derived.keys_extended.pubkey+'" readonly></td>';
|
|
html += '<td><input type="text" class="form-control" value="'+((derived.keys_extended.privkey)?derived.keys_extended.privkey:'')+'" readonly></td>';
|
|
html += '</tr>';
|
|
}
|
|
$(html).appendTo("#verifyHDaddress .derived_data table tbody");
|
|
}
|
|
|
|
|
|
/* sign code */
|
|
|
|
$("#signBtn").click(function(){
|
|
var wifkey = $("#signPrivateKey");
|
|
var script = $("#signTransaction");
|
|
|
|
if(coinjs.addressDecode(wifkey.val())){
|
|
$(wifkey).parent().removeClass('has-error');
|
|
} else {
|
|
$(wifkey).parent().addClass('has-error');
|
|
}
|
|
|
|
if((script.val()).match(/^[a-f0-9]+$/ig)){
|
|
$(script).parent().removeClass('has-error');
|
|
} else {
|
|
$(script).parent().addClass('has-error');
|
|
}
|
|
|
|
if($("#sign .has-error").length==0){
|
|
$("#signedDataError").addClass('hidden');
|
|
try {
|
|
var tx = coinjs.transaction();
|
|
var t = tx.deserialize(script.val());
|
|
|
|
var signed = t.sign(wifkey.val());
|
|
$("#signedData textarea").val(signed);
|
|
$("#signedData .txSize").html(t.size());
|
|
$("#signedData").removeClass('hidden').fadeIn();
|
|
} catch(e) {
|
|
// console.log(e);
|
|
}
|
|
} else {
|
|
$("#signedDataError").removeClass('hidden');
|
|
$("#signedData").addClass('hidden');
|
|
}
|
|
});
|
|
|
|
|
|
/* page load code */
|
|
|
|
function _get(value) {
|
|
var dataArray = (document.location.search).match(/(([a-z0-9\_\[\]]+\=[a-z0-9\_\.\%\@]+))/gi);
|
|
var r = [];
|
|
if(dataArray) {
|
|
for(var x in dataArray) {
|
|
if((dataArray[x]) && typeof(dataArray[x])=='string') {
|
|
if((dataArray[x].split('=')[0].toLowerCase()).replace(/\[\]$/ig,'') == value.toLowerCase()) {
|
|
r.push(unescape(dataArray[x].split('=')[1]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
$("#newKeysBtn, #newHDKeysBtn").click();
|
|
|
|
var _getBroadcast = _get("broadcast");
|
|
if(_getBroadcast[0]){
|
|
$("#rawTransaction").val(_getBroadcast[0]);
|
|
$("#rawSubmitBtn").click();
|
|
window.location.hash = "#broadcast";
|
|
}
|
|
|
|
var _getVerify = _get("verify");
|
|
if(_getVerify[0]){
|
|
$("#verifyScript").val(_getVerify[0]);
|
|
$("#verifyBtn").click();
|
|
window.location.hash = "#verify";
|
|
}
|
|
|
|
$(".qrcodeBtn").click(function(){
|
|
$("#qrcode").html("");
|
|
var thisbtn = $(this).parent().parent();
|
|
var qrstr = false;
|
|
var ta = $("textarea",thisbtn);
|
|
|
|
if(ta.length>0){
|
|
var w = (screen.availWidth > screen.availHeight ? screen.availWidth : screen.availHeight)/3;
|
|
var qrcode = new QRCode("qrcode", {width:w, height:w});
|
|
qrstr = $(ta).val();
|
|
if(qrstr.length > 2000){
|
|
$("#qrcode").html("<p>Sorry the data is too long for the QR generator.</p>");
|
|
}
|
|
} else {
|
|
var qrcode = new QRCode("qrcode");
|
|
qrstr = "bitcoin:"+$('.address',thisbtn).val();
|
|
}
|
|
|
|
if(qrstr){
|
|
qrcode.makeCode(qrstr);
|
|
}
|
|
});
|
|
|
|
$('input[title!=""], abbr[title!=""]').tooltip({'placement':'bottom'});
|
|
|
|
if (location.hash !== ''){
|
|
$('a[href="' + location.hash + '"]').tab('show');
|
|
}
|
|
|
|
$(".showKey").click(function(){
|
|
$("input[type='password']",$(this).parent().parent()).attr('type','text');
|
|
});
|
|
|
|
$("#homeBtn").click(function(e){
|
|
e.preventDefault();
|
|
history.pushState(null, null, '#home');
|
|
$("#header .active, #content .tab-content").removeClass("active");
|
|
$("#home").addClass("active");
|
|
});
|
|
|
|
$('a[data-toggle="tab"]').on('click', function(e) {
|
|
e.preventDefault();
|
|
if(e.target){
|
|
history.pushState(null, null, '#'+$(e.target).attr('href').substr(1));
|
|
}
|
|
});
|
|
|
|
window.addEventListener("popstate", function(e) {
|
|
var activeTab = $('[href=' + location.hash + ']');
|
|
if (activeTab.length) {
|
|
activeTab.tab('show');
|
|
} else {
|
|
$('.nav-tabs a:first').tab('show');
|
|
}
|
|
});
|
|
|
|
for(i=1;i<3;i++){
|
|
$(".pubkeyAdd").click();
|
|
}
|
|
|
|
validateOutputAmount();
|
|
|
|
/* capture mouse movement to add entropy */
|
|
var IE = document.all?true:false // Boolean, is browser IE?
|
|
if (!IE) document.captureEvents(Event.MOUSEMOVE)
|
|
document.onmousemove = getMouseXY;
|
|
function getMouseXY(e) {
|
|
var tempX = 0;
|
|
var tempY = 0;
|
|
if (IE) { // If browser is IE
|
|
tempX = event.clientX + document.body.scrollLeft;
|
|
tempY = event.clientY + document.body.scrollTop;
|
|
} else {
|
|
tempX = e.pageX;
|
|
tempY = e.pageY;
|
|
};
|
|
|
|
if (tempX < 0){tempX = 0};
|
|
if (tempY < 0){tempY = 0};
|
|
var xEnt = Crypto.util.bytesToHex([tempX]).slice(-2);
|
|
var yEnt = Crypto.util.bytesToHex([tempY]).slice(-2);
|
|
var addEnt = xEnt.concat(yEnt);
|
|
|
|
if ($("#entropybucket").html().indexOf(xEnt) == -1 && $("#entropybucket").html().indexOf(yEnt) == -1) {
|
|
$("#entropybucket").html(addEnt + $("#entropybucket").html());
|
|
};
|
|
|
|
if ($("#entropybucket").html().length > 128) {
|
|
$("#entropybucket").html($("#entropybucket").html().slice(0, 128))
|
|
};
|
|
|
|
return true;
|
|
};
|
|
});
|