Compare commits

...

171 Commits

Author SHA1 Message Date
Vivek Teega
ae69e8e1d7
Changing to testnet 2023-10-06 14:09:32 +05:30
Vivek Teega
2ac32bf360
Update floBlockchainAPI.js 2023-10-06 14:07:01 +05:30
0b547f73e9
Update btcOperator.js
Added Taproot Changes
Added support for Multisig fee Editing
Exposed internal functions under btcOperator object
2023-09-30 14:07:02 +05:30
sairajzero
098a62047c floCloudAPI v2.4.5 : file support
- uploadFile: upload a file to the cloud
- downloadFile: download a file from cloud

- upload/download file supports encryption of file. use options.encrypt in uploadFile and options.decrypt in downloadFile
2023-07-29 05:41:06 +05:30
sairajzero
5be35c28ce floDapps v2.4.1
- support for multiple cloud networks
2023-07-29 05:01:14 +05:30
sairajzero
471f291848 floCloudAPI v2.4.4: edit comment feature
- senders can now edit the comment of data they sent (by re-signing the new data)
2023-07-26 03:45:35 +05:30
sairajzero
904deaa63c floBlockchainAPI v3.0.1b - bug fix
- Fixed: vin address at .addresses[0] not .scriptSig.addresses[0]
2023-07-05 13:48:38 +05:30
sairajzero
e120b657da floBlockchainAPI v3.0.1a
- Fixed: readTx and all its dependencies not having txs property when address (floID) doesnt have any tx
2023-07-05 13:35:07 +05:30
sairajzero
5398fcbbbc floBlockchainAPI v3.0.1
- Fixed readTxs: not working
- Removed fromBlock and toBlock options from readTxs

- Fixed: chain querying to blockbook format for readAllTxs, readData, getLatestData

- Changed `mempool` option to `confirmed` option (for blockbook API)
- Removed `before` options from respective fns
2023-07-05 05:14:44 +05:30
sairajzero
d15af05764 floBlockchainAPI v3.0.0: BlockBook API
- Converting API calls to FLO blockbook API
- Changes and fixes required for the same
2023-07-04 00:20:24 +05:30
sairajzero
49d5c2411c btcOperator v1.1.3b: fix
- Fixed: fee increment minimum value
Minimum fee increment should be atleast MIN_FEE_UPDATE (219 satoshi)
2023-06-01 21:09:50 +05:30
sairajzero
365d3d4503 btcOperator v1.1.3a: bug fix
- Fixed: minor syntax errors and typos
- Fixed: createTransaction not giving correct fee and output value when dust is ignored
2023-06-01 21:06:04 +05:30
sairajzero
34c30aea05 btcOperator v1.1.3: increase fee
- editFee: increase the fee for the given transaction.
can pass either tx hex directly to edit it, or pass txid of unconfirmed tx to edit it for rebroadcasting

