Merge pull request #15 from sairajzero/master

This commit is contained in:
Sai Raj 2023-02-08 19:23:28 +05:30 committed by GitHub
commit 837f4b4a6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 4874 additions and 695 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.tmp*

View File

@ -10,7 +10,7 @@ We are offering methods simplifying access to inbuilt browser database IndexedDB
Last but not the least, we are also providing methods for simplying common operations for FLO based Distributed Application Development. Last but not the least, we are also providing methods for simplying common operations for FLO based Distributed Application Development.
# IMPORTANT # IMPORTANT
We have two versions of cloud: old cloud version is 2.0.x in floCloudAPI, and new cloud version is 2.1.x in floCloudAPI. Please check that the version in floCloudAPI is 2.1.x whenever you use floCloudAPI as we are deprecating version 2.0.x We have two versions of cloud: old cloud version is 2.0.x in floCloudAPI, and new cloud version is >2.1.0 in floCloudAPI. Please check that the version in floCloudAPI is >2.1.0 whenever you use floCloudAPI as we are deprecating version 2.0.x
# Background on FLO Distributed Applications # Background on FLO Distributed Applications
@ -208,6 +208,13 @@ In addition, we have these system variables outside FLO Globals but used globall
1. publickey_or_privateKey - public key or private key hex value 1. publickey_or_privateKey - public key or private key hex value
* Returns : floID (string) * Returns : floID (string)
#### Calculate Address
floCrypto.getAddress(privateKey, *strict)
`getAddress` returns respective address from given private-key
1. privateKey - private key in WIF format
2. strict - boolean value (optional, default=false) (false: return flo-id if no prefix match is found)
* Returns : address (string)
#### Verify Private Key #### Verify Private Key
floCrypto.verifyPrivKey(privateKey, pubKey_floID, *isfloID) floCrypto.verifyPrivKey(privateKey, pubKey_floID, *isfloID)
`verifyPrivKey` verify the private-key for the given public-key or flo-ID `verifyPrivKey` verify the private-key for the given public-key or flo-ID
@ -216,12 +223,27 @@ In addition, we have these system variables outside FLO Globals but used globall
3. isfloID - boolean value (true: compare as flo ID, false: compare as public key) (optional, default is true) 3. isfloID - boolean value (true: compare as flo ID, false: compare as public key) (optional, default is true)
* Returns : boolen (true or false) * Returns : boolen (true or false)
#### Validate Address
floCrypto.validateAddr(address, *std, *bech)
`validateAddr` check if the given Address (any blockchain) is valid or not
1. address - address to validate
2. std - checks for legacy version (optional, default=true) (true: allow any, array: list of versions, value: one version only, false: allow none)
3. bech - checks for bech version (optional, default=true) (true: allow any, array: list of versions, value: one version only, false: allow none)
* Returns : boolen (true or false)
#### Validate FLO ID #### Validate FLO ID
floCrypto.validateAddr(floID) floCrypto.validateFloID(floID)
`validateAddr` check if the given Address is valid or not `validateFloID` check if the given floID is valid or not
1. floID - flo ID to validate 1. floID - flo ID to validate
* Returns : boolen (true or false) * Returns : boolen (true or false)
#### Verify Public Key
floCrypto.verifyPubKey(publicKey, address)
`verifyPubKey` verify the public key for the given address (any blockchain)
1. publicKey - public key
2. address - address to verify
* Returns : boolen (true or false)
#### Data Encryption #### Data Encryption
floCrypto.encryptData(data, publicKey) floCrypto.encryptData(data, publicKey)
`encryptData` encrypts the given data using public-key `encryptData` encrypts the given data using public-key

851
btcOperator.js Normal file
View File

