diff --git a/README.md b/README.md index a5080cb..7053f24 100644 --- a/README.md +++ b/README.md @@ -26,5 +26,6 @@ Coinb.in supports a number of key features such as: - An offical .onion address for tor users. - Offline qrcode creator and scanning tool - HD (bip32) support +- supports altcoins such as litecoin Donate to 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg to see more development! diff --git a/index.html b/index.html index 15c4c63..3cf795a 100644 --- a/index.html +++ b/index.html @@ -218,8 +218,8 @@
- - + +

@@ -442,6 +442,12 @@
+ + + +

The settings page can be used to select alternative networks of which you can retrieve your unspent outputs and broadcast a signed transaction into.

+ +
@@ -815,7 +821,8 @@

Select which network you'd like to use for key pair generation.

@@ -858,7 +865,7 @@
-
You will not be able to automatically broadcast or retreive your unspent inputs from coinb.in when using this setting and will need to use your desktop client instead, however everything else will continue to function normally.
+
You will not be able to automatically broadcast or retreive your unspent outputs from coinb.in when using this setting and will need to use your desktop client instead, however everything else such as creating key pairs, addresses, transaction generation and signing will continue to function normally.

@@ -870,7 +877,8 @@ @@ -883,6 +891,8 @@

Select the network you wish to retreive your unspent inputs from

