mostly support lbry

it cannot sign timelocked txns because the private key is for
the underlying address, so it doesn't know which inputs to sign

also broadcasting doesnt work yet because there's no broadcast
endpoint on chainquery
This commit is contained in:
Alex Grintsvayg 2022-02-16 13:49:16 -05:00
parent cda4559cfd
commit f0ace3eae5
No known key found for this signature in database
GPG key ID: AEB3F089F86A22B5
3 changed files with 116 additions and 336 deletions

View file

@ -1382,13 +1382,7 @@
<b>Network</b>: <br>
<p class="text-muted">Select which network you'd like to use for key pair generation.</p>
<select class="form-control" id="coinjs_coin">
<option value="bitcoin_mainnet" rel="0x00;0x80;0x05;0x488b21e;0x488ade4;coinb.in;coinb.in">Bitcoin (mainnet)</option>
<option value="litecoin_mainnet" rel="0x30;0xb0;0x32;0x019da462;0x019d9cfe;blockcypher_litecoin;blockcypher_litecoin">Litecoin (mainnet)</option>
<option value="dogecoin_mainnet" rel="0x1e;0x9e;0x16;0x0827421e;0x089944e4;blockcypher_dogecoin;blockcypher_dogecoin">Dogecoin (mainnet)</option>
<option value="shadowcash_mainnet" rel="0x3f;0xbf;0x7d;0xee80286a;0xee8031e8;false;false">ShadowCash (mainnet)</option>
<option value="bitcoin_testnet" rel="0x6f;0xef;0xc4;0x043587cf;0x04358394;false;false">Bitcoin (testnet)</option>
<option value="custom" rel="0x00;0x80;0x05;0x488b21e;0x488ade4;false;false">Custom</option>
<option value="custom" rel="0x55;0x1c;0x7a;0x488b21e;0x488ade4;false;false">Custom</option>
</select>
</div>
</div>

View file