@ -0,0 +1,851 @@
(function (EXPORTS) { //btcOperator v1.1.1
/* BTC Crypto and API Operator */
const btcOperator = EXPORTS;
//This library uses API provided by chain.so (https://chain.so/)
const URL = "https://blockchain.info/";
const fetch_api = btcOperator.fetch = function (api, json_res = true) {
return new Promise((resolve, reject) => {
console.debug(URL + api);
fetch(URL + api).then(response => {
if (response.ok) {
(json_res ? response.json() : response.text())
.then(result => resolve(result))
.catch(error => reject(error))
} else {
response.json()
.then(result => reject(result))
.catch(error => reject(error))
}
}).catch(error => reject(error))
})
};
const SATOSHI_IN_BTC = 1e8;
const util = btcOperator.util = {};
util.Sat_to_BTC = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
util.BTC_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
function get_fee_rate() {
return new Promise((resolve, reject) => {
fetch('https://api.blockchain.info/mempool/fees').then(response => {
if (response.ok)
response.json()
.then(result => resolve(util.Sat_to_BTC(result.regular)))
.catch(error => reject(error));
else
reject(response);
}).catch(error => reject(error))
})
}
const broadcastTx = btcOperator.broadcastTx = rawTxHex => new Promise((resolve, reject) => {
let url = 'https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction';
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: "rawtx=" + rawTxHex
}).then(response => {
response.text().then(resultText => {
let r = resultText.match(/<result>.*<\/result>/);
if (!r)
reject(resultText);
else {
r = r.pop().replace('<result>', '').replace('</result>', '');
if (r == '1') {
let txid = resultText.match(/<txid>.*<\/txid>/).pop().replace('<txid>', '').replace('</txid>', '');
resolve(txid);
} else if (r == '0') {
let error = resultText.match(/<response>.*<\/response>/).pop().replace('<response>', '').replace('</response>', '');
reject(decodeURIComponent(error.replace(/\+/g, " ")));
} else reject(resultText);
}
}).catch(error => reject(error))
}).catch(error => reject(error))
});
Object.defineProperties(btcOperator, {
newKeys: {
get: () => {
let r = coinjs.newKeys();
r.segwitAddress = coinjs.segwitAddress(r.pubkey).address;
r.bech32Address = coinjs.bech32Address(r.pubkey).address;
return r;
}
},
pubkey: {
value: key => key.length >= 66 ? key : (key.length == 64 ? coinjs.newPubkey(key) : coinjs.wif2pubkey(key).pubkey)
},
address: {
value: (key, prefix = undefined) => coinjs.pubkey2address(btcOperator.pubkey(key), prefix)
},
segwitAddress: {
value: key => coinjs.segwitAddress(btcOperator.pubkey(key)).address
},
bech32Address: {
value: key => coinjs.bech32Address(btcOperator.pubkey(key)).address
}
});
coinjs.compressed = true;
const verifyKey = btcOperator.verifyKey = function (addr, key) {
if (!addr || !key)
return undefined;
switch (coinjs.addressDecode(addr).type) {
case "standard":
return btcOperator.address(key) === addr;
case "multisig":
return btcOperator.segwitAddress(key) === addr;
case "bech32":
return btcOperator.bech32Address(key) === addr;
default:
return null;
}
}
const validateAddress = btcOperator.validateAddress = function (addr) {
if (!addr)
return undefined;
let type = coinjs.addressDecode(addr).type;
if (["standard", "multisig", "bech32", "multisigBech32"].includes(type))
return type;
else
return false;
}
btcOperator.multiSigAddress = function (pubKeys, minRequired, bech32 = true) {
if (!Array.isArray(pubKeys))
throw "pubKeys must be an array of public keys";
else if (pubKeys.length < minRequired)
throw "minimum required should be less than the number of pubKeys";
if (bech32)
return coinjs.pubkeys2MultisigAddressBech32(pubKeys, minRequired);
else
return coinjs.pubkeys2MultisigAddress(pubKeys, minRequired);
}
//convert from one blockchain to another blockchain (target version)
btcOperator.convert = {};
btcOperator.convert.wif = function (source_wif, target_version = coinjs.priv) {
let keyHex = decodeLegacy(source_wif).hex;
if (!keyHex || keyHex.length < 66 || !/01$/.test(keyHex))
return null;
else
return encodeLegacy(keyHex, target_version);
}
btcOperator.convert.legacy2legacy = function (source_addr, target_version = coinjs.pub) {
let rawHex = decodeLegacy(source_addr).hex;
if (!rawHex)
return null;
else
return encodeLegacy(rawHex, target_version);
}
btcOperator.convert.legacy2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) {
let rawHex = decodeLegacy(source_addr).hex;
if (!rawHex)
return null;
else
return encodeBech32(rawHex, target_version, target_hrp);
}
btcOperator.convert.bech2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) {
let rawHex = decodeBech32(source_addr).hex;
if (!rawHex)
return null;
else
return encodeBech32(rawHex, target_version, target_hrp);
}
btcOperator.convert.bech2legacy = function (source_addr, target_version = coinjs.pub) {
let rawHex = decodeBech32(source_addr).hex;
if (!rawHex)
return null;
else
return encodeLegacy(rawHex, target_version);
}
function decodeLegacy(source) {
var decode = coinjs.base58decode(source);
var raw = decode.slice(0, decode.length - 4),
checksum = decode.slice(decode.length - 4);
var hash = Crypto.SHA256(Crypto.SHA256(raw, {
asBytes: true
}), {
asBytes: true
});
if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3])
return null;
let version = raw.shift();
return {
version: version,
hex: Crypto.util.bytesToHex(raw)
}
}
function encodeLegacy(hex, version) {
var bytes = Crypto.util.hexToBytes(hex);
bytes.unshift(version);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
asBytes: true
}), {
asBytes: true
});
var checksum = hash.slice(0, 4);
return coinjs.base58encode(bytes.concat(checksum));
}
function decodeBech32(source) {
let decode = coinjs.bech32_decode(source);
if (!decode)
return null;
var raw = decode.data;
let version = raw.shift();
raw = coinjs.bech32_convert(raw, 5, 8, false);
return {
hrp: decode.hrp,
version: version,
hex: Crypto.util.bytesToHex(raw)
}
}
function encodeBech32(hex, version, hrp) {
var bytes = Crypto.util.hexToBytes(hex);
bytes = coinjs.bech32_convert(bytes, 8, 5, true);
bytes.unshift(version)
return coinjs.bech32_encode(hrp, bytes);
}
//BTC blockchain APIs
btcOperator.getBalance = addr => new Promise((resolve, reject) => {
fetch_api(`q/addressbalance/${addr}`)
.then(result => resolve(util.Sat_to_BTC(result)))
.catch(error => reject(error))
});
const BASE_TX_SIZE = 12,
BASE_INPUT_SIZE = 41,
LEGACY_INPUT_SIZE = 107,
BECH32_INPUT_SIZE = 27,
BECH32_MULTISIG_INPUT_SIZE = 35,
SEGWIT_INPUT_SIZE = 59,
MULTISIG_INPUT_SIZE_ES = 351,
BASE_OUTPUT_SIZE = 9,
LEGACY_OUTPUT_SIZE = 25,
BECH32_OUTPUT_SIZE = 23,
BECH32_MULTISIG_OUTPUT_SIZE = 34,
SEGWIT_OUTPUT_SIZE = 23;
function _redeemScript(addr, key) {
let decode = coinjs.addressDecode(addr);
switch (decode.type) {
case "standard":
return false;
case "multisig":
return key ? coinjs.segwitAddress(btcOperator.pubkey(key)).redeemscript : null;
case "bech32":
return decode.redeemscript;
default:
return null;
}
}
function _sizePerInput(addr, rs) {
switch (coinjs.addressDecode(addr).type) {
case "standard":
return BASE_INPUT_SIZE + LEGACY_INPUT_SIZE;
case "bech32":
return BASE_INPUT_SIZE + BECH32_INPUT_SIZE;
case "multisigBech32":
return BASE_INPUT_SIZE + BECH32_MULTISIG_INPUT_SIZE;
case "multisig":
switch (coinjs.script().decodeRedeemScript(rs).type) {
case "segwit__":
return BASE_INPUT_SIZE + SEGWIT_INPUT_SIZE;
case "multisig__":
return BASE_INPUT_SIZE + MULTISIG_INPUT_SIZE_ES;
default:
return null;
};
default:
return null;
}
}
function _sizePerOutput(addr) {
switch (coinjs.addressDecode(addr).type) {
case "standard":
return BASE_OUTPUT_SIZE + LEGACY_OUTPUT_SIZE;
case "bech32":
return BASE_OUTPUT_SIZE + BECH32_OUTPUT_SIZE;
case "multisigBech32":
return BASE_OUTPUT_SIZE + BECH32_MULTISIG_OUTPUT_SIZE;
case "multisig":
return BASE_OUTPUT_SIZE + SEGWIT_OUTPUT_SIZE;
default:
return null;
}
}
function validateTxParameters(parameters) {
let invalids = [];
//sender-ids
if (parameters.senders) {
if (!Array.isArray(parameters.senders))
parameters.senders = [parameters.senders];
parameters.senders.forEach(id => !validateAddress(id) ? invalids.push(id) : null);
if (invalids.length)
throw "Invalid senders:" + invalids;
}
if (parameters.privkeys) {
if (!Array.isArray(parameters.privkeys))
parameters.privkeys = [parameters.privkeys];
if (parameters.senders.length != parameters.privkeys.length)
throw "Array length for senders and privkeys should be equal";
parameters.senders.forEach((id, i) => {
let key = parameters.privkeys[i];
if (!verifyKey(id, key)) //verify private-key
invalids.push(id);
if (key.length === 64) //convert Hex to WIF if needed
parameters.privkeys[i] = coinjs.privkey2wif(key);
});
if (invalids.length)
throw "Invalid private key for address:" + invalids;
}
//receiver-ids (and change-id)
if (!Array.isArray(parameters.receivers))
parameters.receivers = [parameters.receivers];
parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null);
if (invalids.length)
throw "Invalid receivers:" + invalids;
if (parameters.change_address && !validateAddress(parameters.change_address))
throw "Invalid change_address:" + parameters.change_address;
//fee and amounts
if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc)
throw "Invalid fee:" + parameters.fee;
if (!Array.isArray(parameters.amounts))
parameters.amounts = [parameters.amounts];
if (parameters.receivers.length != parameters.amounts.length)
throw "Array length for receivers and amounts should be equal";
parameters.amounts.forEach(a => typeof a !== "number" || a <= 0 ? invalids.push(a) : null);
if (invalids.length)
throw "Invalid amounts:" + invalids;
//return
return parameters;
}
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver) {
return new Promise((resolve, reject) => {
let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
const tx = coinjs.transaction();
let output_size = addOutputs(tx, receivers, amounts, change_address);
addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver).then(result => {
if (result.change_amount > 0 && result.change_amount > result.fee) //add change amount if any (ignore dust change)
tx.outs[tx.outs.length - 1].value = util.BTC_to_Sat(result.change_amount); //values are in satoshi
if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver
let fee_remaining = util.BTC_to_Sat(result.fee);
for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) {
if (fee_remaining < tx.outs[i].value) {
tx.outs[i].value -= fee_remaining;
fee_remaining = 0;
} else {
fee_remaining -= tx.outs[i].value;
tx.outs[i].value = 0;
}
}
if (fee_remaining > 0)
return reject("Send amount is less than fee");
}
tx.outs = tx.outs.filter(o => o.value != 0); //remove all output with value 0
result.output_size = output_size;
result.output_amount = total_amount - (fee_from_receiver ? result.fee : 0);
result.total_size = BASE_TX_SIZE + output_size + result.input_size;
result.transaction = tx;
resolve(result);
}).catch(error => reject(error))
})
}
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver) {
return new Promise((resolve, reject) => {
if (fee !== null) {
addUTXOs(tx, senders, redeemScripts, fee_from_receiver ? total_amount : total_amount + fee, false).then(result => {
result.fee = fee;
resolve(result);
}).catch(error => reject(error))
} else {
get_fee_rate().then(fee_rate => {
let net_fee = BASE_TX_SIZE * fee_rate;
net_fee += (output_size * fee_rate);
(fee_from_receiver ?
addUTXOs(tx, senders, redeemScripts, total_amount, false) :
addUTXOs(tx, senders, redeemScripts, total_amount + net_fee, fee_rate)
).then(result => {
result.fee = parseFloat((net_fee + (result.input_size * fee_rate)).toFixed(8));
result.fee_rate = fee_rate;
resolve(result);
}).catch(error => reject(error))
}).catch(error => reject(error))
}
})
}
function addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args = {}) {
return new Promise((resolve, reject) => {
required_amount = parseFloat(required_amount.toFixed(8));
if (typeof rec_args.n === "undefined") {
rec_args.n = 0;
rec_args.input_size = 0;
rec_args.input_amount = 0;
}
if (required_amount <= 0)
return resolve({
input_size: rec_args.input_size,
input_amount: rec_args.input_amount,
change_amount: required_amount * -1 //required_amount will be -ve of change_amount
});
else if (rec_args.n >= senders.length)
return reject("Insufficient Balance");
let addr = senders[rec_args.n],
rs = redeemScripts[rec_args.n];
let addr_type = coinjs.addressDecode(addr).type;
let size_per_input = _sizePerInput(addr, rs);
fetch_api(`unspent?active=${addr}`).then(result => {
let utxos = result.unspent_outputs;
console.debug("add-utxo", addr, rs, required_amount, utxos);
for (let i = 0; i < utxos.length && required_amount > 0; i++) {
if (!utxos[i].confirmations) //ignore unconfirmed utxo
continue;
var script;
if (!rs || !rs.length) //legacy script
script = utxos[i].script;
else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi)) || addr_type === 'multisigBech32') {
//redeemScript for segwit/bech32 and multisig (bech32)
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs));
s.writeOp(0);
s.writeBytes(coinjs.numToBytes(utxos[i].value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig (segwit)
script = rs;
tx.addinput(utxos[i].tx_hash_big_endian, utxos[i].tx_output_n, script, 0xfffffffd /*sequence*/); //0xfffffffd for Replace-by-fee
//update track values
rec_args.input_size += size_per_input;
rec_args.input_amount += util.Sat_to_BTC(utxos[i].value);
required_amount -= util.Sat_to_BTC(utxos[i].value);
if (fee_rate) //automatic fee calculation (dynamic)
required_amount += size_per_input * fee_rate;
}
rec_args.n += 1;
addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args)
.then(result => resolve(result))
.catch(error => reject(error))
}).catch(error => reject(error))
})
}
function addOutputs(tx, receivers, amounts, change_address) {
let size = 0;
for (let i in receivers) {
tx.addoutput(receivers[i], amounts[i]);
size += _sizePerOutput(receivers[i]);
}
tx.addoutput(change_address, 0);
size += _sizePerOutput(change_address);
return size;
}
/*
function autoFeeCalc(tx) {
return new Promise((resolve, reject) => {
get_fee_rate().then(fee_rate => {
let tx_size = tx.size();
for (var i = 0; i < this.ins.length; i++)
switch (tx.extractScriptKey(i).type) {
case 'scriptpubkey':
tx_size += SIGN_SIZE;
break;
case 'segwit':
case 'multisig':
tx_size += SIGN_SIZE * 0.25;
break;
default:
console.warn('Unknown script-type');
tx_size += SIGN_SIZE;
}
resolve(tx_size * fee_rate);
}).catch(error => reject(error))
})
}
function editFee(tx, current_fee, target_fee, index = -1) {
//values are in satoshi
index = parseInt(index >= 0 ? index : tx.outs.length - index);
if (index < 0 || index >= tx.outs.length)
throw "Invalid index";
let edit_value = parseInt(current_fee - target_fee), //rip of any decimal places
current_value = tx.outs[index].value; //could be BigInterger
if (edit_value < 0 && edit_value > current_value)
throw "Insufficient value at vout";
tx.outs[index].value = current_value instanceof BigInteger ?
current_value.add(new BigInteger('' + edit_value)) : parseInt(current_value + edit_value);
}
*/
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => {
debugger;
broadcastTx(result.transaction.serialize())
.then(txid => resolve(txid))
.catch(error => reject(error));
}).catch(error => reject(error))
})
}
const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
try {
({
senders,
privkeys,
receivers,
amounts
} = validateTxParameters({
senders,
privkeys,
receivers,
amounts,
fee,
change_address: options.change_address
}));
} catch (e) {
return reject(e)
}
let redeemScripts = [],
wif_keys = [];
for (let i in senders) {
let rs = _redeemScript(senders[i], privkeys[i]); //get redeem-script (segwit/bech32)
redeemScripts.push(rs);
rs === false ? wif_keys.unshift(privkeys[i]) : wif_keys.push(privkeys[i]); //sorting private-keys (wif)
}
if (redeemScripts.includes(null)) //TODO: segwit
return reject("Unable to get redeem-script");
//create transaction
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
let tx = result.transaction;
console.debug("Unsigned:", tx.serialize());
new Set(wif_keys).forEach(key => console.debug("Signing key:", key, tx.sign(key, 1 /*sighashtype*/))); //Sign the tx using private key WIF
console.debug("Signed:", tx.serialize());
resolve(result);
}).catch(error => reject(error));
})
}
btcOperator.createTx = function (senders, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
try {
({
senders,
receivers,
amounts
} = validateTxParameters({
senders,
receivers,
amounts,
fee,
change_address: options.change_address
}));
} catch (e) {
return reject(e)
}
let redeemScripts = senders.map(id => _redeemScript(id));
if (redeemScripts.includes(null)) //TODO: segwit
return reject("Unable to get redeem-script");
//create transaction
createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
result.tx_hex = result.transaction.serialize();
delete result.transaction;
resolve(result);
}).catch(error => reject(error))
})
}
btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
//validate tx parameters
let addr_type = validateAddress(sender);
if (!(["multisig", "multisigBech32"].includes(addr_type)))
return reject("Invalid sender (multisig):" + sender);
else {
let script = coinjs.script();
let decode = (addr_type == "multisig") ?
script.decodeRedeemScript(redeemScript) :
script.decodeRedeemScriptBech32(redeemScript);
if (!decode || decode.address !== sender)
return reject("Invalid redeem-script");
}
try {
({
receivers,
amounts
} = validateTxParameters({
receivers,
amounts,
fee,
change_address: options.change_address
}));
} catch (e) {
return reject(e)
}
//create transaction
createTransaction([sender], [redeemScript], receivers, amounts, fee, options.change_address || sender, options.fee_from_receiver).then(result => {
result.tx_hex = result.transaction.serialize();
delete result.transaction;
resolve(result);
}).catch(error => reject(error))
})
}
function deserializeTx(tx) {
if (typeof tx === 'string' || Array.isArray(tx)) {
try {
tx = coinjs.transaction().deserialize(tx);
} catch {
throw "Invalid transaction hex";
}
} else if (typeof tx !== 'object' || typeof tx.sign !== 'function')
throw "Invalid transaction object";
return tx;
}
btcOperator.signTx = function (tx, privkeys, sighashtype = 1) {
tx = deserializeTx(tx);
if (!Array.isArray(privkeys))
privkeys = [privkeys];
for (let i in privkeys)
if (privkeys[i].length === 64)
privkeys[i] = coinjs.privkey2wif(privkeys[i]);
new Set(privkeys).forEach(key => tx.sign(key, sighashtype)); //Sign the tx using private key WIF
return tx.serialize();
}
const checkSigned = btcOperator.checkSigned = function (tx, bool = true) {
tx = deserializeTx(tx);
let n = [];
for (let i in tx.ins) {
var s = tx.extractScriptKey(i);
if (s['type'] !== 'multisig' && s['type'] !== 'multisig_bech32')
n.push(s.signed == 'true' || (tx.witness[i] && tx.witness[i].length == 2))
else {
var rs = coinjs.script().decodeRedeemScript(s.script); //will work for bech32 too, as only address is diff
let x = {
s: s['signatures'],
r: rs['signaturesRequired'],
t: rs['pubkeys'].length
};
if (x.r > x.t)
throw "signaturesRequired is more than publicKeys";
else if (x.s < x.r)
n.push(x);
else
n.push(true);
}
}
return bool ? !(n.filter(x => x !== true).length) : n;
}
btcOperator.checkIfSameTx = function (tx1, tx2) {
tx1 = deserializeTx(tx1);
tx2 = deserializeTx(tx2);
if (tx1.ins.length !== tx2.ins.length || tx1.outs.length !== tx2.outs.length)
return false;
for (let i = 0; i < tx1.ins.length; i++)
if (tx1.ins[i].outpoint.hash !== tx2.ins[i].outpoint.hash || tx1.ins[i].outpoint.index !== tx2.ins[i].outpoint.index)
return false;
for (let i = 0; i < tx2.ins.length; i++)
if (tx1.outs[i].value !== tx2.outs[i].value || Crypto.util.bytesToHex(tx1.outs[i].script.buffer) !== Crypto.util.bytesToHex(tx2.outs[i].script.buffer))
return false;
return true;
}
const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}`)
.then(result => resolve(result.out[i]))
.catch(error => reject(error))
});
btcOperator.parseTransaction = function (tx) {
return new Promise((resolve, reject) => {
tx = deserializeTx(tx);
let result = {};
let promises = [];
//Parse Inputs
for (let i = 0; i < tx.ins.length; i++)
promises.push(getTxOutput(tx.ins[i].outpoint.hash, tx.ins[i].outpoint.index));
Promise.all(promises).then(inputs => {
result.inputs = inputs.map(inp => Object({
address: inp.addr,
value: util.Sat_to_BTC(inp.value)
}));
let signed = checkSigned(tx, false);
result.inputs.forEach((inp, i) => inp.signed = signed[i]);
//Parse Outputs
result.outputs = tx.outs.map(out => {
var address;
switch (out.script.chunks[0]) {
case 0: //bech32, multisig-bech32
address = encodeBech32(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.bech32.version, coinjs.bech32.hrp);
break;
case 169: //segwit, multisig-segwit
address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.multisig);
break;
case 118: //legacy
address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]), coinjs.pub);
}
return {
address,
value: util.Sat_to_BTC(out.value)
}
});
//Parse Totals
result.total_input = parseFloat(result.inputs.reduce((a, inp) => a += inp.value, 0).toFixed(8));
result.total_output = parseFloat(result.outputs.reduce((a, out) => a += out.value, 0).toFixed(8));
result.fee = parseFloat((result.total_input - result.total_output).toFixed(8));
resolve(result);
}).catch(error => reject(error))
})
}
btcOperator.transactionID = function (tx) {
tx = deserializeTx(tx);
let clone = coinjs.clone(tx);
clone.witness = null;
let raw_bytes = Crypto.util.hexToBytes(clone.serialize());
let txid = Crypto.SHA256(Crypto.SHA256(raw_bytes, { asBytes: true }), { asBytes: true }).reverse();
return Crypto.util.bytesToHex(txid);
}
const getLatestBlock = btcOperator.getLatestBlock = () => new Promise((resolve, reject) => {
fetch_api(`q/getblockcount`)
.then(result => resolve(result))
.catch(error => reject(error))
})
btcOperator.getTx = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}`).then(result => {
getLatestBlock().then(latest_block => resolve({
block: result.block_height,
txid: result.hash,
time: result.time * 1000,
confirmations: result.block_height === null ? 0 : latest_block - result.block_height, //calculate confirmations using latest block number as api doesnt relay it
size: result.size,
fee: util.Sat_to_BTC(result.fee),
inputs: result.inputs.map(i => Object({ address: i.prev_out.addr, value: util.Sat_to_BTC(i.prev_out.value) })),
total_input_value: util.Sat_to_BTC(result.inputs.reduce((a, i) => a + i.prev_out.value, 0)),
outputs: result.out.map(o => Object({ address: o.addr, value: util.Sat_to_BTC(o.value) })),
total_output_value: util.Sat_to_BTC(result.out.reduce((a, o) => a += o.value, 0)),
}))
}).catch(error => reject(error))
});
btcOperator.getTx.hex = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}?format=hex`, false)
.then(result => resolve(result))
.catch(error => reject(error))
})
btcOperator.getAddressData = address => new Promise((resolve, reject) => {
fetch_api(`rawaddr/${address}`).then(data => {
let details = {};
details.balance = util.Sat_to_BTC(data.final_balance);
details.address = data.address;
details.txs = data.txs.map(tx => {
let d = {
txid: tx.hash,
time: tx.time * 1000, //s to ms
block: tx.block_height,
}
//sender list
d.tx_senders = {};
tx.inputs.forEach(i => {
if (i.prev_out.addr in d.tx_senders)
d.tx_senders[i.prev_out.addr] += i.prev_out.value;
else d.tx_senders[i.prev_out.addr] = i.prev_out.value;
});
d.tx_input_value = 0;
for (let s in d.tx_senders) {
let val = d.tx_senders[s];
d.tx_senders[s] = util.Sat_to_BTC(val);
d.tx_input_value += val;
}
d.tx_input_value = util.Sat_to_BTC(d.tx_input_value);
//receiver list
d.tx_receivers = {};
tx.out.forEach(o => {
if (o.addr in d.tx_receivers)
d.tx_receivers[o.addr] += o.value;
else d.tx_receivers[o.addr] = o.value;
});
d.tx_output_value = 0;
for (let r in d.tx_receivers) {
let val = d.tx_receivers[r];
d.tx_receivers[r] = util.Sat_to_BTC(val);
d.tx_output_value += val;
}
d.tx_output_value = util.Sat_to_BTC(d.tx_output_value);
d.tx_fee = util.Sat_to_BTC(tx.fee);
//tx type
if (tx.result > 0) { //net > 0, balance inc => type=in
d.type = "in";
d.amount = util.Sat_to_BTC(tx.result);
d.sender = Object.keys(d.tx_senders).filter(s => s !== address);
} else if (Object.keys(d.tx_receivers).some(r => r !== address)) { //net < 0, balance dec & receiver present => type=out
d.type = "out";
d.amount = util.Sat_to_BTC(tx.result * -1);
d.receiver = Object.keys(d.tx_receivers).filter(r => r !== address);
d.fee = d.tx_fee;
} else { //net < 0 (fee) & no other id in receiver list => type=self
d.type = "self";
d.amount = d.tx_receivers[address];
d.address = address
}
return d;
})
resolve(details);
}).catch(error => reject(error))
});
btcOperator.getBlock = block => new Promise((resolve, reject) => {
fetch_api(`rawblock/${block}`).then(result => resolve({
height: result.height,
hash: result.hash,
merkle_root: result.mrkl_root,
prev_block: result.prev_block,
next_block: result.next_block[0],
size: result.size,
time: result.time * 1000, //s to ms
txs: result.tx.map(t => Object({
fee: t.fee,
size: t.size,
inputs: t.inputs.map(i => Object({ address: i.prev_out.addr, value: util.Sat_to_BTC(i.prev_out.value) })),
total_input_value: util.Sat_to_BTC(t.inputs.reduce((a, i) => a + i.prev_out.value, 0)),
outputs: t.out.map(o => Object({ address: o.addr, value: util.Sat_to_BTC(o.value) })),
total_output_value: util.Sat_to_BTC(t.out.reduce((a, o) => a += o.value, 0)),
}))
})).catch(error => reject(error))
});
})('object' === typeof module ? module.exports : window.btcOperator = {});

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //floBlockchainAPI v2.3.3 (function (EXPORTS) { //floBlockchainAPI v2.3.3e
/* FLO Blockchain Operator to send/receive data from blockchain using API calls*/ /* FLO Blockchain Operator to send/receive data from blockchain using API calls*/
'use strict'; 'use strict';
const floBlockchainAPI = EXPORTS; const floBlockchainAPI = EXPORTS;
@ -6,11 +6,12 @@
const DEFAULT = { const DEFAULT = {
blockchain: floGlobals.blockchain, blockchain: floGlobals.blockchain,
apiURL: { apiURL: {
FLO: ['https://livenet.flocha.in/', 'https://flosight.duckdns.org/'], FLO: ['https://flosight.duckdns.org/', 'https://flosight.ranchimall.net/'],
FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/'] FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/']
}, },
sendAmt: 0.001, sendAmt: 0.001,
fee: 0.0005, fee: 0.0005,
minChangeAmt: 0.0005,
receiverID: floGlobals.adminID receiverID: floGlobals.adminID
}; };
@ -49,7 +50,7 @@
const allServerList = new Set(floGlobals.apiURL && floGlobals.apiURL[DEFAULT.blockchain] ? floGlobals.apiURL[DEFAULT.blockchain] : DEFAULT.apiURL[DEFAULT.blockchain]); const allServerList = new Set(floGlobals.apiURL && floGlobals.apiURL[DEFAULT.blockchain] ? floGlobals.apiURL[DEFAULT.blockchain] : DEFAULT.apiURL[DEFAULT.blockchain]);
var serverList = Array.from(allServerList); var serverList = Array.from(allServerList);
var curPos = floCrypto.randInt(0, serverList - 1); var curPos = floCrypto.randInt(0, serverList.length - 1);
function fetch_retry(apicall, rm_flosight) { function fetch_retry(apicall, rm_flosight) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -125,9 +126,9 @@
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floCrypto.validateASCII(floData)) if (!floCrypto.validateASCII(floData))
return reject("Invalid FLO_Data: only printable ASCII characters are allowed"); return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
else if (!floCrypto.validateAddr(senderAddr)) else if (!floCrypto.validateFloID(senderAddr))
return reject(`Invalid address : ${senderAddr}`); return reject(`Invalid address : ${senderAddr}`);
else if (!floCrypto.validateAddr(receiverAddr)) else if (!floCrypto.validateFloID(receiverAddr))
return reject(`Invalid address : ${receiverAddr}`); return reject(`Invalid address : ${receiverAddr}`);
else if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr)) else if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr))
return reject("Invalid Private key!"); return reject("Invalid Private key!");
@ -171,7 +172,7 @@
else { else {
trx.addoutput(receiverAddr, sendAmt); trx.addoutput(receiverAddr, sendAmt);
var change = utxoAmt - sendAmt - fee; var change = utxoAmt - sendAmt - fee;
if (change > 0) if (change > DEFAULT.minChangeAmt)
trx.addoutput(senderAddr, change); trx.addoutput(senderAddr, change);
trx.addflodata(floData.replace(/\n/g, ' ')); trx.addflodata(floData.replace(/\n/g, ' '));
var signedTxHash = trx.sign(privKey, 1); var signedTxHash = trx.sign(privKey, 1);
@ -202,7 +203,7 @@
//merge all UTXOs of a given floID into a single UTXO //merge all UTXOs of a given floID into a single UTXO
floBlockchainAPI.mergeUTXOs = function (floID, privKey, floData = '') { floBlockchainAPI.mergeUTXOs = function (floID, privKey, floData = '') {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(floID)) if (!floCrypto.validateFloID(floID))
return reject(`Invalid floID`); return reject(`Invalid floID`);
if (!floCrypto.verifyPrivKey(privKey, floID)) if (!floCrypto.verifyPrivKey(privKey, floID))
return reject("Invalid Private Key"); return reject("Invalid Private Key");
@ -326,7 +327,7 @@
} }
//Validate the receiver IDs and receive amount //Validate the receiver IDs and receive amount
for (let floID in receivers) { for (let floID in receivers) {
if (!floCrypto.validateAddr(floID)) if (!floCrypto.validateFloID(floID))
invalids.InvalidReceiverIDs.push(floID); invalids.InvalidReceiverIDs.push(floID);
if (typeof receivers[floID] !== 'number' || receivers[floID] <= 0) if (typeof receivers[floID] !== 'number' || receivers[floID] <= 0)
invalids.InvalidReceiveAmountFor.push(floID); invalids.InvalidReceiveAmountFor.push(floID);
@ -371,18 +372,18 @@
}) })
//Calculate totalSentAmount and check if totalBalance is sufficient //Calculate totalSentAmount and check if totalBalance is sufficient
let totalSendAmt = totalFee; let totalSendAmt = totalFee;
for (floID in receivers) for (let floID in receivers)
totalSendAmt += receivers[floID]; totalSendAmt += receivers[floID];
if (totalBalance < totalSendAmt) if (totalBalance < totalSendAmt)
return reject("Insufficient total Balance"); return reject("Insufficient total Balance");
//Get the UTXOs of the senders //Get the UTXOs of the senders
let promises = []; let promises = [];
for (floID in senders) for (let floID in senders)
promises.push(promisedAPI(`api/addr/${floID}/utxo`)); promises.push(promisedAPI(`api/addr/${floID}/utxo`));
Promise.all(promises).then(results => { Promise.all(promises).then(results => {
let wifSeq = []; let wifSeq = [];
var trx = bitjs.transaction(); var trx = bitjs.transaction();
for (floID in senders) { for (let floID in senders) {
let utxos = results.shift(); let utxos = results.shift();
let sendAmt; let sendAmt;
if (preserveRatio) { if (preserveRatio) {
@ -406,7 +407,7 @@
if (change > 0) if (change > 0)
trx.addoutput(floID, change); trx.addoutput(floID, change);
} }
for (floID in receivers) for (let floID in receivers)
trx.addoutput(floID, receivers[floID]); trx.addoutput(floID, receivers[floID]);
trx.addflodata(floData.replace(/\n/g, ' ')); trx.addflodata(floData.replace(/\n/g, ' '));
for (let i = 0; i < wifSeq.length; i++) for (let i = 0; i < wifSeq.length; i++)
@ -484,8 +485,8 @@
floBlockchainAPI.readData = function (addr, options = {}) { floBlockchainAPI.readData = function (addr, options = {}) {
options.limit = options.limit || 0; options.limit = options.limit || 0;
options.ignoreOld = options.ignoreOld || 0; options.ignoreOld = options.ignoreOld || 0;
if (typeof options.sender === "string") options.sender = [options.sender]; if (typeof options.senders === "string") options.senders = [options.senders];
if (typeof options.receiver === "string") options.receiver = [options.receiver]; if (typeof options.receivers === "string") options.receivers = [options.receivers];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => {
var newItems = response.totalItems - options.ignoreOld; var newItems = response.totalItems - options.ignoreOld;
@ -520,10 +521,10 @@
} }
if (!flag) continue; if (!flag) continue;
} }
if (Array.isArray(options.sender)) { if (Array.isArray(options.senders)) {
let flag = false; let flag = false;
for (let vin of response.items[i].vin) for (let vin of response.items[i].vin)
if (options.sender.includes(vin.addr)) { if (options.senders.includes(vin.addr)) {
flag = true; flag = true;
break; break;
} }
@ -538,10 +539,10 @@
} }
if (!flag) continue; if (!flag) continue;
} }
if (Array.isArray(options.receiver)) { if (Array.isArray(options.receivers)) {
let flag = false; let flag = false;
for (let vout of response.items[i].vout) for (let vout of response.items[i].vout)
if (options.receiver.includes(vout.scriptPubKey.addresses[0])) { if (options.receivers.includes(vout.scriptPubKey.addresses[0])) {
flag = true; flag = true;
break; break;
} }
@ -555,6 +556,8 @@
d.txid = response.items[i].txid; d.txid = response.items[i].txid;
d.time = response.items[i].time; d.time = response.items[i].time;
d.blockheight = response.items[i].blockheight; d.blockheight = response.items[i].blockheight;
d.senders = new Set(response.items[i].vin.map(v => v.addr));
d.receivers = new Set(response.items[i].vout.map(v => v.scriptPubKey.addresses[0]));
d.data = response.items[i].floData; d.data = response.items[i].floData;
filteredData.push(d); filteredData.push(d);
} else } else

View File

@ -1,14 +1,59 @@
(function(EXPORTS) { //floCloudAPI v2.3.0 (function (EXPORTS) { //floCloudAPI v2.4.3
/* 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,
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
@ -18,6 +63,9 @@
}, },
application: { application: {
get: () => DEFAULT.application get: () => DEFAULT.application
},
user: {
get: () => user
} }
}); });
@ -31,6 +79,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
@ -175,7 +226,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 +264,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 => {
@ -269,6 +320,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 +329,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)))
@ -326,12 +378,56 @@
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 +488,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))
@ -416,7 +512,7 @@
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,
@ -432,9 +528,9 @@
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 +539,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))
@ -482,19 +578,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,8 +604,9 @@
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
} }
*/
//(NEEDS UPDATE) edit comment of data in supernode cloud (mutable comments only) /*(NEEDS UPDATE)
//edit comment of data in supernode cloud (mutable comments only)
floCloudAPI.editApplicationData = function(vectorClock, newComment, oldData, options = {}) { floCloudAPI.editApplicationData = function(vectorClock, newComment, oldData, options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let p0 let p0
@ -523,12 +621,12 @@
} }
}) })
p0.then(d => { p0.then(d => {
if (d.senderID != myFloID) if (d.senderID != user.id)
return reject("Invalid requestorID") return reject("Invalid requestorID")
else if (!d.comment.startsWith("EDIT:")) else if (!d.comment.startsWith("EDIT:"))
return reject("Data immutable") return reject("Data immutable")
let data = { let data = {
requestorID: myFloID, requestorID: user.id,
receiverID: d.receiverID, receiverID: d.receiverID,
time: Date.now(), time: Date.now(),
application: d.application, application: d.application,
@ -542,29 +640,30 @@
"comment" "comment"
] ]
.map(x => d[x]).join("|") .map(x => d[x]).join("|")
data.edit.sign = floCrypto.signData(hashcontent, myPrivKey) data.edit.sign = user.sign(hashcontent)
singleRequest(data.receiverID, data) singleRequest(data.receiverID, data)
.then(result => resolve("Data comment updated")) .then(result => resolve("Data comment updated"))
.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))
@ -576,14 +675,14 @@
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))
@ -887,23 +986,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 +1011,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 +1033,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)

View File

@ -1,4 +1,4 @@
(function(EXPORTS) { //floCrypto v2.3.0a (function (EXPORTS) { //floCrypto v2.3.3e
/* 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();
@ -80,7 +81,7 @@
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)
@ -89,7 +90,7 @@
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;
} }
@ -121,12 +122,8 @@
//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;
} }
@ -134,11 +131,9 @@
//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;
} }
@ -153,8 +148,23 @@
} }
} }
Object.defineProperty(floCrypto, 'newID', { Object.defineProperties(floCrypto, {
newID: {
get: () => generateNewID() get: () => generateNewID()
},
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
@ -182,6 +192,25 @@
} }
} }
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)
@ -202,18 +231,118 @@
} }
} }
//Check if the given Address is valid or not //Check if the given flo-id is valid or not
floCrypto.validateFloID = floCrypto.validateAddr = function(inpAddr) { floCrypto.validateFloID = function (floID) {
if (!inpAddr) if (!floID)
return false; return false;
try { try {
let addr = new Bitcoin.Address(inpAddr); let addr = new Bitcoin.Address(floID);
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 for the address (any blockchain)
floCrypto.verifyPubKey = function (pubKeyHex, address) {
let raw = decodeAddress(address),
pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), {
asBytes: true
})));
return raw ? pub_hash === raw.hex : false;
}
//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 (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)));
}
//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
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) { //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 {

View File

@ -1,8 +1,168 @@
(function(EXPORTS) { //floDapps v2.2.1 (function (EXPORTS) { //floDapps v2.3.3
/* 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, settings
Object.defineProperties(floGlobals, {
subAdmins: {
get: () => subAdmins
},
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 = {
@ -21,6 +181,7 @@
credentials: {}, credentials: {},
//for Dapps //for Dapps
subAdmins: {}, subAdmins: {},
trustedIDs: {},
settings: {}, settings: {},
appObjects: {}, appObjects: {},
generalData: {}, generalData: {},
@ -28,41 +189,41 @@
} }
//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'))
}) })
@ -72,7 +233,7 @@
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 => { compactIDB.readData("lastTx", floCloudAPI.SNStorageID, DEFAULT.root).then(lastTx => {
floBlockchainAPI.readData(floCloudAPI.SNStorageID, { floBlockchainAPI.readData(floCloudAPI.SNStorageID, {
ignoreOld: lastTx, ignoreOld: lastTx,
sentOnly: true, sentOnly: true,
@ -80,14 +241,20 @@
}).then(result => { }).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]).SuperNodeStorage; var content = JSON.parse(result.data[i]).SuperNodeStorage;
for (sn in content.removeNodes) for (let sn in content.removeNodes)
compactIDB.removeData("supernodes", sn, "floDapps"); compactIDB.removeData("supernodes", sn, DEFAULT.root);
for (sn in content.newNodes) for (let sn in content.newNodes)
compactIDB.writeData("supernodes", content.newNodes[sn], sn, "floDapps"); compactIDB.writeData("supernodes", content.newNodes[sn], sn, DEFAULT.root);
for (let sn in content.updateNodes)
compactIDB.readData("supernodes", sn, DEFAULT.root).then(r => {
r = r || {}
r.uri = content.updateNodes[sn];
compactIDB.writeData("supernodes", r, sn, DEFAULT.root);
});
} }
compactIDB.writeData("lastTx", result.totalTxs, floCloudAPI.SNStorageID, "floDapps"); compactIDB.writeData("lastTx", result.totalTxs, floCloudAPI.SNStorageID, DEFAULT.root);
compactIDB.readAllData("supernodes", "floDapps").then(result => { compactIDB.readAllData("supernodes", DEFAULT.root).then(nodes => {
floCloudAPI.init(result) floCloudAPI.init(nodes)
.then(result => resolve("Loaded Supernode list\n" + result)) .then(result => resolve("Loaded Supernode list\n" + result))
.catch(error => reject(error)) .catch(error => reject(error))
}) })
@ -98,14 +265,14 @@
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 => { compactIDB.readData("lastTx", `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root).then(lastTx => {
floBlockchainAPI.readData(floGlobals.adminID, { floBlockchainAPI.readData(DEFAULT.adminID, {
ignoreOld: lastTx, ignoreOld: lastTx,
sentOnly: true, sentOnly: true,
pattern: floGlobals.application pattern: DEFAULT.application
}).then(result => { }).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,15 +281,21 @@
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.totalTxs, `${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("settings").then(result => {
floGlobals.settings = result; settings = result;
resolve("Read app configuration from blockchain"); resolve("Read app configuration from blockchain");
}) })
}) })
@ -186,7 +359,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 +370,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 +383,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 +415,14 @@
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) 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)
@ -305,8 +483,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 +493,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")
})
}) })
} }
@ -333,8 +514,8 @@
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))
}); });
@ -342,14 +523,14 @@
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))
}); });
@ -359,11 +540,11 @@
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))
@ -372,20 +553,21 @@
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)
@ -404,14 +586,36 @@
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')
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 (!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') reject('Access Denied for Admin privilege')
else else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey) floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
@ -422,9 +626,9 @@
const clearCredentials = floDapps.clearCredentials = function () { const clearCredentials = floDapps.clearCredentials = function () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
compactIDB.clearData('credentials', floGlobals.application).then(result => { compactIDB.clearData('credentials', DEFAULT.application).then(result => {
localStorage.removeItem(`${floGlobals.application}#privKey`) localStorage.removeItem(`${DEFAULT.application}#privKey`);
myPrivKey = myPubKey = myFloID = undefined; user.clear();
resolve("privKey credentials deleted!") resolve("privKey credentials deleted!")
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
@ -433,7 +637,7 @@
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)
@ -444,10 +648,10 @@
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))
@ -455,17 +659,17 @@
} }
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)
@ -494,7 +698,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')
@ -535,7 +739,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) {
@ -561,14 +765,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 +781,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)