- transaction outputs will be removed if the value is less than DUST amount (546 satoshi)
2023-06-01 20:03:42 +05:30
Sai Raj
daeef19d5a
Merge pull request #10 from ranchimall/master 2023-05-19 01:01:09 +05:30
Sai Raj
3a00221587
Merge branch 'sairajzero:master' into master 2023-05-19 00:59:49 +05:30
sairajzero
92303cbc47 floBlockchainAPI v2.5.6b: bug fix 2023-05-13 18:18:23 +05:30
Sai Raj
0eb4215f23
Merge branch 'sairajzero:master' into master 2023-05-10 02:13:33 +05:30
sairajzero
d777303097 floTokenAPI v1.0.4a
- fixed: typo in bulkTransferTokens
2023-05-10 02:01:47 +05:30
sairajzero
94fb4f4849 compactIDB v2.1.2
- Fixed: options.limit not working in searchData
- Added option reverse to searchData (if set to true, the ordering will be reversed
2023-05-10 01:50:46 +05:30
sairajzero
1390c91907 floBlockchainAPI v2.5.6a
- minor bug fix: default value for options missing in readAllTxs fn
- removed flosight.duckdns.org from apiURL
2023-05-10 01:46:36 +05:30
sairajzero
46ae8581e4 floDapps v2.4.0
- Update for new flosight version
- requires floBlockchainAPI >= v2.5.6
- requires floCloudAPI >= v2.4.3a

- lastTx now stores the lastItem property (ie, txid)
- backward-support: maintaining support for idb data (lastTx) from old floDapps module to be converted to this version
2023-05-04 17:09:02 +05:30
sairajzero
9e10a02dc8 floBlockchainAPI v2.5.6
- All search query (get method) now uses URLSearchParams to construct the string
- renamed all lastKey property to lastItem to match flosight response
2023-05-04 17:04:55 +05:30
sairajzero
704def4db1 floCloudAPI v2.4.3a
- Added property: SNStorageName (string content "SuperNodeStorage"
2023-05-04 16:59:10 +05:30
sairajzero
7e618c98f2 floTokenAPI v1.0.4: Added bunkTransferTokens
- Added bunkTransferTokens: bulk transfer tokens to multiple receivers
- usage: floWebWallet.bunkTransferTokens(sender, privKey, token, receivers)
sender: floID of sender
privKey: private key of sender
token: token to send
receivers: an object representing receiver floID and token amount
ie, {receiverID1: amount1, receiverID2: amount2....}
eg: {Fxyz: 123, Fpqr:321, Fabc: 987}
2023-05-02 22:15:54 +05:30
sairajzero
a762f4441b floBlockchainAPI v2.5.5a: bug fix 2023-05-02 21:47:48 +05:30
sairajzero
b09c88f0df floDapps v2.3.5
- Added get property trustedIDs to floGlobals
ie, floGlobals.trustedIDs will give the list of trustedIDs for the app
2023-05-01 20:44:44 +05:30
sairajzero
8afde73e78 floBlockchainAPI v2.5.5: adding getLatestData
- Added getLatestData: Get the latest flo Data that match the caseFn from txs of given Address
2023-04-29 05:29:39 +05:30
sairajzero
fe33250b47 floBlockchainAPI v2.5.4
- Adding support for 'before' and 'latest' option in readTxs and its derivatives
2023-04-29 04:36:56 +05:30
sairajzero
7bd528d55e floBlockchainAPI v2.5.3
- Added waitForConfirmation: Wait for the given txid to get confirmation in blockchain
2023-04-29 03:22:38 +05:30
sairajzero
456e9097de floBlockchainAPI v2.5.2: Added splitUTXOs
- splitUTXOs: split sufficient UTXOs of a given floID for a parallel sending
2023-04-29 03:03:45 +05:30
Sai Raj
3bb03efa88
Merge pull request #20 from sairajzero/master
Pull updates from master
2023-04-15 00:53:18 +05:30
sairajzero
e37e808a98 floBlockchainAPI v2.5.1: updated readData
- uses readAllTxs to chain query the txs from flosight
- keeping ignoreOld option to support backward compatibility, but resolved value no longer have totalTxs.
- new option 'after': fetch after the given txid
- new option 'mempool': query mempool tx or not (options same as readAllTx, DEFAULT=false: ignore unconfirmed tx)
- the resolved value have the following structure based on options.tx
true: {lastKey, items}
false (DEFAULT}: {lastKey, data}
- Optimized the query and filter process
2023-04-15 00:43:45 +05:30
sairajzero
a296a5c986 floBlockchainAPI v2.5.0
1. Updated readTxs: from, to parameter are replaced with options parameter.
'options' available
- after: query after the given tx
- from/to: query from and to (deprecated, support for backward compatibility)
- mempool: query mempool values (true, false, only)

2. Updated readAllTxs: chain queries the data from flosight when address have too many tx
2023-04-14 03:52:39 +05:30
sairajzero
ca0d8c68f3 floBlockchainAPI v2.4.6
Improved utxo and unconfirmed-spent api fetch in send/create tx
2023-04-14 03:36:23 +05:30
sairajzero
9c0730e051 floBlockchainAPI v2.4.5: getBalance improvements
- Updated getBalance: now chain queries for addr with more than 1000 tx to get the net balance.
- (optional): can use 'after' parameter to get the balance(update) after a given txid
2023-04-13 03:15:26 +05:30
sairaj mote
002a4d4dff grammatical error fix 2023-03-23 18:48:46 +05:30
sairajzero
3643f310c2 floDapps v2.3.4: startUpOptions
Added startUpOptions
- cloud: (DEFAULT=true). set to false for app that doesnt require floCloudAPI module
- app_config: (DEFAULT=true). set to false for app that doesnt require configs from adminID (via blockchain)
2023-03-20 20:10:52 +05:30
sairajzero
0ab284b41b floBlockchainAPI v2.4.4
- Updated writeDataMultiple: use options (bool: preserveRatio, float: sendAmt)
- Updated default values to reduce flo spent during tx
2023-03-20 19:44:52 +05:30
sairajzero
b16473f982 floCrypto v2.3.6
- Added hashID(str): returns a floID based on the hash of given string
- Added rawToFloID(raw_bytes): return floID from the given raw_bytes (20 bytes)
2023-03-08 03:13:36 +05:30
Sai Raj
37408f3983
Merge pull request #19 from sairajzero/master
lib v1.4.2b: bug fix
2023-02-25 15:03:50 +05:30
sairajzero
2e24f024d1 lib v1.4.2b: bug fix
- Fixed: FLO multisig signing not working when redeemscript length is large
2023-02-25 15:01:13 +05:30
Sai Raj
948a58c901
Merge pull request #18 from sairajzero/master
testnet fixes
2023-02-24 18:51:13 +05:30
sairajzero
1a8e1abe45 Update index.html
Only for Testing purpose
- Added quick param: pass quick in url query to ignore onLoadStartup
- Added testnet param: pass testnet in url query to change blockchain to FLO_TEST (testnet FLO)
2023-02-24 18:49:22 +05:30
sairajzero
bf9e018883 lib v1.4.2a and floCrypto v2.3.5a
lib v1.4.2a
- Fixed multisig for testnet

floCrypto v2.3.5a
- Fixed: getMultisigAddress accepting requiredSignatures value greater than pubkeys array length (now it will return null instead)
2023-02-24 18:34:49 +05:30
Sai Raj
009b7e1a0d
Merge branch 'sairajzero:master' into master 2023-02-24 02:14:37 +05:30
Sai Raj
476b95c679
Create LICENCE 2023-02-24 02:12:46 +05:30
Sai Raj
ce2a0daaa5
Merge pull request #17 from sairajzero/master 2023-02-24 02:07:53 +05:30
sairajzero
9db998c176 floBlockchainAPI v2.4.3: utility fns
- Added parseTransaction: parse the given txhex
- Added Sat_to_FLO and FLO_to_Sat to .util
2023-02-24 00:46:00 +05:30
sairajzero
7b298ceb50 floBlockchainAPI v2.4.2: tx utility fns
- Added checkSigned: check if the tx is signed or not (if optional args is passed as false, returns a detailed array indicating if each input is signed or not)
- Added checkIfSameTx: check if the passed 2 tx are same or not (ie, input, output and flodata are same or not)
- Added transactionID: returns the transaction id of the tx hex
2023-02-24 00:05:18 +05:30
sairajzero
daec31c1ac btcOperator v1.1.2a: bug fix
- Fixed: checkIfSameTx loop in comparing outputs using incorrect length
2023-02-23 23:24:55 +05:30
sairajzero
09e306d312 floBlockchainAPI v2.4.1: Improvements and signTx
- Added createTx: create unsigned tx (resolves tx-hex )
- Updated createMultisigTx: resolve tx-hex instead of tx object
- Added signTx: (synchronized fn) sign the given tx (hex or object) with given private key and returns the signed txhex
2023-02-23 23:07:54 +05:30
sairajzero
24415b835c lib v1.4.2: (flo) bitjs.transaction deserialize
- pass tx_data (hex or byte array) to bitjs.transaction to deserialize the transaction
ie, `tx = bitjs.transaction(tx_data)`
- invoking without any parameter `bitjs.transaction()` will create an empty tx as before (no change)
2023-02-23 22:49:32 +05:30
sairajzero
e4fd63912c floCrypto v2.3.5: multisig address utils
- Added toMultisigFloID: converts given multisig address to FLO multisig ID

- Improved verifyPubKey: also support verifying multisig redeemScript for the multisig address

- Improved isSameAddr: supports multisig bech32
- Improved decodeAddress: supports multisig bech32
2023-02-23 02:49:42 +05:30
sairajzero
070d2198ba btcOperator v1.1.2: multisig utils
- Added decodeRedeemScript: decodes the given redeemScript and return {address, pubKeys, required}. (optional bech32; default=true. if true return bech32 address. if false returns legacy multisig address)

- Added convert.multisig2multisig: convert from one multisig address to another. (optional target_version; default=btc-multisig-version)
- Added convert.bech2multisig: converts multisig bech32 address to multisig address. (optional target_version; default=btc-multisig-version)

- Added encodeLegacy, decodeLegacy, encodeBech32, decodeBech32 to util
2023-02-23 02:13:31 +05:30
sairajzero
6288dc82d9 lib v1.4.1b: Minor fixes in coinjs multisig-addr
- pubkeys2MultisigAddressBech32: returns another value 'scripthash'
- Fixed multisigBech32Address: returning scripthash as redeemScript. (now returns both scripthash and redeemScript
2023-02-23 02:04:16 +05:30
Sai Raj
6c8fe10453
Merge pull request #16 from sairajzero/master
support for FLO multisig address and transactions
2023-02-20 17:11:38 +05:30
sairajzero
9132d169d2 lib v1.4.1a: bug fix
- Fixed: bitjs.btrx.decodeRedeemScript not throwing Invalid RedeemScript error
2023-02-20 05:20:04 +05:30
sairajzero
a102f5225b floCrypto v2.3.4a: bug fix
- Fixed: verifyPrivKey incorrectly returning false when pubkey is lowercase. (pubkey hex is case-insensitive)
2023-02-20 05:02:43 +05:30
sairajzero
2e0846edc4 floBlockchainAPI v2.4.0: multisig tx
Changes:
- multisig addresses are accepted as receiver address in all send-tx fns
- multisig addresses are accepted in query fns
- sendTx: sender can be regular address only

Added 3 multisig transaction fns:
- createMultisigTx: creates unsigned tx for multisig sender
- sendMultisigTx: create signed multisig tx and broadcast it
- writeMultisigData: same as writeData(), but for multisig
2023-02-20 04:59:23 +05:30
sairajzero
dac87a8595 floCrypto v2.3.4: multisig address
- Added getMultisigAddress(pubkeys, required_sigs): returns a multisig address from given pubkey list and required signatures
- Added decodeRedeemScript (redeemScript): decodes the given redeemscript (of multisig)
- Updated validateFloID: now validates FLO regular and multisig address. optional argument regularOnly(default=false) if true validates only FLO regular address.
2023-02-20 03:10:15 +05:30
sairajzero
0031761a06 lib v1.4.1: Bitcoin.Address: FLO-multisig support
- Bitcoin.Address now supports multisg version
- new Bitcoin.Address (hex) will now assign the respective version when its regular or multisig address

- Added constants: Bitcoin.Address
  .standardVersion= 0x23
  .multisigVersion = 0x5e
  .testnetVersion = 0x73
2023-02-20 03:09:46 +05:30
sairajzero
1f670be893 lib v1.4.0: Support for FLO multisig address 2023-02-19 17:43:07 +05:30
sairajzero
7d31dec230 lib v1.3.3: Improvements to bitjs for flo
- Updated tx.addflodata: will now check if the data is valid or not
- Optimized tx.serialize: changed complex code for appending flodata to a simple one
- Added bitjs.strToBytes: converts string (ascii) to strToBytes
- Added bitjs.pubkeydecompress: decompresses the public key
- Added bitjs.verifySignature: verify the signature (of hash and pubkey)
2023-02-19 17:39:19 +05:30
Sai Raj
837f4b4a6f
Merge pull request #15 from sairajzero/master 2023-02-08 19:23:28 +05:30
sairajzero
ca0d557d94 btcOperator v1.1.1: getTx.hex
- Added getTx.hex(txid): resolves the tx hex of the txid
- Update: api responses are checked for response.ok
- Fixed: getTx giving incorrect confirmation value for unconfirmed tx
2023-01-31 02:13:37 +05:30
sairajzero
c2a94e6246 btcOperator v1.1.0: updating API provider
updating API link to blockchain.info
2023-01-31 01:21:48 +05:30
sairajzero
c2b836c069 Updating floSight URLs 2022-12-23 22:48:52 +05:30
sairajzero
cc4380d623 floDapps v2.3.3
- Added support: update cloud node URL
- Added support: trusted IDs (configured by adminID)
- Trusted IDs have permission to edit tag data
2022-12-18 03:44:04 +05:30
sairajzero
ea4d1f67d6 bug fix: btcOperator v1.0.14b (ignore dust-change) 2022-12-16 20:47:21 +05:30
sairajzero
a71c851cc2 btcOperator v1.0.14a
- Added multisig-bech32 to auto-fee calc
- Fixed: createMultiSigTx not working with bech32 sender
- Fixed: checkSigned for multisig-bech32
2022-11-27 02:47:02 +05:30
sairajzero
aebda7ec80 btcOperator v1.0.14: multisig-bech32 (p2wsh) 2022-11-24 03:25:52 +05:30
sairajzero
7a6a02f2b6 Update lib.js
- support for multisig-bech32 (p2wsh)
2022-11-24 03:25:35 +05:30
sairajzero
b7254bc3ac btcOperator v1.0.13c bug fix
Fixed: tx outputs with 0 value (such as change amount) not removed
2022-11-20 19:46:22 +05:30
sairajzero
006ff9f0a9 btcOperator v1.0.13b: Fee from receiver
- Added: fee_from_receiver option to create transaction.
- change_address and fee_from_receiver are passed as options for createTx, createSignedTx, sendTx and createMultiSigTx
- Fixed: getTx not rejecting properly when txid is invalid
2022-11-18 17:49:03 +05:30
sairajzero
663dfe2d54 floCloudAPI v2.4.3
- Fixed a rare bug where array has empty value
- now `null` is supported by objectData update (ie, objects can now have null as value)
2022-11-06 04:27:24 +05:30
sairajzero
bffb130bd4 floBlockchainAPI v2.3.3d
- renamed sender and receiver options to senders and receivers (bug fix)
2022-10-26 20:17:48 +05:30
sairajzero
d21db616da btcOperator v1.0.12
- Changed broadcastTx to coinb API to eliminate $.ajax requirement
- Set default value for fee=null (automatic fee) to createSignedTx and createMultiSigTx
2022-10-24 15:39:54 +05:30
sairajzero
87f0260fe4 btcOperator v1.0.11
- Added createSignedTx: creates a signed tx (same as sendTx, but doesnt broadcast)
- Added transactionID: returns the txid of the tx_hex (or tx-object)
2022-10-18 04:43:53 +05:30
sairajzero
c38b7257cf Minor fixes
floCrypto v2.3.3e
- Adding options args to toFloID function. options object accept std and/or bech properties. if specified only converts if given address are from specified version

floBlockchainAPI v2.3.3c
- Fixed: tx failing due to dust amount in change value
- readData: when passes tx option as true, now also gives senders and receivers (Set)

btcOperator v1.0.10b
- Fixed: minor bugs
2022-10-14 23:11:05 +05:30
sairajzero
7d84ded426 btcOperator v1.0.10a: broadcastTx resolves txid
- When broadcast is successful, txid is resolved
- sendTx also resolves txid due to the above change
2022-09-30 05:11:09 +05:30
sairajzero
084ffe10e2 btcOperator v1.0.10: parseTransaction
- Added parseTransaction: parse the given transaction
Parameters:
tx : transaction hex (or tx object from coinjs.transaction)
Resolves: parsed object
2022-09-11 22:36:50 +05:30
sairajzero
39d34f6e8c floCloudAPI v2.4.2e
- Adding generalDataset to floGlobals: returns the filtered general data set. (Parameters type and options)
2022-09-04 04:09:27 +05:30
sairajzero
04aad28600 Improved auto-fee calc
- createTx and createMultiSigTx will now resolve an object with tx_hex and other values (amount, size, etc)
2022-08-25 03:04:56 +05:30
sairajzero
77ff76d787 Replacing Math.random with securedMathRandom 2022-08-22 22:06:14 +05:30
sairajzero
7b5a5e6bfe Bug fixes and improvements
floCrypto v2.3.3b
floCloudAPI v2.4.2d
floDapps v2.3.2d
2022-08-21 21:14:29 +05:30
sairajzero
67386fd69f Update btcOperator.js
- Added checkIfSameTx(tx1, tx2): checks if the transactions is of the same
2022-08-16 20:39:49 +05:30
sairajzero
dc19a4e565 Bug fixes: floBlockchainAPI and lib.js 2022-08-16 17:30:46 +05:30
sairajzero
f0a4837222 btcOperator v1.0.7c
- Fixed: createTransaction not returning promise
- Fixed: script not correctly added for multisig inputs
- Fixed: signTx throwing syntax error
- Added checkSigned(tx, bool?): check if the tx is signed or not. (bool is optional, default=true, if true returns a boolean value, else returns a detailed array of input's length)

- Fixed minor bugs in lib.js
2022-08-16 03:24:55 +05:30
sairajzero
6e5cdbd820 floCrypto v2.3.3b
- Added floCrypto.tmpID: generates a random floID with no private key (use this only for one-time id)
- Added toFloID(address): returns equivalent floID (btc-segwit address NOT supported)
- Fixed bug: legacy addresses of length 33 not working in verifyPubKey and validateAddr
2022-08-16 03:11:24 +05:30
sairajzero
36a4067d67 bug fix 2022-08-16 00:37:38 +05:30
sairajzero
45878e6ebe Update floDapps.js
- myFloID, myPubKey, myPrivKey will now return undefined when user not logged in
2022-08-14 18:38:29 +05:30
sairajzero
91e6b82387 btcOperator v1.0.7b
- Added: signTx
- Added multiSigAddress
2022-08-10 02:58:36 +05:30
sairajzero
bc44405102 btcOperator v1.0.7a
- Adding auto fee calculation
- Optimized the code
- Fixed minor bugs
2022-08-09 03:19:56 +05:30
sairajzero
4244774635 btcOperator v1.0.7
- Added createTx: Create an unsigned tx (no privkey required) [segwit sender not supported for now as redeem-script needed]
- Added createMultiSigTx: Create an unsigned tx for multisig address
- Optimised sendTx to reuse code (no functional change)
2022-08-03 03:54:58 +05:30
sairajzero
9dd132b145 Update README.md 2022-07-28 02:45:56 +05:30
sairajzero
4bced4352d floDapps, floCloudAPI: retain blkchain id on login
floDapps login:
- login to btc address when btc private key is entered
- login to flo id when flo (or any other) private key is entered

floCloudAPI user:
- accept userID and private key for user set

Others:
- Fixed: minor bugs
2022-07-28 02:27:07 +05:30
sairajzero
f673d3f37a floCrypto v2.3.3a
floCrypto.verifyPubKey: verify public key of any blockchain address
- Parameters:
1. pubKeyHex - public key hex
2. address - blockchain address

floCrypto.getAddress: get address respective to the blockchain
- Parameters:
1. privateKeyHex - private key (WIF)
2. strict - (optional, default=false) if false returns floID when no match of privatekey prefix found.
2022-07-28 02:11:48 +05:30
sairajzero
2b8e77b5f7 Bug fixes
- floCrypto.validateAddr: bech default=true
- floCloudAPI: proxyID
- commented coinjs api vars
2022-07-26 15:16:22 +05:30
sairajzero
8a211be849 Adding btcOperator module 2022-07-24 20:33:45 +05:30
sairajzero
0526a1f29f floCloudAPI v2.4.2: cross-blockchain receiverID
- Accept any blockchain ID as receiverID and automatically convert it into floID
2022-07-24 02:34:02 +05:30
sairajzero
fa3d790c6c floDapps v2.3.2: lock privkey by default
- Removed the options for launchStartUp
- private-key is on lock mode by default
- Added utility functions that uses private-key without entering password in .user
  .sign(message): signs message
  .decrypt(data): decrypts ciphertext in asymmetric
  .encipher(message): encrypts message in symmetric
  .decipher(data): decrypts ciphertext in symmetric
2022-07-24 02:29:04 +05:30
sairajzero
0a74e393ed floCrypto v2.3.2: validate any address
floCrypto.validateAddr: validate any blockchain address
- Parameters:
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=false) (true: allow any, array: list of versions, value: one version only, false: allow none)

- For validating only floID, use floCrypto.validateFloID(floID)
2022-07-19 21:21:06 +05:30
sairajzero
fcc5f3ce4e Edit library to accept WIF of other versions 2022-07-17 17:04:03 +05:30
sairajzero
b80bd43313 floDapps, floCloudAPI: Secure private key
- Store AES-encrypted private key in memory. (exploring memory of browser will not have raw private key)
- floDapps lock/unlock (in user): locks or unlock the private key (ie, lock= Password required, unlock= password not required in returning private key)
- floDapps.launchStartUp now accepts options object
- to lock the private key on startup, pass {lock_key: true} in options objects of launchStartUp
2022-07-17 17:02:27 +05:30
sairajzero
5ab0bc36f0 Update floCrypto.js
- Removing explicit use of BigInteger in signing(/verify-sign) data
2022-07-16 03:32:44 +05:30
sairajzero
cfc36b80cd Secure private key using capsule 2022-07-16 03:30:03 +05:30
Sai Raj
80b4a8b9fe
Merge pull request #9 from ranchimall/master
Update README.md
2022-07-12 19:13:20 +05:30
Sai Raj
151ea8f751
Update README.md 2022-07-01 21:55:45 +05:30
sairajzero
9435592c8b floTokenAPI v1.0.3b
- floGlobals.currency serves as getter and setter for DEFAULT.currency in tokenAPI module
2022-05-25 02:45:50 +05:30
sairajzero
29e20de25b floTokenAPI v1.0.3a
check if amount is not NaN before sendToken
2022-05-24 19:41:45 +05:30
sairajzero
960aa1f52d floBlockchainAPI v2.3.3
- If API is called when serverList is empty, reset the list and try again once
2022-05-24 02:25:44 +05:30
sairajzero
0683bf7318 floTokenAPI v1.0.3
- getAllTxs: get all transactions of the given floID and token
- util.parseTxData: parse the txData resolved by getTx and getAllTxs
2022-05-24 01:32:23 +05:30
sairajzero
5d7d3bdb53 floBlockchainAPI v2.3.2a
sendTx:
- check total balance before processing utxos
- reject "Insufficient FLO: Some UTXOs are unconfirmed" when balance is there but some utxo are used/unconfimed
2022-05-21 22:19:54 +05:30
sairajzero
8603cde17e Moving all defaults/containers to resp. modules
- Removed default values from floGlobals
- Default values are now in their respective modules
- The following properties of floGlobals can be use to override the default values of respective modules: apiURL, sendAmt, fee, tokenURL, currency, SNStorageID
- Default/current values can be obtained from each module with getter properties
- Some current values can be set with setter properties
- The containers for appObjects, generalData, lastVC are now in floCloudAPI module. Also automatically adds get/set properties for them in floGlobals. Thus can be accessed from floGlobals as before
2022-05-16 21:20:21 +05:30
sairajzero
99368151d5 Adding floTokenAPI
- Token Operator to send/receive tokens via blockchain using token system API

floBlockchainAPI v2.3.1
- Added options parameter to writeData . Options can have the following properties:
 . strict_utxo : passes strict_utxo parameter to sendTx (Default: true)
 . sendAmt : amount of FLO to send (Default: floGlobals.sendAmt)
2022-05-16 18:46:22 +05:30
sairajzero
94f90807f7 Update lib.js
- Added support when floGlobals is undefined
2022-04-03 20:15:58 +05:30
sairajzero
b34ef8ca93 Merge branch 'master' of https://github.com/sairajzero/Standard_Operations 2022-04-03 20:09:06 +05:30
Sai Raj
ca243bf0e4
Update lib.js
Adding default cryptocoin as FLO
2022-04-03 19:40:01 +05:30
sairajzero
4b7f6184a3 Bug fix: floDapps
- Fixed: setCustomPrivKeyInput not working
- Added semicolon at the end of all stdop files
2022-04-02 02:06:44 +05:30
sairajzero
5bb0ee8588 Update lib.js
- Added require-support for BuildKBucket
- BuildKBucket now works with and without `new`
- Renamed instance name to KBucket
2022-03-31 22:25:22 +05:30
Sai Raj
37edbfc461
Merge pull request #8 from sairajzero/split-files
Split files
2022-03-31 21:58:54 +05:30
sairajzero
f27c8c4f03 Bug fixes
- Renamed standard_Operations.html to index.html
- Fixed some minor bugs in floCloudAPI
- Added: floCrypto.validateFloID (alias for floCrypto.validateAddr)
2022-03-31 21:54:51 +05:30
sairajzero
3d4aee6359 Move scripts to respective split files 2022-03-30 03:45:41 +05:30
sairajzero
a939545e31 Bug fix: floCloudAPI v2.1.3a 2022-03-23 18:10:44 +05:30
sairajzero
481ec83f78 floBlockchainAPI v2.2.1a | floCloudAPI v2.1.3
floBlockchainAPI v2.2.1a
- Minor bug fixed: Out of bound index when handling unconfirmed transactions in readData

floCloudAPI v2.1.3
- fetching object-data will now properly check for correct object when updating in local and ignores vc that have been parsed previously
- fetching general-data will now use afterTime instead of lowerVectorClock (thus, any change in tag/note is reflected in it).
  . existing apps will automatically change the stored lastVC of generalData to respective afterTime value
- Fixed: objectifier not called on singleRequests for objectData and generalData
2022-03-08 04:28:40 +05:30
sairajzero
ecb4266a89 floBlockchainAPI_v2.2.1 | floCloudAPI_v2.1.2a
(Bug fixes)
floBlockchainAPI v2.2.1
- Fixed: Unconfirmed transaction (propably dropped) causing new transaction not to be read in readData

floCloudAPI v2.1.2a
- Fixed: singleRequest not returning response correctly
- Fixed: afterTime optional parameter not added in requests
- Fixed: setStatus and requestStatus not working
2022-03-08 02:19:15 +05:30
sairajzero
85baa8e6e1 floCloudAPI v2.1.2
Added support for status feature
1. setStatus(options): set status as online for myFloID
2. requestStatus(trackList, options) : request status of floID(s) in trackList
- Both uses websocket connection
- resolves a reqID (local) that can be used in closeRequest function
- Use callback parameter in options to use the response from cloud

Added support for note feature
1. noteApplicationData(vectorClock, note): add/edit a note for a data in cloud
- Only allows receiverID of the data to add/edit note
- subAdmin can use it as exception, when receiverID is adminID

Other changes
- Fixed issues of tagApplicationData (previously markApplicationData) not working.
- Fixed issue where senderID option not working correctly in requesting data from cloud (new cloud uses 'senderID' parameter for both array of floIDs or a single floID)
*NOTE: change senderIDs to senderID in apps if any*
2022-03-04 06:47:04 +05:30
Sai Raj
f99a97e53e
Merge pull request #7 from ranchimall/master
Update readme
2022-02-27 22:16:48 +05:30
sairajzero
6f47473408 Moved cryptocoin initialization to 'lib' script-tag 2022-02-27 22:10:49 +05:30
sairajzero
3f54c5aa5c Some minor fixes: floCloudAPI v2.1.1
- MarkApplicationData not working correctly when Array is passed
- Marking data should fetch to data's receiverID supernode
- findDiff and mergeDiff moved to bottom of floCloudAPI tag
2022-02-27 21:41:35 +05:30
sairajzero
0eb4d59dc3 Bug fix 2022-02-27 21:34:05 +05:30
sairajzero
10fa130229 Organizing scripts: floBlockchainAPI v2.2.0
- converted XMLHttpRequest to fetch request
2022-02-27 20:38:14 +05:30
sairajzero
3cb6c3f60c Organizing scripts: floCrypto v2.2.0 2022-02-27 20:33:51 +05:30
sairajzero
8094660f53 Organizing scripts: lib v1.2.0 2022-02-27 20:31:15 +05:30
790db7d276
Update README.md 2021-12-31 08:15:10 +05:30
1e26b5b723
Update README.md 2021-12-31 07:53:27 +05:30
e981c26e25
Update README.md 2021-12-29 19:35:46 +05:30
4fec06df0e
Update README.md 2021-12-29 18:37:18 +05:30
4824c416b5
Update README.md 2021-12-29 18:01:05 +05:30
442982f073
Update README.md 2021-12-29 17:58:54 +05:30
b85ea1cbe6
Update README.md 2021-12-29 17:28:52 +05:30
f426875692
Update README.md 2021-12-29 17:27:02 +05:30
ae82e254d2
Incomplete shares bugfix
Bugfix: Clears credentials and reloads if all shares are not present
2021-12-29 16:05:33 +05:30
822b446d18
Update README.md 2021-12-23 13:47:38 +05:30
4d5196bac4
Update README.md 2021-12-22 12:24:04 +05:30
38d1d798d5
Update README.md 2021-12-22 12:23:08 +05:30
f34bcc9374
Update README.md 2021-12-22 12:22:31 +05:30
9678176f3b
Update README.md 2021-12-22 12:08:24 +05:30
f75337c9b0
Update README.md 2021-12-22 12:06:28 +05:30
f38f9fe28d
Update README.md 2021-12-22 12:02:35 +05:30
86458d5d3c
Update README.md 2021-12-22 12:01:39 +05:30
2c5dac54b2
Update README.md 2021-12-22 12:00:46 +05:30
22f6f5ae81
Update README.md 2021-12-21 08:38:32 +05:30
420c3a7164
Update README.md 2021-12-21 08:29:50 +05:30
b66fe52f54
Update README.md 2021-12-21 08:29:05 +05:30
d34143a3dc
Update README.md 2021-12-21 08:21:46 +05:30
13007684f0
Update README.md 2021-12-21 08:12:49 +05:30
85ddbc16a5
Update README.md 2021-12-21 08:07:11 +05:30
4cb88db46f
Update README.md 2021-12-19 18:28:38 +05:30
aa7fce1c02
Update README.md 2021-12-19 18:27:22 +05:30
Sai Raj
0b42046022
Merge branch 'sairajzero:master' into master 2021-11-05 18:18:23 +05:30
sairajzero
6caa0658f6 floCloudAPI v2.1.0a (bugFix)
Fixed bug: UpdateObjectData not working correctly
2021-11-05 18:18:09 +05:30
Sai Raj
b2d77a3c2a
Merge branch 'sairajzero:master' into master 2021-09-25 16:27:30 +05:30
sairajzero
57ef8d14df floBlockchainAPI_v2.1.1a
- formatting the code
2021-09-17 02:46:48 +05:30
sairajzero
5c21a01b68 floBlockchainAPI v2.1.1
- improved sendTx to check if the utxo is already used but unconfirmed (ie, still in mempool) and ignore those utxos.
This allows users to send multiple tx from the same floID without the need to wait for confirmation of prev tx as long as enough utxos are available.

- Optional parameter: strict_utxo (boolean): Default=true (safe mode).
If false, allows unconfirmed UTXOs to be included in the txn.
This will allow users to chain send tx as long as enough balance FLO is available.
WARNING: if one tx in the chain is dropped, all next tx dependent on that tx will be dropped too.

- writeData (as it invokes sendTx) also have the above improvements
2021-09-17 02:25:16 +05:30
sairajzero
f8954a2532 floBlockchainAPI v2.1.0
- readData: option txid changed to tx. now resolves data with txid, time, blockheight along with flo data as an object [if option tx is true].
Note: backward not compactible when txid was passed as option
2021-08-12 21:47:02 +05:30
Sai Raj
09539c1dc6
Merge pull request #14 from sairajzero/master 2021-07-24 04:24:32 +05:30
Sai Raj
4df09c206f
Merge branch 'master' into master 2021-07-24 04:24:02 +05:30
sairajzero
b575432ace floCloudAPI_v2.1.0
- Updating to node version of SuperNodeStorage
2021-07-24 04:09:44 +05:30
sairajzero
df8df5edbc floBlockchainAPI_2.0.1e
- Adding floBlockchainAPI.util.current(): returns the current working flosight link (to be used after at least 1 API request'
2021-06-07 14:27:52 +05:30
sairajzero
25df4bbbc0 bugfix (floBlockchainAPI_v2.0.1d)
floBlockchainAPI_v2.0.1d
- fetch_retry removes incorrect uri when parallel requests are called.
2021-06-06 17:10:55 +05:30
13 changed files with 15341 additions and 9512 deletions

1
.gitignore vendored Normal file
View File

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

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Sai Raj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

260
README.md
View File

@ -9,18 +9,21 @@ 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.
# IMPORTANT
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
FLO distibuted applications use the FLO Blockchain to store core data, and FLO Blockchain Cloud for other data. The data is accessed directly by browser based clients. No other component or servers are needed typically.
Every application has a role called the Master Admin. The clients trust anything that the Master Admin declares in the FLO Blockchain. Usually Master Admin will only declare the FLO IDs that will have operational roles in the application. These FLO IDs are called SubAdmins. The browser based clients trust actions only and only from subAdmins and no one else. That creates the trust model. First trust the Master Admin. Then find out who the master admin has authorized to act as SubAdmins in the FLO blockchain. Then trust the data and actions signed by approved SubAdmins. Since the entire chain of trust is blockchain based, it enables a blockchain driven trust model. As the blockchain data is immutable and permanent, so long as users can trust the Master Admin, blockchain will ensure that the turst is efficiently transmitted.
Every application has a role called the Master Admin. The clients trust anything that the Master Admin declares in the FLO Blockchain. Usually Master Admin will only declare the FLO IDs that will have operational roles in the application. These FLO IDs are called SubAdmins. The browser based clients trust actions only and only from subAdmins and no one else. That creates the trust model. First trust the Master Admin. Then find out who the master admin has authorized to act as SubAdmins in the FLO blockchain. Then trust the data and actions signed by approved SubAdmins. Since the entire chain of trust is blockchain based, it enables a blockchain driven trust model. As the blockchain data is immutable and permanent, so long as users can trust the Master Admin, blockchain will ensure that the trust is efficiently transmitted.
This approach decentralizes the trust process totally and extends the capacity of the blockchain to model almost any server based IT application.
## RanchiMall recommended approach to create a FLO Distributed Application
1. Create Master Admin FLO ID and private key
2. Role Modeling: Create SubAdmins by having Master Admin declare it in the FLO Blockchain, and decide what roles will differnent kind of SubAdmins play
2. Role Modeling: Create SubAdmins by having Master Admin declare it in the FLO Blockchain, and decide what roles will different kind of SubAdmins play
3. Data Modeling: Create Blockchain cloud data formats for your application. Do it twice: One for system trusted users like SubAdmins using Object Data. And again for normal untrusted users using General Data
4. Define core business functionalities of the app, and create Javascript methods to model it
5. Secure the user Private Key (and other sensitive data, if any)
@ -59,6 +62,89 @@ This approach decentralizes the trust process totally and extends the capacity o
* Consumers of data can ask for data by receiver ID, or filter it by application, type, or comment. They can also ask for data for a given type before and after a certain vector clock.
## Common examples in usage of Standard Operations
* Sends "Hello World" message to the cloud as General Data with type1 as `type` with `myFloID` as default sender and `floGlobals.adminID` as receiver. This has vectorClock support, our the client side will automatically synchronise all the relevant data stored in cloud.
```
floCloudAPI.sendGeneralData("Hello World", "type1")
```
* Request general data of type1 type, but filtered by senderIDs being floGlobals.subAdmins
```
floCloudAPI.requestGeneralData("type1", { senderIDs: floGlobals.subAdmins })
```
* Usage of sendGeneralData from RIBC dApp
```
//take name and comments as function parameters, and sendGeneralData of InternRequests type
applyForIntern: function (name, comments = '') {
return floCloudAPI.sendGeneralData([name, comments], "InternRequests")
},
//take updates as function parameter, and return a promise that sends general data of type "InternUpdates"
postInternUpdate: function (updates) {
return new Promise((resolve, reject) => {
floCloudAPI.sendGeneralData(updates, "InternUpdates")
.then(results => resolve(results))
.catch(error => reject(error))
})
applyForTask: function (projectCode, branch, task, comments = '') {
return floCloudAPI.sendGeneralData([projectCode, branch, task, comments], "TaskRequests")
},
setRequestStatus: function (vectorClock, status) {
return floCloudAPI.sendGeneralData({ vectorClock, status }, "RequestStatus")
},
```
* Object Data handling is automated in Standard operations unlike General data. Define, update and reset, these are only object data operations possible, yet they are very powerful in data capabilities.
```
//Initiates "myFirstObject" with {a:1,b:2}, and sends to cloud with myFloID
as default sender and floGlobals.adminID as receiver
floGlobals.appObjects["myFirstObject"] = {a:1,b:2}
floCloudAPI.resetObjectData("myFirstObject")
//Updates the old value of "myFirstObject" with {a:1,b:2}, and sends to cloud with myFloID as default sender
and floGlobals.adminID as receiver. In case of update, only the object diff will be sent
floGlobals.appObjects["myFirstObject"] = {a:1,c:3,d:4}
floCloudAPI.updateObjectData("myFirstObject")
```
* floGlobals usage from RIBC dApp
```
floGlobals.vectorClock
= {RIBC: '1637841611864_FCja6sLv58e3RMy41T5AmWyvXEWesqBCkX'}
floGlobals.generalVC["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"]
= '1640106537305_FSdjJCJdU43a1dyWY6dRES1ekoupEjFPqQ'
floGlobals.vectorClock.RIBC
= '1637841611864_FCja6sLv58e3RMy41T5AmWyvXEWesqBCkX'
floGlobals.generalVC["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"]
= '1640106537305_FSdjJCJdU43a1dyWY6dRES1ekoupEjFPqQ'
floGlobals.generalData["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"][2]
= {sender: 'FPFeL5PXzW9bGosUjQYCxTHSMHidnygvvd',
vectorClock: '1580815876258_FPFeL5PXzW9bGosUjQYCxTHSMHidnygvvd', message: {…}}
floGlobals.generalData["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"][2]["vectorClock"]
= '1580815876258_FPFeL5PXzW9bGosUjQYCxTHSMHidnygvvd'
floGlobals.generalData["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"][2]["message"]["sender"]
= 'FPFeL5PXzW9bGosUjQYCxTHSMHidnygvvd'
floGlobals.generalData["{\"application\":\"RIBC\",\"type\":\"InternUpdates\"}"][808]["message"]["description"]
= 'Working on the flow and structure of Pearl Harbor topic'
floGlobals.appObjects.RIBC.internList.F7HVKrF68Y6YKE9XXpHhAcxt6MwRLcUD67
= 'Salomi Sarkar'
```
# Technical Details of standard operations
@ -122,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
* 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
floCrypto.verifyPrivKey(privateKey, pubKey_floID, *isfloID)
`verifyPrivKey` verify the private-key for the given public-key or flo-ID
@ -130,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)
* 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
floCrypto.validateAddr(floID)
`validateAddr` check if the given Address is valid or not
floCrypto.validateFloID(floID)
`validateFloID` check if the given floID is valid or not
1. floID - flo ID to validate
* 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
floCrypto.encryptData(data, publicKey)
`encryptData` encrypts the given data using public-key
@ -369,12 +477,47 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved
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. FLO Cloud API functions are promisified and resolves the data or status.
#### NEW providing callback as a new option for automatic refresh of data in browser
We have implemented a new feature where Standard Operations page can listen to any new general data or new object data and request for new data will be automatic. As soon as a new data is available in a cloud address, it will be notified to listening clients. This has been achieved by expanding `options` in `requestGeneralData` and `requestObjectData`
A blank callback function can be provided as an option in `requestGeneralData` in which case the `floGlobals.generalData` will be automatically updated with new generalData relevant to that `requestGeneralData` whenever such data is available with the cloud.
A blank callback function when added as an option to `requestObjectData` will automatically update `floGlobals.appObjects` with all refreshes ar available to that `requestObjectData` without calling `requestObjectData` again explicitly.
You can also specify an actual function name in callback, in which case that function will also be executed in addition to automatic data refreshes. This is usually for UI refresh needs. Alternately, one can monitor changes to `floGlobals.generalData` and `floGlobals.appObjects` via an event mechanism, and update UI whenever there are changes, in which case blank callback function is sufficient.
Note: Callbacks have been implemented using persistent listening websockets in both browsers and the cloud. So it will load the cloud with persistent memory requirements. Use it only when it is absolutely needed.
```javascript
//Requesting general data of type1 with calls in two equivalent ways using callback option for automatic refresh
floCloudAPI.requestGeneralData("type1", {callback(){}})
floCloudAPI.requestGeneralData("type1", {callback: _=> null})
//Requesting Object data for article_valuation_individual object using callback option for automatic refresh
floCloudAPI.requestObjectData("article_valuation_individual", {callback: _=> null) })
//They are all equivalent
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback: ()=>{} })
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback(){} })
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback: _=> null })
//Implementation of a callback function that will update the UI and display data in console
floCloudAPI.requestObjectData("article_valuation_individual", {callback: fnToUseTheData} )
fnToUseTheData example:
fnToUseTheData: function (data, error) {
if(!error) {
console.log(data);
//Add Update UI function
}
}
```
#### sendApplicationData
floCloudAPI.sendApplicationData(message, type, options = {})
`sendApplicationData` sends application data to the cloud.
@ -409,9 +552,10 @@ Sends "Hello World" message to the cloud as General Data with type1 as `type` wi
* Resolves: Status (string) | Rejects: error
###### Minimal Example:
```
floCloudAPI.requestGeneralData("type1")
Requests all messages of General Data nature from the cloud with type1 as `type` sent by anyone and `floGlobals.adminID` as default receiverID
```
#### resetObjectData
floCloudAPI.resetObjectData("objectName", options = {})
`resetObjectData` resets the objectData to cloud.
@ -480,12 +624,14 @@ Note: Application Data results are not stored in local IndexedDB by Standard Op
Note: If a blank REQUEST APPLICATION DATA is made, then cloud will give all application data at the admin ID of the application
###### Minimal Example:
```
floCloudAPI.requestApplicationData()
Requests all messages of Apllication Data nature from the cloud with any `type` sent by anyone and `floGlobals.adminID` as receiverID
```
## 4. GENERAL DATA PARAMETERS AND OPTIONS
### SEND GENERAL DATA
### SEND GENERAL DATA PARAMETERS AND OPTIONS
Parameters while sending
* `Message`: Actual Message to be sent
@ -502,7 +648,12 @@ Type is mandatory in SEND GENERAL DATA because without at least one data identif
Application field is used by the Cloud to judge whether this message should be deleted after 7 days, or stored permanently. Developers should be careful not to change value of application field if the default value enables the message to be stored permanently. Currently messages sent from subadmins to any receiverID for applications notified by the cloud in the FLO Blockchain are stored permanently. So do not change application field as a caution. Use the comment field.
### REQUEST GENERAL DATA
###### Minimal Example:
```
floCloudAPI.sendGeneralData("Hello World", "type1")
Sends "Hello World" message to the cloud as General Data with type1 as `type` with `myFloID` as default sender and `floGlobals.adminID` as receiver
```
### REQUEST GENERAL DATA PARAMETERS AND OPTIONS
Parameters while requesting
@ -517,6 +668,8 @@ Parameters while requesting
* `upperVectorClock` - VC till which the data is to be requested
* `atVectorClock` - VC at which the data is to requested
* `mostRecent` - boolean (true: request only the recent data matching the pattern. Just the last one)
* `callback(){}` - will initialize websocket for automatic updates.
* `callback: fnToUseTheData` - will initialize websocket for automatic updates, and will alse execute fnToUseTheData
The results are available in floGlobals.generalData after the request promise has been resolved
@ -528,9 +681,26 @@ If you want to use requests to give results from all types at one go, use Applic
Application field is used by the Cloud to judge whether this message should be deleted after 7 days, or stored permanently. Developers should be careful not to change value of application field if the default value enables the message to be stored permanently. Currently messages sent from subadmins to any receiverID for applications notified by the cloud in the FLO Blockchain are stored permanently. So do not change application field as a caution. Use the comment field.
###### Minimal Example:
```
floCloudAPI.requestGeneralData("type1")
Requests all messages of General Data nature from the cloud with type1 as `type` sent by anyone and `floGlobals.adminID` as default receiverID
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback: ()=>{}})
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback(){}})
floCloudAPI.requestGeneralData("type1", {senderID:[x,y,z], callback: _=> null})
floCloudAPI.requestObjectData("article_valuation_individual", {callback: fnToUseTheData})
fnToUseTheData example:
fnToUseTheData: function (data, error) {
if(!error) console.log(data)
}
```
## 5. OBJECT DATA PARAMETERS AND OPTIONS
### RESET or UPDATE OBJECT DATA
### RESET or UPDATE OBJECT DATA PARAMETERS AND OPTIONS
Parameters while resetting or updating
* `Object Name`: Name of the object with data populated in floGlobals.appObjects[objectName]
@ -545,7 +715,7 @@ Note: Never use senderIDs in RESET and UPDATE. The system automatically picks th
Note: Type field is never used in RESET, UPDATE or REQUEST operations in Object Data. Type field is internally blocked for Object Data.
### REQUEST OBJECT DATA
### REQUEST OBJECT DATA PARAMETERS AND OPTIONS
#### Mandatory
`Object Name`
@ -559,6 +729,8 @@ Note: Type field is never used in RESET, UPDATE or REQUEST operations in Object
* `upperVectorClock` - VC till which the data is to be requested
* `atVectorClock` - VC at which the data is to requested
* `mostRecent` - boolean (true: request only the recent data matching the pattern. Just the last one.)
* `callback(){}` - will initialize websocket for automatic updates.
* `callback: fnToUseTheData` - will initialize websocket for automatic updates, and will alse execute fnToUseTheData
The output is available in floGlobals.appObjects[objectName] after request Object Data promise is resolved
@ -568,7 +740,7 @@ Application field is used by the Cloud to judge whether this message should be d
## 6. APPLICATION DATA PARAMETERS, OPTIONS AND EXPLANATIONS
### SEND APPLICATION DATA
### SEND APPLICATION DATA PARAMETERS AND OPTIONS
Parameters while sending
* `Message`: Actual Message to be sent
@ -585,7 +757,7 @@ Type is mandatory in SEND APPLICATION DATA because without at least one data ide
Application field is used by the Cloud to judge whether this message should be deleted after 7 days, or stored permanently. Developers should be careful not to change value of application field if the default value enables the message to be stored permanently. Currently messages sent from subadmins to any receiverID for applications notified by the cloud in the FLO Blockchain are stored permanently. So do not change application field as a caution. Use the comment field.
### REQUEST APPLICATION DATA
### REQUEST APPLICATION DATA PARAMETERS AND OPTIONS
#### Mandatory Parameters while requesting
None
@ -600,6 +772,8 @@ None
* `upperVectorClock` - VC till which the data is to be requested
* `atVectorClock` - VC at which the data is to requested
* `mostRecent` - boolean (true: request only the recent data matching the pattern. Just the last one)
* `callback(){}` - will initialize websocket for automatic updates.
* `callback: fnToUseTheData` - will initialize websocket for automatic updates, and will alse execute fnToUseTheData
Note: Results will be available in promise.resolve(requestApplicationData(..)), and user will have to handle the response himself.
@ -615,39 +789,6 @@ Note: Application field is used by the Cloud to judge whether this message shoul
# Examples for FLO Cloud data operations
# Basic Concepts of RanchiMall Blockchain Cloud for developers
* RanchiMall blockchain cloud is a service to provide a set of decentralized servers that will provide data storage to users. These decentralized servers are listed in the FLO Blockchain under an authorized FLO address. This gives the assurance to the users that those data servers can be trusted.
* The user can store his data in these servers freely. RanchiMall cloud service also provides facilities to store Javascript Objects directly. Storage in JavaScript object form makes it easier for JavaScript based applications to process the data.
* The cloud servers provide automatic backup and restoration for each other.
* Using the blockchain based data cloud, users will not need any database to store their data. The cloud will provide data storage, backup and restoration facilites.
* RanchiMall Blockchain Cloud is a password less system. Every sender has to digitally sign his data with the private key associated with its FLO ID. The cloud verifies the digital signature of the sender before storing sender data.
* Since the blockchain cloud is an ensemble of servers, we need a method to pick the right server to store the data. For this purpose, we find the a server closest to receipient of the data according to an artificial distance measure.
* Every client of the cloud can automatically compute the correct server where the data needs to be stored, and sends the message directly to that server.
* Every client of the cloud is the consumer of the data. It can ask the cloud for data sorted by a recipient, or by various options we provide like name of application, type of data, or by specific comments. The client can can also ask for all data after or before a certain point of time using a concept called Vector Clock.
* The cloud attaches the exact epoch time to any message given by a sender, and using the combination of epoch time, and sender FLO ID, the vector clock is constructed.
* The two basic forms in which users can submit data to the cloud are `General Data` and `Object Data`. `General Data` is freely flowing data, and `Object Data` is stored directly as pure Javascript Object.
* Both `General Data` and `Object Data` have been derived from `Application Data` which is the basic system data type in the cloud. Normal users will never need to use Application Data. But for documentation purposes, we are providing the technical details for Application Data as well.
- `General Data` = `Application Data` + User level Vector Clock filtering facilities
- `Object Data` = `Application Data` + Message field modified to handle Javascript Object + User level Vector Clock filtering facilities
* Consistent with blockchain data principles, RanchiMall blockchain cloud will also provide data to everyone who asks for it. So sensitive data should be encrypted using the receiver's public key using Crypto functions of FLO Standard Operations.
* Consumers of data can ask for data by receiver ID, or filter it by application, type, or comment. They can also ask for data for a given type before and after a certain vector clock.
## 1. Data fields stored in each of decentralised servers
vectorClock, senderID, receiverID, pubKey, message, sign, application, type, comment
@ -698,7 +839,7 @@ Application data system is original data field system that we created for our c
Even ObjectData was created on top of Application Data. ObjectData is a special construction provided by framework so that Javacript objects can directly be stored and retrieved from the cloud
#### Vector Clock issues in Application Data
Application data supports Vector clock in REQUEST option, but it is not mandatory. Since Application Data system has no mandatory vector clock requirement in REQUEST OPTIONS, it will always give the entire data set stored in the cloud since start if invoked without vectorClock, and user will have to custom handle the request output himself at client end. Our client side framework will not store it for the user.
Application data supports Vector clock in REQUEST option, but it is not mandatory. Since Application Data system has no mandatory vector clock requirement in REQUEST OPTIONS, it will always give the entire data set stored in the cloud since start if invoked without vectorClock, and user will have to custom handle the request output himself at client end. Our client side framework will not store it for the user. However the application data request invoked with vectorClock, then our cloud will give data after the vectorClock
Usually ObjectData and GeneralData systems will support most of user needs. But for cases when the user wants the entire cloud data set, and no client side framework handling, he should use ApplicationData. Although Application Data system supports lower vectorClock, upper vectorClock, at vectorClock and mostRecentvectorClock as a REQUEST option, but the processing of these have to be done by user. Unlike in case of ObjectData and GeneralData, the output of Request Application Data is not available in floGlobals.
@ -769,6 +910,23 @@ Sample startup is defined in onLoadStartUp function
2. fn - body of the function
Note: startup funtions are called in parallel. Therefore only add custom startup funtion only if it can run in parallel with other startup functions. (default startup funtions are read supernode list and subAdmin list from blockchain API, load data from indexedDB, get login credentials)
```javascript
//Executes automatically on startup before anything else executes
floDapps.addStartUpFunction("myFirstFunction",
function (){
return new Promise (
(resolve,reject)=>{
console.log("First function Executed before everything else");
resolve("My First Function executed")
});
});
// Manual execution on console
floDapps.util.startUpFunctions.myFirstFunction();
```
Philosophy: Suppose you want a function that must run before any of standard operation function runs. Then you need to first create a promisified version of that function
### Advanced Dapp functions usually not needed for users
#### setAppObjectStores
@ -776,12 +934,6 @@ Note: startup funtions are called in parallel. Therefore only add custom startup
`setAppObjectStores` adds additionals objectstores for the app
1. appObs - additionals objects for the app
#### objectDataMapper
floDapps.objectDataMapper(object, path, data)
`objectDataMapper` maps the object and data via path
1. object - object to be mapped
2. path - end path for the data holder
3. data - data to be pushed in map

1135
btcOperator.js Normal file

File diff suppressed because it is too large Load Diff

257
compactIDB.js Normal file
View File

@ -0,0 +1,257 @@
(function (EXPORTS) { //compactIDB v2.1.2
/* Compact IndexedDB operations */
'use strict';
const compactIDB = EXPORTS;
var defaultDB;
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
const IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
const IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
if (!indexedDB) {
console.error("Your browser doesn't support a stable version of IndexedDB.");
return;
}
compactIDB.setDefaultDB = dbName => defaultDB = dbName;
Object.defineProperty(compactIDB, 'default', {
get: () => defaultDB,
set: dbName => defaultDB = dbName
});
function getDBversion(dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
resolve(db.version)
db.close()
}).catch(error => reject(error))
})
}
function upgradeDB(dbName, createList = null, deleteList = null) {
return new Promise((resolve, reject) => {
getDBversion(dbName).then(version => {
var idb = indexedDB.open(dbName, version + 1);
idb.onerror = (event) => reject("Error in opening IndexedDB");
idb.onupgradeneeded = (event) => {
let db = event.target.result;
if (createList instanceof Object) {
if (Array.isArray(createList)) {
let tmp = {}
createList.forEach(o => tmp[o] = {})
createList = tmp
}
for (let o in createList) {
let obs = db.createObjectStore(o, createList[o].options || {});
if (createList[o].indexes instanceof Object)
for (let i in createList[o].indexes)
obs.createIndex(i, i, createList[o].indexes || {});
}
}
if (Array.isArray(deleteList))
deleteList.forEach(o => db.deleteObjectStore(o));
resolve('Database upgraded')
}
idb.onsuccess = (event) => event.target.result.close();
}).catch(error => reject(error))
})
}
compactIDB.initDB = function (dbName, objectStores = {}) {
return new Promise((resolve, reject) => {
if (!(objectStores instanceof Object))
return reject('ObjectStores must be an object or array')
defaultDB = defaultDB || dbName;
var idb = indexedDB.open(dbName);
idb.onerror = (event) => reject("Error in opening IndexedDB");
idb.onsuccess = (event) => {
var db = event.target.result;
let cList = Object.values(db.objectStoreNames);
var obs = {},
a_obs = {},
d_obs = [];
if (!Array.isArray(objectStores))
var obs = objectStores
else
objectStores.forEach(o => obs[o] = {})
let nList = Object.keys(obs)
for (let o of nList)
if (!cList.includes(o))
a_obs[o] = obs[o]
for (let o of cList)
if (!nList.includes(o))
d_obs.push(o)
if (!Object.keys(a_obs).length && !d_obs.length)
resolve("Initiated IndexedDB");
else
upgradeDB(dbName, a_obs, d_obs)
.then(result => resolve(result))
.catch(error => reject(error))
db.close();
}
});
}
const openDB = compactIDB.openDB = function (dbName = defaultDB) {
return new Promise((resolve, reject) => {
var idb = indexedDB.open(dbName);
idb.onerror = (event) => reject("Error in opening IndexedDB");
idb.onupgradeneeded = (event) => {
event.target.result.close();
deleteDB(dbName).then(_ => null).catch(_ => null).finally(_ => reject("Datebase not found"))
}
idb.onsuccess = (event) => resolve(event.target.result);
});
}
const deleteDB = compactIDB.deleteDB = function (dbName = defaultDB) {
return new Promise((resolve, reject) => {
var deleteReq = indexedDB.deleteDatabase(dbName);;
deleteReq.onerror = (event) => reject("Error deleting database!");
deleteReq.onsuccess = (event) => resolve("Database deleted successfully");
});
}
compactIDB.writeData = function (obsName, data, key = false, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
let writeReq = (key ? obs.put(data, key) : obs.put(data));
writeReq.onsuccess = (evt) => resolve(`Write data Successful`);
writeReq.onerror = (evt) => reject(
`Write data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
);
db.close();
}).catch(error => reject(error));
});
}
compactIDB.addData = function (obsName, data, key = false, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
let addReq = (key ? obs.add(data, key) : obs.add(data));
addReq.onsuccess = (evt) => resolve(`Add data successful`);
addReq.onerror = (evt) => reject(
`Add data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
);
db.close();
}).catch(error => reject(error));
});
}
compactIDB.removeData = function (obsName, key, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
let delReq = obs.delete(key);
delReq.onsuccess = (evt) => resolve(`Removed Data ${key}`);
delReq.onerror = (evt) => reject(
`Remove data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
);
db.close();
}).catch(error => reject(error));
});
}
compactIDB.clearData = function (obsName, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readwrite").objectStore(obsName);
let clearReq = obs.clear();
clearReq.onsuccess = (evt) => resolve(`Clear data Successful`);
clearReq.onerror = (evt) => reject(`Clear data Unsuccessful`);
db.close();
}).catch(error => reject(error));
});
}
compactIDB.readData = function (obsName, key, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
let getReq = obs.get(key);
getReq.onsuccess = (evt) => resolve(evt.target.result);
getReq.onerror = (evt) => reject(
`Read data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
);
db.close();
}).catch(error => reject(error));
});
}
compactIDB.readAllData = function (obsName, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
var tmpResult = {}
let curReq = obs.openCursor();
curReq.onsuccess = (evt) => {
var cursor = evt.target.result;
if (cursor) {
tmpResult[cursor.primaryKey] = cursor.value;
cursor.continue();
} else
resolve(tmpResult);
}
curReq.onerror = (evt) => reject(
`Read-All data unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`
);
db.close();
}).catch(error => reject(error));
});
}
/* compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
var filteredResult = {}
let keyRange;
if(options.lowerKey!==null && options.upperKey!==null)
keyRange = IDBKeyRange.bound(options.lowerKey, options.upperKey);
else if(options.lowerKey!==null)
keyRange = IDBKeyRange.lowerBound(options.lowerKey);
else if (options.upperKey!==null)
keyRange = IDBKeyRange.upperBound(options.upperBound);
else if (options.atKey)
let curReq = obs.openCursor(keyRange, )
}).catch(error => reject(error))
})
}*/
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
options.lowerKey = options.atKey || options.lowerKey || 0
options.upperKey = options.atKey || options.upperKey || false
options.patternEval = options.patternEval || ((k, v) => true);
options.limit = options.limit || false;
options.reverse = options.reverse || false;
options.lastOnly = options.lastOnly || false
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
var obs = db.transaction(obsName, "readonly").objectStore(obsName);
var filteredResult = {}
let curReq = obs.openCursor(
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
options.lastOnly || options.reverse ? "prev" : "next");
curReq.onsuccess = (evt) => {
var cursor = evt.target.result;
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
return resolve(filteredResult); //reached end of key list or limit reached
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
filteredResult[cursor.primaryKey] = cursor.value;
options.lastOnly ? resolve(filteredResult) : cursor.continue();
} else
cursor.continue();
}
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
db.close();
}).catch(error => reject(error));
});
}
})(window.compactIDB = {});