@ -10,11 +10,11 @@
var coinjs = window.coinjs = function () { };
/* public vars */
coinjs.pub = 0x00;
coinjs.priv = 0x80;
coinjs.multisig = 0x05;
coinjs.pub = 0x55;
coinjs.priv = 0x1c;
coinjs.multisig = 0x7a;
coinjs.hdkey = {'prv':0x0488ade4, 'pub':0x0488b21e};
coinjs.bech32 = {'charset':'qpzry9x8gf2tvdw0s3jn54khce6mua7l', 'version':0, 'hrp':'bc'};
coinjs.bech32 = {'charset':'qpzry9x8gf2tvdw0s3jn54khce6mua7l', 'version':0, 'hrp':'lbc'};
coinjs.compressed = false;
@ -22,10 +22,13 @@
coinjs.developer = '33tht1bKDgZVxb39MnZsWa8oxHXHvUYE4G'; //bitcoin
/* bit(coinb.in) api vars */
coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length-1]) == 'onion') ? 'coinbin3ravkwb24f7rmxx6w3snkjw45jhs5lxbh3yfeg3vpt6janwqd.onion' : 'coinb.in';
// coinjs.hostname = ((document.location.hostname.split(".")[(document.location.hostname.split(".")).length-1]) == 'onion') ? 'coinbin3ravkwb24f7rmxx6w3snkjw45jhs5lxbh3yfeg3vpt6janwqd.onion' : 'coinb.in';
coinjs.hostname = 'localhost:8000';
coinjs.host = ('https:'==document.location.protocol?'https://':'http://')+coinjs.hostname+'/api/';
coinjs.uid = '1';
coinjs.key = '12345678901234567890123456789012';
coinjs.chainqueryAPI = "https://chainquery.lbry.com/api/sql"
/* start of address functions */
@ -177,6 +180,37 @@
return {'address':address, 'redeemScript':redeemScript};
}
coinjs.timelockedAddress = function(pubkey, checklocktimeverify) {
if(checklocktimeverify < 0) {
throw "Parameter for OP_CHECKLOCKTIMEVERIFY is negative.";
}
var s = coinjs.script();
if (checklocktimeverify <= 16 && checklocktimeverify >= 1) {
s.writeOp(0x50 + checklocktimeverify);//OP_1 to OP_16 for minimal encoding
} else {
s.writeBytes(coinjs.numToScriptNumBytes(checklocktimeverify));
}
s.writeOp(0xb1);//OP_CHECKLOCKTIMEVERIFY
s.writeOp(0x75);//OP_DROP
s.writeOp(0x76);//OP_DUP
s.writeOp(0xa9);//OP_HASH160
s.writeBytes(Crypto.util.hexToBytes(pubkey));
s.writeOp(0x88);//88 OP_EQUALVERIFY
s.writeOp(0xac);//OP_CHECKSIG
var x = ripemd160(Crypto.SHA256(s.buffer, {asBytes: true}), {asBytes: true});
x.unshift(coinjs.multisig);
var r = x;
r = Crypto.SHA256(Crypto.SHA256(r, {asBytes: true}), {asBytes: true});
var checksum = r.slice(0,4);
var redeemScript = Crypto.util.bytesToHex(s.buffer);
var address = coinjs.base58encode(x.concat(checksum));
return {'address':address, 'redeemScript':redeemScript};
}
/* create a new segwit address */
coinjs.segwitAddress = function(pubkey){
var keyhash = [0x00,0x14].concat(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubkey), {asBytes: true}), {asBytes: true}));
@ -321,7 +355,8 @@
/* retreive the balance from a given address */
coinjs.addressBalance = function(address, callback){
coinjs.ajax(coinjs.host+'?uid='+coinjs.uid+'&key='+coinjs.key+'&setmodule=addresses&request=bal&address='+address+'&r='+Math.random(), callback, "GET");
const query = 'select balance from address where address = "'+address+'";'
coinjs.ajax(coinjs.chainqueryAPI+'?query='+encodeURIComponent(query), (response)=>{callback(JSON.parse(response))}, "GET");
}
/* decompress an compressed public key */
@ -899,6 +934,16 @@
var rs = Crypto.util.bytesToHex(s.buffer);
r.redeemscript = rs;
r.type = "hodl__";
} else if(s.chunks.length == 8 && s.chunks[1] == 177 && s.chunks[2] == 117 && s.chunks[3] == 118 && s.chunks[7] == 172){
// ^ <unlocktime> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 <pubkey> OP_EQUALVERIFY OP_CHECKSIG ^
r = {}
r.pubkey = Crypto.util.bytesToHex(s.chunks[5]);
r.checklocktimeverify = coinjs.bytesToNum(s.chunks[0].slice());
r.address = coinjs.timelockedAddress(r.pubkey, r.checklocktimeverify).address;
var rs = Crypto.util.bytesToHex(s.buffer);
r.redeemscript = rs;
r.type = "hodl__";
}
} catch(e) {
// console.log(e);
@ -1064,12 +1109,14 @@
/* list unspent transactions */
r.listUnspent = function(address, callback) {
coinjs.ajax(coinjs.host+'?uid='+coinjs.uid+'&key='+coinjs.key+'&setmodule=addresses&request=unspent&address='+address+'&r='+Math.random(), callback, "GET");
const query = 'select o.transaction_hash, o.vout, o.value, o.script_pub_key_hex, o.type from address a inner join transaction_address ta on a.id = ta.address_id inner join output o on o.transaction_id = ta.transaction_id and o.is_spent = 0 and o.type not in ("nonstandard","nulldata") and o.address_list = \'["'+address+'"]\' where a.address = "'+address+'";'
coinjs.ajax(coinjs.chainqueryAPI+'?query='+encodeURIComponent(query), (response)=>{callback(JSON.parse(response))}, "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");
const query = 'select transaction_hash, vout, value, script_pub_key_hex from output where is_spent = 0 and type not in ("nonstandard","nulldata") and transaction_hash = "'+txid+'"'
coinjs.ajax(coinjs.chainqueryAPI+'?query='+encodeURIComponent(query), (response)=>{callback(JSON.parse(response))}, "GET");
}
/* add unspent to transaction */
@ -1081,23 +1128,12 @@
var total = 0;
var x = {};
if (window.DOMParser) {
parser=new DOMParser();
xmlDoc=parser.parseFromString(data,"text/xml");
} else {
xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async=false;
xmlDoc.loadXML(data);
}
var unspent = xmlDoc.getElementsByTagName("unspent")[0];
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(data){
for(i=0;i<=data["data"].length;i++){
var u = data["data"][i]
var txhash = u["transaction_hash"];
var n = u["vout"];
var scr = script || n["script_pub_key_hex"];
if(segwit){
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
@ -1106,22 +1142,19 @@
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));
s.writeBytes(coinjs.numToBytes(u["value"]));
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;
value += u["value"];
total++;
}
}
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);
});