View File

@ -1,11 +1,11 @@
(function(EXPORTS) { //floTokenAPI v1.0.3b (function (EXPORTS) { //floTokenAPI v1.0.3c
/* 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, {
@ -29,7 +29,7 @@
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));

View File

@ -13,6 +13,7 @@
</script> </script>
<script src="lib.js"></script> <script src="lib.js"></script>
<script src="floCrypto.js"></script> <script src="floCrypto.js"></script>
<script src="btcOperator.js"></script>
<script src="floBlockchainAPI.js"></script> <script src="floBlockchainAPI.js"></script>
<script src="floTokenAPI.js"></script> <script src="floTokenAPI.js"></script>
<script src="compactIDB.js"></script> <script src="compactIDB.js"></script>
@ -24,7 +25,6 @@
//floDapps.addStartUpFunction('Sample', Promised Function) //floDapps.addStartUpFunction('Sample', Promised Function)
//floDapps.setAppObjectStores({sampleObs1:{}, sampleObs2:{options{autoIncrement:true, keyPath:'SampleKey'}, Indexes:{sampleIndex:{}}}}) //floDapps.setAppObjectStores({sampleObs1:{}, sampleObs2:{options{autoIncrement:true, keyPath:'SampleKey'}, Indexes:{sampleIndex:{}}}})
//floDapps.setCustomPrivKeyInput( () => { FUNCTION BODY *must resolve private key* } ) //floDapps.setCustomPrivKeyInput( () => { FUNCTION BODY *must resolve private key* } )
floDapps.launchStartUp().then(result => { floDapps.launchStartUp().then(result => {
console.log(result) console.log(result)
alert(`Welcome FLO_ID: ${myFloID}`) alert(`Welcome FLO_ID: ${myFloID}`)

2965
lib.js

File diff suppressed because it is too large Load Diff