1044
floBlockchainAPI.js Normal file

File diff suppressed because it is too large Load Diff

1106
floCloudAPI.js Normal file

File diff suppressed because it is too large Load Diff

530
floCrypto.js Normal file
View File

@ -0,0 +1,530 @@
(function (EXPORTS) { //floCrypto v2.3.6a
/* FLO Crypto Operators */
'use strict';
const floCrypto = EXPORTS;
const p = BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16);
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
const ascii_alternatives = ` '\n '\n“ "\n” "\n --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
coinjs.compressed = true; //defaulting coinjs compressed to true;
function calculateY(x) {
let exp = exponent1();
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
return x.modPow(BigInteger("3"), p).add(BigInteger("7")).mod(p).modPow(exp, p)
}
function getUncompressedPublicKey(compressedPublicKey) {
// Fetch x from compressedPublicKey
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
const prefix = pubKeyBytes.shift() // remove prefix
let prefix_modulus = prefix % 2;
pubKeyBytes.unshift(0) // add prefix 0
let x = new BigInteger(pubKeyBytes)
let xDecimalValue = x.toString()
// Fetch y
let y = calculateY(x);
let yDecimalValue = y.toString();
// verify y value
let resultBigInt = y.mod(BigInteger("2"));
let check = resultBigInt.toString() % 2;
if (prefix_modulus !== check)
yDecimalValue = y.negate().mod(p).toString();
return {
x: xDecimalValue,
y: yDecimalValue
};
}
function getSenderPublicKeyString() {
let privateKey = ellipticCurveEncryption.senderRandom();
var senderPublicKeyString = ellipticCurveEncryption.senderPublicString(privateKey);
return {
privateKey: privateKey,
senderPublicKeyString: senderPublicKeyString
}
}
function deriveSharedKeySender(receiverPublicKeyHex, senderPrivateKey) {
let receiverPublicKeyString = getUncompressedPublicKey(receiverPublicKeyHex);
var senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
receiverPublicKeyString.x, receiverPublicKeyString.y, senderPrivateKey);
return senderDerivedKey;
}
function deriveSharedKeyReceiver(senderPublicKeyString, receiverPrivateKey) {
return ellipticCurveEncryption.receiverSharedKeyDerivation(
senderPublicKeyString.XValuePublicString, senderPublicKeyString.YValuePublicString, receiverPrivateKey);
}
function getReceiverPublicKeyString(privateKey) {
return ellipticCurveEncryption.receiverPublicString(privateKey);
}
function wifToDecimal(pk_wif, isPubKeyCompressed = false) {
let pk = Bitcoin.Base58.decode(pk_wif)
pk.shift()
pk.splice(-4, 4)
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
if (isPubKeyCompressed == true) pk.pop()
pk.unshift(0)
let privateKeyDecimal = BigInteger(pk).toString()
let privateKeyHex = Crypto.util.bytesToHex(pk)
return {
privateKeyDecimal: privateKeyDecimal,
privateKeyHex: privateKeyHex
}
}
//generate a random Interger within range
floCrypto.randInt = function (min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
}
//generate a random String within length (options : alphaNumeric chars only)
floCrypto.randString = function (length, alphaNumeric = true) {
var result = '';
var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' :
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():';
for (var i = 0; i < length; i++)
result += characters.charAt(Math.floor(securedMathRandom() * characters.length));
return result;
}
//Encrypt Data using public-key
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
var senderECKeyData = getSenderPublicKeyString();
var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey);
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
let secret = Crypto.AES.encrypt(data, senderKey);
return {
secret: secret,
senderPublicKeyString: senderECKeyData.senderPublicKeyString
};
}
//Decrypt Data using private-key
floCrypto.decryptData = function (data, privateKeyHex) {
var receiverECKeyData = {};
if (typeof privateKeyHex !== "string") throw new Error("No private key found.");
let privateKey = wifToDecimal(privateKeyHex, true);
if (typeof privateKey.privateKeyDecimal !== "string") throw new Error("Failed to detremine your private key.");
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
var receiverDerivedKey = deriveSharedKeyReceiver(data.senderPublicKeyString, receiverECKeyData.privateKey);
let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
let decryptMsg = Crypto.AES.decrypt(data.secret, receiverKey);
return decryptMsg;
}
//Sign data using private-key
floCrypto.signData = function (data, privateKeyHex) {
var key = new Bitcoin.ECKey(privateKeyHex);
var messageHash = Crypto.SHA256(data);
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
var sighex = Crypto.util.bytesToHex(messageSign);
return sighex;
}
//Verify signatue of the data using public-key
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
var msgHash = Crypto.SHA256(data);
var sigBytes = Crypto.util.hexToBytes(signatureHex);
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
return verify;
}
//Generates a new flo ID and returns private-key, public-key and floID
const generateNewID = floCrypto.generateNewID = function () {
var key = new Bitcoin.ECKey(false);
key.setCompressed(true);
return {
floID: key.getBitcoinAddress(),
pubKey: key.getPubKeyHex(),
privKey: key.getBitcoinWalletImportFormat()
}
}
Object.defineProperties(floCrypto, {
newID: {
get: () => generateNewID()
},
hashID: {
value: (str) => {
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), { asBytes: true });
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
asBytes: true
}), {
asBytes: true
});
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
}
},
tmpID: {
get: () => {
let bytes = Crypto.util.randomBytes(20);
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
asBytes: true
}), {
asBytes: true
});
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
}
}
});
//Returns public-key from private-key
floCrypto.getPubKeyHex = function (privateKeyHex) {
if (!privateKeyHex)
return null;
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null)
return null;
key.setCompressed(true);
return key.getPubKeyHex();
}
//Returns flo-ID from public-key or private-key
floCrypto.getFloID = function (keyHex) {
if (!keyHex)
return null;
try {
var key = new Bitcoin.ECKey(keyHex);
if (key.priv == null)
key.setPub(keyHex);
return key.getBitcoinAddress();
} catch {
return null;
}
}
floCrypto.getAddress = function (privateKeyHex, strict = false) {
if (!privateKeyHex)
return;
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null)
return null;
key.setCompressed(true);
let pubKey = key.getPubKeyHex(),
version = bitjs.Base58.decode(privateKeyHex)[0];
switch (version) {
case coinjs.priv: //BTC
return coinjs.bech32Address(pubKey).address;
case bitjs.priv: //FLO
return bitjs.pubkey2address(pubKey);
default:
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
}
}
//Verify the private-key for the given public-key or flo-ID
floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) {
if (!privateKeyHex || !pubKey_floID)
return false;
try {
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null)
return false;
key.setCompressed(true);
if (isfloID && pubKey_floID == key.getBitcoinAddress())
return true;
else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
return true;
else
return false;
} catch {
return null;
}
}
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
if (!Array.isArray(publicKeyList) || !publicKeyList.length)
return null;
if (!Number.isInteger(requiredSignatures) || requiredSignatures < 1 || requiredSignatures > publicKeyList.length)
return null;
try {
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
return multisig;
} catch {
return null;
}
}
floCrypto.decodeRedeemScript = function (redeemScript) {
try {
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
return decoded;
} catch {
return null;
}
}
//Check if the given flo-id is valid or not
floCrypto.validateFloID = function (floID, regularOnly = false) {
if (!floID)
return false;
try {
let addr = new Bitcoin.Address(floID);
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
return false;
return true;
} catch {
return false;
}
}
//Check if the given address (any blockchain) is valid or not
floCrypto.validateAddr = function (address, std = true, bech = true) {
let raw = decodeAddress(address);
if (!raw)
return false;
if (typeof raw.version !== 'undefined') { //legacy or segwit
if (std == false)
return false;
else if (std === true || (!Array.isArray(std) && std === raw.version) || (Array.isArray(std) && std.includes(raw.version)))
return true;
else
return false;
} else if (typeof raw.bech_version !== 'undefined') { //bech32
if (bech === false)
return false;
else if (bech === true || (!Array.isArray(bech) && bech === raw.bech_version) || (Array.isArray(bech) && bech.includes(raw.bech_version)))
return true;
else
return false;
} else //unknown
return false;
}
//Check the public-key (or redeem-script) for the address (any blockchain)
floCrypto.verifyPubKey = function (pubKeyHex, address) {
let raw = decodeAddress(address);
if (!raw)
return;
let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
if (typeof raw.bech_version !== 'undefined' && raw.bytes.length == 32) //bech32-multisig
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
return pub_hash === raw.hex;
}
//Convert the given address (any blockchain) to equivalent floID
floCrypto.toFloID = function (address, options = null) {
if (!address)
return;
let raw = decodeAddress(address);
if (!raw)
return;
else if (options) { //if (optional) version check is passed
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
return;
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
return;
}
raw.bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
asBytes: true
}), {
asBytes: true
});
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
}
//Convert raw address bytes to floID
floCrypto.rawToFloID = function (raw_bytes) {
if (typeof raw_bytes === 'string')
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
if (raw_bytes.length != 20)
return null;
raw_bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(Crypto.SHA256(raw_bytes, {
asBytes: true
}), {
asBytes: true
});
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
}
//Convert the given multisig address (any blockchain) to equivalent multisig floID
floCrypto.toMultisigFloID = function (address, options = null) {
if (!address)
return;
let raw = decodeAddress(address);
if (!raw)
return;
else if (options) { //if (optional) version check is passed
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
return;
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
return;
}
if (typeof raw.bech_version !== 'undefined') {
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
raw.bytes = ripemd160(raw.bytes, {
asBytes: true
});
}
raw.bytes.unshift(bitjs.multisig);
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
asBytes: true
}), {
asBytes: true
});
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
}
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
floCrypto.isSameAddr = function (addr1, addr2) {
if (!addr1 || !addr2)
return;
let raw1 = decodeAddress(addr1),
raw2 = decodeAddress(addr2);
if (!raw1 || !raw2)
return false;
else {
if (typeof raw1.bech_version !== 'undefined' && raw1.bytes.length == 32) //bech32-multisig
raw1.hex = Crypto.util.bytesToHex(ripemd160(raw1.bytes, { asBytes: true }));
if (typeof raw2.bech_version !== 'undefined' && raw2.bytes.length == 32) //bech32-multisig
raw2.hex = Crypto.util.bytesToHex(ripemd160(raw2.bytes, { asBytes: true }));
return raw1.hex === raw2.hex;
}
}
const decodeAddress = floCrypto.decodeAddr = function (address) {
if (!address)
return;
else if (address.length == 33 || address.length == 34) { //legacy encoding
let decode = bitjs.Base58.decode(address);
let bytes = decode.slice(0, decode.length - 4);
let checksum = decode.slice(decode.length - 4),
hash = Crypto.SHA256(Crypto.SHA256(bytes, {
asBytes: true
}), {
asBytes: true
});
return (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) ? null : {
version: bytes.shift(),
hex: Crypto.util.bytesToHex(bytes),
bytes
}
} else if (address.length == 42 || address.length == 62) { //bech encoding
let decode = coinjs.bech32_decode(address);
if (decode) {
let bytes = decode.data;
let bech_version = bytes.shift();
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
return {
bech_version,
hrp: decode.hrp,
hex: Crypto.util.bytesToHex(bytes),
bytes
}
} else
return null;
}
}
//Split the str using shamir's Secret and Returns the shares
floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) {
try {
if (str.length > 0) {
var strHex = shamirSecretShare.str2hex(str);
var shares = shamirSecretShare.share(strHex, total_shares, threshold_limit);
return shares;
}
return false;
} catch {
return false
}
}
//Returns the retrived secret by combining the shamirs shares
const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) {
try {
if (sharesArray.length > 0) {
var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length));
comb = shamirSecretShare.hex2str(comb);
return comb;
}
return false;
} catch {
return false;
}
}
//Verifies the shares and str
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
if (!str)
return null;
else if (retrieveShamirSecret(sharesArray) === str)
return true;
else
return false;
}
const validateASCII = floCrypto.validateASCII = function (string, bool = true) {
if (typeof string !== "string")
return null;
if (bool) {
let x;
for (let i = 0; i < string.length; i++) {
x = string.charCodeAt(i);
if (x < 32 || x > 127)
return false;
}
return true;
} else {
let x, invalids = {};
for (let i = 0; i < string.length; i++) {
x = string.charCodeAt(i);
if (x < 32 || x > 127)
if (x in invalids)
invalids[string[i]].push(i)
else
invalids[string[i]] = [i];
}
if (Object.keys(invalids).length)
return invalids;
else
return true;
}
}
floCrypto.convertToASCII = function (string, mode = 'soft-remove') {
let chars = validateASCII(string, false);
if (chars === true)
return string;
else if (chars === null)
return null;
let convertor, result = string,
refAlt = {};
ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2));
mode = mode.toLowerCase();
if (mode === "hard-unicode")
convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "soft-unicode")
convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "hard-remove")
convertor = c => "";
else if (mode === "soft-remove")
convertor = c => refAlt[c] || "";
else
return null;
for (let c in chars)
result = result.replaceAll(c, convertor(c));
return result;
}
floCrypto.revertUnicode = function (string) {
return string.replace(/\\u[\dA-F]{4}/gi,
m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16)));
}
})('object' === typeof module ? module.exports : window.floCrypto = {});

843
floDapps.js Normal file
View File

@ -0,0 +1,843 @@
(function (EXPORTS) { //floDapps v2.4.1
/* General functions for FLO Dapps*/
'use strict';
const floDapps = EXPORTS;
const DEFAULT = {
root: "floDapps",
application: floGlobals.application,
adminID: floGlobals.adminID
};
Object.defineProperties(floDapps, {
application: {
get: () => DEFAULT.application
},
adminID: {
get: () => DEFAULT.adminID
},
root: {
get: () => DEFAULT.root
}
});
var user_priv_raw, aes_key, user_priv_wrap; //private variable inside capsule
const raw_user = {
get private() {
if (!user_priv_raw)
throw "User not logged in";
return Crypto.AES.decrypt(user_priv_raw, aes_key);
}
}
var user_id, user_public, user_private;
const user = floDapps.user = {
get id() {
if (!user_id)
throw "User not logged in";
return user_id;
},
get public() {
if (!user_public)
throw "User not logged in";
return user_public;
},
get private() {
if (!user_private)
throw "User not logged in";
else if (user_private instanceof Function)
return user_private();
else
return Crypto.AES.decrypt(user_private, aes_key);
},
sign(message) {
return floCrypto.signData(message, raw_user.private);
},
decrypt(data) {
return floCrypto.decryptData(data, raw_user.private);
},
encipher(message) {
return Crypto.AES.encrypt(message, raw_user.private);
},
decipher(data) {
return Crypto.AES.decrypt(data, raw_user.private);
},
get db_name() {
return "floDapps#" + floCrypto.toFloID(user.id);
},
lock() {
user_private = user_priv_wrap;
},
async unlock() {
if (await user.private === raw_user.private)
user_private = user_priv_raw;
},
get_contact(id) {
if (!user.contacts)
throw "Contacts not available";
else if (user.contacts[id])
return user.contacts[id];
else {
let id_raw = floCrypto.decodeAddr(id).hex;
for (let i in user.contacts)
if (floCrypto.decodeAddr(i).hex == id_raw)
return user.contacts[i];
}
},
get_pubKey(id) {
if (!user.pubKeys)
throw "Contacts not available";
else if (user.pubKeys[id])
return user.pubKeys[id];
else {
let id_raw = floCrypto.decodeAddr(id).hex;
for (let i in user.pubKeys)
if (floCrypto.decodeAddr(i).hex == id_raw)
return user.pubKeys[i];
}
},
clear() {
user_id = user_public = user_private = undefined;
user_priv_raw = aes_key = undefined;
delete user.contacts;
delete user.pubKeys;
delete user.messages;
}
};
Object.defineProperties(window, {
myFloID: {
get: () => {
try {
return user.id;
} catch {
return;
}
}
},
myUserID: {
get: () => {
try {
return user.id;
} catch {
return;
}
}
},
myPubKey: {
get: () => {
try {
return user.public;
} catch {
return;
}
}
},
myPrivKey: {
get: () => {
try {
return user.private;
} catch {
return;
}
}
}
});
var subAdmins, trustedIDs, settings;
Object.defineProperties(floGlobals, {
subAdmins: {
get: () => subAdmins
},
trustedIDs: {
get: () => trustedIDs
},
settings: {
get: () => settings
},
contacts: {
get: () => user.contacts
},
pubKeys: {
get: () => user.pubKeys
},
messages: {
get: () => user.messages
}
})
function initIndexedDB() {
return new Promise((resolve, reject) => {
var obs_g = {
//general
lastTx: {},
//supernode (cloud list)
supernodes: {}
}
var obs_a = {
//login credentials
credentials: {},
//for Dapps
subAdmins: {},
trustedIDs: {},
settings: {},
appObjects: {},
generalData: {},
lastVC: {}
}
//add other given objectStores
initIndexedDB.appObs = initIndexedDB.appObs || {}
for (let o in initIndexedDB.appObs)
if (!(o in obs_a))
obs_a[o] = initIndexedDB.appObs[o]
Promise.all([
compactIDB.initDB(DEFAULT.application, obs_a),
compactIDB.initDB(DEFAULT.root, obs_g)
]).then(result => {
compactIDB.setDefaultDB(DEFAULT.application)
resolve("IndexedDB App Storage Initated Successfully")
}).catch(error => reject(error));
})
}
function initUserDB() {
return new Promise((resolve, reject) => {
var obs = {
contacts: {},
pubKeys: {},
messages: {}
}
compactIDB.initDB(user.db_name, obs).then(result => {
resolve("UserDB Initated Successfully")
}).catch(error => reject('Init userDB failed'));
})
}
function loadUserDB() {
return new Promise((resolve, reject) => {
var loadData = ["contacts", "pubKeys", "messages"]
var promises = []
for (var i = 0; i < loadData.length; i++)
promises[i] = compactIDB.readAllData(loadData[i], user.db_name)
Promise.all(promises).then(results => {
for (var i = 0; i < loadData.length; i++)
user[loadData[i]] = results[i]
resolve("Loaded Data from userDB")
}).catch(error => reject('Load userDB failed'))
})
}
const startUpOptions = {
cloud: true,
app_config: true,
}
floDapps.startUpOptions = {
set app_config(val) {
if (val === true || val === false)
startUpOptions.app_config = val;
},
get app_config() { return startUpOptions.app_config },
set cloud(val) {
if (val === true || val === false)
startUpOptions.cloud = val;
},
get cloud() { return startUpOptions.cloud },
}
const startUpFunctions = [];
startUpFunctions.push(function readSupernodeListFromAPI() {
return new Promise((resolve, reject) => {
if (!startUpOptions.cloud)
return resolve("No cloud for this app");
const CLOUD_KEY = "floCloudAPI#" + floCloudAPI.SNStorageID;
compactIDB.readData("lastTx", CLOUD_KEY, DEFAULT.root).then(lastTx => {
var query_options = { sentOnly: true, pattern: floCloudAPI.SNStorageName };
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
query_options.ignoreOld = lastTx;
else if (typeof lastTx == 'string') //lastTx is txid of last tx
query_options.after = lastTx;
//fetch data from flosight
floBlockchainAPI.readData(floCloudAPI.SNStorageID, query_options).then(result => {
compactIDB.readData("supernodes", CLOUD_KEY, DEFAULT.root).then(nodes => {
nodes = nodes || {};
for (var i = result.data.length - 1; i >= 0; i--) {
var content = JSON.parse(result.data[i])[floCloudAPI.SNStorageName];
for (let sn in content.removeNodes)
delete nodes[sn];
for (let sn in content.newNodes)
nodes[sn] = content.newNodes[sn];
for (let sn in content.updateNodes)
if (sn in nodes) //check if node is listed
nodes[sn].uri = content.updateNodes[sn];
}
Promise.all([
compactIDB.writeData("lastTx", result.lastItem, CLOUD_KEY, DEFAULT.root),
compactIDB.writeData("supernodes", nodes, CLOUD_KEY, DEFAULT.root)
]).then(_ => {
floCloudAPI.init(nodes)
.then(result => resolve("Loaded Supernode list\n" + result))
.catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}).catch(error => reject(error))
})
});
startUpFunctions.push(function readAppConfigFromAPI() {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return resolve("No configs for this app");
compactIDB.readData("lastTx", `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root).then(lastTx => {
var query_options = { sentOnly: true, pattern: DEFAULT.application };
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
query_options.ignoreOld = lastTx;
else if (typeof lastTx == 'string') //lastTx is txid of last tx
query_options.after = lastTx;
//fetch data from flosight
floBlockchainAPI.readData(DEFAULT.adminID, query_options).then(result => {
for (var i = result.data.length - 1; i >= 0; i--) {
var content = JSON.parse(result.data[i])[DEFAULT.application];
if (!content || typeof content !== "object")
continue;
if (Array.isArray(content.removeSubAdmin))
for (var j = 0; j < content.removeSubAdmin.length; j++)
compactIDB.removeData("subAdmins", content.removeSubAdmin[j]);
if (Array.isArray(content.addSubAdmin))
for (var k = 0; k < content.addSubAdmin.length; k++)
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
if (Array.isArray(content.removeTrustedID))
for (var j = 0; j < content.removeTrustedID.length; j++)
compactIDB.removeData("trustedIDs", content.removeTrustedID[j]);
if (Array.isArray(content.addTrustedID))
for (var k = 0; k < content.addTrustedID.length; k++)
compactIDB.writeData("trustedIDs", true, content.addTrustedID[k]);
if (content.settings)
for (let l in content.settings)
compactIDB.writeData("settings", content.settings[l], l)
}
compactIDB.writeData("lastTx", result.lastItem, `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root);
compactIDB.readAllData("subAdmins").then(result => {
subAdmins = Object.keys(result);
compactIDB.readAllData("trustedIDs").then(result => {
trustedIDs = Object.keys(result);
compactIDB.readAllData("settings").then(result => {
settings = result;
resolve("Read app configuration from blockchain");
})
})
})
})
}).catch(error => reject(error))
})
});
startUpFunctions.push(function loadDataFromAppIDB() {
return new Promise((resolve, reject) => {
if (!startUpOptions.cloud)
return resolve("No cloud for this app");
var loadData = ["appObjects", "generalData", "lastVC"]
var promises = []
for (var i = 0; i < loadData.length; i++)
promises[i] = compactIDB.readAllData(loadData[i])
Promise.all(promises).then(results => {
for (var i = 0; i < loadData.length; i++)
floGlobals[loadData[i]] = results[i]
resolve("Loaded Data from app IDB")
}).catch(error => reject(error))
})
});
var keyInput = type => new Promise((resolve, reject) => {
let inputVal = prompt(`Enter ${type}: `)
if (inputVal === null)
reject(null)
else
resolve(inputVal)
});
function getCredentials() {
const readSharesFromIDB = indexArr => new Promise((resolve, reject) => {
var promises = []
for (var i = 0; i < indexArr.length; i++)
promises.push(compactIDB.readData('credentials', indexArr[i]))
Promise.all(promises).then(shares => {
var secret = floCrypto.retrieveShamirSecret(shares)
if (secret)
resolve(secret)
else
reject("Shares are insufficient or incorrect")
}).catch(error => {
clearCredentials();
location.reload();
})
});
const writeSharesToIDB = (shares, i = 0, resultIndexes = []) => new Promise(resolve => {
if (i >= shares.length)
return resolve(resultIndexes)
var n = floCrypto.randInt(0, 100000)
compactIDB.addData("credentials", shares[i], n).then(res => {
resultIndexes.push(n)
writeSharesToIDB(shares, i + 1, resultIndexes)
.then(result => resolve(result))
}).catch(error => {
writeSharesToIDB(shares, i, resultIndexes)
.then(result => resolve(result))
})
});
const getPrivateKeyCredentials = () => new Promise((resolve, reject) => {
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
if (indexArr) {
readSharesFromIDB(JSON.parse(indexArr))
.then(result => resolve(result))
.catch(error => reject(error))
} else {
var privKey;
keyInput("PRIVATE_KEY").then(result => {
if (!result)
return reject("Empty Private Key")
var floID = floCrypto.getFloID(result)
if (!floID || !floCrypto.validateFloID(floID))
return reject("Invalid Private Key")
privKey = result;
}).catch(error => {
console.log(error, "Generating Random Keys")
privKey = floCrypto.generateNewID().privKey
}).finally(_ => {
if (!privKey)
return;
var threshold = floCrypto.randInt(10, 20)
var shares = floCrypto.createShamirsSecretShares(privKey, threshold, threshold)
writeSharesToIDB(shares).then(resultIndexes => {
//store index keys in localStorage
localStorage.setItem(`${DEFAULT.application}#privKey`, JSON.stringify(resultIndexes))
//also add a dummy privatekey to the IDB
var randomPrivKey = floCrypto.generateNewID().privKey
var randomThreshold = floCrypto.randInt(10, 20)
var randomShares = floCrypto.createShamirsSecretShares(randomPrivKey, randomThreshold, randomThreshold)
writeSharesToIDB(randomShares)
//resolve private Key
resolve(privKey)
})
})
}
});
const checkIfPinRequired = key => new Promise((resolve, reject) => {
if (key.length == 52)
resolve(key)
else {
keyInput("PIN/Password").then(pwd => {
try {
let privKey = Crypto.AES.decrypt(key, pwd);
resolve(privKey)
} catch (error) {
reject("Access Denied: Incorrect PIN/Password")
}
}).catch(error => reject("Access Denied: PIN/Password required"))
}
});
return new Promise((resolve, reject) => {
getPrivateKeyCredentials().then(key => {
checkIfPinRequired(key).then(privKey => {
try {
user_public = floCrypto.getPubKeyHex(privKey);
user_id = floCrypto.getAddress(privKey);
if (startUpOptions.cloud)
floCloudAPI.user(user_id, privKey); //Set user for floCloudAPI
user_priv_wrap = () => checkIfPinRequired(key);
let n = floCrypto.randInt(12, 20);
aes_key = floCrypto.randString(n);
user_priv_raw = Crypto.AES.encrypt(privKey, aes_key);
user_private = user_priv_wrap;
resolve('Login Credentials loaded successful')
} catch (error) {
console.log(error)
reject("Corrupted Private Key")
}
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
var startUpLog = (status, log) => status ? console.log(log) : console.error(log);
const callStartUpFunction = i => new Promise((resolve, reject) => {
startUpFunctions[i]().then(result => {
callStartUpFunction.completed += 1;
startUpLog(true, `${result}\nCompleted ${callStartUpFunction.completed}/${callStartUpFunction.total} Startup functions`)
resolve(true)
}).catch(error => {
callStartUpFunction.failed += 1;
startUpLog(false, `${error}\nFailed ${callStartUpFunction.failed}/${callStartUpFunction.total} Startup functions`)
reject(false)
})
});
var _midFunction;
const midStartUp = () => new Promise((res, rej) => {
if (_midFunction instanceof Function) {
_midFunction()
.then(r => res("Mid startup function completed"))
.catch(e => rej("Mid startup function failed"))
} else
res("No mid startup function")
});
const callAndLog = p => new Promise((res, rej) => {
p.then(r => {
startUpLog(true, r)
res(r)
}).catch(e => {
startUpLog(false, e)
rej(e)
})
});
floDapps.launchStartUp = function () {
return new Promise((resolve, reject) => {
initIndexedDB().then(log => {
console.log(log)
callStartUpFunction.total = startUpFunctions.length;
callStartUpFunction.completed = 0;
callStartUpFunction.failed = 0;
let p1 = new Promise((res, rej) => {
Promise.all(startUpFunctions.map((f, i) => callStartUpFunction(i))).then(r => {
callAndLog(midStartUp())
.then(r => res(true))
.catch(e => rej(false))
})
});
let p2 = new Promise((res, rej) => {
callAndLog(getCredentials()).then(r => {
callAndLog(initUserDB()).then(r => {
callAndLog(loadUserDB())
.then(r => res(true))
.catch(e => rej(false))
}).catch(e => rej(false))
}).catch(e => rej(false))
})
Promise.all([p1, p2])
.then(r => resolve('App Startup finished successful'))
.catch(e => reject('App Startup failed'))
}).catch(error => {
startUpLog(false, error);
reject("App database initiation failed")
})
})
}
floDapps.addStartUpFunction = fn => fn instanceof Function && !startUpFunctions.includes(fn) ? startUpFunctions.push(fn) : false;
floDapps.setMidStartup = fn => fn instanceof Function ? _midFunction = fn : false;
floDapps.setCustomStartupLogger = fn => fn instanceof Function ? startUpLog = fn : false;
floDapps.setCustomPrivKeyInput = fn => fn instanceof Function ? keyInput = fn : false;
floDapps.setAppObjectStores = appObs => initIndexedDB.appObs = appObs;
floDapps.storeContact = function (floID, name) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(floID))
return reject("Invalid floID!")
compactIDB.writeData("contacts", name, floID, user.db_name).then(result => {
user.contacts[floID] = name;
resolve("Contact stored")
}).catch(error => reject(error))
});
}
floDapps.storePubKey = function (floID, pubKey) {
return new Promise((resolve, reject) => {
if (floID in user.pubKeys)
return resolve("pubKey already stored")
if (!floCrypto.validateAddr(floID))
return reject("Invalid floID!")
if (!floCrypto.verifyPubKey(pubKey, floID))
return reject("Incorrect pubKey")
compactIDB.writeData("pubKeys", pubKey, floID, user.db_name).then(result => {
user.pubKeys[floID] = pubKey;
resolve("pubKey stored")
}).catch(error => reject(error))
});
}
floDapps.sendMessage = function (floID, message) {
return new Promise((resolve, reject) => {
let options = {
receiverID: floID,
application: DEFAULT.root,
comment: DEFAULT.application
}
if (floID in user.pubKeys)
message = floCrypto.encryptData(JSON.stringify(message), user.pubKeys[floID])
floCloudAPI.sendApplicationData(message, "Message", options)
.then(result => resolve(result))
.catch(error => reject(error))
})
}
floDapps.requestInbox = function (callback) {
return new Promise((resolve, reject) => {
let lastVC = Object.keys(user.messages).sort().pop()
let options = {
receiverID: user.id,
application: DEFAULT.root,
lowerVectorClock: lastVC + 1
}
let privKey = raw_user.private;
options.callback = (d, e) => {
for (let v in d) {
try {
if (d[v].message instanceof Object && "secret" in d[v].message)
d[v].message = floCrypto.decryptData(d[v].message, privKey)
} catch (error) { }
compactIDB.writeData("messages", d[v], v, user.db_name)
user.messages[v] = d[v]
}
if (callback instanceof Function)
callback(d, e)
}
floCloudAPI.requestApplicationData("Message", options)
.then(result => resolve(result))
.catch(error => reject(error))
})
}
floDapps.manageAppConfig = function (adminPrivKey, addList, rmList, settings) {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return reject("No configs for this app");
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
if (!settings || typeof settings !== "object" || !Object.keys(settings).length) settings = undefined;
if (!addList && !rmList && !settings)
return reject("No configuration change")
var floData = {
[DEFAULT.application]: {
addSubAdmin: addList,
removeSubAdmin: rmList,
settings: settings
}
}
var floID = floCrypto.getFloID(adminPrivKey)
if (floID != DEFAULT.adminID)
reject('Access Denied for Admin privilege')
else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
.then(result => resolve(['Updated App Configuration', result]))
.catch(error => reject(error))
})
}
floDapps.manageAppTrustedIDs = function (adminPrivKey, addList, rmList) {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return reject("No configs for this app");
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
if (!addList && !rmList)
return reject("No change in list")
var floData = {
[DEFAULT.application]: {
addTrustedID: addList,
removeTrustedID: rmList
}
}
var floID = floCrypto.getFloID(adminPrivKey)
if (floID != DEFAULT.adminID)
reject('Access Denied for Admin privilege')
else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
.then(result => resolve(['Updated App Configuration', result]))
.catch(error => reject(error))
})
}
const clearCredentials = floDapps.clearCredentials = function () {
return new Promise((resolve, reject) => {
compactIDB.clearData('credentials', DEFAULT.application).then(result => {
localStorage.removeItem(`${DEFAULT.application}#privKey`);
user.clear();
resolve("privKey credentials deleted!")
}).catch(error => reject(error))
})
}
floDapps.deleteUserData = function (credentials = false) {
return new Promise((resolve, reject) => {
let p = []
p.push(compactIDB.deleteDB(user.db_name))
if (credentials)
p.push(clearCredentials())
Promise.all(p)
.then(result => resolve('User database(local) deleted'))
.catch(error => reject(error))
})
}
floDapps.deleteAppData = function () {
return new Promise((resolve, reject) => {
compactIDB.deleteDB(DEFAULT.application).then(result => {
localStorage.removeItem(`${DEFAULT.application}#privKey`)
user.clear();
compactIDB.removeData('lastTx', `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root)
.then(result => resolve("App database(local) deleted"))
.catch(error => reject(error))
}).catch(error => reject(error))
})
}
floDapps.securePrivKey = function (pwd) {
return new Promise(async (resolve, reject) => {
let indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
if (!indexArr)
return reject("PrivKey not found");
indexArr = JSON.parse(indexArr)
let encryptedKey = Crypto.AES.encrypt(await user.private, pwd);
let threshold = indexArr.length;
let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold)
let promises = [];
let overwriteFn = (share, index) =>
compactIDB.writeData("credentials", share, index, DEFAULT.application);
for (var i = 0; i < threshold; i++)
promises.push(overwriteFn(shares[i], indexArr[i]));
Promise.all(promises)
.then(results => resolve("Private Key Secured"))
.catch(error => reject(error))
})
}
floDapps.verifyPin = function (pin = null) {
const readSharesFromIDB = function (indexArr) {
return new Promise((resolve, reject) => {
var promises = []
for (var i = 0; i < indexArr.length; i++)
promises.push(compactIDB.readData('credentials', indexArr[i]))
Promise.all(promises).then(shares => {
var secret = floCrypto.retrieveShamirSecret(shares)
console.info(shares, secret)
if (secret)
resolve(secret)
else
reject("Shares are insufficient or incorrect")
}).catch(error => {
clearCredentials();
location.reload();
})
})
}
return new Promise((resolve, reject) => {
var indexArr = localStorage.getItem(`${DEFAULT.application}#privKey`)
console.info(indexArr)
if (!indexArr)
reject('No login credentials found')
readSharesFromIDB(JSON.parse(indexArr)).then(key => {
if (key.length == 52) {
if (pin === null)
resolve("Private key not secured")
else
reject("Private key not secured")
} else {
if (pin === null)
return reject("PIN/Password required")
try {
let privKey = Crypto.AES.decrypt(key, pin);
resolve("PIN/Password verified")
} catch (error) {
reject("Incorrect PIN/Password")
}
}
}).catch(error => reject(error))
})
}
const getNextGeneralData = floDapps.getNextGeneralData = function (type, vectorClock = null, options = {}) {
var fk = floCloudAPI.util.filterKey(type, options)
vectorClock = vectorClock || getNextGeneralData[fk] || '0';
var filteredResult = {}
if (floGlobals.generalData[fk]) {
for (let d in floGlobals.generalData[fk])
if (d > vectorClock)
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
} else if (options.comment) {
let comment = options.comment;
delete options.comment;
let fk = floCloudAPI.util.filterKey(type, options);
for (let d in floGlobals.generalData[fk])
if (d > vectorClock && floGlobals.generalData[fk][d].comment == comment)
filteredResult[d] = JSON.parse(JSON.stringify(floGlobals.generalData[fk][d]))
}
if (options.decrypt) {
let decryptionKey = (options.decrypt === true) ? raw_user.private : options.decrypt;
if (!Array.isArray(decryptionKey))
decryptionKey = [decryptionKey];
for (let f in filteredResult) {
let data = filteredResult[f]
try {
if (data.message instanceof Object && "secret" in data.message) {
for (let key of decryptionKey) {
try {
let tmp = floCrypto.decryptData(data.message, key)
data.message = JSON.parse(tmp)
break;
} catch (error) { }
}
}
} catch (error) { }
}
}
getNextGeneralData[fk] = Object.keys(filteredResult).sort().pop();
return filteredResult;
}
const syncData = floDapps.syncData = {};
syncData.oldDevice = () => new Promise((resolve, reject) => {
let sync = {
contacts: user.contacts,
pubKeys: user.pubKeys,
messages: user.messages
}
let message = Crypto.AES.encrypt(JSON.stringify(sync), raw_user.private)
let options = {
receiverID: user.id,
application: DEFAULT.root
}
floCloudAPI.sendApplicationData(message, "syncData", options)
.then(result => resolve(result))
.catch(error => reject(error))
});
syncData.newDevice = () => new Promise((resolve, reject) => {
var options = {
receiverID: user.id,
senderID: user.id,
application: DEFAULT.root,
mostRecent: true,
}
floCloudAPI.requestApplicationData("syncData", options).then(response => {
let vc = Object.keys(response).sort().pop()
let sync = JSON.parse(Crypto.AES.decrypt(response[vc].message, raw_user.private))
let promises = []
let store = (key, val, obs) => promises.push(compactIDB.writeData(obs, val, key, user.db_name));
["contacts", "pubKeys", "messages"].forEach(c => {
for (let i in sync[c]) {
store(i, sync[c][i], c)
user[c][i] = sync[c][i]
}
})
Promise.all(promises)
.then(results => resolve("Sync data successful"))
.catch(error => reject(error))
}).catch(error => reject(error))
});
})('object' === typeof module ? module.exports : window.floDapps = {});

166
floTokenAPI.js Normal file
View File

@ -0,0 +1,166 @@
(function (EXPORTS) { //floTokenAPI v1.0.4a
/* Token Operator to send/receive tokens via blockchain using API calls*/
'use strict';
const tokenAPI = EXPORTS;
const DEFAULT = {
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
currency: floGlobals.currency || "rupee"
}
Object.defineProperties(tokenAPI, {
URL: {
get: () => DEFAULT.apiURL
},
currency: {
get: () => DEFAULT.currency,
set: currency => DEFAULT.currency = currency
}
});
if (floGlobals.currency) tokenAPI.currency = floGlobals.currency;
Object.defineProperties(floGlobals, {
currency: {
get: () => DEFAULT.currency,
set: currency => DEFAULT.currency = currency
}
});
const fetch_api = tokenAPI.fetch = function (apicall) {
return new Promise((resolve, reject) => {
console.debug(DEFAULT.apiURL + apicall);
fetch(DEFAULT.apiURL + apicall).then(response => {
if (response.ok)
response.json().then(data => resolve(data));
else
reject(response)
}).catch(error => reject(error))
})
}
const getBalance = tokenAPI.getBalance = function (floID, token = DEFAULT.currency) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
.then(result => resolve(result.balance || 0))
.catch(error => reject(error))
})
}
tokenAPI.getTx = function (txID) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
if (res.result === "error")
reject(res.description);
else if (!res.parsedFloData)
reject("Data piece (parsedFloData) missing");
else if (!res.transactionDetails)
reject("Data piece (transactionDetails) missing");
else
resolve(res);
}).catch(error => reject(error))
})
}
tokenAPI.sendToken = function (privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
return new Promise((resolve, reject) => {
let senderID = floCrypto.getFloID(privKey);
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
return reject("Invalid amount");
getBalance(senderID, token).then(bal => {
if (amount > bal)
return reject(`Insufficient ${token}# balance`);
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID, options)
.then(txid => resolve(txid))
.catch(error => reject(error))
}).catch(error => reject(error))
});
}
function sendTokens_raw(privKey, receiverID, token, amount, utxo, vout, scriptPubKey) {
return new Promise((resolve, reject) => {
var trx = bitjs.transaction();
trx.addinput(utxo, vout, scriptPubKey)
trx.addoutput(receiverID, floBlockchainAPI.sendAmt);
trx.addflodata(`send ${amount} ${token}#`);
var signedTxHash = trx.sign(privKey, 1);
floBlockchainAPI.broadcastTx(signedTxHash)
.then(txid => resolve([receiverID, txid]))
.catch(error => reject([receiverID, error]))
})
}
//bulk transfer tokens
tokenAPI.bulkTransferTokens = function (sender, privKey, token, receivers) {
return new Promise((resolve, reject) => {
if (typeof receivers !== 'object')
return reject("receivers must be object in format {receiver1: amount1, receiver2:amount2...}")
let receiver_list = Object.keys(receivers), amount_list = Object.values(receivers);
let invalidReceivers = receiver_list.filter(id => !floCrypto.validateFloID(id));
let invalidAmount = amount_list.filter(val => typeof val !== 'number' || val <= 0);
if (invalidReceivers.length)
return reject(`Invalid receivers: ${invalidReceivers}`);
else if (invalidAmount.length)
return reject(`Invalid amounts: ${invalidAmount}`);
if (receiver_list.length == 0)
return reject("Receivers cannot be empty");
if (receiver_list.length == 1) {
let receiver = receiver_list[0], amount = amount_list[0];
floTokenAPI.sendToken(privKey, amount, receiver, "", token)
.then(txid => resolve({ success: { [receiver]: txid } }))
.catch(error => reject(error))
} else {
//check for token balance
floTokenAPI.getBalance(sender, token).then(token_balance => {
let total_token_amout = amount_list.reduce((a, e) => a + e, 0);
if (total_token_amout > token_balance)
return reject(`Insufficient ${token}# balance`);
//split utxos
floBlockchainAPI.splitUTXOs(sender, privKey, receiver_list.length).then(split_txid => {
//wait for the split utxo to get confirmation
floBlockchainAPI.waitForConfirmation(split_txid).then(split_tx => {
//send tokens using the split-utxo
var scriptPubKey = split_tx.vout[0].scriptPubKey.hex;
let promises = [];
for (let i in receiver_list)
promises.push(sendTokens_raw(privKey, receiver_list[i], token, amount_list[i], split_txid, i, scriptPubKey));
Promise.allSettled(promises).then(results => {
let success = Object.fromEntries(results.filter(r => r.status == 'fulfilled').map(r => r.value));
let failed = Object.fromEntries(results.filter(r => r.status == 'rejected').map(r => r.reason));
resolve({ success, failed });
})
}).catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
}
})
}
tokenAPI.getAllTxs = function (floID, token = DEFAULT.currency) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
.then(result => resolve(result))
.catch(error => reject(error))
})
}
const util = tokenAPI.util = {};
util.parseTxData = function (txData) {
let parsedData = {};
for (let p in txData.parsedFloData)
parsedData[p] = txData.parsedFloData[p];
parsedData.sender = txData.transactionDetails.vin[0].addr;
for (let vout of txData.transactionDetails.vout)
if (vout.scriptPubKey.addresses[0] !== parsedData.sender)
parsedData.receiver = vout.scriptPubKey.addresses[0];
parsedData.time = txData.transactionDetails.time;
return parsedData;
}
})('object' === typeof module ? module.exports : window.floTokenAPI = {});