View file

@ -2,9 +2,9 @@ $(document).ready(function() {
/* open wallet code */
var explorer_tx = "https://coinb.in/tx/"
var explorer_addr = "https://coinb.in/addr/"
var explorer_block = "https://coinb.in/block/"
var explorer_tx = "https://explorer.lbry.com/tx/"
var explorer_addr = "https://explorer.lbry.com/address/"
var explorer_block = "https://explorer.lbry.com/blocks/"
var wallet_timer = false;
@ -202,7 +202,7 @@ $(document).ready(function() {
tx2.broadcast(function(data){
if($(data).find("result").text()=="1"){
$("#walletSendConfirmStatus").removeClass('hidden').addClass('alert-success').html('txid: <a href="https://coinb.in/tx/'+$(data).find("txid").text()+'" target="_blank">'+$(data).find("txid").text()+'</a>');
$("#walletSendConfirmStatus").removeClass('hidden').addClass('alert-success').html('txid: <a href="'+explorer_tx+$(data).find("txid").text()+'" target="_blank">'+$(data).find("txid").text()+'</a>');
} else {
$("#walletSendConfirmStatus").removeClass('hidden').addClass('alert-danger').html(unescape($(data).find("response").text()).replace(/\+/g,' '));
$("#walletSendFailTransaction").removeClass('hidden');
@ -302,14 +302,13 @@ $(document).ready(function() {
function walletBalance(){
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();
if(data["success"]){
const v = data["data"][0]["balance"];
$("#walletBalance").html(v+" LBC").attr('rel',v).fadeOut().fadeIn();
} else {
$("#walletBalance").html("0.00 BTC").attr('rel',v).fadeOut().fadeIn();
$("#walletBalance").html("0.00 LBC").attr('rel',0).fadeOut().fadeIn();
}
$("#walletLoader").addClass("hidden");
@ -908,34 +907,7 @@ $(document).ready(function() {
var host = $(this).attr('rel');
// api: blockcypher blockchair chain.so
// network name "btc" "bitcoin" "BTC"
// network name "ltc" "litecoin" "LTC"
// network name "doge" "dogecoin" "DOGE"
if(host=='chain.so_bitcoinmainnet'){
listUnspentChainso(redeem, "BTC");
} else if(host=='chain.so_litecoin'){
listUnspentChainso(redeem, "LTC");
} else if(host=='chain.so_dogecoin'){
listUnspentChainso(redeem, "DOGE");
} else if(host=='blockcypher_bitcoinmainnet'){
listUnspentBlockcypher(redeem, "btc");
} else if(host=='blockcypher_litecoin'){
listUnspentBlockcypher(redeem, "ltc");
} else if(host=='blockcypher_dogecoin'){
listUnspentBlockcypher(redeem, "doge");
} else if(host=='blockchair_bitcoinmainnet'){
listUnspentBlockchair(redeem, "bitcoin");
} else if(host=='blockchair_litecoin'){
listUnspentBlockchair(redeem, "litecoin");
} else if(host=='blockchair_dogecoin'){
listUnspentBlockchair(redeem, "dogecoin");
} else {
listUnspentDefault(redeem);
}
listUnspentDefault(redeem);
if($("#redeemFromStatus").hasClass("hidden")) {
// An ethical dilemma: Should we automatically set nLockTime?
@ -1001,28 +973,30 @@ $(document).ready(function() {
$('#recipients .row[rel="'+redeem.addr+'"]').parent().remove();
$.each(redeem.decodedRs.pubkeys, function(i, o){
$.each($("#mediatorList option"), function(mi, mo){
if (redeem.decodedRs.pubkeys) {
$.each(redeem.decodedRs.pubkeys, function(i, o){
$.each($("#mediatorList option"), function(mi, mo){
var ms = ($(mo).val()).split(";");
var ms = ($(mo).val()).split(";");
var pubkey = ms[0]; // mediators pubkey
var fee = ms[2]*1; // fee in a percentage
var payto = coinjs.pubkey2address(pubkey); // pay to mediators address
var pubkey = ms[0]; // mediators pubkey
var fee = ms[2]*1; // fee in a percentage
var payto = coinjs.pubkey2address(pubkey); // pay to mediators address
if(o==pubkey){ // matched a mediators pubkey?
if(o==pubkey){ // matched a mediators pubkey?
var clone = '<span><div class="row recipients mediator mediator_'+pubkey+'" rel="'+redeem.addr+'">'+$("#recipients .addressAddTo").parent().parent().html()+'</div><br></span>';
$("#recipients").prepend(clone);
var clone = '<span><div class="row recipients mediator mediator_'+pubkey+'" rel="'+redeem.addr+'">'+$("#recipients .addressAddTo").parent().parent().html()+'</div><br></span>';
$("#recipients").prepend(clone);
$("#recipients .mediator_"+pubkey+" .glyphicon-plus:first").removeClass('glyphicon-plus');
$("#recipients .mediator_"+pubkey+" .address:first").val(payto).attr('disabled', true).attr('readonly',true).attr('title','Medation fee for '+$(mo).html());
$("#recipients .mediator_"+pubkey+" .glyphicon-plus:first").removeClass('glyphicon-plus');
$("#recipients .mediator_"+pubkey+" .address:first").val(payto).attr('disabled', true).attr('readonly',true).attr('title','Medation fee for '+$(mo).html());
var amount = ((fee*$("#totalInput").html())/100).toFixed(8);
$("#recipients .mediator_"+pubkey+" .amount:first").attr('disabled',(((amount*1)==0)?false:true)).val(amount).attr('title','Medation fee for '+$(mo).html());
}
var amount = ((fee*$("#totalInput").html())/100).toFixed(8);
$("#recipients .mediator_"+pubkey+" .amount:first").attr('disabled',(((amount*1)==0)?false:true)).val(amount).attr('title','Medation fee for '+$(mo).html());
}
});
});
});
}
validateOutputAmount();
}
@ -1075,18 +1049,21 @@ $(document).ready(function() {
if(redeem.from == 'txid'){
tx.getTransaction(redeem.addr, function(data){
if (data["error"]) {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs. '+data["error"]);
$("#redeemFromBtn").html("Load").attr('disabled',false);
return;
}
$("#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);
$.each(data["data"], function(i,o){
var tx = o["transaction_hash"];
var n = o["vout"];
var script = (redeem.redeemscript==true) ? redeem.decodedRs : o["script_pub_key_hex"];
var amount = o["value"];
addOutput(tx, n, script, amount);
});
$("#recipients .addressRemoveTo").click();
@ -1109,14 +1086,20 @@ $(document).ready(function() {
// unspent from address
tx.listUnspent(redeem.addr, function(data){
if (data["error"]) {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs. '+data["error"]);
$("#redeemFromBtn").html("Load").attr('disabled',false);
return;
}
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>');
$.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 = (redeem.redeemscript==true) ? redeem.decodedRs : $(o).find("script").text();
var amount = (($(o).find("value").text()*1)/100000000).toFixed(8);
$.each(data["data"], function(i,o){
var tx = o["transaction_hash"];
var n = o["vout"];
var script = (redeem.redeemscript==true) ? redeem.decodedRs : o["script_pub_key_hex"];
var amount = o["value"];
addOutput(tx, n, script, amount);
});
@ -1130,107 +1113,6 @@ $(document).ready(function() {
}
/* retrieve unspent data from blockcypher */
function listUnspentBlockcypher(redeem,network){
$.ajax ({
type: "GET",
url: "https://api.blockcypher.com/v1/"+network+"/main/addrs/"+redeem.addr+"?includeScript=true&unspentOnly=true",
dataType: "json",
error: function(data) {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs!');
},
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>');
for(var i in data.txrefs){
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;
var script = (redeem.redeemscript==true) ? redeem.decodedRs : o.script;
var amount = ((o.value.toString()*1)/100000000).toFixed(8);
addOutput(tx, n, script, amount);
}
}
} else {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs.');
}
},
complete: function(data, status) {
$("#redeemFromBtn").html("Load").attr('disabled',false);
totalInputAmount();
}
});
}
/* retrieve unspent data from blockchair */
function listUnspentBlockchair(redeem,network){
$.ajax ({
type: "GET",
url: "https://api.blockchair.com/"+network+"/dashboards/address/"+redeem.addr,
dataType: "json",
error: function(data) {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs!');
},
success: function(data) {
if((data.context && data.data) && data.context.code =='200'){
$("#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>');
var all_info = data.data[redeem.addr];
for(var i in all_info.utxo){
var o = all_info.utxo[i];
var tx = ((""+o.transaction_hash).match(/.{1,2}/g).reverse()).join("")+'';
if(tx.match(/^[a-f0-9]+$/)){
var n = o.index;
var script = (redeem.redeemscript==true) ? redeem.decodedRs : all_info.address.script_hex;
var amount = ((o.value.toString()*1)/100000000).toFixed(8);
addOutput(tx, n, script, amount);
}
}
} else {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs.');
}
},
complete: function(data, status) {
$("#redeemFromBtn").html("Load").attr('disabled',false);
totalInputAmount();
}
});
}
/* retrieve unspent data from chainso */
function listUnspentChainso(redeem, network){
$.ajax ({
type: "GET",
url: "https://chain.so/api/v2/get_tx_unspent/"+network+"/"+redeem.addr,
dataType: "json",
error: function(data) {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> Unexpected error, unable to retrieve unspent outputs!');
},
success: function(data) {
if((data.status && data.data) && data.status=='success'){
$("#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.data.txs){
var o = data.data.txs[i];
var tx = ((""+o.txid).match(/.{1,2}/g).reverse()).join("")+'';
if(tx.match(/^[a-f0-9]+$/)){
var n = o.output_no;
var script = (redeem.redeemscript==true) ? redeem.decodedRs : o.script_hex;
var amount = o.value;
addOutput(tx, n, script, amount);
}
}
} else {
$("#redeemFromStatus").removeClass('hidden').html('<span class="glyphicon glyphicon-exclamation-sign"></span> 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 */
@ -1303,7 +1185,7 @@ $(document).ready(function() {
error: function(data) {
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(" There was an error submitting your request, please try again").prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
},
success: function(data) {
success: 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').removeClass("hidden").html(' TXID: ' + $(data).find("txid").text() + '<br> <a href="https://coinb.in/tx/' + $(data).find("txid").text() + '" target="_blank">View on Blockchain</a>');
@ -1318,89 +1200,6 @@ $(document).ready(function() {
});
}
// broadcast transaction via chain.so (mainnet)
function rawSubmitChainso(thisbtn, network){
$(thisbtn).val('Please wait, loading...').attr('disabled',true);
$.ajax ({
type: "POST",
url: "https://chain.so/api/v2/send_tx/"+network+"/",
data: {"tx_hex":$("#rawTransaction").val()},
dataType: "json",
error: function(data) {
var obj = $.parseJSON(data.responseText);
var r = ' ';
r += (obj.data.tx_hex) ? obj.data.tx_hex : '';
r = (r!='') ? r : ' Failed to broadcast'; // build response
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(r).prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
},
success: function(data) {
if(data.status && data.data.txid){
$("#rawTransactionStatus").addClass('alert-success').removeClass('alert-danger').removeClass("hidden").html(' TXID: ' + data.data.txid + '<br> <a href="https://chain.so/tx/'+network+'/' + data.data.txid + '" target="_blank">View on Blockchain Explorer</a>');
} else {
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(' Unexpected error, please try again').prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
}
},
complete: function(data, status) {
$("#rawTransactionStatus").fadeOut().fadeIn();
$(thisbtn).val('Submit').attr('disabled',false);
}
});
}
// broadcast transaction via blockcypher.com (mainnet)
function rawSubmitblockcypher(thisbtn, network){
$(thisbtn).val('Please wait, loading...').attr('disabled',true);
$.ajax ({
type: "POST",
url: "https://api.blockcypher.com/v1/"+network+"/main/txs/push",
data: JSON.stringify({"tx":$("#rawTransaction").val()}),
error: function(data) {
var r = 'Failed to broadcast: error code=' + data.status.toString() + ' ' + data.statusText;
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(r).prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
},
success: function(data) {
if((data.tx) && data.tx.hash){
$("#rawTransactionStatus").addClass('alert-success').removeClass('alert-danger').removeClass("hidden")
.html(' TXID: ' + data.tx.hash + '<br> <a href="https://live.blockcypher.com/'+network+'/tx/' + data.tx.hash + '" target="_blank">View on Blockchain Explorer</a>');
} else {
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(' Unexpected error, please try again').prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
}
},
complete: function(data, status) {
$("#rawTransactionStatus").fadeOut().fadeIn();
$(thisbtn).val('Submit').attr('disabled',false);
}
});
}
// broadcast transaction via blockchair
function rawSubmitblockchair(thisbtn, network){
$(thisbtn).val('Please wait, loading...').attr('disabled',true);
$.ajax ({
type: "POST",
url: "https://api.blockchair.com/"+network+"/push/transaction",
data: {"data":$("#rawTransaction").val()},
dataType: "json",
error: function(data) {
var r = 'Failed to broadcast: error code=' + data.status.toString() + ' ' + data.statusText;
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(r).prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
// console.error(JSON.stringify(data, null, 4));
},
success: function(data) {
// console.info(JSON.stringify(data, null, 4));
if((data.context && data.data) && data.context.code=='200'){
$("#rawTransactionStatus").addClass('alert-success').removeClass('alert-danger').removeClass("hidden")
.html(' TXID: ' + data.data.transaction_hash + '<br> <a href="https://blockchair.com/'+network+'/transaction/' + data.data.transaction_hash + '" target="_blank">View on Blockchain Explorer</a>');
} else {
$("#rawTransactionStatus").addClass('alert-danger').removeClass('alert-success').removeClass("hidden").html(' Unexpected error, please try again').prepend('<span class="glyphicon glyphicon-exclamation-sign"></span>');
}
},
complete: function(data, status) {
$("#rawTransactionStatus").fadeOut().fadeIn();
$(thisbtn).val('Submit').attr('disabled',false);
}
});
}
@ -1915,55 +1714,9 @@ $(document).ready(function() {
});
function configureBroadcast(){
var host = $("#coinjs_broadcast option:selected").val();
// api: blockcypher blockchair chain.so
// network name "btc" "bitcoin" "BTC"
// network name "ltc" "litecoin" "LTC"
// network name "doge" "dogecoin" "DOGE"
$("#rawSubmitBtn").unbind("");
if(host=="chain.so_bitcoinmainnet"){
$("#rawSubmitBtn").click(function(){
rawSubmitChainso(this, "BTC");
});
} else if(host=="chain.so_litecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitChainso(this, "LTC");
});
} else if(host=="chain.so_dogecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitChainso(this, "DOGE");
});
} else if(host=="blockcypher_bitcoinmainnet"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockcypher(this, "btc");
});
} else if(host=="blockcypher_litecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockcypher(this, "ltc");
});
} else if(host=="blockcypher_dogecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockcypher(this, "doge");
});
} else if(host=="blockchair_bitcoinmainnet"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockchair(this, "bitcoin");
});
} else if(host=="blockchair_litecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockchair(this, "litecoin");
});
} else if(host=="blockchair_dogecoin"){
$("#rawSubmitBtn").click(function(){
rawSubmitblockchair(this, "dogecoin");
});
} else {
$("#rawSubmitBtn").click(function(){
rawSubmitDefault(this); // revert to default
});
}
$("#rawSubmitBtn").click(function(){
rawSubmitDefault(this); // revert to default
});
}
function configureGetUnspentTx(){