Merge pull request #9 from sairajzero/master

This commit is contained in:
Sai Raj 2020-08-20 17:53:05 +05:30 committed by GitHub
commit 49d4377900
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 168 additions and 71 deletions

View File

@ -282,16 +282,15 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
`readData` reads FLO data from transactions of specified address
1. addr - FLO address for which the transactions data has to be read.
2. options - Contains options for filter data from transactions.
- limit : maximum number of filtered data (default = 1000, negative = no limit)
- limit : maximum number of filtered data (default = no limit)
- ignoreOld : ignore old transactions (default = 0)
- sentOnly : filters only sent data
- pattern : filters data that starts with a pattern
- contains : filters data that contains a string
- pattern : filters data that contains pattern as an object key in the JSON string
- filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'})
* Resolves: Object {totalTxs, floData (Array)}
## Compact IndexedDB operations
`compactIDB` operations can be used to perform basic IndexedDB operations such as add, read/write, modify and remove.Contains following operations.
`compactIDB` operations can be used to perform basic IndexedDB operations such as add, read/write, modify and remove. These functions are asynchronous and return a promise. Contains the following operations.
#### Important: Compact IndexedDB operations have all been promisified. All output needs to be handled using .then These operations do not return function return values. Once again, they resolve: they do not return.
@ -299,22 +298,26 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
compactIDB.setDefaultDB(dbName)
`setDefaultDB` sets the database on which further operations will be performed.
1. dbName - This is the name of default database to be used.
* Note: this operation is neither promisified nor returns a value. It just sets the default DB
#### initDB
compactIDB.initDB(dbName, objectStores = {})
`initDB` initializes new IndexedDB.
1. dbName - Specifies database to be initialized.
2. objectStores - This is an object containing various objectStores to be initiazed when creating an IDB.
* Resolves: Status (string) | Rejects: error
#### openDB
compactIDB.openDB(dbName = this.defaultDB)
`openDB` returns a promise that resolves to a default database object.
1. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: database (IDB) | Rejects: error
#### deleteDB
compactIDB.deleteDB(dbName = this.defaultDB)
`deleteDB` deletes the specified database.
1. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: Status (string) | Rejects: error
#### writeData
compactIDB.writeData(obsName, data, key = false, dbName = this.defaultDB)
@ -323,6 +326,7 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
2. data - data that has to be written in specified object store.
3. key - Primary key of the data (optional, false indicates key is autoincremented or passed in data)
4. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: Status (string) | Rejects: error
#### addData
compactIDB.addData(obsName, data, key = false, dbName = this.defaultDB)
@ -331,6 +335,7 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
2. data - The data which has to be added to obeject store.
3. key - Primary key of the data (optional, false indicates key is autoincremented or passed in data)
4. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: Status (string) | Rejects: error
#### removeData
compactDB.removeData(obsName, key, dbName = this.defaultDB)
@ -338,12 +343,14 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
1. obsName - Name of object store from which the data has to be removed.
2. key - Primary key of the data.
3. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: Status (string) | Rejects: error
#### clearData
compactDB.clearData(obsName, dbName = this.defaultDB)
`clearData` clears all data in the objectStore.
1. obsName - Name of object store from which the data has to be removed.
2. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: Status (string) | Rejects: error
#### readData
compactDB.readData(obsName, key, dbName = this.defaultDB)
@ -351,20 +358,34 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
1. obsName - Name of object store from which the data has to be retrieved.
2. key - Primary key of the data to read.
3. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: data (Object) | Rejects: error
#### readAllData
compactDB.readAllData(obsName, dbName = this.defaultDB)
`readAllData` reads all the data from specified object store using IndexedDB openCursor method.
1. obsName - Name of object store from which the data has to be retrieved.
2. dbName - Name of the database (optional, uses defaultDB if not specified)
* Resolves: data (Object) | Rejects: error
## FLO Supernode module
This module contains functions that interact with the supernode to send and retrive data in the backend. Use floClouldAPI operations to send and receive data for application.
## FLO Cloud API operations
`floCloudAPI` operations can interact with floSupernode cloud to send and retrieve data for applications. floCloudAPI uses floSupernode module for backend interactions.
`floCloudAPI` operations can interact with floSupernode cloud to send and retrieve data for applications. floCloudAPI uses floSupernode module for backend interactions. FLO Cloud API functions are promisified and resolves the data or status.
FLO Cloud API operations have all been promisified. All output needs to be handled using .then These operations do not return function return values. Once again, they resolve: they do not return.
#### sendApplicationData
floCloudAPI.sendApplicationData(message, type, options = {})
`sendApplicationData` sends application data to the cloud.
1. message - data to be sent
2. type - type of the data
3. options - (optional, options detailed at end of module)
* Resolves: Sent-Status (string) | Rejects: error
#### requestApplicationData
floCloudAPI.requestApplicationData(options = {})
`requestApplicationData` requests application data from the cloud.
1. options - (optional, options detailed at end of module)
* Resolves: data (Object) | Rejects: error
#### sendGeneralData
floCloudAPI.sendGeneralData(message, type, options = {})
@ -372,6 +393,7 @@ FLO Cloud API operations have all been promisified. All output needs to be handl
1. message - data to be sent
2. type - type of the data
3. options - (optional, options detailed at end of module)
* Resolves: Sent-Status (string) | Rejects: error
###### Minimal Example:
floCloudAPI.sendGeneralData("Hello World", "type1")
@ -382,6 +404,7 @@ Sends "Hello World" message to the cloud as General Data with type1 as `type` wi
`requestGeneralData` requests application data from the cloud.
1. type - type of the data
2. options - (optional, options detailed at end of module)
* Resolves: Status (string) | Rejects: error
###### Minimal Example:
floCloudAPI.requestGeneralData("type1")
@ -392,9 +415,9 @@ Requests all messages of General Data nature from the cloud with type1 as `type`
`resetObjectData` resets the objectData to cloud.
1. "objectName" - Name of the objectData to be reset. Quotes are must
2. options - (optional, options detailed at end of module)
* Resolves: Sent-Status (string) | Rejects: error
Note: value of objectData is taken from floGlobals.appObjects["objectName"]
The object data corresponding with Object Name must be defined in floGlobals.appObjects["objectName"] before a reset can be done
###### Minimal Example:
@ -407,9 +430,9 @@ Initiates "myFirstObject" with {a:1,b:2}, and sends to cloud with `myFloID` as d
`updateObjectData` updates the objectData to cloud.
1. "objectName" - Name of the objectData to be updated. Quotes are must.
2. options - (optional, options detailed at end of module)
* Resolves: Sent-Status (string) | Rejects: error
Note: value of objectData is taken from floGlobals.appObjects["objectName"]
The object data corresponding with Object Name must be defined in floGlobals.appObjects["objectName"] before an update can be done
###### Minimal Example:
@ -422,6 +445,7 @@ Updates "myFirstObject" with {a:1,c:3,d:4}, and sends to cloud with `myFloID` as
`requestObjectData` requests application data from the cloud.
1. "objectName" - Name of the objectData to be requested. Quotes are must.
2. options - (optional, options detailed at end of module)
* Resolves: Status (string) | Rejects: error
Note: The output is available at floGlobals.appObjects["objectName"] after the promise is resolved

View File

@ -7400,7 +7400,7 @@ Bitcoin.Util = {
var change = utxoAmt - sendAmt - fee;
if (change > 0)
trx.addoutput(senderAddr, change);
trx.addflodata(floData.replace(/\n/g,' '));
trx.addflodata(floData.replace(/\n/g, ' '));
var signedTxHash = trx.sign(privKey, 1);
this.broadcastTx(signedTxHash)
.then(txid => resolve(txid))
@ -7431,7 +7431,7 @@ Bitcoin.Util = {
}
}
trx.addoutput(floID, utxoAmt - fee);
trx.addflodata(floData.replace(/\n/g,' '));
trx.addflodata(floData.replace(/\n/g, ' '));
var signedTxHash = trx.sign(privKey, 1);
this.broadcastTx(signedTxHash)
.then(txid => resolve(txid))
@ -7447,11 +7447,11 @@ Bitcoin.Util = {
* @param {boolean} preserveRatio (optional) preserve ratio or equal contribution
* @return {Promise}
*/
writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true){
writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true) {
return new Promise((resolve, reject) => {
if (!Array.isArray(senderPrivKeys))
return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array")
if(!preserveRatio){
if (!preserveRatio) {
let tmp = {};
let amount = (floGlobals.sendAmt * receivers.length) / senderPrivKeys.length;
senderPrivKeys.forEach(key => tmp[key] = amount);
@ -7482,53 +7482,58 @@ Bitcoin.Util = {
sendTxMultiple: function (senderPrivKeys, receivers, floData = '') {
return new Promise((resolve, reject) => {
let senders = {}, preserveRatio;
let senders = {},
preserveRatio;
//check for argument validations
try{
try {
let invalids = {
InvalidSenderPrivKeys: [],
InvalidSenderAmountFor: [],
InvalidReceiverIDs: [],
InvalidReceiveAmountFor: []
}
let inputVal = 0, outputVal = 0;
let inputVal = 0,
outputVal = 0;
//Validate sender privatekeys (and send amount if passed)
//conversion when only privateKeys are passed (preserveRatio mode)
if(Array.isArray(senderPrivKeys)){
if (Array.isArray(senderPrivKeys)) {
senderPrivKeys.forEach(key => {
try{
if(!key)
try {
if (!key)
invalids.InvalidSenderPrivKeys.push(key);
else{
let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(key));
else {
let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto
.getPubKeyHex(key));
senders[floID] = {
wif: key
}
}
}catch(error){
} catch (error) {
invalids.InvalidSenderPrivKeys.push(key)
}
})
preserveRatio = true;
}
//conversion when privatekeys are passed with send amount
else{
for(let key in senderPrivKeys){
try{
if(!key)
else {
for (let key in senderPrivKeys) {
try {
if (!key)
invalids.InvalidSenderPrivKeys.push(key);
else{
if(typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[key] <= 0)
else {
if (typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[
key] <= 0)
invalids.InvalidSenderAmountFor.push(key)
else
inputVal += senderPrivKeys[key];
let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(key));
let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(
key));
senders[floID] = {
wif: key,
coins: senderPrivKeys[key]
}
}
}catch(error){
} catch (error) {
invalids.InvalidSenderPrivKeys.push(key)
}
}
@ -7550,9 +7555,10 @@ Bitcoin.Util = {
if (Object.keys(invalids).length)
return reject(invalids);
//Reject if given inputVal and outputVal are not equal
if(!preserveRatio && inputVal != outputVal)
return reject(`Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`)
}catch(error){
if (!preserveRatio && inputVal != outputVal)
return reject(
`Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`)
} catch (error) {
return reject(error)
}
//Get balance of senders
@ -7564,18 +7570,22 @@ Bitcoin.Util = {
totalFee = floGlobals.fee,
balance = {};
//Divide fee among sender if not for preserveRatio
if(!preserveRatio)
if (!preserveRatio)
var dividedFee = totalFee / Object.keys(senders).length;
//Check if balance of each sender is sufficient enough
let insufficient = [];
for (let floID in senders) {
balance[floID] = parseFloat(results.shift());
if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <= totalFee) || (!preserveRatio && balance[floID] < senders[floID].coins + dividedFee))
if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <=
totalFee) || (!preserveRatio && balance[floID] < senders[floID]
.coins + dividedFee))
insufficient.push(floID)
totalBalance += balance[floID];
}
if (insufficient.length)
return reject({InsufficientBalance: insufficient})
return reject({
InsufficientBalance: insufficient
})
//Calculate totalSentAmount and check if totalBalance is sufficient
let totalSendAmt = totalFee;
for (floID in receivers)
@ -7592,7 +7602,7 @@ Bitcoin.Util = {
for (floID in senders) {
let utxos = results.shift();
let sendAmt;
if(preserveRatio){
if (preserveRatio) {
let ratio = (balance[floID] / totalBalance);
sendAmt = totalSendAmt * ratio;
} else
@ -7616,7 +7626,7 @@ Bitcoin.Util = {
}
for (floID in receivers)
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++)
trx.signinput(i, wifSeq[i], 1);
var signedTxHash = trx.serialize();
@ -7683,7 +7693,7 @@ Bitcoin.Util = {
filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'})
*/
readData: function (addr, options = {}) {
options.limit = options.limit | 1000
options.limit = options.limit | 0
options.ignoreOld = options.ignoreOld | 0
return new Promise((resolve, reject) => {
this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => {
@ -7698,13 +7708,17 @@ Bitcoin.Util = {
if (options.sentOnly && response.items[i].vin[0].addr !==
addr)
continue;
if (options.pattern && !response.items[i].floData
.startsWith(options.pattern, 0) && !response.items[i]
.floData.startsWith(options.pattern, 2))
continue;
if (options.contains && !response.items[i].floData.includes(
options.contains))
continue;
if (options.pattern) {
try {
let jsonContent = JSON.parse(response.items[i]
.floData)
if (!Object.keys(jsonContent).includes(options
.pattern))
continue;
} catch (error) {
continue;
}
}
if (options.filter && !options.filter(response.items[i]
.floData))
continue;
@ -8606,7 +8620,9 @@ Bitcoin.Util = {
floGlobals.generalData[filterStr].push({
sender: dataSet[vc].senderID,
vectorClock: vc,
message: dataSet[vc].message
message: dataSet[vc].message,
sign: dataSet[vc].sign,
pubKey: dataSet[vc].pubKey
})
compactIDB.writeData("generalData", floGlobals.generalData[filterStr], filterStr)
floGlobals.generalVC[filterStr] = vc
@ -8660,6 +8676,10 @@ Bitcoin.Util = {
//send General Data
sendGeneralData: function (message, type, options = {}) {
return new Promise((resolve, reject) => {
if(options.encrypt){
let encryptionKey = (options.encrypt === true) ? floGlobals.settings.encryptionKey : options.encrypt
message = floCrypto.encryptData(JSON.stringify(message), encryptionKey)
}
this.sendApplicationData(message, type, options)
.then(result => resolve(result))
.catch(error => reject(error))
@ -8772,6 +8792,7 @@ Bitcoin.Util = {
credentials: {},
//for Dapps
subAdmins: {},
settings: {},
appObjects: {},
vectorClock: {},
generalData: {},
@ -8823,7 +8844,7 @@ Bitcoin.Util = {
})
},
readSubAdminListFromAPI: function () {
readAppConfigFromAPI: function () {
return new Promise((resolve, reject) => {
compactIDB.readData("lastTx", floGlobals.adminID).then(lastTx => {
floBlockchainAPI.readData(floGlobals.adminID, {
@ -8834,6 +8855,8 @@ Bitcoin.Util = {
for (var i = result.data.length - 1; i >= 0; i--) {
var content = JSON.parse(result.data[i])[floGlobals
.application];
if (!content || typeof content !== "object")
continue;
if (Array.isArray(content.removeSubAdmin))
for (var j = 0; j < content.removeSubAdmin
.length; j++)
@ -8844,12 +8867,21 @@ Bitcoin.Util = {
.length; k++)
compactIDB.writeData("subAdmins", true,
content.addSubAdmin[k]);
if (content.settings)
for (let l in content.settings)
compactIDB.writeData("settings", content
.settings[l], l)
}
compactIDB.writeData("lastTx", result.totalTxs,
floGlobals.adminID);
compactIDB.readAllData("subAdmins").then(result => {
floGlobals.subAdmins = Object.keys(result);
resolve("Read subAdmins from blockchain");
compactIDB.readAllData("settings").then(
result => {
floGlobals.settings = result;
resolve(
"Read app configuration from blockchain");
})
})
})
}).catch(error => reject(error))
@ -8949,7 +8981,7 @@ Bitcoin.Util = {
JSON.stringify(resultIndexes))
//also add a dummy privatekey to the IDB
var randomPrivKey = floCrypto
.generateNewID().privKey
.generateNewID().privKey
var randomThreshold = floCrypto.randInt(10,
20)
writeSharesToIDB(floCrypto
@ -8964,19 +8996,20 @@ Bitcoin.Util = {
})
}
const checkIfPinRequired = function(key){
const checkIfPinRequired = function (key) {
return new Promise((resolve, reject) => {
if(key.length == 52)
if (key.length == 52)
resolve(key)
else {
inputFn("PIN/Password").then(pwd => {
try{
try {
let privKey = Crypto.AES.decrypt(key, pwd);
resolve(privKey)
}catch(error){
} catch (error) {
reject("Access Denied: Incorrect PIN/Password")
}
}).catch(error => reject("Access Denied: PIN/Password required"))
}).catch(error => reject(
"Access Denied: PIN/Password required"))
}
})
}
@ -8984,12 +9017,12 @@ Bitcoin.Util = {
return new Promise((resolve, reject) => {
getPrivateKeyCredentials().then(key => {
checkIfPinRequired(key).then(privKey => {
try{
try {
myPrivKey = privKey
myPubKey = floCrypto.getPubKeyHex(myPrivKey)
myFloID = floCrypto.getFloIDfromPubkeyHex(myPubKey)
resolve('Login Credentials loaded successful')
}catch(error){
} catch (error) {
reject("Corrupted Private Key")
}
}).catch(error => reject(error))
@ -9004,13 +9037,13 @@ Bitcoin.Util = {
this.callStartUpFunction.completed += 1
reactor.dispatchEvent("startUpSuccessLog",
`${result}\nCompleted ${this.callStartUpFunction.completed}/${this.callStartUpFunction.total} Startup functions`
)
)
resolve(true)
}).catch(error => {
this.callStartUpFunction.failed += 1
reactor.dispatchEvent("startUpErrorLog",
`${error}\nFailed ${this.callStartUpFunction.failed}/${this.callStartUpFunction.total} Startup functions`
)
)
reject(false)
})
})
@ -9060,8 +9093,10 @@ Bitcoin.Util = {
manageSubAdmins(adminPrivKey, addList, rmList) {
return new Promise((resolve, reject) => {
if (!Array.isArray(addList)) addList = undefined;
if (!Array.isArray(rmList)) rmList = undefined;
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
if (!addList && !rmList)
return reject("subAdmin manage list is empty")
var floData = {
[floGlobals.application]: {
addSubAdmin: addList,
@ -9078,6 +9113,25 @@ Bitcoin.Util = {
})
},
setApplicationSettings(adminPrivKey, settings = {}) {
return new Promise((resolve, reject) => {
if (!settings || typeof settings !== "object")
return reject("Settings must be object")
var floData = {
[floGlobals.application]: {
settings: settings
}
}
var floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(adminPrivKey))
if (floID != floGlobals.adminID)
reject('Access Denied for Admin privilege')
else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
.then(result => resolve(['Updated app settings', result]))
.catch(error => reject(error))
})
},
clearCredentials: function () {
return new Promise((resolve, reject) => {
compactIDB.clearData('credentials').then(result => {
@ -9088,18 +9142,19 @@ Bitcoin.Util = {
})
},
securePrivKey: function(pwd){
securePrivKey: function (pwd) {
return new Promise((resolve, reject) => {
let indexArr = localStorage.getItem(`${floGlobals.application}#privKey`)
if(!indexArr)
if (!indexArr)
return reject("PrivKey not found");
indexArr = JSON.parse(indexArr)
let encryptedKey = Crypto.AES.encrypt(myPrivKey, pwd);
let threshold = indexArr.length;
let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold)
let promises = [];
for(var i=0; i<threshold;i++)
promises.push(compactIDB.writeData("credentials", shares[i], indexArr[i], floGlobals.application));
for (var i = 0; i < threshold; i++)
promises.push(compactIDB.writeData("credentials", shares[i], indexArr[i], floGlobals
.application));
Promise.all(promises)
.then(results => resolve("Private Key Secured"))
.catch(error => reject(error))
@ -9122,7 +9177,25 @@ Bitcoin.Util = {
var filteredResult = []
for (var i = 0; i < floGlobals.generalData[filter].length; i++)
if (floGlobals.generalData[filter][i].vectorClock > vectorClock)
filteredResult.push(floGlobals.generalData[filter][i])
filteredResult.push(JSON.parse(JSON.stringify(floGlobals.generalData[filter][i])))
if (options.decrypt) {
let decryptionKey = (options.decrypt === true) ? myPrivKey : options.decrypt;
if (!Array.isArray(decryptionKey))
decryptionKey = [decryptionKey];
filteredResult.forEach(data => {
try {
if ("secret" in data.message && "senderPublicKeyString" in data.message) {
for (let key of decryptionKey) {
try {
let tmp = floCrypto.decryptData(data.message, key)
data.message = JSON.parse(tmp)
break;
} catch (error) {}
}
}
} catch (error) {}
})
}
return filteredResult
}
}