diff --git a/js/coinbin.js b/js/coinbin.js index 6e5b5f7..0faef17 100644 --- a/js/coinbin.js +++ b/js/coinbin.js @@ -507,71 +507,177 @@ $(document).ready(function() { } } + /* redeem from button code */ + $("#redeemFromBtn").click(function(){ - var thisbtn = this; - var addr = ''; - var isMultiSig = false; - var s = $("#redeemFrom").val(); + var string = $("#redeemFrom").val(); + var redeem = redeemingFrom(string); $("#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){ + if(redeem.from=='multisigAddress'){ $("#redeemFromStatus").removeClass('hidden').html(' 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(' The address or multisig redeem script you have entered is invalid'); - } + return false; } + if(redeem.from=='other'){ + $("#redeemFromStatus").removeClass('hidden').html(' The address or multisig redeem script you have entered is invalid'); + return false; + } + + if($("#clearInputsOnLoad").is(":checked")){ + $("#inputs .txidRemove, #inputs .txidClear").click(); + } + + $("#redeemFromBtn").html("Please wait, loading...").attr('disabled',true); + + var host = $(this).attr('rel'); + if(host=='blockr.io_bitcoinmainnet'){ + listUnspentBlockrio_BitcoinMainnet(redeem.addr); + } else if(host=='chain.so_litecoin'){ + listUnspentChainso_Litecoin(redeem.addr); + } else { + listUnspentDefault(redeem.addr); + } + }); + + /* function to determine what we are redeeming from */ + function redeemingFrom(string){ + var r = {}; + var decode = coinjs.addressDecode(string); + if(decode.version == coinjs.pub){ // regular address + r.addr = string; + r.from = 'address'; + r.isMultisig = false; + } else if (decode.version == coinjs.priv){ // wif key + var a = coinjs.wif2address(string); + r.addr = a['address']; + r.from = 'wif'; + r.isMultisig = false; + } else if (decode.version == coinjs.multisig){ // mulisig address + r.addr = ''; + r.from = 'multisigAddress'; + r.isMultisig = false; + } else { + var script = coinjs.script(); + var decodeRs = script.decodeRedeemScript(string); + if(decodeRs){ // redeem script + r.addr = decodeRs['address']; + r.from = 'redeemScript'; + r.isMultisig = true; + } else { // something else + r.addr = ''; + r.from = 'other'; + r.isMultisig = false; + } + } + return r; + } + + /* global function to add outputs to page */ + function addOutput(tx, n, script, amount) { + if(tx){ + if($("#inputs .txId:last").val()!=""){ + $("#inputs .txidAdd").click(); + } + + $("#inputs .row:last input").attr('disabled',true); + + var txid = ((tx).match(/.{1,2}/g).reverse()).join("")+''; + + $("#inputs .txId:last").val(txid); + $("#inputs .txIdN:last").val(n); + $("#inputs .txIdAmount:last").val(amount); + $("#inputs .txIdScript:last").val(script); + } + } + + /* default function to retreive unspent outputs*/ + function listUnspentDefault(addr){ var tx = coinjs.transaction(); tx.listUnspent(addr, function(data){ if(addr) { - if($("#clearInputsOnLoad").is(":checked")){ - $("#inputs .txidRemove, #inputs .txidClear").click(); - } - $("#redeemFromAddress").removeClass('hidden').html(' Retrieved unspent inputs from address '+addr+''); $.each($(data).find("unspent").children(), function(i,o){ + var tx = $(o).find("tx_hash").text(); + var n = $(o).find("tx_output_n").text(); + var script = (addr.isMultisig==true) ? $("#inputs .txIdScript:last").val(string) :$(o).find("script").text(); + var amount = (($(o).find("value").text()*1)/100000000).toFixed(8); - 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()); - } + addOutput(tx, n, script, amount); }); } - $(thisbtn).html("Load").attr('disabled',false); + $("#redeemFromBtn").html("Load").attr('disabled',false); totalInputAmount(); }); - }); + } + + /* retrieve unspent data from blockrio for mainnet */ + function listUnspentBlockrio_BitcoinMainnet(addr){ + $.ajax ({ + type: "POST", + url: "https://btc.blockr.io/api/v1/address/unspent/"+addr, + dataType: "json", + error: function(data) { + $("#redeemFromStatus").removeClass('hidden').html(' Unexpected error, unable to retrieve unspent outputs!'); + }, + success: function(data) { + if((data.status && data.data) && data.status=='success'){ + $("#redeemFromAddress").removeClass('hidden').html(' Retrieved unspent inputs from address '+addr+''); + for(var i in data.data.unspent){ + var o = data.data.unspent[i]; + var tx = o.tx; + var n = o.n; + var script = o.script; + var amount = o.amount; + addOutput(tx, n, script, amount); + } + } else { + $("#redeemFromStatus").removeClass('hidden').html(' Unexpected error, unable to retrieve unspent outputs.'); + } + }, + complete: function(data, status) { + $("#redeemFromBtn").html("Load").attr('disabled',false); + totalInputAmount(); + } + }); + } + + /* retrieve unspent data from blockrio for litecoin */ + function listUnspentChainso_Litecoin(addr){ + $.ajax ({ + type: "GET", + url: "https://chain.so/api/v2/get_tx_unspent/ltc/"+addr, + dataType: "json", + error: function(data) { + $("#redeemFromStatus").removeClass('hidden').html(' Unexpected error, unable to retrieve unspent outputs!'); + }, + success: function(data) { + console.log(data); + if((data.status && data.data) && data.status=='success'){ + $("#redeemFromAddress").removeClass('hidden').html(' Retrieved unspent inputs from address '+addr+''); + for(var i in data.data.txs){ + var o = data.data.txs[i]; + var tx = o.txid; + var n = o.output_no; + var script = o.script_hex; + var amount = o.value; + addOutput(tx, n, script, amount); + } + } else { + $("#redeemFromStatus").removeClass('hidden').html(' Unexpected error, unable to retrieve unspent outputs.'); + } + }, + complete: function(data, status) { + $("#redeemFromBtn").html("Load").attr('disabled',false); + totalInputAmount(); + } + }); + } + + /* math to calculate the inputs and outputs */ function totalInputAmount(){ $("#totalInput").html('0.00'); @@ -679,12 +785,12 @@ $(document).ready(function() { }); } - // broadcast transaction via blockr.io (testnet) - function rawSubmitBlockrio_BitcoinTestnet(thisbtn){ + // broadcast transaction via blockr.io for litecoin + function rawSubmitBlockrio_litecoin(thisbtn){ $(thisbtn).val('Please wait, loading...').attr('disabled',true); $.ajax ({ type: "POST", - url: "https://tbtc.blockr.io/api/v1/tx/push", + url: "https://ltc.blockr.io/api/v1/tx/push", data: {"hex":$("#rawTransaction").val()}, dataType: "json", error: function(data) { @@ -1068,6 +1174,9 @@ $(document).ready(function() { $("#settingsBtn").click(function(){ + // log out of openwallet + $("#walletLogout").click(); + $("#statusSettings").removeClass("alert-success").removeClass("alert-danger").addClass("hidden").html(""); $("#settings .has-error").removeClass("has-error"); @@ -1096,8 +1205,6 @@ $(document).ready(function() { }); $("#coinjs_coin").change(function(){ - // log out of openwallet - $("#walletLogout").click(); var o = ($("option:selected",this).attr("rel")).split(";"); @@ -1136,11 +1243,10 @@ $(document).ready(function() { function configureBroadcast(){ var host = $("#coinjs_broadcast option:selected").val(); - var tx = coinjs.transaction(); $("#rawSubmitBtn").unbind(""); - if(host=="blockr.io_bitcointestnet"){ + if(host=="blockr.io_litecoin"){ $("#rawSubmitBtn").click(function(){ - rawSubmitBlockrio_BitcoinTestnet(this) + rawSubmitBlockrio_litecoin(this) }); } else if(host=="blockr.io_bitcoinmainnet"){ $("#rawSubmitBtn").click(function(){ @@ -1151,14 +1257,10 @@ $(document).ready(function() { rawSubmitDefault(this); // revert to default }); } - } function configureGetUnspentTx(){ - // function coming soon, which will allow you to retrieve unspent inputs - // from other block chain providers - - return false; + $("#redeemFromBtn").attr('rel',$("#coinjs_utxo option:selected").val()); } /* capture mouse movement to add entropy */