Updating std ops
This commit is contained in:
parent
b88497cc2f
commit
64d2dce582
257
compactIDB.js
257
compactIDB.js
@ -1,257 +0,0 @@
|
|||||||
(function (EXPORTS) { //compactIDB v2.1.2
|
|
||||||
/* Compact IndexedDB operations */
|
|
||||||
'use strict';
|
|
||||||
const compactIDB = EXPORTS;
|
|
||||||
|
|
||||||
var defaultDB;
|
|
||||||
|
|
||||||
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
|
|
||||||
const IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
|
|
||||||
const IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
|
|
||||||
|
|
||||||
if (!indexedDB) {
|
|
||||||
console.error("Your browser doesn't support a stable version of IndexedDB.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.setDefaultDB = dbName => defaultDB = dbName;
|
|
||||||
|
|
||||||
Object.defineProperty(compactIDB, 'default', {
|
|
||||||
get: () => defaultDB,
|
|
||||||
set: dbName => defaultDB = dbName
|
|
||||||
});
|
|
||||||
|
|
||||||
function getDBversion(dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
resolve(db.version)
|
|
||||||
db.close()
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function upgradeDB(dbName, createList = null, deleteList = null) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getDBversion(dbName).then(version => {
|
|
||||||
var idb = indexedDB.open(dbName, version + 1);
|
|
||||||
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
|
||||||
idb.onupgradeneeded = (event) => {
|
|
||||||
let db = event.target.result;
|
|
||||||
if (createList instanceof Object) {
|
|
||||||
if (Array.isArray(createList)) {
|
|
||||||
let tmp = {}
|
|
||||||
createList.forEach(o => tmp[o] = {})
|
|
||||||
createList = tmp
|
|
||||||
}
|
|
||||||
for (let o in createList) {
|
|
||||||
let obs = db.createObjectStore(o, createList[o].options || {});
|
|
||||||
if (createList[o].indexes instanceof Object)
|
|
||||||
for (let i in createList[o].indexes)
|
|
||||||
obs.createIndex(i, i, createList[o].indexes || {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Array.isArray(deleteList))
|
|
||||||
deleteList.forEach(o => db.deleteObjectStore(o));
|
|
||||||
resolve('Database upgraded')
|
|
||||||
}
|
|
||||||
idb.onsuccess = (event) => event.target.result.close();
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.initDB = function (dbName, objectStores = {}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!(objectStores instanceof Object))
|
|
||||||
return reject('ObjectStores must be an object or array')
|
|
||||||
defaultDB = defaultDB || dbName;
|
|
||||||
var idb = indexedDB.open(dbName);
|
|
||||||
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
|
||||||
idb.onsuccess = (event) => {
|
|
||||||
var db = event.target.result;
|
|
||||||
let cList = Object.values(db.objectStoreNames);
|
|
||||||
var obs = {},
|
|
||||||
a_obs = {},
|
|
||||||
d_obs = [];
|
|
||||||
if (!Array.isArray(objectStores))
|
|
||||||
var obs = objectStores
|
|
||||||
else
|
|
||||||
objectStores.forEach(o => obs[o] = {})
|
|
||||||
let nList = Object.keys(obs)
|
|
||||||
for (let o of nList)
|
|
||||||
if (!cList.includes(o))
|
|
||||||
a_obs[o] = obs[o]
|
|
||||||
for (let o of cList)
|
|
||||||
if (!nList.includes(o))
|
|
||||||
d_obs.push(o)
|
|
||||||
if (!Object.keys(a_obs).length && !d_obs.length)
|
|
||||||
resolve("Initiated IndexedDB");
|
|
||||||
else
|
|
||||||
upgradeDB(dbName, a_obs, d_obs)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var idb = indexedDB.open(dbName);
|
|
||||||
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
|
||||||
idb.onupgradeneeded = (event) => {
|
|
||||||
event.target.result.close();
|
|
||||||
deleteDB(dbName).then(_ => null).catch(_ => null).finally(_ => reject("Datebase not found"))
|
|
||||||
}
|
|
||||||
idb.onsuccess = (event) => resolve(event.target.result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var deleteReq = indexedDB.deleteDatabase(dbName);;
|
|
||||||
deleteReq.onerror = (event) => reject("Error deleting database!");
|
|
||||||
deleteReq.onsuccess = (event) => resolve("Database deleted successfully");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
|
||||||
let writeReq = (key ? obs.put(data, key) : obs.put(data));
|
|
||||||
writeReq.onsuccess = (evt) => resolve(`Write data Successful`);
|
|
||||||
writeReq.onerror = (evt) => reject(
|
|
||||||
`Write data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
|
||||||
let addReq = (key ? obs.add(data, key) : obs.add(data));
|
|
||||||
addReq.onsuccess = (evt) => resolve(`Add data successful`);
|
|
||||||
addReq.onerror = (evt) => reject(
|
|
||||||
`Add data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
|
||||||
let delReq = obs.delete(key);
|
|
||||||
delReq.onsuccess = (evt) => resolve(`Removed Data ${key}`);
|
|
||||||
delReq.onerror = (evt) => reject(
|
|
||||||
`Remove data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.clearData = function (obsName, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
|
||||||
let clearReq = obs.clear();
|
|
||||||
clearReq.onsuccess = (evt) => resolve(`Clear data Successful`);
|
|
||||||
clearReq.onerror = (evt) => reject(`Clear data Unsuccessful`);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
|
||||||
let getReq = obs.get(key);
|
|
||||||
getReq.onsuccess = (evt) => resolve(evt.target.result);
|
|
||||||
getReq.onerror = (evt) => reject(
|
|
||||||
`Read data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
|
||||||
var tmpResult = {}
|
|
||||||
let curReq = obs.openCursor();
|
|
||||||
curReq.onsuccess = (evt) => {
|
|
||||||
var cursor = evt.target.result;
|
|
||||||
if (cursor) {
|
|
||||||
tmpResult[cursor.primaryKey] = cursor.value;
|
|
||||||
cursor.continue();
|
|
||||||
} else
|
|
||||||
resolve(tmpResult);
|
|
||||||
}
|
|
||||||
curReq.onerror = (evt) => reject(
|
|
||||||
`Read-All data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
|
|
||||||
);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
|
||||||
var filteredResult = {}
|
|
||||||
let keyRange;
|
|
||||||
if(options.lowerKey!==null && options.upperKey!==null)
|
|
||||||
keyRange = IDBKeyRange.bound(options.lowerKey, options.upperKey);
|
|
||||||
else if(options.lowerKey!==null)
|
|
||||||
keyRange = IDBKeyRange.lowerBound(options.lowerKey);
|
|
||||||
else if (options.upperKey!==null)
|
|
||||||
keyRange = IDBKeyRange.upperBound(options.upperBound);
|
|
||||||
else if (options.atKey)
|
|
||||||
let curReq = obs.openCursor(keyRange, )
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}*/
|
|
||||||
|
|
||||||
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
|
||||||
options.lowerKey = options.atKey || options.lowerKey || 0
|
|
||||||
options.upperKey = options.atKey || options.upperKey || false
|
|
||||||
options.patternEval = options.patternEval || ((k, v) => true);
|
|
||||||
options.limit = options.limit || false;
|
|
||||||
options.reverse = options.reverse || false;
|
|
||||||
options.lastOnly = options.lastOnly || false
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
openDB(dbName).then(db => {
|
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
|
||||||
var filteredResult = {}
|
|
||||||
let curReq = obs.openCursor(
|
|
||||||
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
|
|
||||||
options.lastOnly || options.reverse ? "prev" : "next");
|
|
||||||
curReq.onsuccess = (evt) => {
|
|
||||||
var cursor = evt.target.result;
|
|
||||||
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
|
|
||||||
return resolve(filteredResult); //reached end of key list or limit reached
|
|
||||||
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
|
|
||||||
filteredResult[cursor.primaryKey] = cursor.value;
|
|
||||||
options.lastOnly ? resolve(filteredResult) : cursor.continue();
|
|
||||||
} else
|
|
||||||
cursor.continue();
|
|
||||||
}
|
|
||||||
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
|
|
||||||
db.close();
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
})(window.compactIDB = {});
|
|
||||||
1044
floBlockchainAPI.js
1044
floBlockchainAPI.js
File diff suppressed because it is too large
Load Diff
1106
floCloudAPI.js
1106
floCloudAPI.js
File diff suppressed because it is too large
Load Diff
530
floCrypto.js
530
floCrypto.js
@ -1,530 +0,0 @@
|
|||||||
(function (EXPORTS) { //floCrypto v2.3.6a
|
|
||||||
/* FLO Crypto Operators */
|
|
||||||
'use strict';
|
|
||||||
const floCrypto = EXPORTS;
|
|
||||||
|
|
||||||
const p = BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
|
|
||||||
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
|
||||||
const ascii_alternatives = `‘ '\n’ '\n“ "\n” "\n– --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
|
|
||||||
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
|
|
||||||
coinjs.compressed = true; //defaulting coinjs compressed to true;
|
|
||||||
|
|
||||||
function calculateY(x) {
|
|
||||||
let exp = exponent1();
|
|
||||||
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
|
|
||||||
return x.modPow(BigInteger("3"), p).add(BigInteger("7")).mod(p).modPow(exp, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUncompressedPublicKey(compressedPublicKey) {
|
|
||||||
// Fetch x from compressedPublicKey
|
|
||||||
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
|
|
||||||
const prefix = pubKeyBytes.shift() // remove prefix
|
|
||||||
let prefix_modulus = prefix % 2;
|
|
||||||
pubKeyBytes.unshift(0) // add prefix 0
|
|
||||||
let x = new BigInteger(pubKeyBytes)
|
|
||||||
let xDecimalValue = x.toString()
|
|
||||||
// Fetch y
|
|
||||||
let y = calculateY(x);
|
|
||||||
let yDecimalValue = y.toString();
|
|
||||||
// verify y value
|
|
||||||
let resultBigInt = y.mod(BigInteger("2"));
|
|
||||||
let check = resultBigInt.toString() % 2;
|
|
||||||
if (prefix_modulus !== check)
|
|
||||||
yDecimalValue = y.negate().mod(p).toString();
|
|
||||||
return {
|
|
||||||
x: xDecimalValue,
|
|
||||||
y: yDecimalValue
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSenderPublicKeyString() {
|
|
||||||
let privateKey = ellipticCurveEncryption.senderRandom();
|
|
||||||
var senderPublicKeyString = ellipticCurveEncryption.senderPublicString(privateKey);
|
|
||||||
return {
|
|
||||||
privateKey: privateKey,
|
|
||||||
senderPublicKeyString: senderPublicKeyString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveSharedKeySender(receiverPublicKeyHex, senderPrivateKey) {
|
|
||||||
let receiverPublicKeyString = getUncompressedPublicKey(receiverPublicKeyHex);
|
|
||||||
var senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
|
|
||||||
receiverPublicKeyString.x, receiverPublicKeyString.y, senderPrivateKey);
|
|
||||||
return senderDerivedKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
function deriveSharedKeyReceiver(senderPublicKeyString, receiverPrivateKey) {
|
|
||||||
return ellipticCurveEncryption.receiverSharedKeyDerivation(
|
|
||||||
senderPublicKeyString.XValuePublicString, senderPublicKeyString.YValuePublicString, receiverPrivateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getReceiverPublicKeyString(privateKey) {
|
|
||||||
return ellipticCurveEncryption.receiverPublicString(privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wifToDecimal(pk_wif, isPubKeyCompressed = false) {
|
|
||||||
let pk = Bitcoin.Base58.decode(pk_wif)
|
|
||||||
pk.shift()
|
|
||||||
pk.splice(-4, 4)
|
|
||||||
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
|
|
||||||
if (isPubKeyCompressed == true) pk.pop()
|
|
||||||
pk.unshift(0)
|
|
||||||
let privateKeyDecimal = BigInteger(pk).toString()
|
|
||||||
let privateKeyHex = Crypto.util.bytesToHex(pk)
|
|
||||||
return {
|
|
||||||
privateKeyDecimal: privateKeyDecimal,
|
|
||||||
privateKeyHex: privateKeyHex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//generate a random Interger within range
|
|
||||||
floCrypto.randInt = function (min, max) {
|
|
||||||
min = Math.ceil(min);
|
|
||||||
max = Math.floor(max);
|
|
||||||
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
|
|
||||||
}
|
|
||||||
|
|
||||||
//generate a random String within length (options : alphaNumeric chars only)
|
|
||||||
floCrypto.randString = function (length, alphaNumeric = true) {
|
|
||||||
var result = '';
|
|
||||||
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
|
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
|
|
||||||
for (var i = 0; i < length; i++)
|
|
||||||
result += characters.charAt(Math.floor(securedMathRandom() * characters.length));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Encrypt Data using public-key
|
|
||||||
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
|
|
||||||
var senderECKeyData = getSenderPublicKeyString();
|
|
||||||
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
|
|
||||||
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
|
|
||||||
let secret = Crypto.AES.encrypt(data, senderKey);
|
|
||||||
return {
|
|
||||||
secret: secret,
|
|
||||||
senderPublicKeyString: senderECKeyData.senderPublicKeyString
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//Decrypt Data using private-key
|
|
||||||
floCrypto.decryptData = function (data, privateKeyHex) {
|
|
||||||
var receiverECKeyData = {};
|
|
||||||
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
|
|
||||||
let privateKey = wifToDecimal(privateKeyHex, true);
|
|
||||||
if (typeof privateKey.privateKeyDecimal !== "string") throw new Error("Failed to detremine your private key.");
|
|
||||||
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
|
|
||||||
var receiverDerivedKey = deriveSharedKeyReceiver(data.senderPublicKeyString, receiverECKeyData.privateKey);
|
|
||||||
let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
|
|
||||||
let decryptMsg = Crypto.AES.decrypt(data.secret, receiverKey);
|
|
||||||
return decryptMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sign data using private-key
|
|
||||||
floCrypto.signData = function (data, privateKeyHex) {
|
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
|
||||||
var messageHash = Crypto.SHA256(data);
|
|
||||||
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
|
|
||||||
var sighex = Crypto.util.bytesToHex(messageSign);
|
|
||||||
return sighex;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Verify signatue of the data using public-key
|
|
||||||
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
|
|
||||||
var msgHash = Crypto.SHA256(data);
|
|
||||||
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
|
||||||
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
|
|
||||||
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
|
|
||||||
return verify;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Generates a new flo ID and returns private-key, public-key and floID
|
|
||||||
const generateNewID = floCrypto.generateNewID = function () {
|
|
||||||
var key = new Bitcoin.ECKey(false);
|
|
||||||
key.setCompressed(true);
|
|
||||||
return {
|
|
||||||
floID: key.getBitcoinAddress(),
|
|
||||||
pubKey: key.getPubKeyHex(),
|
|
||||||
privKey: key.getBitcoinWalletImportFormat()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperties(floCrypto, {
|
|
||||||
newID: {
|
|
||||||
get: () => generateNewID()
|
|
||||||
},
|
|
||||||
hashID: {
|
|
||||||
value: (str) => {
|
|
||||||
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), { asBytes: true });
|
|
||||||
bytes.unshift(bitjs.pub);
|
|
||||||
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
var checksum = hash.slice(0, 4);
|
|
||||||
return bitjs.Base58.encode(bytes.concat(checksum));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tmpID: {
|
|
||||||
get: () => {
|
|
||||||
let bytes = Crypto.util.randomBytes(20);
|
|
||||||
bytes.unshift(bitjs.pub);
|
|
||||||
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
var checksum = hash.slice(0, 4);
|
|
||||||
return bitjs.Base58.encode(bytes.concat(checksum));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Returns public-key from private-key
|
|
||||||
floCrypto.getPubKeyHex = function (privateKeyHex) {
|
|
||||||
if (!privateKeyHex)
|
|
||||||
return null;
|
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
|
||||||
if (key.priv == null)
|
|
||||||
return null;
|
|
||||||
key.setCompressed(true);
|
|
||||||
return key.getPubKeyHex();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns flo-ID from public-key or private-key
|
|
||||||
floCrypto.getFloID = function (keyHex) {
|
|
||||||
if (!keyHex)
|
|
||||||
return null;
|
|
||||||
try {
|
|
||||||
var key = new Bitcoin.ECKey(keyHex);
|
|
||||||
if (key.priv == null)
|
|
||||||
key.setPub(keyHex);
|
|
||||||
return key.getBitcoinAddress();
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
floCrypto.getAddress = function (privateKeyHex, strict = false) {
|
|
||||||
if (!privateKeyHex)
|
|
||||||
return;
|
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
|
||||||
if (key.priv == null)
|
|
||||||
return null;
|
|
||||||
key.setCompressed(true);
|
|
||||||
let pubKey = key.getPubKeyHex(),
|
|
||||||
version = bitjs.Base58.decode(privateKeyHex)[0];
|
|
||||||
switch (version) {
|
|
||||||
case coinjs.priv: //BTC
|
|
||||||
return coinjs.bech32Address(pubKey).address;
|
|
||||||
case bitjs.priv: //FLO
|
|
||||||
return bitjs.pubkey2address(pubKey);
|
|
||||||
default:
|
|
||||||
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Verify the private-key for the given public-key or flo-ID
|
|
||||||
floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) {
|
|
||||||
if (!privateKeyHex || !pubKey_floID)
|
|
||||||
return false;
|
|
||||||
try {
|
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
|
||||||
if (key.priv == null)
|
|
||||||
return false;
|
|
||||||
key.setCompressed(true);
|
|
||||||
if (isfloID && pubKey_floID == key.getBitcoinAddress())
|
|
||||||
return true;
|
|
||||||
else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
|
|
||||||
if (!Array.isArray(publicKeyList) || !publicKeyList.length)
|
|
||||||
return null;
|
|
||||||
if (!Number.isInteger(requiredSignatures) || requiredSignatures < 1 || requiredSignatures > publicKeyList.length)
|
|
||||||
return null;
|
|
||||||
try {
|
|
||||||
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
|
|
||||||
return multisig;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
floCrypto.decodeRedeemScript = function (redeemScript) {
|
|
||||||
try {
|
|
||||||
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
|
|
||||||
return decoded;
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if the given flo-id is valid or not
|
|
||||||
floCrypto.validateFloID = function (floID, regularOnly = false) {
|
|
||||||
if (!floID)
|
|
||||||
return false;
|
|
||||||
try {
|
|
||||||
let addr = new Bitcoin.Address(floID);
|
|
||||||
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check if the given address (any blockchain) is valid or not
|
|
||||||
floCrypto.validateAddr = function (address, std = true, bech = true) {
|
|
||||||
let raw = decodeAddress(address);
|
|
||||||
if (!raw)
|
|
||||||
return false;
|
|
||||||
if (typeof raw.version !== 'undefined') { //legacy or segwit
|
|
||||||
if (std == false)
|
|
||||||
return false;
|
|
||||||
else if (std === true || (!Array.isArray(std) && std === raw.version) || (Array.isArray(std) && std.includes(raw.version)))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
} else if (typeof raw.bech_version !== 'undefined') { //bech32
|
|
||||||
if (bech === false)
|
|
||||||
return false;
|
|
||||||
else if (bech === true || (!Array.isArray(bech) && bech === raw.bech_version) || (Array.isArray(bech) && bech.includes(raw.bech_version)))
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
} else //unknown
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check the public-key (or redeem-script) for the address (any blockchain)
|
|
||||||
floCrypto.verifyPubKey = function (pubKeyHex, address) {
|
|
||||||
let raw = decodeAddress(address);
|
|
||||||
if (!raw)
|
|
||||||
return;
|
|
||||||
let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
|
|
||||||
if (typeof raw.bech_version !== 'undefined' && raw.bytes.length == 32) //bech32-multisig
|
|
||||||
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
|
|
||||||
return pub_hash === raw.hex;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Convert the given address (any blockchain) to equivalent floID
|
|
||||||
floCrypto.toFloID = function (address, options = null) {
|
|
||||||
if (!address)
|
|
||||||
return;
|
|
||||||
let raw = decodeAddress(address);
|
|
||||||
if (!raw)
|
|
||||||
return;
|
|
||||||
else if (options) { //if (optional) version check is passed
|
|
||||||
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
|
||||||
return;
|
|
||||||
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
raw.bytes.unshift(bitjs.pub);
|
|
||||||
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Convert raw address bytes to floID
|
|
||||||
floCrypto.rawToFloID = function (raw_bytes) {
|
|
||||||
if (typeof raw_bytes === 'string')
|
|
||||||
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
|
|
||||||
if (raw_bytes.length != 20)
|
|
||||||
return null;
|
|
||||||
raw_bytes.unshift(bitjs.pub);
|
|
||||||
let hash = Crypto.SHA256(Crypto.SHA256(raw_bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Convert the given multisig address (any blockchain) to equivalent multisig floID
|
|
||||||
floCrypto.toMultisigFloID = function (address, options = null) {
|
|
||||||
if (!address)
|
|
||||||
return;
|
|
||||||
let raw = decodeAddress(address);
|
|
||||||
if (!raw)
|
|
||||||
return;
|
|
||||||
else if (options) { //if (optional) version check is passed
|
|
||||||
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
|
||||||
return;
|
|
||||||
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (typeof raw.bech_version !== 'undefined') {
|
|
||||||
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
|
|
||||||
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
|
|
||||||
raw.bytes = ripemd160(raw.bytes, {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
raw.bytes.unshift(bitjs.multisig);
|
|
||||||
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
|
||||||
}
|
|
||||||
|
|
||||||
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
|
|
||||||
floCrypto.isSameAddr = function (addr1, addr2) {
|
|
||||||
if (!addr1 || !addr2)
|
|
||||||
return;
|
|
||||||
let raw1 = decodeAddress(addr1),
|
|
||||||
raw2 = decodeAddress(addr2);
|
|
||||||
if (!raw1 || !raw2)
|
|
||||||
return false;
|
|
||||||
else {
|
|
||||||
if (typeof raw1.bech_version !== 'undefined' && raw1.bytes.length == 32) //bech32-multisig
|
|
||||||
raw1.hex = Crypto.util.bytesToHex(ripemd160(raw1.bytes, { asBytes: true }));
|
|
||||||
if (typeof raw2.bech_version !== 'undefined' && raw2.bytes.length == 32) //bech32-multisig
|
|
||||||
raw2.hex = Crypto.util.bytesToHex(ripemd160(raw2.bytes, { asBytes: true }));
|
|
||||||
return raw1.hex === raw2.hex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const decodeAddress = floCrypto.decodeAddr = function (address) {
|
|
||||||
if (!address)
|
|
||||||
return;
|
|
||||||
else if (address.length == 33 || address.length == 34) { //legacy encoding
|
|
||||||
let decode = bitjs.Base58.decode(address);
|
|
||||||
let bytes = decode.slice(0, decode.length - 4);
|
|
||||||
let checksum = decode.slice(decode.length - 4),
|
|
||||||
hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
|
||||||
asBytes: true
|
|
||||||
}), {
|
|
||||||
asBytes: true
|
|
||||||
});
|
|
||||||
return (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) ? null : {
|
|
||||||
version: bytes.shift(),
|
|
||||||
hex: Crypto.util.bytesToHex(bytes),
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
} else if (address.length == 42 || address.length == 62) { //bech encoding
|
|
||||||
let decode = coinjs.bech32_decode(address);
|
|
||||||
if (decode) {
|
|
||||||
let bytes = decode.data;
|
|
||||||
let bech_version = bytes.shift();
|
|
||||||
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
|
||||||
return {
|
|
||||||
bech_version,
|
|
||||||
hrp: decode.hrp,
|
|
||||||
hex: Crypto.util.bytesToHex(bytes),
|
|
||||||
bytes
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Split the str using shamir's Secret and Returns the shares
|
|
||||||
floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) {
|
|
||||||
try {
|
|
||||||
if (str.length > 0) {
|
|
||||||
var strHex = shamirSecretShare.str2hex(str);
|
|
||||||
var shares = shamirSecretShare.share(strHex, total_shares, threshold_limit);
|
|
||||||
return shares;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Returns the retrived secret by combining the shamirs shares
|
|
||||||
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) {
|
|
||||||
try {
|
|
||||||
if (sharesArray.length > 0) {
|
|
||||||
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
|
|
||||||
comb = shamirSecretShare.hex2str(comb);
|
|
||||||
return comb;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Verifies the shares and str
|
|
||||||
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
|
|
||||||
if (!str)
|
|
||||||
return null;
|
|
||||||
else if (retrieveShamirSecret(sharesArray) === str)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validateASCII = floCrypto.validateASCII = function (string, bool = true) {
|
|
||||||
if (typeof string !== "string")
|
|
||||||
return null;
|
|
||||||
if (bool) {
|
|
||||||
let x;
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
x = string.charCodeAt(i);
|
|
||||||
if (x < 32 || x > 127)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
let x, invalids = {};
|
|
||||||
for (let i = 0; i < string.length; i++) {
|
|
||||||
x = string.charCodeAt(i);
|
|
||||||
if (x < 32 || x > 127)
|
|
||||||
if (x in invalids)
|
|
||||||
invalids[string[i]].push(i)
|
|
||||||
else
|
|
||||||
invalids[string[i]] = [i];
|
|
||||||
}
|
|
||||||
if (Object.keys(invalids).length)
|
|
||||||
return invalids;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
floCrypto.convertToASCII = function (string, mode = 'soft-remove') {
|
|
||||||
let chars = validateASCII(string, false);
|
|
||||||
if (chars === true)
|
|
||||||
return string;
|
|
||||||
else if (chars === null)
|
|
||||||
return null;
|
|
||||||
let convertor, result = string,
|
|
||||||
refAlt = {};
|
|
||||||
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
|
|
||||||
mode = mode.toLowerCase();
|
|
||||||
if (mode === "hard-unicode")
|
|
||||||
convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
|
||||||
else if (mode === "soft-unicode")
|
|
||||||
convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
|
||||||
else if (mode === "hard-remove")
|
|
||||||
convertor = c => "";
|
|
||||||
else if (mode === "soft-remove")
|
|
||||||
convertor = c => refAlt[c] || "";
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
for (let c in chars)
|
|
||||||
result = result.replaceAll(c, convertor(c));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
floCrypto.revertUnicode = function (string) {
|
|
||||||
return string.replace(/\\u[\dA-F]{4}/gi,
|
|
||||||
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
})('object' === typeof module ? module.exports : window.floCrypto = {});
|
|
||||||
843
floDapps.js
843
floDapps.js
@ -1,843 +0,0 @@
|
|||||||
(function (EXPORTS) { //floDapps v2.4.1
|
|
||||||
/* General functions for FLO Dapps*/
|
|
||||||
'use strict';
|
|
||||||
const floDapps = EXPORTS;
|
|
||||||
|
|
||||||
const DEFAULT = {
|
|
||||||
root: "floDapps",
|
|
||||||
application: floGlobals.application,
|
|
||||||
adminID: floGlobals.adminID
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperties(floDapps, {
|
|
||||||
application: {
|
|
||||||
get: () => DEFAULT.application
|
|
||||||
},
|
|
||||||
adminID: {
|
|
||||||
get: () => DEFAULT.adminID
|
|
||||||
},
|
|
||||||
root: {
|
|
||||||
get: () => DEFAULT.root
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var user_priv_raw, aes_key, user_priv_wrap; //private variable inside capsule
|
|
||||||
const raw_user = {
|
|
||||||
get private() {
|
|
||||||
if (!user_priv_raw)
|
|
||||||
throw "User not logged in";
|
|
||||||
return Crypto.AES.decrypt(user_priv_raw, aes_key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var user_id, user_public, user_private;
|
|
||||||
const user = floDapps.user = {
|
|
||||||
get id() {
|
|
||||||
if (!user_id)
|
|
||||||
throw "User not logged in";
|
|
||||||
return user_id;
|
|
||||||
},
|
|
||||||
get public() {
|
|
||||||
if (!user_public)
|
|
||||||
throw "User not logged in";
|
|
||||||
return user_public;
|
|
||||||
},
|
|
||||||
get private() {
|
|
||||||
if (!user_private)
|
|
||||||
throw "User not logged in";
|
|
||||||
else if (user_private instanceof Function)
|
|
||||||
return user_private();
|
|
||||||
else
|
|
||||||
return Crypto.AES.decrypt(user_private, aes_key);
|
|
||||||
},
|
|
||||||
sign(message) {
|
|
||||||
return floCrypto.signData(message, raw_user.private);
|
|
||||||
},
|
|
||||||
decrypt(data) {
|
|
||||||
return floCrypto.decryptData(data, raw_user.private);
|
|
||||||
},
|
|
||||||
encipher(message) {
|
|
||||||
return Crypto.AES.encrypt(message, raw_user.private);
|
|
||||||
},
|
|
||||||
decipher(data) {
|
|
||||||
return Crypto.AES.decrypt(data, raw_user.private);
|
|
||||||
},
|
|
||||||
get db_name() {
|
|
||||||
return "floDapps#" + floCrypto.toFloID(user.id);
|
|
||||||
},
|
|
||||||
lock() {
|
|
||||||
user_private = user_priv_wrap;
|
|
||||||
},
|
|
||||||
async unlock() {
|
|
||||||
if (await user.private === raw_user.private)
|
|
||||||
user_private = user_priv_raw;
|
|
||||||
},
|
|
||||||
get_contact(id) {
|
|
||||||
if (!user.contacts)
|
|
||||||
throw "Contacts not available";
|
|
||||||
else if (user.contacts[id])
|
|
||||||
return user.contacts[id];
|
|
||||||
else {
|
|
||||||
let id_raw = floCrypto.decodeAddr(id).hex;
|
|
||||||
for (let i in user.contacts)
|
|
||||||
if (floCrypto.decodeAddr(i).hex == id_raw)
|
|
||||||
return user.contacts[i];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
get_pubKey(id) {
|
|
||||||
if (!user.pubKeys)
|
|
||||||
throw "Contacts not available";
|
|
||||||
else if (user.pubKeys[id])
|
|
||||||
return user.pubKeys[id];
|
|
||||||
else {
|
|
||||||
let id_raw = floCrypto.decodeAddr(id).hex;
|
|
||||||
for (let i in user.pubKeys)
|
|
||||||
if (floCrypto.decodeAddr(i).hex == id_raw)
|
|
||||||
return user.pubKeys[i];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clear() {
|
|
||||||
user_id = user_public = user_private = undefined;
|
|
||||||
user_priv_raw = aes_key = undefined;
|
|
||||||
delete user.contacts;
|
|
||||||
delete user.pubKeys;
|
|
||||||
delete user.messages;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.defineProperties(window, {
|
|
||||||
myFloID: {
|
|
||||||
get: () => {
|
|
||||||
try {
|
|
||||||
return user.id;
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
myUserID: {
|
|
||||||
get: () => {
|
|
||||||
try {
|
|
||||||
return user.id;
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
myPubKey: {
|
|
||||||
get: () => {
|
|
||||||
try {
|
|
||||||
return user.public;
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
myPrivKey: {
|
|
||||||
get: () => {
|
|
||||||
try {
|
|
||||||
return user.private;
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var subAdmins, trustedIDs, settings;
|
|
||||||
Object.defineProperties(floGlobals, {
|
|
||||||
subAdmins: {
|
|
||||||
get: () => subAdmins
|
|
||||||
},
|
|
||||||
trustedIDs: {
|
|
||||||
get: () => trustedIDs
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
get: () => settings
|
|
||||||
},
|
|
||||||
contacts: {
|
|
||||||
get: () => user.contacts
|
|
||||||
},
|
|
||||||
pubKeys: {
|
|
||||||
get: () => user.pubKeys
|
|
||||||
},
|
|
||||||
messages: {
|
|
||||||
get: () => user.messages
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
function initIndexedDB() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var obs_g = {
|
|
||||||
//general
|
|
||||||
lastTx: {},
|
|
||||||
//supernode (cloud list)
|
|
||||||
supernodes: {}
|
|
||||||
}
|
|
||||||
var obs_a = {
|
|
||||||
//login credentials
|
|
||||||
credentials: {},
|
|
||||||
//for Dapps
|
|
||||||
subAdmins: {},
|
|
||||||
trustedIDs: {},
|
|
||||||
settings: {},
|
|
||||||
appObjects: {},
|
|
||||||
generalData: {},
|
|
||||||
lastVC: {}
|
|
||||||
}
|
|
||||||
//add other given objectStores
|
|
||||||
initIndexedDB.appObs = initIndexedDB.appObs || {}
|
|
||||||
for (let o in initIndexedDB.appObs)
|
|
||||||
if (!(o in obs_a))
|
|
||||||
obs_a[o] = initIndexedDB.appObs[o]
|
|
||||||
Promise.all([
|
|
||||||
compactIDB.initDB(DEFAULT.application, obs_a),
|
|
||||||
compactIDB.initDB(DEFAULT.root, obs_g)
|
|
||||||
]).then(result => {
|
|
||||||
compactIDB.setDefaultDB(DEFAULT.application)
|
|
||||||
resolve("IndexedDB App Storage Initated Successfully")
|
|
||||||
}).catch(error => reject(error));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function initUserDB() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var obs = {
|
|
||||||
contacts: {},
|
|
||||||
pubKeys: {},
|
|
||||||
messages: {}
|
|
||||||
}
|
|
||||||
compactIDB.initDB(user.db_name, obs).then(result => {
|
|
||||||
resolve("UserDB Initated Successfully")
|
|
||||||
}).catch(error => reject('Init userDB failed'));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadUserDB() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var loadData = ["contacts", "pubKeys", "messages"]
|
|
||||||
var promises = []
|
|
||||||
for (var i = 0; i < loadData.length; i++)
|
|
||||||
promises[i] = compactIDB.readAllData(loadData[i], user.db_name)
|
|
||||||
Promise.all(promises).then(results => {
|
|
||||||
for (var i = 0; i < loadData.length; i++)
|
|
||||||
user[loadData[i]] = results[i]
|
|
||||||
resolve("Loaded Data from userDB")
|
|
||||||
}).catch(error => reject('Load userDB failed'))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const startUpOptions = {
|
|
||||||
cloud: true,
|
|
||||||
app_config: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.startUpOptions = {
|
|
||||||
set app_config(val) {
|
|
||||||
if (val === true || val === false)
|
|
||||||
startUpOptions.app_config = val;
|
|
||||||
},
|
|
||||||
get app_config() { return startUpOptions.app_config },
|
|
||||||
|
|
||||||
set cloud(val) {
|
|
||||||
if (val === true || val === false)
|
|
||||||
startUpOptions.cloud = val;
|
|
||||||
},
|
|
||||||
get cloud() { return startUpOptions.cloud },
|
|
||||||
}
|
|
||||||
|
|
||||||
const startUpFunctions = [];
|
|
||||||
|
|
||||||
startUpFunctions.push(function readSupernodeListFromAPI() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!startUpOptions.cloud)
|
|
||||||
return resolve("No cloud for this app");
|
|
||||||
const CLOUD_KEY = "floCloudAPI#" + floCloudAPI.SNStorageID;
|
|
||||||
compactIDB.readData("lastTx", CLOUD_KEY, DEFAULT.root).then(lastTx => {
|
|
||||||
var query_options = { sentOnly: true, pattern: floCloudAPI.SNStorageName };
|
|
||||||
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
|
|
||||||
query_options.ignoreOld = lastTx;
|
|
||||||
else if (typeof lastTx == 'string') //lastTx is txid of last tx
|
|
||||||
query_options.after = lastTx;
|
|
||||||
//fetch data from flosight
|
|
||||||
floBlockchainAPI.readData(floCloudAPI.SNStorageID, query_options).then(result => {
|
|
||||||
compactIDB.readData("supernodes", CLOUD_KEY, DEFAULT.root).then(nodes => {
|
|
||||||
nodes = nodes || {};
|
|
||||||
for (var i = result.data.length - 1; i >= 0; i--) {
|
|
||||||
var content = JSON.parse(result.data[i])[floCloudAPI.SNStorageName];
|
|
||||||
for (let sn in content.removeNodes)
|
|
||||||
delete nodes[sn];
|
|
||||||
for (let sn in content.newNodes)
|
|
||||||
nodes[sn] = content.newNodes[sn];
|
|
||||||
for (let sn in content.updateNodes)
|
|
||||||
if (sn in nodes) //check if node is listed
|
|
||||||
nodes[sn].uri = content.updateNodes[sn];
|
|
||||||
}
|
|
||||||
Promise.all([
|
|
||||||
compactIDB.writeData("lastTx", result.lastItem, CLOUD_KEY, DEFAULT.root),
|
|
||||||
compactIDB.writeData("supernodes", nodes, CLOUD_KEY, DEFAULT.root)
|
|
||||||
]).then(_ => {
|
|
||||||
floCloudAPI.init(nodes)
|
|
||||||
.then(result => resolve("Loaded Supernode list\n" + result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
startUpFunctions.push(function readAppConfigFromAPI() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!startUpOptions.app_config)
|
|
||||||
return resolve("No configs for this app");
|
|
||||||
compactIDB.readData("lastTx", `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root).then(lastTx => {
|
|
||||||
var query_options = { sentOnly: true, pattern: DEFAULT.application };
|
|
||||||
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
|
|
||||||
query_options.ignoreOld = lastTx;
|
|
||||||
else if (typeof lastTx == 'string') //lastTx is txid of last tx
|
|
||||||
query_options.after = lastTx;
|
|
||||||
//fetch data from flosight
|
|
||||||
floBlockchainAPI.readData(DEFAULT.adminID, query_options).then(result => {
|
|
||||||
for (var i = result.data.length - 1; i >= 0; i--) {
|
|
||||||
var content = JSON.parse(result.data[i])[DEFAULT.application];
|
|
||||||
if (!content || typeof content !== "object")
|
|
||||||
continue;
|
|
||||||
if (Array.isArray(content.removeSubAdmin))
|
|
||||||
for (var j = 0; j < content.removeSubAdmin.length; j++)
|
|
||||||
compactIDB.removeData("subAdmins", content.removeSubAdmin[j]);
|
|
||||||
if (Array.isArray(content.addSubAdmin))
|
|
||||||
for (var k = 0; k < content.addSubAdmin.length; k++)
|
|
||||||
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
|
|
||||||
if (Array.isArray(content.removeTrustedID))
|
|
||||||
for (var j = 0; j < content.removeTrustedID.length; j++)
|
|
||||||
compactIDB.removeData("trustedIDs", content.removeTrustedID[j]);
|
|
||||||
if (Array.isArray(content.addTrustedID))
|
|
||||||
for (var k = 0; k < content.addTrustedID.length; k++)
|
|
||||||
compactIDB.writeData("trustedIDs", true, content.addTrustedID[k]);
|
|
||||||
if (content.settings)
|
|
||||||
for (let l in content.settings)
|
|
||||||
compactIDB.writeData("settings", content.settings[l], l)
|
|
||||||
}
|
|
||||||
compactIDB.writeData("lastTx", result.lastItem, `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root);
|
|
||||||
compactIDB.readAllData("subAdmins").then(result => {
|
|
||||||
subAdmins = Object.keys(result);
|
|
||||||
compactIDB.readAllData("trustedIDs").then(result => {
|
|
||||||
trustedIDs = Object.keys(result);
|
|
||||||
compactIDB.readAllData("settings").then(result => {
|
|
||||||
settings = result;
|
|
||||||
resolve("Read app configuration from blockchain");
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
startUpFunctions.push(function loadDataFromAppIDB() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!startUpOptions.cloud)
|
|
||||||
return resolve("No cloud for this app");
|
|
||||||
var loadData = ["appObjects", "generalData", "lastVC"]
|
|
||||||
var promises = []
|
|
||||||
for (var i = 0; i < loadData.length; i++)
|
|
||||||
promises[i] = compactIDB.readAllData(loadData[i])
|
|
||||||
Promise.all(promises).then(results => {
|
|
||||||
for (var i = 0; i < loadData.length; i++)
|
|
||||||
floGlobals[loadData[i]] = results[i]
|
|
||||||
resolve("Loaded Data from app IDB")
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
var keyInput = type => new Promise((resolve, reject) => {
|
|
||||||
let inputVal = prompt(`Enter ${type}: `)
|
|
||||||
if (inputVal === null)
|
|
||||||
reject(null)
|
|
||||||
else
|
|
||||||
resolve(inputVal)
|
|
||||||
});
|
|
||||||
|
|
||||||
function getCredentials() {
|
|
||||||
|
|
||||||
const readSharesFromIDB = indexArr => new Promise((resolve, reject) => {
|
|
||||||
var promises = []
|
|
||||||
for (var i = 0; i < indexArr.length; i++)
|
|
||||||
promises.push(compactIDB.readData('credentials', indexArr[i]))
|
|
||||||
Promise.all(promises).then(shares => {
|
|
||||||
var secret = floCrypto.retrieveShamirSecret(shares)
|
|
||||||
if (secret)
|
|
||||||
resolve(secret)
|
|
||||||
else
|
|
||||||
reject("Shares are insufficient or incorrect")
|
|
||||||
}).catch(error => {
|
|
||||||
clearCredentials();
|
|
||||||
location.reload();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const writeSharesToIDB = (shares, i = 0, resultIndexes = []) => new Promise(resolve => {
|
|
||||||
if (i >= shares.length)
|
|
||||||
return resolve(resultIndexes)
|
|
||||||
var n = floCrypto.randInt(0, 100000)
|
|
||||||
compactIDB.addData("credentials", shares[i], n).then(res => {
|
|
||||||
resultIndexes.push(n)
|
|
||||||
writeSharesToIDB(shares, i + 1, resultIndexes)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
}).catch(error => {
|
|
||||||
writeSharesToIDB(shares, i, resultIndexes)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const getPrivateKeyCredentials = () => new Promise((resolve, reject) => {
|
|
||||||
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
|
||||||
if (indexArr) {
|
|
||||||
readSharesFromIDB(JSON.parse(indexArr))
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
} else {
|
|
||||||
var privKey;
|
|
||||||
keyInput("PRIVATE_KEY").then(result => {
|
|
||||||
if (!result)
|
|
||||||
return reject("Empty Private Key")
|
|
||||||
var floID = floCrypto.getFloID(result)
|
|
||||||
if (!floID || !floCrypto.validateFloID(floID))
|
|
||||||
return reject("Invalid Private Key")
|
|
||||||
privKey = result;
|
|
||||||
}).catch(error => {
|
|
||||||
console.log(error, "Generating Random Keys")
|
|
||||||
privKey = floCrypto.generateNewID().privKey
|
|
||||||
}).finally(_ => {
|
|
||||||
if (!privKey)
|
|
||||||
return;
|
|
||||||
var threshold = floCrypto.randInt(10, 20)
|
|
||||||
var shares = floCrypto.createShamirsSecretShares(privKey, threshold, threshold)
|
|
||||||
writeSharesToIDB(shares).then(resultIndexes => {
|
|
||||||
//store index keys in localStorage
|
|
||||||
localStorage.setItem(`${DEFAULT.application}#privKey`, JSON.stringify(resultIndexes))
|
|
||||||
//also add a dummy privatekey to the IDB
|
|
||||||
var randomPrivKey = floCrypto.generateNewID().privKey
|
|
||||||
var randomThreshold = floCrypto.randInt(10, 20)
|
|
||||||
var randomShares = floCrypto.createShamirsSecretShares(randomPrivKey, randomThreshold, randomThreshold)
|
|
||||||
writeSharesToIDB(randomShares)
|
|
||||||
//resolve private Key
|
|
||||||
resolve(privKey)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const checkIfPinRequired = key => new Promise((resolve, reject) => {
|
|
||||||
if (key.length == 52)
|
|
||||||
resolve(key)
|
|
||||||
else {
|
|
||||||
keyInput("PIN/Password").then(pwd => {
|
|
||||||
try {
|
|
||||||
let privKey = Crypto.AES.decrypt(key, pwd);
|
|
||||||
resolve(privKey)
|
|
||||||
} catch (error) {
|
|
||||||
reject("Access Denied: Incorrect PIN/Password")
|
|
||||||
}
|
|
||||||
}).catch(error => reject("Access Denied: PIN/Password required"))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
getPrivateKeyCredentials().then(key => {
|
|
||||||
checkIfPinRequired(key).then(privKey => {
|
|
||||||
try {
|
|
||||||
user_public = floCrypto.getPubKeyHex(privKey);
|
|
||||||
user_id = floCrypto.getAddress(privKey);
|
|
||||||
if (startUpOptions.cloud)
|
|
||||||
floCloudAPI.user(user_id, privKey); //Set user for floCloudAPI
|
|
||||||
user_priv_wrap = () => checkIfPinRequired(key);
|
|
||||||
let n = floCrypto.randInt(12, 20);
|
|
||||||
aes_key = floCrypto.randString(n);
|
|
||||||
user_priv_raw = Crypto.AES.encrypt(privKey, aes_key);
|
|
||||||
user_private = user_priv_wrap;
|
|
||||||
resolve('Login Credentials loaded successful')
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
reject("Corrupted Private Key")
|
|
||||||
}
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var startUpLog = (status, log) => status ? console.log(log) : console.error(log);
|
|
||||||
|
|
||||||
const callStartUpFunction = i => new Promise((resolve, reject) => {
|
|
||||||
startUpFunctions[i]().then(result => {
|
|
||||||
callStartUpFunction.completed += 1;
|
|
||||||
startUpLog(true, `${result}\nCompleted ${callStartUpFunction.completed}/${callStartUpFunction.total} Startup functions`)
|
|
||||||
resolve(true)
|
|
||||||
}).catch(error => {
|
|
||||||
callStartUpFunction.failed += 1;
|
|
||||||
startUpLog(false, `${error}\nFailed ${callStartUpFunction.failed}/${callStartUpFunction.total} Startup functions`)
|
|
||||||
reject(false)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
var _midFunction;
|
|
||||||
const midStartUp = () => new Promise((res, rej) => {
|
|
||||||
if (_midFunction instanceof Function) {
|
|
||||||
_midFunction()
|
|
||||||
.then(r => res("Mid startup function completed"))
|
|
||||||
.catch(e => rej("Mid startup function failed"))
|
|
||||||
} else
|
|
||||||
res("No mid startup function")
|
|
||||||
});
|
|
||||||
|
|
||||||
const callAndLog = p => new Promise((res, rej) => {
|
|
||||||
p.then(r => {
|
|
||||||
startUpLog(true, r)
|
|
||||||
res(r)
|
|
||||||
}).catch(e => {
|
|
||||||
startUpLog(false, e)
|
|
||||||
rej(e)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
floDapps.launchStartUp = function () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
initIndexedDB().then(log => {
|
|
||||||
console.log(log)
|
|
||||||
callStartUpFunction.total = startUpFunctions.length;
|
|
||||||
callStartUpFunction.completed = 0;
|
|
||||||
callStartUpFunction.failed = 0;
|
|
||||||
let p1 = new Promise((res, rej) => {
|
|
||||||
Promise.all(startUpFunctions.map((f, i) => callStartUpFunction(i))).then(r => {
|
|
||||||
callAndLog(midStartUp())
|
|
||||||
.then(r => res(true))
|
|
||||||
.catch(e => rej(false))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let p2 = new Promise((res, rej) => {
|
|
||||||
callAndLog(getCredentials()).then(r => {
|
|
||||||
callAndLog(initUserDB()).then(r => {
|
|
||||||
callAndLog(loadUserDB())
|
|
||||||
.then(r => res(true))
|
|
||||||
.catch(e => rej(false))
|
|
||||||
}).catch(e => rej(false))
|
|
||||||
}).catch(e => rej(false))
|
|
||||||
})
|
|
||||||
Promise.all([p1, p2])
|
|
||||||
.then(r => resolve('App Startup finished successful'))
|
|
||||||
.catch(e => reject('App Startup failed'))
|
|
||||||
}).catch(error => {
|
|
||||||
startUpLog(false, error);
|
|
||||||
reject("App database initiation failed")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.addStartUpFunction = fn => fn instanceof Function && !startUpFunctions.includes(fn) ? startUpFunctions.push(fn) : false;
|
|
||||||
|
|
||||||
floDapps.setMidStartup = fn => fn instanceof Function ? _midFunction = fn : false;
|
|
||||||
|
|
||||||
floDapps.setCustomStartupLogger = fn => fn instanceof Function ? startUpLog = fn : false;
|
|
||||||
|
|
||||||
floDapps.setCustomPrivKeyInput = fn => fn instanceof Function ? keyInput = fn : false;
|
|
||||||
|
|
||||||
floDapps.setAppObjectStores = appObs => initIndexedDB.appObs = appObs;
|
|
||||||
|
|
||||||
floDapps.storeContact = function (floID, name) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!floCrypto.validateAddr(floID))
|
|
||||||
return reject("Invalid floID!")
|
|
||||||
compactIDB.writeData("contacts", name, floID, user.db_name).then(result => {
|
|
||||||
user.contacts[floID] = name;
|
|
||||||
resolve("Contact stored")
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.storePubKey = function (floID, pubKey) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (floID in user.pubKeys)
|
|
||||||
return resolve("pubKey already stored")
|
|
||||||
if (!floCrypto.validateAddr(floID))
|
|
||||||
return reject("Invalid floID!")
|
|
||||||
if (!floCrypto.verifyPubKey(pubKey, floID))
|
|
||||||
return reject("Incorrect pubKey")
|
|
||||||
compactIDB.writeData("pubKeys", pubKey, floID, user.db_name).then(result => {
|
|
||||||
user.pubKeys[floID] = pubKey;
|
|
||||||
resolve("pubKey stored")
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.sendMessage = function (floID, message) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let options = {
|
|
||||||
receiverID: floID,
|
|
||||||
application: DEFAULT.root,
|
|
||||||
comment: DEFAULT.application
|
|
||||||
}
|
|
||||||
if (floID in user.pubKeys)
|
|
||||||
message = floCrypto.encryptData(JSON.stringify(message), user.pubKeys[floID])
|
|
||||||
floCloudAPI.sendApplicationData(message, "Message", options)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.requestInbox = function (callback) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let lastVC = Object.keys(user.messages).sort().pop()
|
|
||||||
let options = {
|
|
||||||
receiverID: user.id,
|
|
||||||
application: DEFAULT.root,
|
|
||||||
lowerVectorClock: lastVC + 1
|
|
||||||
}
|
|
||||||
let privKey = raw_user.private;
|
|
||||||
options.callback = (d, e) => {
|
|
||||||
for (let v in d) {
|
|
||||||
try {
|
|
||||||
if (d[v].message instanceof Object && "secret" in d[v].message)
|
|
||||||
d[v].message = floCrypto.decryptData(d[v].message, privKey)
|
|
||||||
} catch (error) { }
|
|
||||||
compactIDB.writeData("messages", d[v], v, user.db_name)
|
|
||||||
user.messages[v] = d[v]
|
|
||||||
}
|
|
||||||
if (callback instanceof Function)
|
|
||||||
callback(d, e)
|
|
||||||
}
|
|
||||||
floCloudAPI.requestApplicationData("Message", options)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.manageAppConfig = function (adminPrivKey, addList, rmList, settings) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!startUpOptions.app_config)
|
|
||||||
return reject("No configs for this app");
|
|
||||||
if (!Array.isArray(addList) || !addList.length) addList = undefined;
|
|
||||||
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
|
|
||||||
if (!settings || typeof settings !== "object" || !Object.keys(settings).length) settings = undefined;
|
|
||||||
if (!addList && !rmList && !settings)
|
|
||||||
return reject("No configuration change")
|
|
||||||
var floData = {
|
|
||||||
[DEFAULT.application]: {
|
|
||||||
addSubAdmin: addList,
|
|
||||||
removeSubAdmin: rmList,
|
|
||||||
settings: settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var floID = floCrypto.getFloID(adminPrivKey)
|
|
||||||
if (floID != DEFAULT.adminID)
|
|
||||||
reject('Access Denied for Admin privilege')
|
|
||||||
else
|
|
||||||
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
|
|
||||||
.then(result => resolve(['Updated App Configuration', result]))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.manageAppTrustedIDs = function (adminPrivKey, addList, rmList) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!startUpOptions.app_config)
|
|
||||||
return reject("No configs for this app");
|
|
||||||
if (!Array.isArray(addList) || !addList.length) addList = undefined;
|
|
||||||
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
|
|
||||||
if (!addList && !rmList)
|
|
||||||
return reject("No change in list")
|
|
||||||
var floData = {
|
|
||||||
[DEFAULT.application]: {
|
|
||||||
addTrustedID: addList,
|
|
||||||
removeTrustedID: rmList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var floID = floCrypto.getFloID(adminPrivKey)
|
|
||||||
if (floID != DEFAULT.adminID)
|
|
||||||
reject('Access Denied for Admin privilege')
|
|
||||||
else
|
|
||||||
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
|
|
||||||
.then(result => resolve(['Updated App Configuration', result]))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearCredentials = floDapps.clearCredentials = function () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
compactIDB.clearData('credentials', DEFAULT.application).then(result => {
|
|
||||||
localStorage.removeItem(`${DEFAULT.application}#privKey`);
|
|
||||||
user.clear();
|
|
||||||
resolve("privKey credentials deleted!")
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.deleteUserData = function (credentials = false) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let p = []
|
|
||||||
p.push(compactIDB.deleteDB(user.db_name))
|
|
||||||
if (credentials)
|
|
||||||
p.push(clearCredentials())
|
|
||||||
Promise.all(p)
|
|
||||||
.then(result => resolve('User database(local) deleted'))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.deleteAppData = function () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
compactIDB.deleteDB(DEFAULT.application).then(result => {
|
|
||||||
localStorage.removeItem(`${DEFAULT.application}#privKey`)
|
|
||||||
user.clear();
|
|
||||||
compactIDB.removeData('lastTx', `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root)
|
|
||||||
.then(result => resolve("App database(local) deleted"))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.securePrivKey = function (pwd) {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
let indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
|
||||||
if (!indexArr)
|
|
||||||
return reject("PrivKey not found");
|
|
||||||
indexArr = JSON.parse(indexArr)
|
|
||||||
let encryptedKey = Crypto.AES.encrypt(await user.private, pwd);
|
|
||||||
let threshold = indexArr.length;
|
|
||||||
let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold)
|
|
||||||
let promises = [];
|
|
||||||
let overwriteFn = (share, index) =>
|
|
||||||
compactIDB.writeData("credentials", share, index, DEFAULT.application);
|
|
||||||
for (var i = 0; i < threshold; i++)
|
|
||||||
promises.push(overwriteFn(shares[i], indexArr[i]));
|
|
||||||
Promise.all(promises)
|
|
||||||
.then(results => resolve("Private Key Secured"))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
floDapps.verifyPin = function (pin = null) {
|
|
||||||
const readSharesFromIDB = function (indexArr) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var promises = []
|
|
||||||
for (var i = 0; i < indexArr.length; i++)
|
|
||||||
promises.push(compactIDB.readData('credentials', indexArr[i]))
|
|
||||||
Promise.all(promises).then(shares => {
|
|
||||||
var secret = floCrypto.retrieveShamirSecret(shares)
|
|
||||||
console.info(shares, secret)
|
|
||||||
if (secret)
|
|
||||||
resolve(secret)
|
|
||||||
else
|
|
||||||
reject("Shares are insufficient or incorrect")
|
|
||||||
}).catch(error => {
|
|
||||||
clearCredentials();
|
|
||||||
location.reload();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
|
||||||
console.info(indexArr)
|
|
||||||
if (!indexArr)
|
|
||||||
reject('No login credentials found')
|
|
||||||
readSharesFromIDB(JSON.parse(indexArr)).then(key => {
|
|
||||||
if (key.length == 52) {
|
|
||||||
if (pin === null)
|
|
||||||
resolve("Private key not secured")
|
|
||||||
else
|
|
||||||
reject("Private key not secured")
|
|
||||||
} else {
|
|
||||||
if (pin === null)
|
|
||||||
return reject("PIN/Password required")
|
|
||||||
try {
|
|
||||||
let privKey = Crypto.AES.decrypt(key, pin);
|
|
||||||
resolve("PIN/Password verified")
|
|
||||||
} catch (error) {
|
|
||||||
reject("Incorrect PIN/Password")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const getNextGeneralData = floDapps.getNextGeneralData = function (type, vectorClock = null, options = {}) {
|
|
||||||
var fk = floCloudAPI.util.filterKey(type, options)
|
|
||||||
vectorClock = vectorClock || getNextGeneralData[fk] || '0';
|
|
||||||
var filteredResult = {}
|
|
||||||
if (floGlobals.generalData[fk]) {
|
|
||||||
for (let d in floGlobals.generalData[fk])
|
|
||||||
if (d > vectorClock)
|
|
||||||
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
|
|
||||||
} else if (options.comment) {
|
|
||||||
let comment = options.comment;
|
|
||||||
delete options.comment;
|
|
||||||
let fk = floCloudAPI.util.filterKey(type, options);
|
|
||||||
for (let d in floGlobals.generalData[fk])
|
|
||||||
if (d > vectorClock && floGlobals.generalData[fk][d].comment == comment)
|
|
||||||
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
|
|
||||||
}
|
|
||||||
if (options.decrypt) {
|
|
||||||
let decryptionKey = (options.decrypt === true) ? raw_user.private : options.decrypt;
|
|
||||||
if (!Array.isArray(decryptionKey))
|
|
||||||
decryptionKey = [decryptionKey];
|
|
||||||
for (let f in filteredResult) {
|
|
||||||
let data = filteredResult[f]
|
|
||||||
try {
|
|
||||||
if (data.message instanceof Object && "secret" in data.message) {
|
|
||||||
for (let key of decryptionKey) {
|
|
||||||
try {
|
|
||||||
let tmp = floCrypto.decryptData(data.message, key)
|
|
||||||
data.message = JSON.parse(tmp)
|
|
||||||
break;
|
|
||||||
} catch (error) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getNextGeneralData[fk] = Object.keys(filteredResult).sort().pop();
|
|
||||||
return filteredResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
const syncData = floDapps.syncData = {};
|
|
||||||
|
|
||||||
syncData.oldDevice = () => new Promise((resolve, reject) => {
|
|
||||||
let sync = {
|
|
||||||
contacts: user.contacts,
|
|
||||||
pubKeys: user.pubKeys,
|
|
||||||
messages: user.messages
|
|
||||||
}
|
|
||||||
let message = Crypto.AES.encrypt(JSON.stringify(sync), raw_user.private)
|
|
||||||
let options = {
|
|
||||||
receiverID: user.id,
|
|
||||||
application: DEFAULT.root
|
|
||||||
}
|
|
||||||
floCloudAPI.sendApplicationData(message, "syncData", options)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
});
|
|
||||||
|
|
||||||
syncData.newDevice = () => new Promise((resolve, reject) => {
|
|
||||||
var options = {
|
|
||||||
receiverID: user.id,
|
|
||||||
senderID: user.id,
|
|
||||||
application: DEFAULT.root,
|
|
||||||
mostRecent: true,
|
|
||||||
}
|
|
||||||
floCloudAPI.requestApplicationData("syncData", options).then(response => {
|
|
||||||
let vc = Object.keys(response).sort().pop()
|
|
||||||
let sync = JSON.parse(Crypto.AES.decrypt(response[vc].message, raw_user.private))
|
|
||||||
let promises = []
|
|
||||||
let store = (key, val, obs) => promises.push(compactIDB.writeData(obs, val, key, user.db_name));
|
|
||||||
["contacts", "pubKeys", "messages"].forEach(c => {
|
|
||||||
for (let i in sync[c]) {
|
|
||||||
store(i, sync[c][i], c)
|
|
||||||
user[c][i] = sync[c][i]
|
|
||||||
}
|
|
||||||
})
|
|
||||||
Promise.all(promises)
|
|
||||||
.then(results => resolve("Sync data successful"))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
});
|
|
||||||
})('object' === typeof module ? module.exports : window.floDapps = {});
|
|
||||||
166
floTokenAPI.js
166
floTokenAPI.js
@ -1,166 +0,0 @@
|
|||||||
(function (EXPORTS) { //floTokenAPI v1.0.4a
|
|
||||||
/* Token Operator to send/receive tokens via blockchain using API calls*/
|
|
||||||
'use strict';
|
|
||||||
const tokenAPI = EXPORTS;
|
|
||||||
|
|
||||||
const DEFAULT = {
|
|
||||||
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
|
|
||||||
currency: floGlobals.currency || "rupee"
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperties(tokenAPI, {
|
|
||||||
URL: {
|
|
||||||
get: () => DEFAULT.apiURL
|
|
||||||
},
|
|
||||||
currency: {
|
|
||||||
get: () => DEFAULT.currency,
|
|
||||||
set: currency => DEFAULT.currency = currency
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (floGlobals.currency) tokenAPI.currency = floGlobals.currency;
|
|
||||||
|
|
||||||
Object.defineProperties(floGlobals, {
|
|
||||||
currency: {
|
|
||||||
get: () => DEFAULT.currency,
|
|
||||||
set: currency => DEFAULT.currency = currency
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const fetch_api = tokenAPI.fetch = function (apicall) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
console.debug(DEFAULT.apiURL + apicall);
|
|
||||||
fetch(DEFAULT.apiURL + apicall).then(response => {
|
|
||||||
if (response.ok)
|
|
||||||
response.json().then(data => resolve(data));
|
|
||||||
else
|
|
||||||
reject(response)
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const getBalance = tokenAPI.getBalance = function (floID, token = DEFAULT.currency) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
|
|
||||||
.then(result => resolve(result.balance || 0))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenAPI.getTx = function (txID) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
|
|
||||||
if (res.result === "error")
|
|
||||||
reject(res.description);
|
|
||||||
else if (!res.parsedFloData)
|
|
||||||
reject("Data piece (parsedFloData) missing");
|
|
||||||
else if (!res.transactionDetails)
|
|
||||||
reject("Data piece (transactionDetails) missing");
|
|
||||||
else
|
|
||||||
resolve(res);
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenAPI.sendToken = function (privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
let senderID = floCrypto.getFloID(privKey);
|
|
||||||
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
|
|
||||||
return reject("Invalid amount");
|
|
||||||
getBalance(senderID, token).then(bal => {
|
|
||||||
if (amount > bal)
|
|
||||||
return reject(`Insufficient ${token}# balance`);
|
|
||||||
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID, options)
|
|
||||||
.then(txid => resolve(txid))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendTokens_raw(privKey, receiverID, token, amount, utxo, vout, scriptPubKey) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
var trx = bitjs.transaction();
|
|
||||||
trx.addinput(utxo, vout, scriptPubKey)
|
|
||||||
trx.addoutput(receiverID, floBlockchainAPI.sendAmt);
|
|
||||||
trx.addflodata(`send ${amount} ${token}#`);
|
|
||||||
var signedTxHash = trx.sign(privKey, 1);
|
|
||||||
floBlockchainAPI.broadcastTx(signedTxHash)
|
|
||||||
.then(txid => resolve([receiverID, txid]))
|
|
||||||
.catch(error => reject([receiverID, error]))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
//bulk transfer tokens
|
|
||||||
tokenAPI.bulkTransferTokens = function (sender, privKey, token, receivers) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (typeof receivers !== 'object')
|
|
||||||
return reject("receivers must be object in format {receiver1: amount1, receiver2:amount2...}")
|
|
||||||
|
|
||||||
let receiver_list = Object.keys(receivers), amount_list = Object.values(receivers);
|
|
||||||
let invalidReceivers = receiver_list.filter(id => !floCrypto.validateFloID(id));
|
|
||||||
let invalidAmount = amount_list.filter(val => typeof val !== 'number' || val <= 0);
|
|
||||||
if (invalidReceivers.length)
|
|
||||||
return reject(`Invalid receivers: ${invalidReceivers}`);
|
|
||||||
else if (invalidAmount.length)
|
|
||||||
return reject(`Invalid amounts: ${invalidAmount}`);
|
|
||||||
|
|
||||||
if (receiver_list.length == 0)
|
|
||||||
return reject("Receivers cannot be empty");
|
|
||||||
|
|
||||||
if (receiver_list.length == 1) {
|
|
||||||
let receiver = receiver_list[0], amount = amount_list[0];
|
|
||||||
floTokenAPI.sendToken(privKey, amount, receiver, "", token)
|
|
||||||
.then(txid => resolve({ success: { [receiver]: txid } }))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
} else {
|
|
||||||
//check for token balance
|
|
||||||
floTokenAPI.getBalance(sender, token).then(token_balance => {
|
|
||||||
let total_token_amout = amount_list.reduce((a, e) => a + e, 0);
|
|
||||||
if (total_token_amout > token_balance)
|
|
||||||
return reject(`Insufficient ${token}# balance`);
|
|
||||||
|
|
||||||
//split utxos
|
|
||||||
floBlockchainAPI.splitUTXOs(sender, privKey, receiver_list.length).then(split_txid => {
|
|
||||||
//wait for the split utxo to get confirmation
|
|
||||||
floBlockchainAPI.waitForConfirmation(split_txid).then(split_tx => {
|
|
||||||
//send tokens using the split-utxo
|
|
||||||
var scriptPubKey = split_tx.vout[0].scriptPubKey.hex;
|
|
||||||
let promises = [];
|
|
||||||
for (let i in receiver_list)
|
|
||||||
promises.push(sendTokens_raw(privKey, receiver_list[i], token, amount_list[i], split_txid, i, scriptPubKey));
|
|
||||||
Promise.allSettled(promises).then(results => {
|
|
||||||
let success = Object.fromEntries(results.filter(r => r.status == 'fulfilled').map(r => r.value));
|
|
||||||
let failed = Object.fromEntries(results.filter(r => r.status == 'rejected').map(r => r.reason));
|
|
||||||
resolve({ success, failed });
|
|
||||||
})
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
}).catch(error => reject(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenAPI.getAllTxs = function (floID, token = DEFAULT.currency) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
|
|
||||||
.then(result => resolve(result))
|
|
||||||
.catch(error => reject(error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const util = tokenAPI.util = {};
|
|
||||||
|
|
||||||
util.parseTxData = function (txData) {
|
|
||||||
let parsedData = {};
|
|
||||||
for (let p in txData.parsedFloData)
|
|
||||||
parsedData[p] = txData.parsedFloData[p];
|
|
||||||
parsedData.sender = txData.transactionDetails.vin[0].addr;
|
|
||||||
for (let vout of txData.transactionDetails.vout)
|
|
||||||
if (vout.scriptPubKey.addresses[0] !== parsedData.sender)
|
|
||||||
parsedData.receiver = vout.scriptPubKey.addresses[0];
|
|
||||||
parsedData.time = txData.transactionDetails.time;
|
|
||||||
return parsedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
})('object' === typeof module ? module.exports : window.floTokenAPI = {});
|
|
||||||
@ -18,7 +18,7 @@
|
|||||||
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.6.2" defer></script>
|
<script src="https://cdn.jsdelivr.net/npm/fuse.js@6.6.2" defer></script>
|
||||||
<script id="floGlobals">
|
<script id="floGlobals">
|
||||||
/* Constants for FLO blockchain operations !!Make sure to add this at begining!! */
|
/* Constants for FLO blockchain operations !!Make sure to add this at beginning!! */
|
||||||
const floGlobals = {
|
const floGlobals = {
|
||||||
|
|
||||||
//Required for all
|
//Required for all
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
(function(EXPORTS) { //compactIDB v2.1.0
|
(function (EXPORTS) { //compactIDB v2.1.2
|
||||||
/* Compact IndexedDB operations */
|
/* Compact IndexedDB operations */
|
||||||
'use strict';
|
'use strict';
|
||||||
const compactIDB = EXPORTS;
|
const compactIDB = EXPORTS;
|
||||||
@ -59,7 +59,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.initDB = function(dbName, objectStores = {}) {
|
compactIDB.initDB = function (dbName, objectStores = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!(objectStores instanceof Object))
|
if (!(objectStores instanceof Object))
|
||||||
return reject('ObjectStores must be an object or array')
|
return reject('ObjectStores must be an object or array')
|
||||||
@ -87,14 +87,14 @@
|
|||||||
resolve("Initiated IndexedDB");
|
resolve("Initiated IndexedDB");
|
||||||
else
|
else
|
||||||
upgradeDB(dbName, a_obs, d_obs)
|
upgradeDB(dbName, a_obs, d_obs)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const openDB = compactIDB.openDB = function(dbName = defaultDB) {
|
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var idb = indexedDB.open(dbName);
|
var idb = indexedDB.open(dbName);
|
||||||
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
idb.onerror = (event) => reject("Error in opening IndexedDB");
|
||||||
@ -106,7 +106,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteDB = compactIDB.deleteDB = function(dbName = defaultDB) {
|
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var deleteReq = indexedDB.deleteDatabase(dbName);;
|
var deleteReq = indexedDB.deleteDatabase(dbName);;
|
||||||
deleteReq.onerror = (event) => reject("Error deleting database!");
|
deleteReq.onerror = (event) => reject("Error deleting database!");
|
||||||
@ -114,7 +114,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.writeData = function(obsName, data, key = false, dbName = defaultDB) {
|
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
@ -128,7 +128,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.addData = function(obsName, data, key = false, dbName = defaultDB) {
|
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
@ -142,7 +142,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.removeData = function(obsName, key, dbName = defaultDB) {
|
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
@ -156,7 +156,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.clearData = function(obsName, dbName = defaultDB) {
|
compactIDB.clearData = function (obsName, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
|
||||||
@ -168,7 +168,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.readData = function(obsName, key, dbName = defaultDB) {
|
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
@ -182,7 +182,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
compactIDB.readAllData = function(obsName, dbName = defaultDB) {
|
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
|
||||||
@ -223,13 +223,12 @@
|
|||||||
})
|
})
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
compactIDB.searchData = function(obsName, options = {}, dbName = defaultDB) {
|
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
|
||||||
options.lowerKey = options.atKey || options.lowerKey || 0
|
options.lowerKey = options.atKey || options.lowerKey || 0
|
||||||
options.upperKey = options.atKey || options.upperKey || false
|
options.upperKey = options.atKey || options.upperKey || false
|
||||||
options.patternEval = options.patternEval || ((k, v) => {
|
options.patternEval = options.patternEval || ((k, v) => true);
|
||||||
return true
|
|
||||||
})
|
|
||||||
options.limit = options.limit || false;
|
options.limit = options.limit || false;
|
||||||
|
options.reverse = options.reverse || false;
|
||||||
options.lastOnly = options.lastOnly || false
|
options.lastOnly = options.lastOnly || false
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
openDB(dbName).then(db => {
|
openDB(dbName).then(db => {
|
||||||
@ -237,17 +236,16 @@
|
|||||||
var filteredResult = {}
|
var filteredResult = {}
|
||||||
let curReq = obs.openCursor(
|
let curReq = obs.openCursor(
|
||||||
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
|
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
|
||||||
options.lastOnly ? "prev" : "next");
|
options.lastOnly || options.reverse ? "prev" : "next");
|
||||||
curReq.onsuccess = (evt) => {
|
curReq.onsuccess = (evt) => {
|
||||||
var cursor = evt.target.result;
|
var cursor = evt.target.result;
|
||||||
if (cursor) {
|
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
|
||||||
if (options.patternEval(cursor.primaryKey, cursor.value)) {
|
return resolve(filteredResult); //reached end of key list or limit reached
|
||||||
filteredResult[cursor.primaryKey] = cursor.value;
|
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
|
||||||
options.lastOnly ? resolve(filteredResult) : cursor.continue();
|
filteredResult[cursor.primaryKey] = cursor.value;
|
||||||
} else
|
options.lastOnly ? resolve(filteredResult) : cursor.continue();
|
||||||
cursor.continue();
|
|
||||||
} else
|
} else
|
||||||
resolve(filteredResult);
|
cursor.continue();
|
||||||
}
|
}
|
||||||
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
|
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
|
||||||
db.close();
|
db.close();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,75 @@
|
|||||||
(function(EXPORTS) { //floCloudAPI v2.3.0
|
(function (EXPORTS) { //floCloudAPI v2.4.5
|
||||||
/* FLO Cloud operations to send/request application data*/
|
/* FLO Cloud operations to send/request application data*/
|
||||||
'use strict';
|
'use strict';
|
||||||
const floCloudAPI = EXPORTS;
|
const floCloudAPI = EXPORTS;
|
||||||
|
|
||||||
const DEFAULT = {
|
const DEFAULT = {
|
||||||
|
blockchainPrefix: 0x23, //Prefix version for FLO blockchain
|
||||||
SNStorageID: floGlobals.SNStorageID || "FNaN9McoBAEFUjkRmNQRYLmBF8SpS7Tgfk",
|
SNStorageID: floGlobals.SNStorageID || "FNaN9McoBAEFUjkRmNQRYLmBF8SpS7Tgfk",
|
||||||
adminID: floGlobals.adminID,
|
adminID: floGlobals.adminID,
|
||||||
application: floGlobals.application
|
application: floGlobals.application,
|
||||||
|
SNStorageName: "SuperNodeStorage",
|
||||||
|
callback: (d, e) => console.debug(d, e)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var user_id, user_public, user_private, aes_key;
|
||||||
|
|
||||||
|
function user(id, priv) {
|
||||||
|
if (!priv || !id)
|
||||||
|
return user.clear();
|
||||||
|
let pub = floCrypto.getPubKeyHex(priv);
|
||||||
|
if (!pub || !floCrypto.verifyPubKey(pub, id))
|
||||||
|
return user.clear();
|
||||||
|
let n = floCrypto.randInt(12, 20);
|
||||||
|
aes_key = floCrypto.randString(n);
|
||||||
|
user_private = Crypto.AES.encrypt(priv, aes_key);
|
||||||
|
user_public = pub;
|
||||||
|
user_id = id;
|
||||||
|
return user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperties(user, {
|
||||||
|
id: {
|
||||||
|
get: () => {
|
||||||
|
if (!user_id)
|
||||||
|
throw "User not set";
|
||||||
|
return user_id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
public: {
|
||||||
|
get: () => {
|
||||||
|
if (!user_public)
|
||||||
|
throw "User not set";
|
||||||
|
return user_public;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sign: {
|
||||||
|
value: msg => {
|
||||||
|
if (!user_private)
|
||||||
|
throw "User not set";
|
||||||
|
return floCrypto.signData(msg, Crypto.AES.decrypt(user_private, aes_key));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clear: {
|
||||||
|
value: () => user_id = user_public = user_private = aes_key = undefined
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
Object.defineProperties(floCloudAPI, {
|
Object.defineProperties(floCloudAPI, {
|
||||||
SNStorageID: {
|
SNStorageID: {
|
||||||
get: () => DEFAULT.SNStorageID
|
get: () => DEFAULT.SNStorageID
|
||||||
},
|
},
|
||||||
|
SNStorageName: {
|
||||||
|
get: () => DEFAULT.SNStorageName
|
||||||
|
},
|
||||||
adminID: {
|
adminID: {
|
||||||
get: () => DEFAULT.adminID
|
get: () => DEFAULT.adminID
|
||||||
},
|
},
|
||||||
application: {
|
application: {
|
||||||
get: () => DEFAULT.application
|
get: () => DEFAULT.application
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
get: () => user
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -31,6 +83,9 @@
|
|||||||
get: () => generalData,
|
get: () => generalData,
|
||||||
set: data => generalData = data
|
set: data => generalData = data
|
||||||
},
|
},
|
||||||
|
generalDataset: {
|
||||||
|
value: (type, options = {}) => generalData[filterKey(type, options)]
|
||||||
|
},
|
||||||
lastVC: {
|
lastVC: {
|
||||||
get: () => lastVC,
|
get: () => lastVC,
|
||||||
set: vc => lastVC = vc
|
set: vc => lastVC = vc
|
||||||
@ -43,7 +98,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
var kBucket;
|
var kBucket;
|
||||||
const K_Bucket = floCloudAPI.K_Bucket = function(masterID, nodeList) {
|
const K_Bucket = floCloudAPI.K_Bucket = function (masterID, nodeList) {
|
||||||
|
|
||||||
const decodeID = floID => {
|
const decodeID = floID => {
|
||||||
let k = bitjs.Base58.decode(floID);
|
let k = bitjs.Base58.decode(floID);
|
||||||
@ -77,7 +132,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
self.isNode = floID => _CO.includes(floID);
|
self.isNode = floID => _CO.includes(floID);
|
||||||
self.innerNodes = function(id1, id2) {
|
self.innerNodes = function (id1, id2) {
|
||||||
if (!_CO.includes(id1) || !_CO.includes(id2))
|
if (!_CO.includes(id1) || !_CO.includes(id2))
|
||||||
throw Error('Given nodes are not supernode');
|
throw Error('Given nodes are not supernode');
|
||||||
let iNodes = []
|
let iNodes = []
|
||||||
@ -88,7 +143,7 @@
|
|||||||
}
|
}
|
||||||
return iNodes
|
return iNodes
|
||||||
}
|
}
|
||||||
self.outterNodes = function(id1, id2) {
|
self.outterNodes = function (id1, id2) {
|
||||||
if (!_CO.includes(id1) || !_CO.includes(id2))
|
if (!_CO.includes(id1) || !_CO.includes(id2))
|
||||||
throw Error('Given nodes are not supernode');
|
throw Error('Given nodes are not supernode');
|
||||||
let oNodes = []
|
let oNodes = []
|
||||||
@ -99,7 +154,7 @@
|
|||||||
}
|
}
|
||||||
return oNodes
|
return oNodes
|
||||||
}
|
}
|
||||||
self.prevNode = function(id, N = 1) {
|
self.prevNode = function (id, N = 1) {
|
||||||
let n = N || _CO.length;
|
let n = N || _CO.length;
|
||||||
if (!_CO.includes(id))
|
if (!_CO.includes(id))
|
||||||
throw Error('Given node is not supernode');
|
throw Error('Given node is not supernode');
|
||||||
@ -113,7 +168,7 @@
|
|||||||
}
|
}
|
||||||
return (N == 1 ? pNodes[0] : pNodes)
|
return (N == 1 ? pNodes[0] : pNodes)
|
||||||
}
|
}
|
||||||
self.nextNode = function(id, N = 1) {
|
self.nextNode = function (id, N = 1) {
|
||||||
let n = N || _CO.length;
|
let n = N || _CO.length;
|
||||||
if (!_CO.includes(id))
|
if (!_CO.includes(id))
|
||||||
throw Error('Given node is not supernode');
|
throw Error('Given node is not supernode');
|
||||||
@ -128,7 +183,7 @@
|
|||||||
}
|
}
|
||||||
return (N == 1 ? nNodes[0] : nNodes)
|
return (N == 1 ? nNodes[0] : nNodes)
|
||||||
}
|
}
|
||||||
self.closestNode = function(id, N = 1) {
|
self.closestNode = function (id, N = 1) {
|
||||||
let decodedId = decodeID(id);
|
let decodedId = decodeID(id);
|
||||||
let n = N || _CO.length;
|
let n = N || _CO.length;
|
||||||
let cNodes = _KB.closest(decodedId, n)
|
let cNodes = _KB.closest(decodedId, n)
|
||||||
@ -175,7 +230,7 @@
|
|||||||
if (_inactive.size === kBucket.list.length)
|
if (_inactive.size === kBucket.list.length)
|
||||||
return reject('Cloud offline');
|
return reject('Cloud offline');
|
||||||
if (!(snID in supernodes))
|
if (!(snID in supernodes))
|
||||||
snID = kBucket.closestNode(snID);
|
snID = kBucket.closestNode(proxyID(snID));
|
||||||
ws_connect(snID)
|
ws_connect(snID)
|
||||||
.then(node => resolve(node))
|
.then(node => resolve(node))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -213,7 +268,7 @@
|
|||||||
if (_inactive.size === kBucket.list.length)
|
if (_inactive.size === kBucket.list.length)
|
||||||
return reject('Cloud offline');
|
return reject('Cloud offline');
|
||||||
if (!(snID in supernodes))
|
if (!(snID in supernodes))
|
||||||
snID = kBucket.closestNode(snID);
|
snID = kBucket.closestNode(proxyID(snID));
|
||||||
fetch_API(snID, data)
|
fetch_API(snID, data)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@ -242,8 +297,8 @@
|
|||||||
fetch_ActiveAPI(floID, data).then(response => {
|
fetch_ActiveAPI(floID, data).then(response => {
|
||||||
if (response.ok)
|
if (response.ok)
|
||||||
response.json()
|
response.json()
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
else response.text()
|
else response.text()
|
||||||
.then(result => reject(response.status + ": " + result)) //Error Message from Node
|
.then(result => reject(response.status + ": " + result)) //Error Message from Node
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -269,6 +324,7 @@
|
|||||||
data => {
|
data => {
|
||||||
data = objectifier(data);
|
data = objectifier(data);
|
||||||
let filtered = {},
|
let filtered = {},
|
||||||
|
proxy = proxyID(request.receiverID),
|
||||||
r = request;
|
r = request;
|
||||||
for (let v in data) {
|
for (let v in data) {
|
||||||
let d = data[v];
|
let d = data[v];
|
||||||
@ -277,7 +333,7 @@
|
|||||||
(r.atVectorClock || !r.upperVectorClock || r.upperVectorClock >= v) &&
|
(r.atVectorClock || !r.upperVectorClock || r.upperVectorClock >= v) &&
|
||||||
(!r.afterTime || r.afterTime < d.log_time) &&
|
(!r.afterTime || r.afterTime < d.log_time) &&
|
||||||
r.application == d.application &&
|
r.application == d.application &&
|
||||||
r.receiverID == d.receiverID &&
|
(proxy == d.receiverID || proxy == d.proxyID) &&
|
||||||
(!r.comment || r.comment == d.comment) &&
|
(!r.comment || r.comment == d.comment) &&
|
||||||
(!r.type || r.type == d.type) &&
|
(!r.type || r.type == d.type) &&
|
||||||
(!r.senderID || r.senderID.includes(d.senderID)))
|
(!r.senderID || r.senderID.includes(d.senderID)))
|
||||||
@ -318,20 +374,64 @@
|
|||||||
|
|
||||||
const util = floCloudAPI.util = {};
|
const util = floCloudAPI.util = {};
|
||||||
|
|
||||||
const encodeMessage = util.encodeMessage = function(message) {
|
const encodeMessage = util.encodeMessage = function (message) {
|
||||||
return btoa(unescape(encodeURIComponent(JSON.stringify(message))))
|
return btoa(unescape(encodeURIComponent(JSON.stringify(message))))
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodeMessage = util.decodeMessage = function(message) {
|
const decodeMessage = util.decodeMessage = function (message) {
|
||||||
return JSON.parse(decodeURIComponent(escape(atob(message))))
|
return JSON.parse(decodeURIComponent(escape(atob(message))))
|
||||||
}
|
}
|
||||||
|
|
||||||
const filterKey = util.filterKey = function(type, options) {
|
const filterKey = util.filterKey = function (type, options = {}) {
|
||||||
return type + (options.comment ? ':' + options.comment : '') +
|
return type + (options.comment ? ':' + options.comment : '') +
|
||||||
'|' + (options.group || options.receiverID || DEFAULT.adminID) +
|
'|' + (options.group || options.receiverID || DEFAULT.adminID) +
|
||||||
'|' + (options.application || DEFAULT.application);
|
'|' + (options.application || DEFAULT.application);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const proxyID = util.proxyID = function (address) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
var bytes;
|
||||||
|
if (address.length == 33 || address.length == 34) { //legacy encoding
|
||||||
|
let decode = bitjs.Base58.decode(address);
|
||||||
|
bytes = decode.slice(0, decode.length - 4);
|
||||||
|
let checksum = decode.slice(decode.length - 4),
|
||||||
|
hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3] ?
|
||||||
|
bytes = undefined : bytes.shift();
|
||||||
|
} else if (address.length == 42 || address.length == 62) { //bech encoding
|
||||||
|
if (typeof coinjs !== 'function')
|
||||||
|
throw "library missing (lib_btc.js)";
|
||||||
|
let decode = coinjs.bech32_decode(address);
|
||||||
|
if (decode) {
|
||||||
|
bytes = decode.data;
|
||||||
|
bytes.shift();
|
||||||
|
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
||||||
|
if (address.length == 62) //for long bech, aggregate once more to get 160 bit
|
||||||
|
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
||||||
|
}
|
||||||
|
} else if (address.length == 66) { //public key hex
|
||||||
|
bytes = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(address), {
|
||||||
|
asBytes: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (!bytes)
|
||||||
|
throw "Invalid address: " + address;
|
||||||
|
else {
|
||||||
|
bytes.unshift(DEFAULT.blockchainPrefix);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const lastCommit = {};
|
const lastCommit = {};
|
||||||
Object.defineProperty(lastCommit, 'get', {
|
Object.defineProperty(lastCommit, 'get', {
|
||||||
value: objName => JSON.parse(lastCommit[objName])
|
value: objName => JSON.parse(lastCommit[objName])
|
||||||
@ -392,19 +492,19 @@
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
//set status as online for myFloID
|
//set status as online for user_id
|
||||||
floCloudAPI.setStatus = function(options = {}) {
|
floCloudAPI.setStatus = function (options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let callback = options.callback instanceof Function ? options.callback : (d, e) => console.debug(d, e);
|
let callback = options.callback instanceof Function ? options.callback : DEFAULT.callback;
|
||||||
var request = {
|
var request = {
|
||||||
floID: myFloID,
|
floID: user.id,
|
||||||
application: options.application || DEFAULT.application,
|
application: options.application || DEFAULT.application,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
status: true,
|
status: true,
|
||||||
pubKey: myPubKey
|
pubKey: user.public
|
||||||
}
|
}
|
||||||
let hashcontent = ["time", "application", "floID"].map(d => request[d]).join("|");
|
let hashcontent = ["time", "application", "floID"].map(d => request[d]).join("|");
|
||||||
request.sign = floCrypto.signData(hashcontent, myPrivKey);
|
request.sign = user.sign(hashcontent);
|
||||||
liveRequest(options.refID || DEFAULT.adminID, request, callback)
|
liveRequest(options.refID || DEFAULT.adminID, request, callback)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -412,11 +512,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//request status of floID(s) in trackList
|
//request status of floID(s) in trackList
|
||||||
floCloudAPI.requestStatus = function(trackList, options = {}) {
|
floCloudAPI.requestStatus = function (trackList, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!Array.isArray(trackList))
|
if (!Array.isArray(trackList))
|
||||||
trackList = [trackList];
|
trackList = [trackList];
|
||||||
let callback = options.callback instanceof Function ? options.callback : (d, e) => console.debug(d, e);
|
let callback = options.callback instanceof Function ? options.callback : DEFAULT.callback;
|
||||||
let request = {
|
let request = {
|
||||||
status: false,
|
status: false,
|
||||||
application: options.application || DEFAULT.application,
|
application: options.application || DEFAULT.application,
|
||||||
@ -429,12 +529,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//send any message to supernode cloud storage
|
//send any message to supernode cloud storage
|
||||||
const sendApplicationData = floCloudAPI.sendApplicationData = function(message, type, options = {}) {
|
const sendApplicationData = floCloudAPI.sendApplicationData = function (message, type, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var data = {
|
var data = {
|
||||||
senderID: myFloID,
|
senderID: user.id,
|
||||||
receiverID: options.receiverID || DEFAULT.adminID,
|
receiverID: options.receiverID || DEFAULT.adminID,
|
||||||
pubKey: myPubKey,
|
pubKey: user.public,
|
||||||
message: encodeMessage(message),
|
message: encodeMessage(message),
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
application: options.application || DEFAULT.application,
|
application: options.application || DEFAULT.application,
|
||||||
@ -443,7 +543,7 @@
|
|||||||
}
|
}
|
||||||
let hashcontent = ["receiverID", "time", "application", "type", "message", "comment"]
|
let hashcontent = ["receiverID", "time", "application", "type", "message", "comment"]
|
||||||
.map(d => data[d]).join("|")
|
.map(d => data[d]).join("|")
|
||||||
data.sign = floCrypto.signData(hashcontent, myPrivKey);
|
data.sign = user.sign(hashcontent);
|
||||||
singleRequest(data.receiverID, data)
|
singleRequest(data.receiverID, data)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -451,7 +551,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//request any data from supernode cloud
|
//request any data from supernode cloud
|
||||||
const requestApplicationData = floCloudAPI.requestApplicationData = function(type, options = {}) {
|
const requestApplicationData = floCloudAPI.requestApplicationData = function (type, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var request = {
|
var request = {
|
||||||
receiverID: options.receiverID || DEFAULT.adminID,
|
receiverID: options.receiverID || DEFAULT.adminID,
|
||||||
@ -482,19 +582,20 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//(NEEDS UPDATE) delete data from supernode cloud (received only)
|
/*(NEEDS UPDATE)
|
||||||
|
//delete data from supernode cloud (received only)
|
||||||
floCloudAPI.deleteApplicationData = function(vectorClocks, options = {}) {
|
floCloudAPI.deleteApplicationData = function(vectorClocks, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var delreq = {
|
var delreq = {
|
||||||
requestorID: myFloID,
|
requestorID: user.id,
|
||||||
pubKey: myPubKey,
|
pubKey: user.public,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
delete: (Array.isArray(vectorClocks) ? vectorClocks : [vectorClocks]),
|
delete: (Array.isArray(vectorClocks) ? vectorClocks : [vectorClocks]),
|
||||||
application: options.application || DEFAULT.application
|
application: options.application || DEFAULT.application
|
||||||
}
|
}
|
||||||
let hashcontent = ["time", "application", "delete"]
|
let hashcontent = ["time", "application", "delete"]
|
||||||
.map(d => delreq[d]).join("|")
|
.map(d => delreq[d]).join("|")
|
||||||
delreq.sign = floCrypto.signData(hashcontent, myPrivKey)
|
delreq.sign = user.sign(hashcontent)
|
||||||
singleRequest(delreq.requestorID, delreq).then(result => {
|
singleRequest(delreq.requestorID, delreq).then(result => {
|
||||||
let success = [],
|
let success = [],
|
||||||
failed = [];
|
failed = [];
|
||||||
@ -507,64 +608,56 @@
|
|||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
//(NEEDS UPDATE) edit comment of data in supernode cloud (mutable comments only)
|
//edit comment of data in supernode cloud (sender only)
|
||||||
floCloudAPI.editApplicationData = function(vectorClock, newComment, oldData, options = {}) {
|
floCloudAPI.editApplicationData = function (vectorClock, comment_edit, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let p0
|
//request the data from cloud for resigning
|
||||||
if (!oldData) {
|
let req_options = Object.assign({}, options);
|
||||||
options.atVectorClock = vectorClock;
|
req_options.atVectorClock = vectorClock;
|
||||||
options.callback = false;
|
requestApplicationData(undefined, req_options).then(result => {
|
||||||
p0 = requestApplicationData(false, options)
|
if (!result.length)
|
||||||
} else
|
return reject("Data not found");
|
||||||
p0 = Promise.resolve({
|
let data = result[0];
|
||||||
vectorClock: {
|
if (data.senderID !== user.id)
|
||||||
...oldData
|
return reject("Only sender can edit comment");
|
||||||
}
|
data.comment = comment_edit;
|
||||||
})
|
let hashcontent = ["receiverID", "time", "application", "type", "message", "comment"]
|
||||||
p0.then(d => {
|
.map(d => data[d]).join("|");
|
||||||
if (d.senderID != myFloID)
|
let re_sign = user.sign(hashcontent);
|
||||||
return reject("Invalid requestorID")
|
var request = {
|
||||||
else if (!d.comment.startsWith("EDIT:"))
|
receiverID: options.receiverID || DEFAULT.adminID,
|
||||||
return reject("Data immutable")
|
requestorID: user.id,
|
||||||
let data = {
|
pubKey: user.public,
|
||||||
requestorID: myFloID,
|
|
||||||
receiverID: d.receiverID,
|
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
application: d.application,
|
vectorClock: vectorClock,
|
||||||
edit: {
|
edit: comment_edit,
|
||||||
vectorClock: vectorClock,
|
re_sign: re_sign
|
||||||
comment: newComment
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
d.comment = data.edit.comment;
|
let request_hash = ["time", "vectorClock", "edit", "re_sign"].map(d => request[d]).join("|");
|
||||||
let hashcontent = ["receiverID", "time", "application", "type", "message",
|
request.sign = user.sign(request_hash);
|
||||||
"comment"
|
singleRequest(request.receiverID, request)
|
||||||
]
|
.then(result => resolve(result))
|
||||||
.map(x => d[x]).join("|")
|
|
||||||
data.edit.sign = floCrypto.signData(hashcontent, myPrivKey)
|
|
||||||
singleRequest(data.receiverID, data)
|
|
||||||
.then(result => resolve("Data comment updated"))
|
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
})
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//tag data in supernode cloud (subAdmin access only)
|
//tag data in supernode cloud (subAdmin access only)
|
||||||
floCloudAPI.tagApplicationData = function(vectorClock, tag, options = {}) {
|
floCloudAPI.tagApplicationData = function (vectorClock, tag, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floGlobals.subAdmins.includes(myFloID))
|
if (!floGlobals.subAdmins.includes(user.id))
|
||||||
return reject("Only subAdmins can tag data")
|
return reject("Only subAdmins can tag data")
|
||||||
var request = {
|
var request = {
|
||||||
receiverID: options.receiverID || DEFAULT.adminID,
|
receiverID: options.receiverID || DEFAULT.adminID,
|
||||||
requestorID: myFloID,
|
requestorID: user.id,
|
||||||
pubKey: myPubKey,
|
pubKey: user.public,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
vectorClock: vectorClock,
|
vectorClock: vectorClock,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
}
|
}
|
||||||
let hashcontent = ["time", "vectorClock", 'tag'].map(d => request[d]).join("|");
|
let hashcontent = ["time", "vectorClock", 'tag'].map(d => request[d]).join("|");
|
||||||
request.sign = floCrypto.signData(hashcontent, myPrivKey);
|
request.sign = user.sign(hashcontent);
|
||||||
singleRequest(request.receiverID, request)
|
singleRequest(request.receiverID, request)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -572,18 +665,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//note data in supernode cloud (receiver only or subAdmin allowed if receiver is adminID)
|
//note data in supernode cloud (receiver only or subAdmin allowed if receiver is adminID)
|
||||||
floCloudAPI.noteApplicationData = function(vectorClock, note, options = {}) {
|
floCloudAPI.noteApplicationData = function (vectorClock, note, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var request = {
|
var request = {
|
||||||
receiverID: options.receiverID || DEFAULT.adminID,
|
receiverID: options.receiverID || DEFAULT.adminID,
|
||||||
requestorID: myFloID,
|
requestorID: user.id,
|
||||||
pubKey: myPubKey,
|
pubKey: user.public,
|
||||||
time: Date.now(),
|
time: Date.now(),
|
||||||
vectorClock: vectorClock,
|
vectorClock: vectorClock,
|
||||||
note: note,
|
note: note,
|
||||||
}
|
}
|
||||||
let hashcontent = ["time", "vectorClock", 'note'].map(d => request[d]).join("|");
|
let hashcontent = ["time", "vectorClock", 'note'].map(d => request[d]).join("|");
|
||||||
request.sign = floCrypto.signData(hashcontent, myPrivKey);
|
request.sign = user.sign(hashcontent);
|
||||||
singleRequest(request.receiverID, request)
|
singleRequest(request.receiverID, request)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
@ -591,7 +684,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//send general data
|
//send general data
|
||||||
floCloudAPI.sendGeneralData = function(message, type, options = {}) {
|
floCloudAPI.sendGeneralData = function (message, type, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (options.encrypt) {
|
if (options.encrypt) {
|
||||||
let encryptionKey = options.encrypt === true ?
|
let encryptionKey = options.encrypt === true ?
|
||||||
@ -605,7 +698,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//request general data
|
//request general data
|
||||||
floCloudAPI.requestGeneralData = function(type, options = {}) {
|
floCloudAPI.requestGeneralData = function (type, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var fk = filterKey(type, options)
|
var fk = filterKey(type, options)
|
||||||
lastVC[fk] = parseInt(lastVC[fk]) || 0;
|
lastVC[fk] = parseInt(lastVC[fk]) || 0;
|
||||||
@ -629,7 +722,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//request an object data from supernode cloud
|
//request an object data from supernode cloud
|
||||||
floCloudAPI.requestObjectData = function(objectName, options = {}) {
|
floCloudAPI.requestObjectData = function (objectName, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
options.lowerVectorClock = options.lowerVectorClock || lastVC[objectName] + 1;
|
options.lowerVectorClock = options.lowerVectorClock || lastVC[objectName] + 1;
|
||||||
options.senderID = [false, null].includes(options.senderID) ? null :
|
options.senderID = [false, null].includes(options.senderID) ? null :
|
||||||
@ -666,7 +759,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floCloudAPI.closeRequest = function(requestID) {
|
floCloudAPI.closeRequest = function (requestID) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let conn = _liveRequest[requestID]
|
let conn = _liveRequest[requestID]
|
||||||
if (!conn)
|
if (!conn)
|
||||||
@ -680,7 +773,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//reset or initialize an object and send it to cloud
|
//reset or initialize an object and send it to cloud
|
||||||
floCloudAPI.resetObjectData = function(objectName, options = {}) {
|
floCloudAPI.resetObjectData = function (objectName, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let message = {
|
let message = {
|
||||||
reset: appObjects[objectName]
|
reset: appObjects[objectName]
|
||||||
@ -694,7 +787,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//update the diff and send it to cloud
|
//update the diff and send it to cloud
|
||||||
floCloudAPI.updateObjectData = function(objectName, options = {}) {
|
floCloudAPI.updateObjectData = function (objectName, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let message = {
|
let message = {
|
||||||
diff: diff.find(lastCommit.get(objectName), appObjects[
|
diff: diff.find(lastCommit.get(objectName), appObjects[
|
||||||
@ -708,12 +801,73 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//upload file
|
||||||
|
floCloudAPI.uploadFile = function (fileBlob, type, options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!(fileBlob instanceof File) && !(fileBlob instanceof Blob))
|
||||||
|
return reject("file must be instance of File/Blob");
|
||||||
|
fileBlob.arrayBuffer().then(arraybuf => {
|
||||||
|
let file_data = { type: fileBlob.type, name: fileBlob.name };
|
||||||
|
file_data.content = Crypto.util.bytesToBase64(new Uint8Array(arraybuf));
|
||||||
|
if (options.encrypt) {
|
||||||
|
let encryptionKey = options.encrypt === true ?
|
||||||
|
floGlobals.settings.encryptionKey : options.encrypt
|
||||||
|
file_data = floCrypto.encryptData(JSON.stringify(file_data), encryptionKey)
|
||||||
|
}
|
||||||
|
sendApplicationData(file_data, type, options)
|
||||||
|
.then(({ vectorClock, receiverID, type, application }) => resolve({ vectorClock, receiverID, type, application }))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//download file
|
||||||
|
floCloudAPI.downloadFile = function (vectorClock, options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
options.atVectorClock = vectorClock;
|
||||||
|
requestApplicationData(options.type, options).then(result => {
|
||||||
|
if (!result.length)
|
||||||
|
return reject("File not found");
|
||||||
|
result = result[0];
|
||||||
|
try {
|
||||||
|
let file_data = decodeMessage(result.message);
|
||||||
|
//file is encrypted: decryption required
|
||||||
|
if (file_data instanceof Object && "secret" in file_data) {
|
||||||
|
if (!options.decrypt)
|
||||||
|
return reject("Data is encrypted");
|
||||||
|
let decryptionKey = (options.decrypt === true) ? Crypto.AES.decrypt(user_private, aes_key) : options.decrypt;
|
||||||
|
if (!Array.isArray(decryptionKey))
|
||||||
|
decryptionKey = [decryptionKey];
|
||||||
|
let flag = false;
|
||||||
|
for (let key of decryptionKey) {
|
||||||
|
try {
|
||||||
|
let tmp = floCrypto.decryptData(file_data, key);
|
||||||
|
file_data = JSON.parse(tmp);
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
} catch (error) { }
|
||||||
|
}
|
||||||
|
if (!flag)
|
||||||
|
return reject("Unable to decrypt file: Invalid private key");
|
||||||
|
}
|
||||||
|
//reconstruct the file
|
||||||
|
let arraybuf = new Uint8Array(Crypto.util.base64ToBytes(file_data.content))
|
||||||
|
result.file = new File([arraybuf], file_data.name, { type: file_data.type });
|
||||||
|
resolve(result)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
reject("Data is not a file");
|
||||||
|
}
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Functions:
|
Functions:
|
||||||
findDiff(original, updatedObj) returns an object with the added, deleted and updated differences
|
findDiff(original, updatedObj) returns an object with the added, deleted and updated differences
|
||||||
mergeDiff(original, allDiff) returns a new object from original object merged with all differences (allDiff is returned object of findDiff)
|
mergeDiff(original, allDiff) returns a new object from original object merged with all differences (allDiff is returned object of findDiff)
|
||||||
*/
|
*/
|
||||||
var diff = (function() {
|
var diff = (function () {
|
||||||
const isDate = d => d instanceof Date;
|
const isDate = d => d instanceof Date;
|
||||||
const isEmpty = o => Object.keys(o).length === 0;
|
const isEmpty = o => Object.keys(o).length === 0;
|
||||||
const isObject = o => o != null && typeof o === 'object';
|
const isObject = o => o != null && typeof o === 'object';
|
||||||
@ -887,23 +1041,23 @@
|
|||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
const mergeRecursive = (obj1, obj2) => {
|
const mergeRecursive = (obj1, obj2, deleteMode = false) => {
|
||||||
for (var p in obj2) {
|
for (var p in obj2) {
|
||||||
try {
|
try {
|
||||||
if (obj2[p].constructor == Object)
|
if (obj2[p].constructor == Object)
|
||||||
obj1[p] = mergeRecursive(obj1[p], obj2[p]);
|
obj1[p] = mergeRecursive(obj1[p], obj2[p], deleteMode);
|
||||||
// Property in destination object set; update its value.
|
// Property in destination object set; update its value.
|
||||||
else if (Ext.isArray(obj2[p])) {
|
else if (Array.isArray(obj2[p])) {
|
||||||
// obj1[p] = [];
|
// obj1[p] = [];
|
||||||
if (obj2[p].length < 1)
|
if (obj2[p].length < 1)
|
||||||
obj1[p] = obj2[p];
|
obj1[p] = obj2[p];
|
||||||
else
|
else
|
||||||
obj1[p] = mergeRecursive(obj1[p], obj2[p]);
|
obj1[p] = mergeRecursive(obj1[p], obj2[p], deleteMode);
|
||||||
} else
|
} else
|
||||||
obj1[p] = obj2[p];
|
obj1[p] = deleteMode && obj2[p] === null ? undefined : obj2[p];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Property in destination object not set; create it and set its value.
|
// Property in destination object not set; create it and set its value.
|
||||||
obj1[p] = obj2[p];
|
obj1[p] = deleteMode && obj2[p] === null ? undefined : obj2[p];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj1;
|
return obj1;
|
||||||
@ -912,20 +1066,13 @@
|
|||||||
const cleanse = (obj) => {
|
const cleanse = (obj) => {
|
||||||
Object.keys(obj).forEach(key => {
|
Object.keys(obj).forEach(key => {
|
||||||
var value = obj[key];
|
var value = obj[key];
|
||||||
if (typeof value === "object" && value !== null) {
|
if (typeof value === "object" && value !== null)
|
||||||
// Recurse...
|
obj[key] = cleanse(value);
|
||||||
cleanse(value);
|
else if (typeof value === 'undefined')
|
||||||
// ...and remove if now "empty" (NOTE: insert your definition of "empty" here)
|
delete obj[key]; // undefined, remove it
|
||||||
//if (!Object.keys(value).length)
|
|
||||||
// delete obj[key];
|
|
||||||
} else if (value === null)
|
|
||||||
delete obj[key]; // null, remove it
|
|
||||||
});
|
});
|
||||||
if (obj.constructor.toString().indexOf("Array") != -1) {
|
if (Array.isArray(obj))
|
||||||
obj = obj.filter(function(el) {
|
obj = obj.filter(v => typeof v !== 'undefined');
|
||||||
return el != null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,7 +1088,7 @@
|
|||||||
if (Object.keys(diff.updated).length !== 0)
|
if (Object.keys(diff.updated).length !== 0)
|
||||||
obj = mergeRecursive(obj, diff.updated)
|
obj = mergeRecursive(obj, diff.updated)
|
||||||
if (Object.keys(diff.deleted).length !== 0) {
|
if (Object.keys(diff.deleted).length !== 0) {
|
||||||
obj = mergeRecursive(obj, diff.deleted)
|
obj = mergeRecursive(obj, diff.deleted, true)
|
||||||
obj = cleanse(obj)
|
obj = cleanse(obj)
|
||||||
}
|
}
|
||||||
if (Object.keys(diff.added).length !== 0)
|
if (Object.keys(diff.added).length !== 0)
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
(function(EXPORTS) { //floCrypto v2.3.0a
|
(function (EXPORTS) { //floCrypto v2.3.6a
|
||||||
/* FLO Crypto Operators */
|
/* FLO Crypto Operators */
|
||||||
'use strict';
|
'use strict';
|
||||||
const floCrypto = EXPORTS;
|
const floCrypto = EXPORTS;
|
||||||
@ -7,6 +7,7 @@
|
|||||||
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
|
||||||
const ascii_alternatives = `‘ '\n’ '\n“ "\n” "\n– --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
|
const ascii_alternatives = `‘ '\n’ '\n“ "\n” "\n– --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
|
||||||
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
|
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
|
||||||
|
coinjs.compressed = true; //defaulting coinjs compressed to true;
|
||||||
|
|
||||||
function calculateY(x) {
|
function calculateY(x) {
|
||||||
let exp = exponent1();
|
let exp = exponent1();
|
||||||
@ -77,24 +78,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//generate a random Interger within range
|
//generate a random Interger within range
|
||||||
floCrypto.randInt = function(min, max) {
|
floCrypto.randInt = function (min, max) {
|
||||||
min = Math.ceil(min);
|
min = Math.ceil(min);
|
||||||
max = Math.floor(max);
|
max = Math.floor(max);
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate a random String within length (options : alphaNumeric chars only)
|
//generate a random String within length (options : alphaNumeric chars only)
|
||||||
floCrypto.randString = function(length, alphaNumeric = true) {
|
floCrypto.randString = function (length, alphaNumeric = true) {
|
||||||
var result = '';
|
var result = '';
|
||||||
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
|
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
|
||||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
|
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
|
||||||
for (var i = 0; i < length; i++)
|
for (var i = 0; i < length; i++)
|
||||||
result += characters.charAt(Math.floor(Math.random() * characters.length));
|
result += characters.charAt(Math.floor(securedMathRandom() * characters.length));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Encrypt Data using public-key
|
//Encrypt Data using public-key
|
||||||
floCrypto.encryptData = function(data, receiverPublicKeyHex) {
|
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
|
||||||
var senderECKeyData = getSenderPublicKeyString();
|
var senderECKeyData = getSenderPublicKeyString();
|
||||||
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
|
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
|
||||||
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
|
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
|
||||||
@ -106,7 +107,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Decrypt Data using private-key
|
//Decrypt Data using private-key
|
||||||
floCrypto.decryptData = function(data, privateKeyHex) {
|
floCrypto.decryptData = function (data, privateKeyHex) {
|
||||||
var receiverECKeyData = {};
|
var receiverECKeyData = {};
|
||||||
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
|
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
|
||||||
let privateKey = wifToDecimal(privateKeyHex, true);
|
let privateKey = wifToDecimal(privateKeyHex, true);
|
||||||
@ -119,31 +120,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Sign data using private-key
|
//Sign data using private-key
|
||||||
floCrypto.signData = function(data, privateKeyHex) {
|
floCrypto.signData = function (data, privateKeyHex) {
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
key.setCompressed(true);
|
|
||||||
var privateKeyArr = key.getBitcoinPrivateKeyByteArray();
|
|
||||||
var privateKey = BigInteger.fromByteArrayUnsigned(privateKeyArr);
|
|
||||||
var messageHash = Crypto.SHA256(data);
|
var messageHash = Crypto.SHA256(data);
|
||||||
var messageHashBigInteger = new BigInteger(messageHash);
|
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
|
||||||
var messageSign = Bitcoin.ECDSA.sign(messageHashBigInteger, key.priv);
|
|
||||||
var sighex = Crypto.util.bytesToHex(messageSign);
|
var sighex = Crypto.util.bytesToHex(messageSign);
|
||||||
return sighex;
|
return sighex;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Verify signatue of the data using public-key
|
//Verify signatue of the data using public-key
|
||||||
floCrypto.verifySign = function(data, signatureHex, publicKeyHex) {
|
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
|
||||||
var msgHash = Crypto.SHA256(data);
|
var msgHash = Crypto.SHA256(data);
|
||||||
var messageHashBigInteger = new BigInteger(msgHash);
|
|
||||||
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
var sigBytes = Crypto.util.hexToBytes(signatureHex);
|
||||||
var signature = Bitcoin.ECDSA.parseSig(sigBytes);
|
|
||||||
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
|
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
|
||||||
var verify = Bitcoin.ECDSA.verifyRaw(messageHashBigInteger, signature.r, signature.s, publicKeyPoint);
|
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
|
||||||
return verify;
|
return verify;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Generates a new flo ID and returns private-key, public-key and floID
|
//Generates a new flo ID and returns private-key, public-key and floID
|
||||||
const generateNewID = floCrypto.generateNewID = function() {
|
const generateNewID = floCrypto.generateNewID = function () {
|
||||||
var key = new Bitcoin.ECKey(false);
|
var key = new Bitcoin.ECKey(false);
|
||||||
key.setCompressed(true);
|
key.setCompressed(true);
|
||||||
return {
|
return {
|
||||||
@ -153,12 +148,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperty(floCrypto, 'newID', {
|
Object.defineProperties(floCrypto, {
|
||||||
get: () => generateNewID()
|
newID: {
|
||||||
|
get: () => generateNewID()
|
||||||
|
},
|
||||||
|
hashID: {
|
||||||
|
value: (str) => {
|
||||||
|
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), { asBytes: true });
|
||||||
|
bytes.unshift(bitjs.pub);
|
||||||
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
var checksum = hash.slice(0, 4);
|
||||||
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tmpID: {
|
||||||
|
get: () => {
|
||||||
|
let bytes = Crypto.util.randomBytes(20);
|
||||||
|
bytes.unshift(bitjs.pub);
|
||||||
|
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
var checksum = hash.slice(0, 4);
|
||||||
|
return bitjs.Base58.encode(bytes.concat(checksum));
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Returns public-key from private-key
|
//Returns public-key from private-key
|
||||||
floCrypto.getPubKeyHex = function(privateKeyHex) {
|
floCrypto.getPubKeyHex = function (privateKeyHex) {
|
||||||
if (!privateKeyHex)
|
if (!privateKeyHex)
|
||||||
return null;
|
return null;
|
||||||
var key = new Bitcoin.ECKey(privateKeyHex);
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
@ -169,7 +192,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Returns flo-ID from public-key or private-key
|
//Returns flo-ID from public-key or private-key
|
||||||
floCrypto.getFloID = function(keyHex) {
|
floCrypto.getFloID = function (keyHex) {
|
||||||
if (!keyHex)
|
if (!keyHex)
|
||||||
return null;
|
return null;
|
||||||
try {
|
try {
|
||||||
@ -182,8 +205,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
floCrypto.getAddress = function (privateKeyHex, strict = false) {
|
||||||
|
if (!privateKeyHex)
|
||||||
|
return;
|
||||||
|
var key = new Bitcoin.ECKey(privateKeyHex);
|
||||||
|
if (key.priv == null)
|
||||||
|
return null;
|
||||||
|
key.setCompressed(true);
|
||||||
|
let pubKey = key.getPubKeyHex(),
|
||||||
|
version = bitjs.Base58.decode(privateKeyHex)[0];
|
||||||
|
switch (version) {
|
||||||
|
case coinjs.priv: //BTC
|
||||||
|
return coinjs.bech32Address(pubKey).address;
|
||||||
|
case bitjs.priv: //FLO
|
||||||
|
return bitjs.pubkey2address(pubKey);
|
||||||
|
default:
|
||||||
|
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Verify the private-key for the given public-key or flo-ID
|
//Verify the private-key for the given public-key or flo-ID
|
||||||
floCrypto.verifyPrivKey = function(privateKeyHex, pubKey_floID, isfloID = true) {
|
floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) {
|
||||||
if (!privateKeyHex || !pubKey_floID)
|
if (!privateKeyHex || !pubKey_floID)
|
||||||
return false;
|
return false;
|
||||||
try {
|
try {
|
||||||
@ -193,7 +235,7 @@
|
|||||||
key.setCompressed(true);
|
key.setCompressed(true);
|
||||||
if (isfloID && pubKey_floID == key.getBitcoinAddress())
|
if (isfloID && pubKey_floID == key.getBitcoinAddress())
|
||||||
return true;
|
return true;
|
||||||
else if (!isfloID && pubKey_floID == key.getPubKeyHex())
|
else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
@ -202,20 +244,195 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if the given Address is valid or not
|
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
|
||||||
floCrypto.validateFloID = floCrypto.validateAddr = function(inpAddr) {
|
if (!Array.isArray(publicKeyList) || !publicKeyList.length)
|
||||||
if (!inpAddr)
|
return null;
|
||||||
|
if (!Number.isInteger(requiredSignatures) || requiredSignatures < 1 || requiredSignatures > publicKeyList.length)
|
||||||
|
return null;
|
||||||
|
try {
|
||||||
|
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
|
||||||
|
return multisig;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
floCrypto.decodeRedeemScript = function (redeemScript) {
|
||||||
|
try {
|
||||||
|
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
|
||||||
|
return decoded;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if the given flo-id is valid or not
|
||||||
|
floCrypto.validateFloID = function (floID, regularOnly = false) {
|
||||||
|
if (!floID)
|
||||||
return false;
|
return false;
|
||||||
try {
|
try {
|
||||||
let addr = new Bitcoin.Address(inpAddr);
|
let addr = new Bitcoin.Address(floID);
|
||||||
|
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check if the given address (any blockchain) is valid or not
|
||||||
|
floCrypto.validateAddr = function (address, std = true, bech = true) {
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return false;
|
||||||
|
if (typeof raw.version !== 'undefined') { //legacy or segwit
|
||||||
|
if (std == false)
|
||||||
|
return false;
|
||||||
|
else if (std === true || (!Array.isArray(std) && std === raw.version) || (Array.isArray(std) && std.includes(raw.version)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else if (typeof raw.bech_version !== 'undefined') { //bech32
|
||||||
|
if (bech === false)
|
||||||
|
return false;
|
||||||
|
else if (bech === true || (!Array.isArray(bech) && bech === raw.bech_version) || (Array.isArray(bech) && bech.includes(raw.bech_version)))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
} else //unknown
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check the public-key (or redeem-script) for the address (any blockchain)
|
||||||
|
floCrypto.verifyPubKey = function (pubKeyHex, address) {
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && raw.bytes.length == 32) //bech32-multisig
|
||||||
|
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
|
||||||
|
return pub_hash === raw.hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the given address (any blockchain) to equivalent floID
|
||||||
|
floCrypto.toFloID = function (address, options = null) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
else if (options) { //if (optional) version check is passed
|
||||||
|
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
||||||
|
return;
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
raw.bytes.unshift(bitjs.pub);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert raw address bytes to floID
|
||||||
|
floCrypto.rawToFloID = function (raw_bytes) {
|
||||||
|
if (typeof raw_bytes === 'string')
|
||||||
|
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
|
||||||
|
if (raw_bytes.length != 20)
|
||||||
|
return null;
|
||||||
|
raw_bytes.unshift(bitjs.pub);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw_bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the given multisig address (any blockchain) to equivalent multisig floID
|
||||||
|
floCrypto.toMultisigFloID = function (address, options = null) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
let raw = decodeAddress(address);
|
||||||
|
if (!raw)
|
||||||
|
return;
|
||||||
|
else if (options) { //if (optional) version check is passed
|
||||||
|
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
|
||||||
|
return;
|
||||||
|
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof raw.bech_version !== 'undefined') {
|
||||||
|
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
|
||||||
|
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
|
||||||
|
raw.bytes = ripemd160(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
raw.bytes.unshift(bitjs.multisig);
|
||||||
|
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
|
||||||
|
floCrypto.isSameAddr = function (addr1, addr2) {
|
||||||
|
if (!addr1 || !addr2)
|
||||||
|
return;
|
||||||
|
let raw1 = decodeAddress(addr1),
|
||||||
|
raw2 = decodeAddress(addr2);
|
||||||
|
if (!raw1 || !raw2)
|
||||||
|
return false;
|
||||||
|
else {
|
||||||
|
if (typeof raw1.bech_version !== 'undefined' && raw1.bytes.length == 32) //bech32-multisig
|
||||||
|
raw1.hex = Crypto.util.bytesToHex(ripemd160(raw1.bytes, { asBytes: true }));
|
||||||
|
if (typeof raw2.bech_version !== 'undefined' && raw2.bytes.length == 32) //bech32-multisig
|
||||||
|
raw2.hex = Crypto.util.bytesToHex(ripemd160(raw2.bytes, { asBytes: true }));
|
||||||
|
return raw1.hex === raw2.hex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const decodeAddress = floCrypto.decodeAddr = function (address) {
|
||||||
|
if (!address)
|
||||||
|
return;
|
||||||
|
else if (address.length == 33 || address.length == 34) { //legacy encoding
|
||||||
|
let decode = bitjs.Base58.decode(address);
|
||||||
|
let bytes = decode.slice(0, decode.length - 4);
|
||||||
|
let checksum = decode.slice(decode.length - 4),
|
||||||
|
hash = Crypto.SHA256(Crypto.SHA256(bytes, {
|
||||||
|
asBytes: true
|
||||||
|
}), {
|
||||||
|
asBytes: true
|
||||||
|
});
|
||||||
|
return (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) ? null : {
|
||||||
|
version: bytes.shift(),
|
||||||
|
hex: Crypto.util.bytesToHex(bytes),
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
} else if (address.length == 42 || address.length == 62) { //bech encoding
|
||||||
|
let decode = coinjs.bech32_decode(address);
|
||||||
|
if (decode) {
|
||||||
|
let bytes = decode.data;
|
||||||
|
let bech_version = bytes.shift();
|
||||||
|
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
|
||||||
|
return {
|
||||||
|
bech_version,
|
||||||
|
hrp: decode.hrp,
|
||||||
|
hex: Crypto.util.bytesToHex(bytes),
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Split the str using shamir's Secret and Returns the shares
|
//Split the str using shamir's Secret and Returns the shares
|
||||||
floCrypto.createShamirsSecretShares = function(str, total_shares, threshold_limit) {
|
floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) {
|
||||||
try {
|
try {
|
||||||
if (str.length > 0) {
|
if (str.length > 0) {
|
||||||
var strHex = shamirSecretShare.str2hex(str);
|
var strHex = shamirSecretShare.str2hex(str);
|
||||||
@ -229,7 +446,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Returns the retrived secret by combining the shamirs shares
|
//Returns the retrived secret by combining the shamirs shares
|
||||||
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function(sharesArray) {
|
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) {
|
||||||
try {
|
try {
|
||||||
if (sharesArray.length > 0) {
|
if (sharesArray.length > 0) {
|
||||||
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
|
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
|
||||||
@ -243,7 +460,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Verifies the shares and str
|
//Verifies the shares and str
|
||||||
floCrypto.verifyShamirsSecret = function(sharesArray, str) {
|
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
|
||||||
if (!str)
|
if (!str)
|
||||||
return null;
|
return null;
|
||||||
else if (retrieveShamirSecret(sharesArray) === str)
|
else if (retrieveShamirSecret(sharesArray) === str)
|
||||||
@ -252,7 +469,7 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateASCII = floCrypto.validateASCII = function(string, bool = true) {
|
const validateASCII = floCrypto.validateASCII = function (string, bool = true) {
|
||||||
if (typeof string !== "string")
|
if (typeof string !== "string")
|
||||||
return null;
|
return null;
|
||||||
if (bool) {
|
if (bool) {
|
||||||
@ -270,8 +487,8 @@
|
|||||||
if (x < 32 || x > 127)
|
if (x < 32 || x > 127)
|
||||||
if (x in invalids)
|
if (x in invalids)
|
||||||
invalids[string[i]].push(i)
|
invalids[string[i]].push(i)
|
||||||
else
|
else
|
||||||
invalids[string[i]] = [i];
|
invalids[string[i]] = [i];
|
||||||
}
|
}
|
||||||
if (Object.keys(invalids).length)
|
if (Object.keys(invalids).length)
|
||||||
return invalids;
|
return invalids;
|
||||||
@ -280,7 +497,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
floCrypto.convertToASCII = function(string, mode = 'soft-remove') {
|
floCrypto.convertToASCII = function (string, mode = 'soft-remove') {
|
||||||
let chars = validateASCII(string, false);
|
let chars = validateASCII(string, false);
|
||||||
if (chars === true)
|
if (chars === true)
|
||||||
return string;
|
return string;
|
||||||
@ -291,9 +508,9 @@
|
|||||||
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
|
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
|
||||||
mode = mode.toLowerCase();
|
mode = mode.toLowerCase();
|
||||||
if (mode === "hard-unicode")
|
if (mode === "hard-unicode")
|
||||||
convertor = (c) => `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`;
|
convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||||
else if (mode === "soft-unicode")
|
else if (mode === "soft-unicode")
|
||||||
convertor = (c) => refAlt[c] || `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`;
|
convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
|
||||||
else if (mode === "hard-remove")
|
else if (mode === "hard-remove")
|
||||||
convertor = c => "";
|
convertor = c => "";
|
||||||
else if (mode === "soft-remove")
|
else if (mode === "soft-remove")
|
||||||
@ -305,7 +522,7 @@
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
floCrypto.revertUnicode = function(string) {
|
floCrypto.revertUnicode = function (string) {
|
||||||
return string.replace(/\\u[\dA-F]{4}/gi,
|
return string.replace(/\\u[\dA-F]{4}/gi,
|
||||||
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
|
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,185 @@
|
|||||||
(function(EXPORTS) { //floDapps v2.2.1
|
(function (EXPORTS) { //floDapps v2.4.1
|
||||||
/* General functions for FLO Dapps*/
|
/* General functions for FLO Dapps*/
|
||||||
//'use strict';
|
'use strict';
|
||||||
const floDapps = EXPORTS;
|
const floDapps = EXPORTS;
|
||||||
|
|
||||||
|
const DEFAULT = {
|
||||||
|
root: "floDapps",
|
||||||
|
application: floGlobals.application,
|
||||||
|
adminID: floGlobals.adminID
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperties(floDapps, {
|
||||||
|
application: {
|
||||||
|
get: () => DEFAULT.application
|
||||||
|
},
|
||||||
|
adminID: {
|
||||||
|
get: () => DEFAULT.adminID
|
||||||
|
},
|
||||||
|
root: {
|
||||||
|
get: () => DEFAULT.root
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var user_priv_raw, aes_key, user_priv_wrap; //private variable inside capsule
|
||||||
|
const raw_user = {
|
||||||
|
get private() {
|
||||||
|
if (!user_priv_raw)
|
||||||
|
throw "User not logged in";
|
||||||
|
return Crypto.AES.decrypt(user_priv_raw, aes_key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var user_id, user_public, user_private;
|
||||||
|
const user = floDapps.user = {
|
||||||
|
get id() {
|
||||||
|
if (!user_id)
|
||||||
|
throw "User not logged in";
|
||||||
|
return user_id;
|
||||||
|
},
|
||||||
|
get public() {
|
||||||
|
if (!user_public)
|
||||||
|
throw "User not logged in";
|
||||||
|
return user_public;
|
||||||
|
},
|
||||||
|
get private() {
|
||||||
|
if (!user_private)
|
||||||
|
throw "User not logged in";
|
||||||
|
else if (user_private instanceof Function)
|
||||||
|
return user_private();
|
||||||
|
else
|
||||||
|
return Crypto.AES.decrypt(user_private, aes_key);
|
||||||
|
},
|
||||||
|
sign(message) {
|
||||||
|
return floCrypto.signData(message, raw_user.private);
|
||||||
|
},
|
||||||
|
decrypt(data) {
|
||||||
|
return floCrypto.decryptData(data, raw_user.private);
|
||||||
|
},
|
||||||
|
encipher(message) {
|
||||||
|
return Crypto.AES.encrypt(message, raw_user.private);
|
||||||
|
},
|
||||||
|
decipher(data) {
|
||||||
|
return Crypto.AES.decrypt(data, raw_user.private);
|
||||||
|
},
|
||||||
|
get db_name() {
|
||||||
|
return "floDapps#" + floCrypto.toFloID(user.id);
|
||||||
|
},
|
||||||
|
lock() {
|
||||||
|
user_private = user_priv_wrap;
|
||||||
|
},
|
||||||
|
async unlock() {
|
||||||
|
if (await user.private === raw_user.private)
|
||||||
|
user_private = user_priv_raw;
|
||||||
|
},
|
||||||
|
get_contact(id) {
|
||||||
|
if (!user.contacts)
|
||||||
|
throw "Contacts not available";
|
||||||
|
else if (user.contacts[id])
|
||||||
|
return user.contacts[id];
|
||||||
|
else {
|
||||||
|
let id_raw = floCrypto.decodeAddr(id).hex;
|
||||||
|
for (let i in user.contacts)
|
||||||
|
if (floCrypto.decodeAddr(i).hex == id_raw)
|
||||||
|
return user.contacts[i];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get_pubKey(id) {
|
||||||
|
if (!user.pubKeys)
|
||||||
|
throw "Contacts not available";
|
||||||
|
else if (user.pubKeys[id])
|
||||||
|
return user.pubKeys[id];
|
||||||
|
else {
|
||||||
|
let id_raw = floCrypto.decodeAddr(id).hex;
|
||||||
|
for (let i in user.pubKeys)
|
||||||
|
if (floCrypto.decodeAddr(i).hex == id_raw)
|
||||||
|
return user.pubKeys[i];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
user_id = user_public = user_private = undefined;
|
||||||
|
user_priv_raw = aes_key = undefined;
|
||||||
|
delete user.contacts;
|
||||||
|
delete user.pubKeys;
|
||||||
|
delete user.messages;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.defineProperties(window, {
|
||||||
|
myFloID: {
|
||||||
|
get: () => {
|
||||||
|
try {
|
||||||
|
return user.id;
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
myUserID: {
|
||||||
|
get: () => {
|
||||||
|
try {
|
||||||
|
return user.id;
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
myPubKey: {
|
||||||
|
get: () => {
|
||||||
|
try {
|
||||||
|
return user.public;
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
myPrivKey: {
|
||||||
|
get: () => {
|
||||||
|
try {
|
||||||
|
return user.private;
|
||||||
|
} catch {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var subAdmins, trustedIDs, settings;
|
||||||
|
Object.defineProperties(floGlobals, {
|
||||||
|
subAdmins: {
|
||||||
|
get: () => subAdmins
|
||||||
|
},
|
||||||
|
trustedIDs: {
|
||||||
|
get: () => trustedIDs
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
get: () => settings
|
||||||
|
},
|
||||||
|
contacts: {
|
||||||
|
get: () => user.contacts
|
||||||
|
},
|
||||||
|
pubKeys: {
|
||||||
|
get: () => user.pubKeys
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
get: () => user.messages
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function initIndexedDB() {
|
function initIndexedDB() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var obs_g = {
|
var obs_g = {
|
||||||
//general
|
//general
|
||||||
lastTx: {},
|
lastTx: {},
|
||||||
//supernode (cloud list)
|
//supernode (cloud list)
|
||||||
supernodes: {
|
supernodes: {}
|
||||||
indexes: {
|
|
||||||
uri: null,
|
|
||||||
pubKey: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var obs_a = {
|
var obs_a = {
|
||||||
//login credentials
|
//login credentials
|
||||||
credentials: {},
|
credentials: {},
|
||||||
//for Dapps
|
//for Dapps
|
||||||
subAdmins: {},
|
subAdmins: {},
|
||||||
|
trustedIDs: {},
|
||||||
settings: {},
|
settings: {},
|
||||||
appObjects: {},
|
appObjects: {},
|
||||||
generalData: {},
|
generalData: {},
|
||||||
@ -28,69 +187,101 @@
|
|||||||
}
|
}
|
||||||
//add other given objectStores
|
//add other given objectStores
|
||||||
initIndexedDB.appObs = initIndexedDB.appObs || {}
|
initIndexedDB.appObs = initIndexedDB.appObs || {}
|
||||||
for (o in initIndexedDB.appObs)
|
for (let o in initIndexedDB.appObs)
|
||||||
if (!(o in obs_a))
|
if (!(o in obs_a))
|
||||||
obs_a[o] = initIndexedDB.appObs[o]
|
obs_a[o] = initIndexedDB.appObs[o]
|
||||||
Promise.all([
|
Promise.all([
|
||||||
compactIDB.initDB(floGlobals.application, obs_a),
|
compactIDB.initDB(DEFAULT.application, obs_a),
|
||||||
compactIDB.initDB("floDapps", obs_g)
|
compactIDB.initDB(DEFAULT.root, obs_g)
|
||||||
]).then(result => {
|
]).then(result => {
|
||||||
compactIDB.setDefaultDB(floGlobals.application)
|
compactIDB.setDefaultDB(DEFAULT.application)
|
||||||
resolve("IndexedDB App Storage Initated Successfully")
|
resolve("IndexedDB App Storage Initated Successfully")
|
||||||
}).catch(error => reject(error));
|
}).catch(error => reject(error));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function initUserDB(floID) {
|
function initUserDB() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var obs = {
|
var obs = {
|
||||||
contacts: {},
|
contacts: {},
|
||||||
pubKeys: {},
|
pubKeys: {},
|
||||||
messages: {}
|
messages: {}
|
||||||
}
|
}
|
||||||
compactIDB.initDB(`floDapps#${floID}`, obs).then(result => {
|
compactIDB.initDB(user.db_name, obs).then(result => {
|
||||||
resolve("UserDB Initated Successfully")
|
resolve("UserDB Initated Successfully")
|
||||||
}).catch(error => reject('Init userDB failed'));
|
}).catch(error => reject('Init userDB failed'));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadUserDB(floID) {
|
function loadUserDB() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var loadData = ["contacts", "pubKeys", "messages"]
|
var loadData = ["contacts", "pubKeys", "messages"]
|
||||||
var promises = []
|
var promises = []
|
||||||
for (var i = 0; i < loadData.length; i++)
|
for (var i = 0; i < loadData.length; i++)
|
||||||
promises[i] = compactIDB.readAllData(loadData[i], `floDapps#${floID}`)
|
promises[i] = compactIDB.readAllData(loadData[i], user.db_name)
|
||||||
Promise.all(promises).then(results => {
|
Promise.all(promises).then(results => {
|
||||||
for (var i = 0; i < loadData.length; i++)
|
for (var i = 0; i < loadData.length; i++)
|
||||||
floGlobals[loadData[i]] = results[i]
|
user[loadData[i]] = results[i]
|
||||||
resolve("Loaded Data from userDB")
|
resolve("Loaded Data from userDB")
|
||||||
}).catch(error => reject('Load userDB failed'))
|
}).catch(error => reject('Load userDB failed'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const startUpOptions = {
|
||||||
|
cloud: true,
|
||||||
|
app_config: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
floDapps.startUpOptions = {
|
||||||
|
set app_config(val) {
|
||||||
|
if (val === true || val === false)
|
||||||
|
startUpOptions.app_config = val;
|
||||||
|
},
|
||||||
|
get app_config() { return startUpOptions.app_config },
|
||||||
|
|
||||||
|
set cloud(val) {
|
||||||
|
if (val === true || val === false)
|
||||||
|
startUpOptions.cloud = val;
|
||||||
|
},
|
||||||
|
get cloud() { return startUpOptions.cloud },
|
||||||
|
}
|
||||||
|
|
||||||
const startUpFunctions = [];
|
const startUpFunctions = [];
|
||||||
|
|
||||||
startUpFunctions.push(function readSupernodeListFromAPI() {
|
startUpFunctions.push(function readSupernodeListFromAPI() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
compactIDB.readData("lastTx", floCloudAPI.SNStorageID, "floDapps").then(lastTx => {
|
if (!startUpOptions.cloud)
|
||||||
floBlockchainAPI.readData(floCloudAPI.SNStorageID, {
|
return resolve("No cloud for this app");
|
||||||
ignoreOld: lastTx,
|
const CLOUD_KEY = "floCloudAPI#" + floCloudAPI.SNStorageID;
|
||||||
sentOnly: true,
|
compactIDB.readData("lastTx", CLOUD_KEY, DEFAULT.root).then(lastTx => {
|
||||||
pattern: "SuperNodeStorage"
|
var query_options = { sentOnly: true, pattern: floCloudAPI.SNStorageName };
|
||||||
}).then(result => {
|
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
|
||||||
for (var i = result.data.length - 1; i >= 0; i--) {
|
query_options.ignoreOld = lastTx;
|
||||||
var content = JSON.parse(result.data[i]).SuperNodeStorage;
|
else if (typeof lastTx == 'string') //lastTx is txid of last tx
|
||||||
for (sn in content.removeNodes)
|
query_options.after = lastTx;
|
||||||
compactIDB.removeData("supernodes", sn, "floDapps");
|
//fetch data from flosight
|
||||||
for (sn in content.newNodes)
|
floBlockchainAPI.readData(floCloudAPI.SNStorageID, query_options).then(result => {
|
||||||
compactIDB.writeData("supernodes", content.newNodes[sn], sn, "floDapps");
|
compactIDB.readData("supernodes", CLOUD_KEY, DEFAULT.root).then(nodes => {
|
||||||
}
|
nodes = nodes || {};
|
||||||
compactIDB.writeData("lastTx", result.totalTxs, floCloudAPI.SNStorageID, "floDapps");
|
for (var i = result.data.length - 1; i >= 0; i--) {
|
||||||
compactIDB.readAllData("supernodes", "floDapps").then(result => {
|
var content = JSON.parse(result.data[i])[floCloudAPI.SNStorageName];
|
||||||
floCloudAPI.init(result)
|
for (let sn in content.removeNodes)
|
||||||
.then(result => resolve("Loaded Supernode list\n" + result))
|
delete nodes[sn];
|
||||||
.catch(error => reject(error))
|
for (let sn in content.newNodes)
|
||||||
})
|
nodes[sn] = content.newNodes[sn];
|
||||||
|
for (let sn in content.updateNodes)
|
||||||
|
if (sn in nodes) //check if node is listed
|
||||||
|
nodes[sn].uri = content.updateNodes[sn];
|
||||||
|
}
|
||||||
|
Promise.all([
|
||||||
|
compactIDB.writeData("lastTx", result.lastItem, CLOUD_KEY, DEFAULT.root),
|
||||||
|
compactIDB.writeData("supernodes", nodes, CLOUD_KEY, DEFAULT.root)
|
||||||
|
]).then(_ => {
|
||||||
|
floCloudAPI.init(nodes)
|
||||||
|
.then(result => resolve("Loaded Supernode list\n" + result))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
@ -98,14 +289,18 @@
|
|||||||
|
|
||||||
startUpFunctions.push(function readAppConfigFromAPI() {
|
startUpFunctions.push(function readAppConfigFromAPI() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
compactIDB.readData("lastTx", `${floGlobals.application}|${floGlobals.adminID}`, "floDapps").then(lastTx => {
|
if (!startUpOptions.app_config)
|
||||||
floBlockchainAPI.readData(floGlobals.adminID, {
|
return resolve("No configs for this app");
|
||||||
ignoreOld: lastTx,
|
compactIDB.readData("lastTx", `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root).then(lastTx => {
|
||||||
sentOnly: true,
|
var query_options = { sentOnly: true, pattern: DEFAULT.application };
|
||||||
pattern: floGlobals.application
|
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
|
||||||
}).then(result => {
|
query_options.ignoreOld = lastTx;
|
||||||
|
else if (typeof lastTx == 'string') //lastTx is txid of last tx
|
||||||
|
query_options.after = lastTx;
|
||||||
|
//fetch data from flosight
|
||||||
|
floBlockchainAPI.readData(DEFAULT.adminID, query_options).then(result => {
|
||||||
for (var i = result.data.length - 1; i >= 0; i--) {
|
for (var i = result.data.length - 1; i >= 0; i--) {
|
||||||
var content = JSON.parse(result.data[i])[floGlobals.application];
|
var content = JSON.parse(result.data[i])[DEFAULT.application];
|
||||||
if (!content || typeof content !== "object")
|
if (!content || typeof content !== "object")
|
||||||
continue;
|
continue;
|
||||||
if (Array.isArray(content.removeSubAdmin))
|
if (Array.isArray(content.removeSubAdmin))
|
||||||
@ -114,16 +309,25 @@
|
|||||||
if (Array.isArray(content.addSubAdmin))
|
if (Array.isArray(content.addSubAdmin))
|
||||||
for (var k = 0; k < content.addSubAdmin.length; k++)
|
for (var k = 0; k < content.addSubAdmin.length; k++)
|
||||||
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
|
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
|
||||||
|
if (Array.isArray(content.removeTrustedID))
|
||||||
|
for (var j = 0; j < content.removeTrustedID.length; j++)
|
||||||
|
compactIDB.removeData("trustedIDs", content.removeTrustedID[j]);
|
||||||
|
if (Array.isArray(content.addTrustedID))
|
||||||
|
for (var k = 0; k < content.addTrustedID.length; k++)
|
||||||
|
compactIDB.writeData("trustedIDs", true, content.addTrustedID[k]);
|
||||||
if (content.settings)
|
if (content.settings)
|
||||||
for (let l in content.settings)
|
for (let l in content.settings)
|
||||||
compactIDB.writeData("settings", content.settings[l], l)
|
compactIDB.writeData("settings", content.settings[l], l)
|
||||||
}
|
}
|
||||||
compactIDB.writeData("lastTx", result.totalTxs, `${floGlobals.application}|${floGlobals.adminID}`, "floDapps");
|
compactIDB.writeData("lastTx", result.lastItem, `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root);
|
||||||
compactIDB.readAllData("subAdmins").then(result => {
|
compactIDB.readAllData("subAdmins").then(result => {
|
||||||
floGlobals.subAdmins = Object.keys(result);
|
subAdmins = Object.keys(result);
|
||||||
compactIDB.readAllData("settings").then(result => {
|
compactIDB.readAllData("trustedIDs").then(result => {
|
||||||
floGlobals.settings = result;
|
trustedIDs = Object.keys(result);
|
||||||
resolve("Read app configuration from blockchain");
|
compactIDB.readAllData("settings").then(result => {
|
||||||
|
settings = result;
|
||||||
|
resolve("Read app configuration from blockchain");
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -133,6 +337,8 @@
|
|||||||
|
|
||||||
startUpFunctions.push(function loadDataFromAppIDB() {
|
startUpFunctions.push(function loadDataFromAppIDB() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!startUpOptions.cloud)
|
||||||
|
return resolve("No cloud for this app");
|
||||||
var loadData = ["appObjects", "generalData", "lastVC"]
|
var loadData = ["appObjects", "generalData", "lastVC"]
|
||||||
var promises = []
|
var promises = []
|
||||||
for (var i = 0; i < loadData.length; i++)
|
for (var i = 0; i < loadData.length; i++)
|
||||||
@ -186,7 +392,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getPrivateKeyCredentials = () => new Promise((resolve, reject) => {
|
const getPrivateKeyCredentials = () => new Promise((resolve, reject) => {
|
||||||
var indexArr = localStorage.getItem(`${floGlobals.application}#privKey`)
|
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
||||||
if (indexArr) {
|
if (indexArr) {
|
||||||
readSharesFromIDB(JSON.parse(indexArr))
|
readSharesFromIDB(JSON.parse(indexArr))
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
@ -197,7 +403,7 @@
|
|||||||
if (!result)
|
if (!result)
|
||||||
return reject("Empty Private Key")
|
return reject("Empty Private Key")
|
||||||
var floID = floCrypto.getFloID(result)
|
var floID = floCrypto.getFloID(result)
|
||||||
if (!floID || !floCrypto.validateAddr(floID))
|
if (!floID || !floCrypto.validateFloID(floID))
|
||||||
return reject("Invalid Private Key")
|
return reject("Invalid Private Key")
|
||||||
privKey = result;
|
privKey = result;
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@ -210,7 +416,7 @@
|
|||||||
var shares = floCrypto.createShamirsSecretShares(privKey, threshold, threshold)
|
var shares = floCrypto.createShamirsSecretShares(privKey, threshold, threshold)
|
||||||
writeSharesToIDB(shares).then(resultIndexes => {
|
writeSharesToIDB(shares).then(resultIndexes => {
|
||||||
//store index keys in localStorage
|
//store index keys in localStorage
|
||||||
localStorage.setItem(`${floGlobals.application}#privKey`, JSON.stringify(resultIndexes))
|
localStorage.setItem(`${DEFAULT.application}#privKey`, JSON.stringify(resultIndexes))
|
||||||
//also add a dummy privatekey to the IDB
|
//also add a dummy privatekey to the IDB
|
||||||
var randomPrivKey = floCrypto.generateNewID().privKey
|
var randomPrivKey = floCrypto.generateNewID().privKey
|
||||||
var randomThreshold = floCrypto.randInt(10, 20)
|
var randomThreshold = floCrypto.randInt(10, 20)
|
||||||
@ -242,9 +448,15 @@
|
|||||||
getPrivateKeyCredentials().then(key => {
|
getPrivateKeyCredentials().then(key => {
|
||||||
checkIfPinRequired(key).then(privKey => {
|
checkIfPinRequired(key).then(privKey => {
|
||||||
try {
|
try {
|
||||||
myPrivKey = privKey
|
user_public = floCrypto.getPubKeyHex(privKey);
|
||||||
myPubKey = floCrypto.getPubKeyHex(myPrivKey)
|
user_id = floCrypto.getAddress(privKey);
|
||||||
myFloID = floCrypto.getFloID(myPubKey)
|
if (startUpOptions.cloud)
|
||||||
|
floCloudAPI.user(user_id, privKey); //Set user for floCloudAPI
|
||||||
|
user_priv_wrap = () => checkIfPinRequired(key);
|
||||||
|
let n = floCrypto.randInt(12, 20);
|
||||||
|
aes_key = floCrypto.randString(n);
|
||||||
|
user_priv_raw = Crypto.AES.encrypt(privKey, aes_key);
|
||||||
|
user_private = user_priv_wrap;
|
||||||
resolve('Login Credentials loaded successful')
|
resolve('Login Credentials loaded successful')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
@ -289,7 +501,7 @@
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
floDapps.launchStartUp = function() {
|
floDapps.launchStartUp = function () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
initIndexedDB().then(log => {
|
initIndexedDB().then(log => {
|
||||||
console.log(log)
|
console.log(log)
|
||||||
@ -305,8 +517,8 @@
|
|||||||
});
|
});
|
||||||
let p2 = new Promise((res, rej) => {
|
let p2 = new Promise((res, rej) => {
|
||||||
callAndLog(getCredentials()).then(r => {
|
callAndLog(getCredentials()).then(r => {
|
||||||
callAndLog(initUserDB(myFloID)).then(r => {
|
callAndLog(initUserDB()).then(r => {
|
||||||
callAndLog(loadUserDB(myFloID))
|
callAndLog(loadUserDB())
|
||||||
.then(r => res(true))
|
.then(r => res(true))
|
||||||
.catch(e => rej(false))
|
.catch(e => rej(false))
|
||||||
}).catch(e => rej(false))
|
}).catch(e => rej(false))
|
||||||
@ -315,7 +527,10 @@
|
|||||||
Promise.all([p1, p2])
|
Promise.all([p1, p2])
|
||||||
.then(r => resolve('App Startup finished successful'))
|
.then(r => resolve('App Startup finished successful'))
|
||||||
.catch(e => reject('App Startup failed'))
|
.catch(e => reject('App Startup failed'))
|
||||||
}).catch(error => reject("App database initiation failed"))
|
}).catch(error => {
|
||||||
|
startUpLog(false, error);
|
||||||
|
reject("App database initiation failed")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,63 +544,64 @@
|
|||||||
|
|
||||||
floDapps.setAppObjectStores = appObs => initIndexedDB.appObs = appObs;
|
floDapps.setAppObjectStores = appObs => initIndexedDB.appObs = appObs;
|
||||||
|
|
||||||
floDapps.storeContact = function(floID, name) {
|
floDapps.storeContact = function (floID, name) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject("Invalid floID!")
|
return reject("Invalid floID!")
|
||||||
compactIDB.writeData("contacts", name, floID, `floDapps#${myFloID}`).then(result => {
|
compactIDB.writeData("contacts", name, floID, user.db_name).then(result => {
|
||||||
floGlobals.contacts[floID] = name;
|
user.contacts[floID] = name;
|
||||||
resolve("Contact stored")
|
resolve("Contact stored")
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.storePubKey = function(floID, pubKey) {
|
floDapps.storePubKey = function (floID, pubKey) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (floID in floGlobals.pubKeys)
|
if (floID in user.pubKeys)
|
||||||
return resolve("pubKey already stored")
|
return resolve("pubKey already stored")
|
||||||
if (!floCrypto.validateAddr(floID))
|
if (!floCrypto.validateAddr(floID))
|
||||||
return reject("Invalid floID!")
|
return reject("Invalid floID!")
|
||||||
if (floCrypto.getFloID(pubKey) != floID)
|
if (!floCrypto.verifyPubKey(pubKey, floID))
|
||||||
return reject("Incorrect pubKey")
|
return reject("Incorrect pubKey")
|
||||||
compactIDB.writeData("pubKeys", pubKey, floID, `floDapps#${myFloID}`).then(result => {
|
compactIDB.writeData("pubKeys", pubKey, floID, user.db_name).then(result => {
|
||||||
floGlobals.pubKeys[floID] = pubKey;
|
user.pubKeys[floID] = pubKey;
|
||||||
resolve("pubKey stored")
|
resolve("pubKey stored")
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.sendMessage = function(floID, message) {
|
floDapps.sendMessage = function (floID, message) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let options = {
|
let options = {
|
||||||
receiverID: floID,
|
receiverID: floID,
|
||||||
application: "floDapps",
|
application: DEFAULT.root,
|
||||||
comment: floGlobals.application
|
comment: DEFAULT.application
|
||||||
}
|
}
|
||||||
if (floID in floGlobals.pubKeys)
|
if (floID in user.pubKeys)
|
||||||
message = floCrypto.encryptData(JSON.stringify(message), floGlobals.pubKeys[floID])
|
message = floCrypto.encryptData(JSON.stringify(message), user.pubKeys[floID])
|
||||||
floCloudAPI.sendApplicationData(message, "Message", options)
|
floCloudAPI.sendApplicationData(message, "Message", options)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.requestInbox = function(callback) {
|
floDapps.requestInbox = function (callback) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let lastVC = Object.keys(floGlobals.messages).sort().pop()
|
let lastVC = Object.keys(user.messages).sort().pop()
|
||||||
let options = {
|
let options = {
|
||||||
receiverID: myFloID,
|
receiverID: user.id,
|
||||||
application: "floDapps",
|
application: DEFAULT.root,
|
||||||
lowerVectorClock: lastVC + 1
|
lowerVectorClock: lastVC + 1
|
||||||
}
|
}
|
||||||
|
let privKey = raw_user.private;
|
||||||
options.callback = (d, e) => {
|
options.callback = (d, e) => {
|
||||||
for (let v in d) {
|
for (let v in d) {
|
||||||
try {
|
try {
|
||||||
if (d[v].message instanceof Object && "secret" in d[v].message)
|
if (d[v].message instanceof Object && "secret" in d[v].message)
|
||||||
d[v].message = floCrypto.decryptData(d[v].message, myPrivKey)
|
d[v].message = floCrypto.decryptData(d[v].message, privKey)
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
compactIDB.writeData("messages", d[v], v, `floDapps#${myFloID}`)
|
compactIDB.writeData("messages", d[v], v, user.db_name)
|
||||||
floGlobals.messages[v] = d[v]
|
user.messages[v] = d[v]
|
||||||
}
|
}
|
||||||
if (callback instanceof Function)
|
if (callback instanceof Function)
|
||||||
callback(d, e)
|
callback(d, e)
|
||||||
@ -396,44 +612,70 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.manageAppConfig = function(adminPrivKey, addList, rmList, settings) {
|
floDapps.manageAppConfig = function (adminPrivKey, addList, rmList, settings) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!startUpOptions.app_config)
|
||||||
|
return reject("No configs for this app");
|
||||||
if (!Array.isArray(addList) || !addList.length) addList = undefined;
|
if (!Array.isArray(addList) || !addList.length) addList = undefined;
|
||||||
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
|
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
|
||||||
if (!settings || typeof settings !== "object" || !Object.keys(settings).length) settings = undefined;
|
if (!settings || typeof settings !== "object" || !Object.keys(settings).length) settings = undefined;
|
||||||
if (!addList && !rmList && !settings)
|
if (!addList && !rmList && !settings)
|
||||||
return reject("No configuration change")
|
return reject("No configuration change")
|
||||||
var floData = {
|
var floData = {
|
||||||
[floGlobals.application]: {
|
[DEFAULT.application]: {
|
||||||
addSubAdmin: addList,
|
addSubAdmin: addList,
|
||||||
removeSubAdmin: rmList,
|
removeSubAdmin: rmList,
|
||||||
settings: settings
|
settings: settings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var floID = floCrypto.getFloID(adminPrivKey)
|
var floID = floCrypto.getFloID(adminPrivKey)
|
||||||
if (floID != floGlobals.adminID)
|
if (floID != DEFAULT.adminID)
|
||||||
reject('Access Denied for Admin privilege')
|
reject('Access Denied for Admin privilege')
|
||||||
else
|
else
|
||||||
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
|
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
|
||||||
.then(result => resolve(['Updated App Configuration', result]))
|
.then(result => resolve(['Updated App Configuration', result]))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearCredentials = floDapps.clearCredentials = function() {
|
floDapps.manageAppTrustedIDs = function (adminPrivKey, addList, rmList) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
compactIDB.clearData('credentials', floGlobals.application).then(result => {
|
if (!startUpOptions.app_config)
|
||||||
localStorage.removeItem(`${floGlobals.application}#privKey`)
|
return reject("No configs for this app");
|
||||||
myPrivKey = myPubKey = myFloID = undefined;
|
if (!Array.isArray(addList) || !addList.length) addList = undefined;
|
||||||
|
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
|
||||||
|
if (!addList && !rmList)
|
||||||
|
return reject("No change in list")
|
||||||
|
var floData = {
|
||||||
|
[DEFAULT.application]: {
|
||||||
|
addTrustedID: addList,
|
||||||
|
removeTrustedID: rmList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var floID = floCrypto.getFloID(adminPrivKey)
|
||||||
|
if (floID != DEFAULT.adminID)
|
||||||
|
reject('Access Denied for Admin privilege')
|
||||||
|
else
|
||||||
|
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
|
||||||
|
.then(result => resolve(['Updated App Configuration', result]))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearCredentials = floDapps.clearCredentials = function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
compactIDB.clearData('credentials', DEFAULT.application).then(result => {
|
||||||
|
localStorage.removeItem(`${DEFAULT.application}#privKey`);
|
||||||
|
user.clear();
|
||||||
resolve("privKey credentials deleted!")
|
resolve("privKey credentials deleted!")
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.deleteUserData = function(credentials = false) {
|
floDapps.deleteUserData = function (credentials = false) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let p = []
|
let p = []
|
||||||
p.push(compactIDB.deleteDB(`floDapps#${myFloID}`))
|
p.push(compactIDB.deleteDB(user.db_name))
|
||||||
if (credentials)
|
if (credentials)
|
||||||
p.push(clearCredentials())
|
p.push(clearCredentials())
|
||||||
Promise.all(p)
|
Promise.all(p)
|
||||||
@ -442,30 +684,30 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.deleteAppData = function() {
|
floDapps.deleteAppData = function () {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
compactIDB.deleteDB(floGlobals.application).then(result => {
|
compactIDB.deleteDB(DEFAULT.application).then(result => {
|
||||||
localStorage.removeItem(`${floGlobals.application}#privKey`)
|
localStorage.removeItem(`${DEFAULT.application}#privKey`)
|
||||||
myPrivKey = myPubKey = myFloID = undefined;
|
user.clear();
|
||||||
compactIDB.removeData('lastTx', `${floGlobals.application}|${floGlobals.adminID}`, 'floDapps')
|
compactIDB.removeData('lastTx', `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root)
|
||||||
.then(result => resolve("App database(local) deleted"))
|
.then(result => resolve("App database(local) deleted"))
|
||||||
.catch(error => reject(error))
|
.catch(error => reject(error))
|
||||||
}).catch(error => reject(error))
|
}).catch(error => reject(error))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.securePrivKey = function(pwd) {
|
floDapps.securePrivKey = function (pwd) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let indexArr = localStorage.getItem(`${floGlobals.application}#privKey`)
|
let indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
||||||
if (!indexArr)
|
if (!indexArr)
|
||||||
return reject("PrivKey not found");
|
return reject("PrivKey not found");
|
||||||
indexArr = JSON.parse(indexArr)
|
indexArr = JSON.parse(indexArr)
|
||||||
let encryptedKey = Crypto.AES.encrypt(myPrivKey, pwd);
|
let encryptedKey = Crypto.AES.encrypt(await user.private, pwd);
|
||||||
let threshold = indexArr.length;
|
let threshold = indexArr.length;
|
||||||
let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold)
|
let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold)
|
||||||
let promises = [];
|
let promises = [];
|
||||||
let overwriteFn = (share, index) =>
|
let overwriteFn = (share, index) =>
|
||||||
compactIDB.writeData("credentials", share, index, floGlobals.application);
|
compactIDB.writeData("credentials", share, index, DEFAULT.application);
|
||||||
for (var i = 0; i < threshold; i++)
|
for (var i = 0; i < threshold; i++)
|
||||||
promises.push(overwriteFn(shares[i], indexArr[i]));
|
promises.push(overwriteFn(shares[i], indexArr[i]));
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
@ -474,8 +716,8 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
floDapps.verifyPin = function(pin = null) {
|
floDapps.verifyPin = function (pin = null) {
|
||||||
const readSharesFromIDB = function(indexArr) {
|
const readSharesFromIDB = function (indexArr) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var promises = []
|
var promises = []
|
||||||
for (var i = 0; i < indexArr.length; i++)
|
for (var i = 0; i < indexArr.length; i++)
|
||||||
@ -494,7 +736,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
var indexArr = localStorage.getItem(`${floGlobals.application}#privKey`)
|
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
|
||||||
console.info(indexArr)
|
console.info(indexArr)
|
||||||
if (!indexArr)
|
if (!indexArr)
|
||||||
reject('No login credentials found')
|
reject('No login credentials found')
|
||||||
@ -518,7 +760,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNextGeneralData = floDapps.getNextGeneralData = function(type, vectorClock = null, options = {}) {
|
const getNextGeneralData = floDapps.getNextGeneralData = function (type, vectorClock = null, options = {}) {
|
||||||
var fk = floCloudAPI.util.filterKey(type, options)
|
var fk = floCloudAPI.util.filterKey(type, options)
|
||||||
vectorClock = vectorClock || getNextGeneralData[fk] || '0';
|
vectorClock = vectorClock || getNextGeneralData[fk] || '0';
|
||||||
var filteredResult = {}
|
var filteredResult = {}
|
||||||
@ -535,7 +777,7 @@
|
|||||||
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
|
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
|
||||||
}
|
}
|
||||||
if (options.decrypt) {
|
if (options.decrypt) {
|
||||||
let decryptionKey = (options.decrypt === true) ? myPrivKey : options.decrypt;
|
let decryptionKey = (options.decrypt === true) ? raw_user.private : options.decrypt;
|
||||||
if (!Array.isArray(decryptionKey))
|
if (!Array.isArray(decryptionKey))
|
||||||
decryptionKey = [decryptionKey];
|
decryptionKey = [decryptionKey];
|
||||||
for (let f in filteredResult) {
|
for (let f in filteredResult) {
|
||||||
@ -547,10 +789,10 @@
|
|||||||
let tmp = floCrypto.decryptData(data.message, key)
|
let tmp = floCrypto.decryptData(data.message, key)
|
||||||
data.message = JSON.parse(tmp)
|
data.message = JSON.parse(tmp)
|
||||||
break;
|
break;
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getNextGeneralData[fk] = Object.keys(filteredResult).sort().pop();
|
getNextGeneralData[fk] = Object.keys(filteredResult).sort().pop();
|
||||||
@ -561,14 +803,14 @@
|
|||||||
|
|
||||||
syncData.oldDevice = () => new Promise((resolve, reject) => {
|
syncData.oldDevice = () => new Promise((resolve, reject) => {
|
||||||
let sync = {
|
let sync = {
|
||||||
contacts: floGlobals.contacts,
|
contacts: user.contacts,
|
||||||
pubKeys: floGlobals.pubKeys,
|
pubKeys: user.pubKeys,
|
||||||
messages: floGlobals.messages
|
messages: user.messages
|
||||||
}
|
}
|
||||||
let message = Crypto.AES.encrypt(JSON.stringify(sync), myPrivKey)
|
let message = Crypto.AES.encrypt(JSON.stringify(sync), raw_user.private)
|
||||||
let options = {
|
let options = {
|
||||||
receiverID: myFloID,
|
receiverID: user.id,
|
||||||
application: "floDapps"
|
application: DEFAULT.root
|
||||||
}
|
}
|
||||||
floCloudAPI.sendApplicationData(message, "syncData", options)
|
floCloudAPI.sendApplicationData(message, "syncData", options)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
@ -577,20 +819,20 @@
|
|||||||
|
|
||||||
syncData.newDevice = () => new Promise((resolve, reject) => {
|
syncData.newDevice = () => new Promise((resolve, reject) => {
|
||||||
var options = {
|
var options = {
|
||||||
receiverID: myFloID,
|
receiverID: user.id,
|
||||||
senderID: myFloID,
|
senderID: user.id,
|
||||||
application: "floDapps",
|
application: DEFAULT.root,
|
||||||
mostRecent: true,
|
mostRecent: true,
|
||||||
}
|
}
|
||||||
floCloudAPI.requestApplicationData("syncData", options).then(response => {
|
floCloudAPI.requestApplicationData("syncData", options).then(response => {
|
||||||
let vc = Object.keys(response).sort().pop()
|
let vc = Object.keys(response).sort().pop()
|
||||||
let sync = JSON.parse(Crypto.AES.decrypt(response[vc].message, myPrivKey))
|
let sync = JSON.parse(Crypto.AES.decrypt(response[vc].message, raw_user.private))
|
||||||
let promises = []
|
let promises = []
|
||||||
let store = (key, val, obs) => promises.push(compactIDB.writeData(obs, val, key, `floDapps#${floID}`));
|
let store = (key, val, obs) => promises.push(compactIDB.writeData(obs, val, key, user.db_name));
|
||||||
["contacts", "pubKeys", "messages"].forEach(c => {
|
["contacts", "pubKeys", "messages"].forEach(c => {
|
||||||
for (let i in sync[c]) {
|
for (let i in sync[c]) {
|
||||||
store(i, sync[c][i], c)
|
store(i, sync[c][i], c)
|
||||||
floGlobals[c][i] = sync[c][i]
|
user[c][i] = sync[c][i]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
(function(EXPORTS) { //floTokenAPI v1.0.3b
|
(function (EXPORTS) { //floTokenAPI v1.0.4a
|
||||||
/* Token Operator to send/receive tokens via blockchain using API calls*/
|
/* Token Operator to send/receive tokens via blockchain using API calls*/
|
||||||
'use strict';
|
'use strict';
|
||||||
const tokenAPI = EXPORTS;
|
const tokenAPI = EXPORTS;
|
||||||
|
|
||||||
const DEFAULT = {
|
const DEFAULT = {
|
||||||
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
|
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
|
||||||
currency: "rupee"
|
currency: floGlobals.currency || "rupee"
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.defineProperties(tokenAPI, {
|
Object.defineProperties(tokenAPI, {
|
||||||
@ -27,9 +27,9 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetch_api = tokenAPI.fetch = function(apicall) {
|
const fetch_api = tokenAPI.fetch = function (apicall) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log(DEFAULT.apiURL + apicall);
|
console.debug(DEFAULT.apiURL + apicall);
|
||||||
fetch(DEFAULT.apiURL + apicall).then(response => {
|
fetch(DEFAULT.apiURL + apicall).then(response => {
|
||||||
if (response.ok)
|
if (response.ok)
|
||||||
response.json().then(data => resolve(data));
|
response.json().then(data => resolve(data));
|
||||||
@ -39,7 +39,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getBalance = tokenAPI.getBalance = function(floID, token = DEFAULT.currency) {
|
const getBalance = tokenAPI.getBalance = function (floID, token = DEFAULT.currency) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
|
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
|
||||||
.then(result => resolve(result.balance || 0))
|
.then(result => resolve(result.balance || 0))
|
||||||
@ -47,7 +47,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAPI.getTx = function(txID) {
|
tokenAPI.getTx = function (txID) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
|
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
|
||||||
if (res.result === "error")
|
if (res.result === "error")
|
||||||
@ -62,7 +62,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAPI.sendToken = function(privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
|
tokenAPI.sendToken = function (privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let senderID = floCrypto.getFloID(privKey);
|
let senderID = floCrypto.getFloID(privKey);
|
||||||
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
|
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
|
||||||
@ -77,7 +77,71 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenAPI.getAllTxs = function(floID, token = DEFAULT.currency) {
|
function sendTokens_raw(privKey, receiverID, token, amount, utxo, vout, scriptPubKey) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var trx = bitjs.transaction();
|
||||||
|
trx.addinput(utxo, vout, scriptPubKey)
|
||||||
|
trx.addoutput(receiverID, floBlockchainAPI.sendAmt);
|
||||||
|
trx.addflodata(`send ${amount} ${token}#`);
|
||||||
|
var signedTxHash = trx.sign(privKey, 1);
|
||||||
|
floBlockchainAPI.broadcastTx(signedTxHash)
|
||||||
|
.then(txid => resolve([receiverID, txid]))
|
||||||
|
.catch(error => reject([receiverID, error]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//bulk transfer tokens
|
||||||
|
tokenAPI.bulkTransferTokens = function (sender, privKey, token, receivers) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (typeof receivers !== 'object')
|
||||||
|
return reject("receivers must be object in format {receiver1: amount1, receiver2:amount2...}")
|
||||||
|
|
||||||
|
let receiver_list = Object.keys(receivers), amount_list = Object.values(receivers);
|
||||||
|
let invalidReceivers = receiver_list.filter(id => !floCrypto.validateFloID(id));
|
||||||
|
let invalidAmount = amount_list.filter(val => typeof val !== 'number' || val <= 0);
|
||||||
|
if (invalidReceivers.length)
|
||||||
|
return reject(`Invalid receivers: ${invalidReceivers}`);
|
||||||
|
else if (invalidAmount.length)
|
||||||
|
return reject(`Invalid amounts: ${invalidAmount}`);
|
||||||
|
|
||||||
|
if (receiver_list.length == 0)
|
||||||
|
return reject("Receivers cannot be empty");
|
||||||
|
|
||||||
|
if (receiver_list.length == 1) {
|
||||||
|
let receiver = receiver_list[0], amount = amount_list[0];
|
||||||
|
floTokenAPI.sendToken(privKey, amount, receiver, "", token)
|
||||||
|
.then(txid => resolve({ success: { [receiver]: txid } }))
|
||||||
|
.catch(error => reject(error))
|
||||||
|
} else {
|
||||||
|
//check for token balance
|
||||||
|
floTokenAPI.getBalance(sender, token).then(token_balance => {
|
||||||
|
let total_token_amout = amount_list.reduce((a, e) => a + e, 0);
|
||||||
|
if (total_token_amout > token_balance)
|
||||||
|
return reject(`Insufficient ${token}# balance`);
|
||||||
|
|
||||||
|
//split utxos
|
||||||
|
floBlockchainAPI.splitUTXOs(sender, privKey, receiver_list.length).then(split_txid => {
|
||||||
|
//wait for the split utxo to get confirmation
|
||||||
|
floBlockchainAPI.waitForConfirmation(split_txid).then(split_tx => {
|
||||||
|
//send tokens using the split-utxo
|
||||||
|
var scriptPubKey = split_tx.vout[0].scriptPubKey.hex;
|
||||||
|
let promises = [];
|
||||||
|
for (let i in receiver_list)
|
||||||
|
promises.push(sendTokens_raw(privKey, receiver_list[i], token, amount_list[i], split_txid, i, scriptPubKey));
|
||||||
|
Promise.allSettled(promises).then(results => {
|
||||||
|
let success = Object.fromEntries(results.filter(r => r.status == 'fulfilled').map(r => r.value));
|
||||||
|
let failed = Object.fromEntries(results.filter(r => r.status == 'rejected').map(r => r.reason));
|
||||||
|
resolve({ success, failed });
|
||||||
|
})
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}).catch(error => reject(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenAPI.getAllTxs = function (floID, token = DEFAULT.currency) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
|
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
|
||||||
.then(result => resolve(result))
|
.then(result => resolve(result))
|
||||||
@ -87,7 +151,7 @@
|
|||||||
|
|
||||||
const util = tokenAPI.util = {};
|
const util = tokenAPI.util = {};
|
||||||
|
|
||||||
util.parseTxData = function(txData) {
|
util.parseTxData = function (txData) {
|
||||||
let parsedData = {};
|
let parsedData = {};
|
||||||
for (let p in txData.parsedFloData)
|
for (let p in txData.parsedFloData)
|
||||||
parsedData[p] = txData.parsedFloData[p];
|
parsedData[p] = txData.parsedFloData[p];
|
||||||
|
|||||||
4170
scripts/lib.js
4170
scripts/lib.js
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user