57
index.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>FLO Standard Operators</title>
<script id="floGlobals">
/* Constants for FLO blockchain operations !!Make sure to add this at beginning!! */
const floGlobals = {
blockchain: "FLO_TEST",
adminID: "oKKHdK5uYAJ52U91sYsWhnEaEAAhZP779B",
application: "TEST_MODE_testnet",
}
</script>
<script>
(function () {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
if ('testnet' in params) {
floGlobals.blockchain = "FLO_TEST";
floGlobals.adminID = "oKKHdK5uYAJ52U91sYsWhnEaEAAhZP779B";
floGlobals.application = "TEST_MODE_testnet";
}
if ('quick' in params)
window.quick = true;
})();
</script>
<script src="lib.js"></script>
<script src="floCrypto.js"></script>
<script src="btcOperator.js"></script>
<script src="floBlockchainAPI.js"></script>
<script src="floTokenAPI.js"></script>
<script src="compactIDB.js"></script>
<script src="floCloudAPI.js"></script>
<script src="floDapps.js"></script>
<script id="onLoadStartUp">
function onLoadStartUp() {
if (window.quick) return;
//floDapps.addStartUpFunction('Sample', Promised Function)
//floDapps.setAppObjectStores({sampleObs1:{}, sampleObs2:{options{autoIncrement:true, keyPath:'SampleKey'}, Indexes:{sampleIndex:{}}}})
//floDapps.setCustomPrivKeyInput( () => { FUNCTION BODY *must resolve private key* } )
floDapps.launchStartUp().then(result => {
console.log(result)
alert(`Welcome FLO_ID: ${myFloID}`)
//App functions....
}).catch(error => console.error(error))
}
</script>
</head>
<body onload="onLoadStartUp()">
TEST_MODE
(use console)
</body>
</html>

9975
lib.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff