The reload API data button (top-right) refreshes the API for new data The refreshed API data are added to memory as well as stored in IDB. The kBucket is reloaded
342 lines
13 KiB
JavaScript
342 lines
13 KiB
JavaScript
if( sessionStorage.profiles && sessionStorage.privKey && sessionStorage.selfID && sessionStorage.serverPass && sessionStorage.superNodeList ){
|
|
window.location.replace("home.html");
|
|
return;
|
|
}
|
|
|
|
const adminID = "FMabh7gTSyKPAb2Wi9sK5CBhV8nVFk783i";
|
|
|
|
if(blockchain == "FLO")
|
|
var api_url = `https://flosight.duckdns.org/`;
|
|
else if(blockchain == "FLO_TEST")
|
|
var api_url = `https://testnet-flosight.duckdns.org/`;
|
|
|
|
const sendAmt = 0.001 ;
|
|
const fee = 0.0005;
|
|
var username,privKey,floID;
|
|
var profiles;
|
|
|
|
function initAPIdata(){
|
|
return new Promise((resolve,reject) => {
|
|
console.log("initAPIdata");
|
|
getDatafromAPI().then(result => {
|
|
console.log(result);
|
|
getProfilesfromIDB().then(result => {
|
|
profiles = arrayToObject(result);
|
|
console.log(profiles);
|
|
sessionStorage.profiles = JSON.stringify(profiles);
|
|
getSuperNodeListfromIDB().then(result => {
|
|
console.log(result)
|
|
superNodeList = new Set(result);
|
|
sessionStorage.superNodeList = JSON.stringify(Array.from(superNodeList));
|
|
resolve('Refresh API data Successful!')
|
|
}).catch(error => {
|
|
reject(error);
|
|
});
|
|
}).catch(error => {
|
|
reject(error);
|
|
});
|
|
}).catch(error => {
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
function customAlert(msg,type){
|
|
color = {info:"#2196F3",danger:"#f44336",warning:"#ff9800",success:"#4CAF50"};
|
|
var alertBox = document.createElement('div');
|
|
alertBox.setAttribute('class','alert');
|
|
alertBox.style.backgroundColor = color[type];
|
|
alertBox.innerHTML = `<span class="closebtn" onclick="this.parentElement.style.display='none';">✖</span>
|
|
${msg}`;
|
|
document.getElementById("alert-container").appendChild(alertBox);
|
|
}
|
|
|
|
function disableForm(formId, disableFlag) {
|
|
var form = document.getElementById(formId);
|
|
for(var i=0; i<form.length; i++)
|
|
form[i].disabled = disableFlag;
|
|
}
|
|
|
|
function connect(){
|
|
document.getElementById("alert-container").innerHTML = '';
|
|
var serverPass = document.getElementById('serverPass');
|
|
initselfWebSocket(serverPass).then(result => {
|
|
customAlert(result,success);
|
|
disableForm('serverConnect',true);
|
|
disableForm('userSignIn',false);
|
|
}).catch(error => {
|
|
customAlert(error,danger);
|
|
});
|
|
}
|
|
|
|
function signIn(){
|
|
document.getElementById("alert-container").innerHTML = '';
|
|
username = document.getElementById('username').value;
|
|
privKey = document.getElementById('privKey').value;
|
|
var key = new Bitcoin.ECKey(privKey);
|
|
if(key.priv == null){
|
|
customAlert("Invalid FLO Private Key!",'danger');
|
|
return false;
|
|
}
|
|
key.setCompressed(true);
|
|
floID = key.getBitcoinAddress();
|
|
try{
|
|
console.log(floID)
|
|
if (profiles[floID] === undefined)
|
|
throw `${floID} is not registers to FLO Tweet!<br/>Register FLO ID to this onion?`;
|
|
if (profiles[floID].onionAddr != window.location.host)
|
|
throw `${floID} is registered to another onion!<br/>Change to this onion?`;
|
|
if (profiles[floID].name != username)
|
|
throw `${floID} is registered to another username!<br/>Change username?`;
|
|
login();
|
|
}catch(msg){
|
|
console.log(msg)
|
|
customAlert(`${msg}<span class="closebtn" onclick="this.parentElement.parentElement.style.display='none'; signUp();">✔</span>`,'warning');
|
|
}
|
|
}
|
|
|
|
async function login(){
|
|
sessionStorage.privKey = JSON.stringify(encrypt.createShamirsSecretShares(privKey,10,10));
|
|
sessionStorage.selfID = floID;
|
|
customAlert(`Welcome ${username}`,'info');
|
|
await sleep(3000);
|
|
window.location.replace("home.html");
|
|
}
|
|
|
|
function signUp(){
|
|
registerID(floID,window.location.host,privKey,privKey.getPubKeyHex(),username).then(result =>{
|
|
customAlert(`Registration Successful!<br/> txid : ${result}`,'success');
|
|
refreshAPIdata().then(result => {
|
|
console.log(result);
|
|
login();
|
|
}).catch(error => {
|
|
console.log(error);
|
|
});
|
|
}).catch(error => {
|
|
customAlert(error,'danger');
|
|
});
|
|
}
|
|
|
|
function ajax(method, uri){
|
|
var request = new XMLHttpRequest();
|
|
var url = `${api_url}/${uri}`
|
|
console.log(url)
|
|
var result;
|
|
request.open(method,url , false);
|
|
request.onload = function () {
|
|
if (request.readyState == 4 && request.status == 200)
|
|
result = this.response;
|
|
else {
|
|
console.log('error');
|
|
result = false;
|
|
}
|
|
};
|
|
request.send();
|
|
console.log(result);
|
|
return result;
|
|
}
|
|
|
|
function getDatafromAPI(){
|
|
return new Promise((resolve,reject) => {
|
|
var addr = adminID;
|
|
var idb = indexedDB.open("FLO_Tweet");
|
|
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
|
idb.onupgradeneeded = (event) => {
|
|
var db = event.target.result;
|
|
var objectStore0 = event.target.result.createObjectStore("superNodes");
|
|
var objectStore1 = db.createObjectStore("profiles",{ keyPath: 'floID' });
|
|
objectStore1.createIndex('onionAddr', 'onionAddr', { unique: false });
|
|
objectStore1.createIndex('name', 'name', { unique: false });
|
|
objectStore1.createIndex('pubKey', 'pubKey', { unique: false });
|
|
var objectStore2 = db.createObjectStore("lastTx");
|
|
var objectStore3 = db.createObjectStore("tweets",{ keyPath: 'tweetID' });
|
|
objectStore3.createIndex('tid', 'tid', { unique: false });
|
|
objectStore3.createIndex('floID', 'floID', { unique: false });
|
|
objectStore3.createIndex('time', 'time', { unique: false });
|
|
objectStore3.createIndex('data', 'data', { unique: false });
|
|
var objectStore4 = db.createObjectStore("lastTweet");
|
|
var objectStore5 = db.createObjectStore("followers");
|
|
var objectStore6 = db.createObjectStore("following");
|
|
var objectStore7 = event.target.result.createObjectStore("messages",{ keyPath: 'msgID' });
|
|
objectStore7.createIndex('time', 'time', { unique: false });
|
|
objectStore7.createIndex('text', 'text', { unique: false });
|
|
objectStore7.createIndex('floID', 'floID', { unique: false });
|
|
objectStore7.createIndex('type', 'type', { unique: false });
|
|
};
|
|
idb.onsuccess = (event) => {
|
|
var db = event.target.result;
|
|
var lastTx = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
|
console.log(addr);
|
|
new Promise((res,rej) => {
|
|
var lastTxReq = lastTx.get(addr);
|
|
lastTxReq.onsuccess = (event) => {
|
|
var lasttx = event.target.result;
|
|
if(lasttx === undefined){
|
|
lasttx = 0;
|
|
}
|
|
res(lasttx);
|
|
}
|
|
}).then(lasttx => {
|
|
var response = ajax("GET",`api/addrs/${addr}/txs`);
|
|
var nRequired = JSON.parse(response).totalItems - lasttx;
|
|
console.log(nRequired);
|
|
while(true && nRequired){
|
|
var response = ajax("GET",`api/addrs/${addr}/txs?from=0&to=${nRequired}`);
|
|
response = JSON.parse(response);
|
|
if (nRequired + lasttx != response.totalItems ){
|
|
nRequired = response.totalItems - lasttx;
|
|
continue;
|
|
}
|
|
response.items.reverse().forEach(tx => {
|
|
try {
|
|
if (tx.vin[0].addr == addr){
|
|
var data = JSON.parse(tx.floData).FLO_Tweet_SuperNode;
|
|
if(data !== undefined){
|
|
storeSuperNodeData(data).then(function (response) {
|
|
}).catch(error => {
|
|
console.log(error);
|
|
});
|
|
}
|
|
}else{
|
|
var data = JSON.parse(tx.floData).FLO_Tweet;
|
|
if(data !== undefined){
|
|
if(encrypt.getFLOIDfromPubkeyHex(data.pubKey)!=tx.vin[0].addr)
|
|
throw("PublicKey doesnot match with floID")
|
|
data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name, pubKey:data.pubKey};
|
|
storeProfile(data).then(function (response) {
|
|
}).catch(error => {
|
|
console.log(error);
|
|
});
|
|
}
|
|
}
|
|
} catch (error) {
|
|
//console.log(error)
|
|
}
|
|
});
|
|
var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
|
|
obs.put(response.totalItems,addr);
|
|
break;
|
|
}
|
|
db.close();
|
|
resolve('retrived data from API');
|
|
});
|
|
};
|
|
});
|
|
}
|
|
|
|
function getProfilesfromIDB(){
|
|
return new Promise((resolve,reject) => {
|
|
var idb = indexedDB.open("FLO_Tweet");
|
|
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
|
idb.onsuccess = (event) => {
|
|
var db = event.target.result;
|
|
var obs = db.transaction("profiles", "readwrite").objectStore("profiles");
|
|
var getReq = obs.getAll();
|
|
getReq.onsuccess = (event) => { resolve(event.target.result) }
|
|
getReq.onerror = (event) => { reject('Unable to read profiles!') }
|
|
db.close();
|
|
};
|
|
});
|
|
}
|
|
|
|
function getSuperNodeListfromIDB(){
|
|
return new Promise((resolve,reject) => {
|
|
var idb = indexedDB.open("FLO_Tweet");
|
|
idb.onerror = (event) => { reject("Error in opening IndexedDB!") };
|
|
idb.onsuccess = (event) => {
|
|
var db = event.target.result;
|
|
var obs = db.transaction("superNodes", "readwrite").objectStore("superNodes");
|
|
var getReq = obs.getAllKeys();
|
|
getReq.onsuccess = (event) => { resolve(event.target.result) }
|
|
getReq.onerror = (event) => { reject('Unable to read superNode list!') }
|
|
db.close();
|
|
};
|
|
});
|
|
}
|
|
|
|
function initselfWebSocket(serverPass){
|
|
return new Promise((resolve,reject) => {
|
|
selfWebsocket = new WebSocket("ws://"+location.host+"/ws");
|
|
selfWebsocket.onopen = (event) => {
|
|
console.log("Connecting");
|
|
selfWebsocket.send("$"+serverPass);
|
|
};
|
|
selfWebsocket.onclose = (event) => { console.log("DISCONNECTED") };
|
|
selfWebsocket.onmessage = (event) => {
|
|
console.log(event.data);
|
|
if(event.data == "$Access Denied!")
|
|
reject("Access Denied!");
|
|
else if(event.data == "$Access Granted!")
|
|
resolve("Access Granted!");
|
|
};
|
|
selfWebsocket.onerror = (event) => { console.log(event) };
|
|
});
|
|
}
|
|
|
|
function registerID(sender,onionAddr,wif,pubkey,username) {
|
|
return new Promise((resolve,reject) => {
|
|
var trx = bitjs.transaction();
|
|
var utxoAmt = 0.0;
|
|
var x = sendAmt+fee;
|
|
var response = ajax("GET",`api/addr/${sender}/utxo`);
|
|
var utxos = JSON.parse(response);
|
|
for(var x = utxos.length-1; x >= 0; x--){
|
|
if(utxoAmt < sendAmt+fee){
|
|
trx.addinput(utxos[x].txid, utxos[x].vout, utxos[x].scriptPubKey);
|
|
utxoAmt += utxos[x].amount;
|
|
}else
|
|
break;
|
|
}
|
|
console.log(utxoAmt+":"+(sendAmt+fee));
|
|
if(utxoAmt < sendAmt+fee)
|
|
reject("Insufficient balance!");
|
|
trx.addoutput(adminID, sendAmt);
|
|
console.log(adminID+":"+ sendAmt);
|
|
var change = utxoAmt-sendAmt-fee;
|
|
if(change>0)
|
|
trx.addoutput(sender, change);
|
|
console.log(sender+":"+ change);
|
|
var key = new Bitcoin.ECKey(wif);
|
|
var sendFloData = JSON.stringify({FLO_Tweet:{onionAddr:onionAddr, name: username, pubKey: pubkey}});;
|
|
trx.addflodata(sendFloData);
|
|
console.log(sendFloData);
|
|
var signedTxHash = trx.sign(wif, 1);
|
|
console.log(signedTxHash);
|
|
var txid = broadcastTx(signedTxHash);
|
|
if (txid)
|
|
resolve(txid);
|
|
else
|
|
reject('Registration Unsuccessful!')
|
|
});
|
|
}
|
|
|
|
function broadcastTx(signedTxHash) {
|
|
var http = new XMLHttpRequest();
|
|
var url = `${api_url}/api/tx/send`;
|
|
if (signedTxHash.length < 1) {
|
|
return false;
|
|
}
|
|
|
|
var params = `{"rawtx":"${signedTxHash}"}`;
|
|
var result;
|
|
http.open('POST', url, false);
|
|
|
|
//Send the proper header information along with the request
|
|
http.setRequestHeader('Content-type', 'application/json');
|
|
|
|
http.onreadystatechange = function () { //Call a function when the state changes.
|
|
if (http.readyState == 4 && http.status == 200) {
|
|
console.log(http.response);
|
|
var txid = JSON.parse(http.response).txid.result;
|
|
console.log("Transaction successful! txid : " + txid);
|
|
result = txid;
|
|
} else {
|
|
console.log(http.responseText);
|
|
result = false;
|
|
}
|
|
}
|
|
http.send(params);
|
|
return result;
|
|
}
|
|
|