Compare commits

...

377 Commits

Author SHA1 Message Date
b70b2211ab
Update floDapps.js
Some checks failed
Workflow push to Dappbundle / Build (push) Has been cancelled
2025-08-28 13:11:29 +05:30
f51619f8dd
Update floCloudAPI.js 2025-08-28 13:05:31 +05:30
0494bc6d3f
Update push-dappbundle.yml 2025-08-25 08:00:56 +05:30
9ea694d160
Update floCloudAPI.js
Updating offline error capabilities
2025-08-25 07:58:28 +05:30
4d6f7c68f2
Update floDapps.js
Improving offline capabilities
2025-08-25 07:57:16 +05:30
sairaj mote
b8d0d50039 Moving to new tor links 2024-01-21 01:26:36 +05:30
sairaj mote
27739db63b updating floBlockchainAPI module 2024-01-19 01:28:41 +05:30
sairaj mote
d200b1f6c2 preserving white space 2024-01-18 02:06:10 +05:30
sairaj mote
7e75aadc40 Create push-dappbundle.yml 2023-12-18 22:05:45 +05:30
sairaj mote
dc62b016fb Merge https://github.com/ranchimall/standard-operations 2023-07-30 15:35:52 +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
SaketAnand
30c9d4d4e8
Delete user documentation 2023-07-16 21:05:56 +05:30
SaketAnand
54216e01ac
Update README.md 2023-07-16 20:59:23 +05:30
SaketAnand
3daa0486a7
Update README.md 2023-07-16 20:55:15 +05:30
SaketAnand
ff9a7aeb00
Create user documentation 2023-07-16 20:51:58 +05:30
sairaj mote
b11c217ec4 Update index.html 2023-07-14 02:10:38 +05:30
sairajzero
8b6646a2eb relocate script files 2023-07-06 05:18:20 +05:30
sairajzero
a8c6e75a6c Merge https://github.com/ranchimall/Standard_Operations 2023-07-06 05:18:06 +05:30
sairajzero
5223c00375 Remove stdop files 2023-07-06 05:08:27 +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
sairaj mote
68ce717eb1 Bug fix
-- Updated components
-- Fixed score update bug
2023-07-02 14:42:22 +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
sairaj mote
c3570a25ce fixed loader alignment 2023-05-27 12:27: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
43ff4f3793 Update floDapps.js 2023-05-01 20:45:36 +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
sairaj mote
c13d7e7346 Merge branch 'master' of https://github.com/ranchimall/cc 2023-03-14 19:20:15 +05:30
sairaj mote
0ee8c2b9e8 Updated components 2023-03-14 19:19:48 +05:30
sairaj mote
d322f4912f
Update README.md 2023-03-14 18:57:02 +05:30
sairaj mote
03f72cb14f Feature update
-- added ability to rearrange article sections
2023-03-14 18:52:57 +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
sairaj mote
2d4741fdec Feature update
-- added feature to open article correctly even if not logged in
2023-03-06 01:42:44 +05:30
sairaj mote
5ae03dc85f text change 2023-03-03 02:06:24 +05:30
sairaj mote
7c8be2dbd0 changed FLO private key sign in text 2023-03-02 23:40:37 +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
sairaj mote
3f50ab3454 standardizing key generation 2023-02-13 23:40:42 +05:30
Sai Raj
837f4b4a6f
Merge pull request #15 from sairajzero/master 2023-02-08 19:23:28 +05:30
sairaj mote
c46a53debf Updated std ops
-- Login with BTC private should work now
2023-02-08 17:52:06 +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
sairaj mote
95137ca7b5 minor code refactoring 2022-08-07 17:10:39 +05:30
sairaj mote
a12d1ca380 bug fix 2022-08-07 17:09:12 +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
sairaj mote
ba680273b4 minor bug fix 2022-07-28 01:10:34 +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
sairaj mote
8730afbbbe bug fix 2022-07-19 16:59:26 +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
sairaj mote
15d4c69c3a bug fixes 2022-07-14 19:36:05 +05:30
sairaj mote
1e8d7f18a5 Bug fixes 2022-07-14 16:16:20 +05:30
sairaj mote
450d4c9e5c Fixed adding link doesn't show submit option 2022-07-13 16:33:25 +05:30
Sai Raj
80b4a8b9fe
Merge pull request #9 from ranchimall/master
Update README.md
2022-07-12 19:13:20 +05:30
sairaj mote
3ae866a08c minor UI fixes 2022-07-11 20:20:43 +05:30
sairaj mote
02b5b68016 feature update and bug fixes
-- added option to refresh article without reloading whole page
2022-07-10 20:40:58 +05:30
sairaj mote
5b3bfffafd feature update
-- added focus mode to randomly show one section only
-- added option to see plot
2022-07-10 02:36:34 +05:30
sairaj mote
a2b36fa6ac added flo-btc address conversion 2022-07-09 01:07:32 +05:30
sairaj mote
ac0d22e897 Updated std ops 2022-07-08 23:53:35 +05:30
sairaj mote
da289259ed bug fix 2022-07-06 01:25:04 +05:30
sairaj mote
b746e6d879 UI/UX changes 2022-07-05 19:44:01 +05:30
sairaj mote
c9eaaf8f57
CC overhaul 2022-07-04 15:55:04 +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
Keerthana.A.V
22fbedcae7
Update README.md 2022-02-02 17:06:03 +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
Vivek Teega
327ed2b318
Merge pull request #8 from ranchimall/component-js
Escape template literal in component.js
2021-12-08 16:36:30 +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
Sai Raj
571499907e
Update README.md 2021-06-25 15:16:48 +05:30
ea2a586b5a
Updted Flosight list 2021-06-22 18:59:26 +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
Sai Raj
f7fcaf12c6 Merge branch 'sairajzero:master' into master 2021-06-05 12:34:53 +05:30
sairajzero
99ac3accb9 bugfix: (floBlockchainAPI v2.0.1c)
-Fixed: no Flosight working error shown even when there  are flosight(s) working
2021-06-05 12:27:32 +05:30
sairajzero
962f68c230 floBlockchainAPI v2.0.1b
Additional filter options to readData function
- receivedOnly: filters only received data
- txid: (boolean) resolve txid or not
- sender: flo-id(s) of sender
- receiver: flo-id(s) of receiver
2021-04-22 02:09:48 +05:30
sairajzero
05f110661e floCrypto_v2.0.1, floBlockchainAPI_v2.0.1a
floCrypto_v2.0.1 (ASCII fns)
- validateASCII
- convertToASCII
- revertUnicode

floBlockchainAPI_v2.0.1a
bugfix
2021-04-18 02:36:51 +05:30
sairajzero
836f359cf2 floDapps_v2.0.1c
- Added verifyPin: function to verify the pin of login credentials without logging in (ie, not storing the keys in memory)
- 'pin' argument can be ignored to check if private key is secured or not.
2021-02-12 03:06:42 +05:30
sairajzero
dd59c25d73 floCloudAPI_v2.0.2e
- Fixed bug: multiple cloud connection caused issue with inactive array.
Hence replaced Array with Set
2021-01-20 22:54:38 +05:30
sairajzero
b5532444fb floCloudAPI_v2.0.2d | floDapps_v2.0.1b
floCloudAPI_v2.0.2d
- Fixed bug: syntax error in requestObjectData
 - Added closeRequest: closes the active live requests

floDapps_v2.0.1b
- minor improvements and alignment.
- Fixed: deleteAppData deletes the lastTx data of the adminID.
2021-01-02 19:11:15 +05:30
sairajzero
1a57f74704 Alignment
- Moving onLoadStartUp script tag to head
2020-12-31 23:20:51 +05:30
Sai Raj
634aa5aebd
Merge pull request #13 from sairajzero/master 2020-12-19 22:35:42 +05:30
sairajzero
fc747dae83 floBlockchainAPI v2.0.1 | floCloudAPI v2.0.2c
- Added: getTx(txid) to floBlockchainAPI. resolves the tx data of the given txid.

-
2020-12-19 22:26:00 +05:30
sairajzero
30c13ae12f compactIDB v2.0.1
- Fixed bug: database upgrade not working correctly when upgrading existing db.
- Added upgradeDB as a standalone function. (also called by initDB if upgrade is required)
- Added getDBversion: resolves the version of the db
- Improved openDB: rejects if the db is not found
2020-12-15 05:02:50 +05:30
sairajzero
b0e15d1a7f Minor bug fixes 2020-12-09 03:49:32 +05:30
Sai Raj
cafd76655e
Merge pull request #12 from sairajzero/master 2020-11-30 03:31:31 +05:30
sairajzero
99ce43c089 floCloudAPI_v2.0.2a | floDapps_v2.0.1
floCloudAPI v2.0.2a
- deleteApplicationData accepts options instead of application (to match other functions).
- Added editApplicationData
- sendGeneralData not longer stores data in IDB.
- fixed bug: Infinite callback loop in requestGeneralData and requestObjectData

floDapps v2.0.1
- added mid startup support
- added deleteUserData and deleteAppData
- replaced reactor startup logger with setCustomStartupLogger
2020-11-30 03:29:06 +05:30
Sai Raj
5a1aff68ce
Merge pull request #11 from sairajzero/master 2020-10-15 01:46:04 +05:30
Sai Raj
a650ed17a8
Merge pull request #6 from sairajzero/dev
version update (Major changes)
2020-10-15 01:43:29 +05:30
Sai Raj
50594b6bd9
Merge pull request #5 from ranchimall/master 2020-10-15 01:41:32 +05:30
sairajzero
f5f29cb620 bug fix
- Changed SNStorageID
- fixed bug: supernodes not read from blockchain
- generalData incorrectly stored
2020-10-15 01:33:19 +05:30
sairajzero
a8218630ea Contacts feature
- spliting IDB into 3 parts:
1. floDapps - common DB for all apps across all users
2. userDB - common DB for all apps for specific user floID
3. appDB - app DB across all users

- contacts, pubKeys can be stored by users in userDB
- users can send direct message across all apps using floDapps.sendMessage
- syncData can be used to sync user data between devices
2020-10-12 02:38:28 +05:30
sairajzero
cc2d4f8248 Mark feature 2020-09-27 03:21:43 +05:30
sairajzero
65f16fa14b Bug fixes
minor bug fixes
2020-09-20 13:11:07 +05:30
sairajzero
4e9af8060a stdop Improvement
STD_OP

floGlobals
- combined vectorClock (for objectData) and generalVC (for generalData) into lastVC.

init
- Bug fixed: floData of 16 character dropped by floSight.

floCrypto
- Improved getFloIDfromPubkeyHex to getFloID: return floID from either pubKey or privKey.
- verifyPrivKey, validateAddr return false if arguments are empty.

floSupernode
- Removed floSupernode module.
- Kbucket submodule moved to floCloudAPI

Kbucket (subModule)
- Improved KBucket module
- Kbucket cyclic array (SNCO) is formed (w.r.t. distance) when kBucket is launched
- nextNode, prevNode are determined by SNCO

compactIDB
- Improved searchData (accepts option parameter instead of patternEval)
	.options (lowerKey, upperKey, atKey, patternEval, lastOnly)

floCloudAPI
- Improved data signature during sending.
	.current format ( ["receiverID", "time", "application", "type", "message", "comment"])
- Adding live request:
	.requesting data now accepts option callback
	.when callback option is passed, the request becomes live request
	.ie, cloud automatically forwards new data received by the requested floID
	.callback function is called with new data is received
	.callback supported functions: requestApplicationData, requestGeneralData, requestObjectData
- Adding feature to delete from cloud.
	.data received can now be deleted from cloud
	.signature is required for verification (thus requires privKey logged in)
	.can only delete own data (received only)

floDapps
- merged manageSubAdmins and setApplicationSettings into manageAppConfig(adminPrivKey, addList, rmList, settings)
- Improved getNextGeneralData
	.getNextGeneralData can now sub-categorize comment (even when generalData is request for all comment)
	.getNextGeneralData now keeps a track pointer of previously called. [vectorClock (ie, second parameter) must be null, default is null]
2020-09-19 03:39:19 +05:30
sairaj mote
2c58bd662a
Added link for different sections 2020-09-17 13:59:05 +05:30
Sai Raj
8078a83122
Merge pull request #10 from sairajzero/master 2020-08-25 18:05:57 +05:30
sairajzero
9b7dd8214d bug fix
- Fixed: Invalid private key on signing in error not working correctly and cause other bugs
2020-08-25 18:03:12 +05:30
Sai Raj
49d4377900
Merge pull request #9 from sairajzero/master 2020-08-20 17:53:05 +05:30
sairajzero
5e8f0dddb5 Update README.md 2020-08-07 04:34:14 +05:30
sairajzero
fe1ce64562 minor change
-  floBlockchainAPI's readData: changing default limit option value from 1000 to no limit
2020-08-07 04:34:03 +05:30
sairajzero
58bee2e9db minor bug fix
- floBlockchainAPI.readData's option pattern ll now test for JSON key matching the pattern
- startUp fn readAppConfigFromAPI checks if the content is a object or not to prevent errors
2020-08-07 04:19:38 +05:30
sairajzero
ceecfab2b7 Merge branch 'master' of https://github.com/sairajzero/Standard_Operations 2020-08-05 14:05:37 +05:30
sairajzero
c3607aa58f Update README.md 2020-08-05 13:56:12 +05:30
Sai Raj
d4ce67033d
Merge pull request #4 from ranchimall/master 2020-08-05 13:51:17 +05:30
5b93464f42
Update README.md 2020-08-05 13:49:07 +05:30
sairajzero
f6a07b20ba Minor Improvements
- generalData now also stores sign and pubKey.
- renamed startup fn readSubAdminListFromAPI to readAppConfigFromAPI.
- readAppConfigFromAPI also loads settings from the IDB to floGlobals.
2020-08-01 18:38:10 +05:30
sairajzero
62c190567e Adding settings and encrypted generalData feature
- floDapps now reads 'settings' from blockchain
- improved manageSubAdmins: doesnot send tx if addList and rmList are empty (i.e, no changes)
- setApplicationSettings: used to set settings for the application.
Note: existing settings are overwritten (ie, not appended) if same key is used.
- sendGeneralData now supports a new option (encrypt): Encrypts the message before sending.
Note: option value can be boolean true or pubKey of a floID. Passing true will encrypt the data using the default encryptionKey (default 'encryptionKey' must be set in settings).
- getNextGeneralData now supports a new option (decrypt): Decrypts the message if possible.
Note: option value can be true, privateKey or array of privateKeys.
If boolean true is passed, decrypts using myPrivKey (loggedIn privateKey).
If privateKey (string) is passed, decrypts using the given privateKey.
If array of privateKeys are passed, decrypts the message using the suitable key from the given array. (for example, say messages A, B, C are encrypted for keys X, Y, Z respectively, passing [X, Z] will decrypt messages A and C.
2020-07-31 22:24:27 +05:30
e2d9adde07
Update README.md 2020-07-05 23:16:52 +05:30
9d909ebc8c
Update README.md 2020-07-05 23:14:32 +05:30
2edf26228a
Update README.md 2020-07-05 22:26:22 +05:30
be52706a05
Update README.md 2020-07-05 22:16:52 +05:30
9be2c5103d
Update README.md 2020-07-05 19:03:29 +05:30
f13e87dbf9
Update README.md 2020-07-05 18:18:11 +05:30
084aced9b7
Update README.md 2020-07-05 18:15:34 +05:30
70f8cd95b1
Update README.md 2020-07-05 17:15:35 +05:30
e065f688af
Update README.md 2020-07-05 17:06:15 +05:30
6c55653f2a
Update README.md 2020-07-05 14:26:54 +05:30
0050648da2
Update README.md 2020-07-05 14:24:37 +05:30
120ebcf036
Update README.md 2020-07-05 14:21:28 +05:30
5216154940
Update README.md 2020-07-05 14:14:39 +05:30
8a6a4fb1d6
Update README.md 2020-07-05 14:06:27 +05:30
4818cc410a
Update README.md 2020-07-05 13:43:32 +05:30
ec0d420ca7
Update README.md 2020-07-05 13:36:14 +05:30
beebb5795a
Update README.md 2020-07-05 13:12:16 +05:30
39a09be7d9
Update README.md 2020-07-05 11:16:08 +05:30
01b5c5f490
Update README.md 2020-07-05 10:26:00 +05:30
b6affaf6ea
Update README.md 2020-07-05 10:23:00 +05:30
863e4f465d
Update README.md 2020-06-23 20:36:11 +05:30
a33e79a8d0
Update README.md 2020-06-23 20:35:41 +05:30
8e61606de7
Update README.md 2020-06-23 20:33:29 +05:30
25424b9476
Update README.md 2020-06-23 20:21:57 +05:30
14d54c2046
Update README.md 2020-06-23 20:19:01 +05:30
754738f06f
Update README.md 2020-06-23 20:08:13 +05:30
ee26c73936
Update README.md 2020-06-23 19:59:09 +05:30
4f91a3e44e
Update README.md 2020-06-23 19:57:55 +05:30
f23dd9e6e8
Update README.md 2020-06-23 19:53:38 +05:30
33ec57cea1
Update README.md 2020-06-23 19:52:57 +05:30
e7fcd3dd0e
Update README.md 2020-06-23 19:48:38 +05:30
ee267bc31d
Update README.md 2020-06-23 19:32:49 +05:30
1925462202
Update README.md 2020-06-23 19:28:29 +05:30
Sai Raj
09569e729b
Merge pull request #8 from sairajzero/master 2020-05-22 18:53:22 +05:30
Sai Raj
7cbb0908d8
Merge pull request #3 from ranchimall/master 2020-05-22 18:52:17 +05:30
sairajzero
5e1e8a624f bug fix: new line in floData cause error
since newline char is not supported in floData, new line (\n) is replaced with space.
2020-05-22 18:50:29 +05:30
SaketAnand
57ff5ae0b9
README.md
Corrected the spelling of "retrieve" from "retrive' in floCloudAPI Section
2020-05-17 10:45:43 +05:30
5699e8dee4
Update README.md 2020-05-16 18:13:42 +05:30
aa7c5c909e
Update README.md 2020-05-16 17:55:30 +05:30
2265581971
Update README.md 2020-05-16 17:53:05 +05:30
6175ac9314
Update README.md 2020-05-16 17:47:27 +05:30
3a23206af9
Update README.md 2020-05-16 15:35:51 +05:30
cc20e28de3
Update README.md 2020-05-16 10:39:59 +05:30
afc286964f
Update README.md 2020-05-16 10:36:45 +05:30
572c559096
Update README.md 2020-05-16 10:33:04 +05:30
09490c560d
Update README.md 2020-05-16 10:14:11 +05:30
2099fa61ba
Update README.md 2020-05-16 09:37:08 +05:30
59dfc6f449
Update README.md 2020-05-16 09:35:17 +05:30
42f493b01b
Update README.md 2020-05-16 09:32:12 +05:30
3d9c763bee
Update README.md 2020-05-16 09:30:31 +05:30
aae73da57f
Update README.md 2020-05-16 09:07:07 +05:30
2f118217d9
Update README.md 2020-05-15 13:40:16 +05:30
Sai Raj
eee8e2e365
Merge pull request #7 from sairajzero/master 2020-05-14 04:00:51 +05:30
Sai Raj
aff5b927e9
Merge branch 'master' into master 2020-05-14 04:00:38 +05:30
Sai Raj
7d65c4da8e
Update README.md 2020-05-14 03:57:23 +05:30
sairajzero
fccf2fdebd Update README.md 2020-05-14 03:52:45 +05:30
sairajzero
a66c2d91c2 minor changes
-Renamed defaultDB propertyName in compactIDB
-Fixed error in writeDataMultiple comment
2020-05-14 03:52:39 +05:30
4eb9649622
Added new standard functions 2020-05-13 10:04:27 +05:30
sairajzero
32da4e28af Improved initDB
initDB also deletes objectstores on upgrade needed (version change)
2020-05-07 15:46:58 +05:30
sairajzero
489996c1b8 Improve clearCredentials
Clearing the private key, public key and floID from memory on clearCredentials
2020-05-06 19:56:31 +05:30
Sai Raj
1e6d2462af
Merge pull request #6 from sairajzero/master 2020-05-06 19:03:08 +05:30
sairajzero
d90e9f6ed4 Adding secure PrivateKey feature
floDapps.securePrivKey(pwd): replaces the stored private key with encrypted variant.
Changed made in startup function getCredentials to support securePrivateKey.

Improved and promisified clearCredentials.
2020-05-06 00:31:44 +05:30
sairajzero
0308af983f Adding clearData to CompactIDB
compactIDB.clearData clears all data in the given objectstore.
2020-05-02 18:48:02 +05:30
sairajzero
38f43d805d InitDB: add missing objectStores
InitDB now automatically adds the missing objectstore instead of rejecting
2020-05-02 18:10:47 +05:30
sairajzero
1954fc0782 Improved sendTxMultiple and writeDataMultiple
sendTxMultiple:
There are 2 modes (given send amount, preserve ratio)
- Given Send Amount: Uses the given send amount from each sender IDs. Pass the senderPrivKeys as object with amount to invoke this mode
eg. { privateKey1: sendAmt1, privateKey2: sendAmt2...}
- preserveRatio: The ratio of the balance of the senders are maintained. Pass the senderPrivKeys as array of private keys.
eg. [privatekey1, privatekey2....]
*Note: receivers must always be object of floIDs with receive amount.
eg. {receiverID1: receiveAmt1, receiver2: receiveAmt2...}

writeDataMultiple:
There are 2 modes (preserveRatio, equalContribution)
- preserveRatio: preserves the ratio of the balance of senders
- equalContribution: all senders contribute equal amount to the transaction
Note:
*senderPrivKey and receivers are arrays
eq. [privateKey1, privateKey2, ..] and [receiverID1, receiverID2...]
*sends default amount (floGlobals.sendAmt) to each receiver)
*last (4th) parameter is optional:
(default) true: invokes preserveRatio mode
false: invokes equalContibution mode
2020-04-26 01:04:12 +05:30
sairajzero
a569adcb74 Adding mergeUTXOs, sendTxMulti and writeDataMulti
mergeUTXOs: /merge all UTXOs of a given floID into a single UTXO

sendTxMulti: Send Tx from (and/or) to multiple floID

writeDataMulti: Write data into blockchain from (and/or) to multiple floID
2020-04-25 17:54:13 +05:30
sairajzero
0a6fb69349 Beautify the scripts 2020-04-23 21:16:54 +05:30
sairajzero
3d0c8883b7 Improved floCloudAPI
floCloudAPI now stores the lastCommit value of the objects via requestObjectData, resetObjectData, updateObjectData

resetObjectData and updateObjectData now takes the new and old value from floGlobals.appObjects and lastCommit respectively

Parameter changes:
resetObjectData: function(objectName, options = {})
updateObjectData: function(objectName, options = {})
2020-03-10 22:00:56 +05:30
sairajzero
8652959e54 floCrypto: errorHanding
errorHanding added to floCrypto.getPubKeyHex and floCrypto.getFloIDfromPubkeyHex
2020-03-04 03:45:50 +05:30
sairajzero
47ca6a802e moving onloadStartup to a seperate script tag 2020-02-16 02:38:51 +05:30
sairajzero
d6c4b4882e changing TEST_MODE adminID 2020-02-16 02:36:35 +05:30
sairajzero
86ea714139 adding manage subAdmin to floDapps
floDapps.manageSubAdmins() allows the user to add and/or remove subadmin list via blockchain
*Requires Admin Privatekey of the app
2020-02-16 02:34:58 +05:30
sairajzero
c3a51f44a7 bug fix 2020-02-06 14:56:12 +05:30
sairajzero
7a6a2dc9ea Custom privKeyInput is made promisified
privKeyInput now uses given key when resolved and generates random key when rejected
2020-02-06 13:38:09 +05:30
sairajzero
cdeab33c20 Login as guest feature
Entering a empty string for private Key now automatically generated a random key and logins as guest
2020-02-03 18:43:29 +05:30
Sai Raj
43afe64fbf
Merge pull request #4 from sairajzero/master 2020-01-31 22:06:52 +05:30
sairajzero
2bf166ffd1 bug fix: floSupernode module 2020-01-31 20:22:35 +05:30
sairajzero
a44fe4be72 adding custom input for privkey 2020-01-31 14:42:42 +05:30
sairajzero
e3e034ae0a bug fix: vectorClock IDB 2020-01-22 22:25:02 +05:30
sairajzero
556f4ddadd bug fix: floCloudAPI 2020-01-07 15:06:03 +05:30
Sai Raj
bbb2a4596b
Merge pull request #3 from sairajzero/master 2020-01-05 20:53:21 +05:30
sairajzero
f5f44f9415 Merge branch 'master' of https://github.com/sairajzero/Standard_Operations 2020-01-05 20:44:14 +05:30
sairajzero
efbb6fa9e2 bug fix 2020-01-05 20:44:09 +05:30
sairajzero
73965d87fc adding event listner to startup functions
startUpSuccessLog and startUpSuccessLog Events are registered to log the start up functions
2020-01-05 20:40:53 +05:30
Sai Raj
2c6b422533
Merge pull request #2 from sairajzero/master 2020-01-05 20:17:37 +05:30
Sai Raj
2b847b7a4e
Update README.md 2020-01-05 20:13:53 +05:30
sairajzero
1301caaa40 adding a dummy floID as adminID 2020-01-05 20:00:02 +05:30
Sai Raj
8dbace647f
Merge pull request #1 from sairajzero/master 2020-01-05 19:55:09 +05:30
sairajzero
e40771eb23 bug fix: some missing libraries 2020-01-05 19:43:09 +05:30
sairajzero
c473d200c0 Improving floSupernode.kBucket module
Adding getInnerNodes and getOuterNodes
getPrevSupernode, getNextSupernode and determineClosestSupernode now return an array of floIDs
2020-01-05 19:42:34 +05:30
sairajzero
5e9926b87b bug fix: floDapps 2019-12-19 22:28:43 +05:30
sairajzero
5a2b6250ca adding floDapps std_Op
This std_op contains functions for FLO Dapp startup and other useful functions for FLO Dapps
2019-12-19 22:03:53 +05:30
sairajzero
e42a972f12 floCloudAPI: moving the reactor events to util and Promisifying all canon operations 2019-12-19 21:42:02 +05:30
sairajzero
afa8714d0e bug fix 2019-12-19 20:31:30 +05:30
sairajzero
8f3ef66d15 moving KBucket library to floSupernode tag 2019-12-19 20:27:21 +05:30
sairajzero
d09ada8261 floCrypto improvement
moving non canon functions to util property
2019-12-19 20:25:14 +05:30
sairajzero
7a60c9f134 Moving libraries to init_lib 2019-12-19 20:23:24 +05:30
sairajzero
a06f455026 Moved floGlobals to to head and added id to std_op script tag 2019-12-19 20:08:11 +05:30
sairajzero
e691662c82 adding send and request general data to floCloudAPI
sendGeneralData : sends general data to the cloud
requestGeneralData : request general data from the cloud
2019-12-12 18:11:26 +05:30
sairajzero
e7a74c785e Adding Shamir's Shared Secret to floCrypto
Adding Shamir's Shared Secret to floCrypto module
createShamirsSecretShares: create the shamirs secret shares
retrieveShamirSecret: retrives the secret from shares
verifyShamirsSecret: verify the shares with secret
Also adding randInt and randString to floCrypto module
2019-12-01 11:45:00 +05:30
sairajzero
577bee4d37 Adding requestApplicationData
requestApplicationData : request any message from the supernode cloud
Fixed minor bugs
2019-11-20 20:20:39 +05:30
Sai Raj
62ac2a9371
Merge pull request #2 from ranchimall/master 2019-11-20 18:20:13 +05:30
Sai Raj
5ae3214a67
Rename index.html to standard_Operations.html 2019-11-20 18:15:34 +05:30
sairajzero
ce28df6834 adding automatic fetch for flosight API 2019-11-20 02:07:35 +05:30
sairajzero
289089d972 Adding floCloudAPI
floCloudAPI has methods to send application data, request application data, reset or update object data.
2019-11-19 20:11:32 +05:30
sairajzero
d79d54b400 Updating to match the backup node feature of SN 2019-11-19 20:09:15 +05:30
sairaj mote
10a5393373
Update README.md 2019-10-20 01:15:39 +05:30
sairaj mote
41eb1dc452
added description for floBlockchainAPI and compact IDB operations 2019-10-20 01:07:28 +05:30
Sai Raj
57d16248d9
fixing bug in initDB 2019-10-17 17:21:07 +05:30
Sai Raj
049cd5823c
changing ws:// to wss:// in supernode module 2019-10-17 15:55:24 +05:30
Sai Raj
b1c6d8c153
fixing broadcastTx not been asynchronized call 2019-10-13 03:04:28 +05:30
sairajzero
ed59eeeb65 Added removeData in compactIDB 2019-10-06 03:51:13 +05:30
sairajzero
68472cad56 floBlockchainAPI.readData will now return a object with totalTxs and data 2019-10-06 03:15:18 +05:30
sairajzero
9da3168f45 Improved Read Data from blockchain via API
Replaced floBlockchainAPI's readSentData, readDataPattern, readSentDataPattern, readDataContains, readSentDataContains with a improved version 'readData'
readData is Promisfied function that reads and resolves floData from blockchain using API
Arguments :
addr - address from which the floData should be read
options - (optional object, default = {})
Options :
limit - maximum number of filtered data (default = 1000, negative  = no limit)
ignoreOld - ignore old txs (default = 0)
sentOnly - filters only sent data
pattern - filters data that starts with a pattern
contains - filters data that contains a string
filter - custom filter funtion for floData (eg . filter: d => {return d[0] == '$'})
2019-10-06 02:52:34 +05:30
sairajzero
8fd579dc5e Removed supernode Server
Removed supernode server so that this repo can be templated directly for projects
2019-10-04 21:16:55 +05:30
sairajzero
243e3300ae Merge branch 'master' of https://github.com/ranchimall/Standard_Operations 2019-10-04 20:41:08 +05:30
sairajzero
ae71390df4 fixing bugs 2019-10-04 20:35:10 +05:30
sairajzero
9bdac8b042 fixing some bugs 2019-10-04 20:34:26 +05:30
sairajzero
2ea373ef2d improved reading floData via API 2019-10-04 20:31:56 +05:30
Sai Raj
268ffe4197
Update index.html 2019-10-03 00:20:20 +05:30
Sai Raj
e2948b1db5
Update README.md 2019-09-29 22:07:01 +05:30
Sai Raj
d7f1d215dd
Update README.md 2019-09-29 22:04:11 +05:30
sairajzero
bd52c44169 Added compactIDB operations and fixed bugs 2019-09-29 13:55:46 +05:30
sairajzero
dd402cd454 Adding files 2019-09-27 21:34:16 +05:30
sairajzero
bf24208898 Initial commit 2019-09-27 21:10:01 +05:30
26 changed files with 35404 additions and 27108 deletions

32
.github/workflows/push-dappbundle.yml vendored Normal file
View File

@ -0,0 +1,32 @@
name: Workflow push to Dappbundle
on: [push]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Executing remote command
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.R_HOST }}
username: ${{ secrets.P_USERNAME }}
password: ${{ secrets.P_PASSWORD }}
port: ${{ secrets.SSH_PORT }}
script: |
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle" ]; then
echo "Folder exists. Skipping Git clone."
else
echo "Folder does not exist. Cloning repository..."
cd ${{ secrets.DEPLOYMENT_LOCATION}}/ && git clone https://github.com/ranchimall/dappbundle.git
fi
if [ -d "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" ]; then
echo "Repository exists. Remove folder "
rm -r "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}"
fi
echo "Cloning repository..."
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle && git clone https://github.com/ranchimall/${{ github.event.repository.name }}
cd "${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/${{ github.event.repository.name }}" && rm -rf .gitattributes .git .github .gitignore
cd ${{ secrets.DEPLOYMENT_LOCATION}}/dappbundle/ && git add . && git commit -m "Workflow updating files of ${{ github.event.repository.name }}" && git push "https://ranchimalldev:${{ secrets.RM_ACCESS_TOKEN }}@github.com/ranchimall/dappbundle.git"

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
playground/ playground/
combo.html combo.html
*.tmp*

View File

@ -1,19 +1,26 @@
# p2p-content-collaboration # p2p-content-collaboration
This is a peer to peer FLO Blockchain based content collaboration software that runs on a single HTML file. This peer-to-peer FLO Blockchain-based content collaboration software runs on a single HTML file.
### Live URL for CC
*https://ranchimall.github.io/cc/*
## How to run this software ## How to run this software
1. Download and run the HTML file on any browser (Google Chrome is preferred) 1. Download and run the HTML file on any browser (Google Chrome is preferred)
2. It will ask for a FLO private key. If you do not have one please generate it from flo-webwallet.duckdns.org 2. Or access the LIVE URL
3. Once you provide your private keys you are logged in forever 3. It will ask for a FLO private key. If you do not have one please generate it.
4. To add your suggestions please write inside the boxes under respective sections 4. Once you provide a correct private key you are logged in
5. You can also chose to edit any content by adding/deleting contents 5. Inside the app, you can find the contents that are active
6. To submit your views just click outside the box anywhere. Your views will be recorded 6. To add your writeup please write inside the boxes under respective sections or subtopics
7. The respective admins can score the content to choose the best ones 7. You can also choose to edit any content by adding/deleting contents
8. To score the content please click on the star icon and provide a score. Score will be recorded. 8. To submit your writeup click on the 'Submit' button. Your entries will be recorded
9. The respective admins or sub-admins can score the content to choose the best ones
10. To score the content, please click on the star icon and give a score. The score will be recorded. (Only authorized sub-admins can score content)
11. Also you can download any content by selecting it and exporting it as a file.
Any user running the file gets to edit a shared content. Hence, this application can be used for multiple purposes Any user running the file gets to edit shared content. Hence, this application can be used for multiple purposes
like create shared content, write book by multiple authors or reach a consensus through collaborative discussion. like creating shared content, writing books by multiple authors, or reaching a consensus through collaborative discussion.
Please fork the project and contribute. Please fork the project and contribute.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2
css/main.min.css vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

27188
index.html

File diff suppressed because one or more lines are too long

13
package-lock.json generated Normal file
View File

@ -0,0 +1,13 @@
{
"name": "p2p-content-collaboration",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"tiny-editor": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tiny-editor/-/tiny-editor-0.2.5.tgz",
"integrity": "sha512-K4luWQbam/TrgjLst+Ztb7uPltXHwtFm+Oi1hs3xM6biI7N1X8HfyDtmROOEbyJM8+mMP5/FjsMnc96zJZoZCg=="
}
}
}

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "p2p-content-collaboration",
"version": "1.0.0",
"description": "",
"main": "components.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ranchimall/p2p-content-collaboration.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/ranchimall/p2p-content-collaboration/issues"
},
"homepage": "https://github.com/ranchimall/p2p-content-collaboration#readme",
"dependencies": {
"tiny-editor": "^0.2.5"
}
}

996
scripts/btcOperator.js Normal file
View File

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

257
scripts/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 = {});

16
scripts/components.js Normal file

File diff suppressed because one or more lines are too long

1063
scripts/floBlockchainAPI.js Normal file

File diff suppressed because one or more lines are too long

1
scripts/floBlockchainAPI.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1189
scripts/floCloudAPI.js Normal file

File diff suppressed because it is too large Load Diff

530
scripts/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 = {});

891
scripts/floDapps.js Normal file
View File

@ -0,0 +1,891 @@
(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) => {
if (!startUpOptions.cloud)
return resolve("No cloud for this app");
const CLOUD_KEY = "floCloudAPI#" + floCloudAPI.SNStorageID;
// Fallback: init from cached nodes (never reject)
const initFromCache = (tag) =>
compactIDB.readData("supernodes", CLOUD_KEY, DEFAULT.root)
.then(nodes => {
nodes = nodes || {};
return floCloudAPI.init(nodes)
.then(r => resolve(`${tag} (from cache)\n${r}`))
.catch(() => resolve(`${tag} (cache present, init skipped)`));
})
.catch(() => resolve(`${tag} (no cache)`));
compactIDB.readData("lastTx", CLOUD_KEY, DEFAULT.root).then(lastTx => {
const query_options = { sentOnly: true, pattern: floCloudAPI.SNStorageName };
if (typeof lastTx === 'number') // backward support (tx count)
query_options.ignoreOld = lastTx;
else if (typeof lastTx === 'string') // last txid
query_options.after = lastTx;
// Try online; if it fails, fall back to cache
floBlockchainAPI.readData(floCloudAPI.SNStorageID, query_options).then(result => {
compactIDB.readData("supernodes", CLOUD_KEY, DEFAULT.root).then(nodes => {
nodes = nodes || {};
for (let i = result.data.length - 1; i >= 0; i--) {
const content = JSON.parse(result.data[i])[floCloudAPI.SNStorageName];
if (!content || typeof content !== 'object') continue;
if (content.removeNodes)
for (let sn in content.removeNodes) delete nodes[sn];
if (content.newNodes)
for (let sn in content.newNodes) nodes[sn] = content.newNodes[sn];
if (content.updateNodes)
for (let sn in content.updateNodes)
if (sn in nodes) 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(r => resolve("Loaded Supernode list\n" + r))
.catch(() => resolve("Loaded Supernode list (init deferred)"));
}).catch(() => resolve("Supernode list updated (persist partial)"));
}).catch(() => initFromCache("Supernode list read failed"));
}).catch(() => initFromCache("Supernode network fetch failed"));
}).catch(() => initFromCache("Supernode lastTx read failed"));
});
});
startUpFunctions.push(function readAppConfigFromAPI() {
return new Promise((resolve) => {
if (!startUpOptions.app_config)
return resolve("No configs for this app");
// small helper: load cached directives into memory and resolve
const loadFromIDB = (msg) => Promise.all([
compactIDB.readAllData("subAdmins"),
compactIDB.readAllData("trustedIDs"),
compactIDB.readAllData("settings")
]).then(([sub, trust, set]) => {
subAdmins = Object.keys(sub || {}); // arrays of IDs
trustedIDs = Object.keys(trust || {});
settings = set || {};
resolve(msg);
}).catch(() => {
// safe defaults if cache missing
subAdmins = []; trustedIDs = []; settings = {};
resolve(msg + " (no local cache)");
});
// If cloud is disabled, use cached config and move on
if (!startUpOptions.cloud)
return loadFromIDB("Read app configuration from local cache (offline)");
const lastKey = `${DEFAULT.application}|${DEFAULT.adminID}`;
// Try to read lastTx; on failure, just use cache (dont block startup)
compactIDB.readData("lastTx", lastKey, DEFAULT.root).then(lastTx => {
const query_options = { sentOnly: true, pattern: DEFAULT.application };
if (typeof lastTx === 'number') query_options.ignoreOld = lastTx;
else if (typeof lastTx === 'string') query_options.after = lastTx;
// Fetch deltas from chain; on failure, fall back to cache
floBlockchainAPI.readData(DEFAULT.adminID, query_options).then(result => {
for (let i = result.data.length - 1; i >= 0; i--) {
const content = JSON.parse(result.data[i])[DEFAULT.application];
if (!content || typeof content !== "object") continue;
if (Array.isArray(content.removeSubAdmin))
for (let j = 0; j < content.removeSubAdmin.length; j++)
compactIDB.removeData("subAdmins", content.removeSubAdmin[j]);
if (Array.isArray(content.addSubAdmin))
for (let k = 0; k < content.addSubAdmin.length; k++)
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
if (Array.isArray(content.removeTrustedID))
for (let j = 0; j < content.removeTrustedID.length; j++)
compactIDB.removeData("trustedIDs", content.removeTrustedID[j]);
if (Array.isArray(content.addTrustedID))
for (let 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);
}
// persist last item marker (best effort)
compactIDB.writeData("lastTx", result.lastItem, lastKey, DEFAULT.root).catch(() => {});
// load fresh values from IDB into memory and finish
loadFromIDB("Read app configuration from blockchain");
}).catch(() => {
// network failed → boot from cache
loadFromIDB("Read app configuration from local cache (network fail)");
});
}).catch(() => {
// couldn't read lastTx → still boot from cache
loadFromIDB("Read app configuration from local cache (no lastTx)");
});
});
});
startUpFunctions.push(function loadDataFromAppIDB() {
return new Promise((resolve, reject) => {
const loadData = ["appObjects", "generalData", "lastVC"];
// If cloud is disabled AND no IDB stores are expected, skip early
if (!startUpOptions.cloud && (!initIndexedDB.appObs || Object.keys(initIndexedDB.appObs).length === 0))
return resolve("No cloud and no local data to load");
// Otherwise, read from IDB
Promise.all(loadData.map(item => compactIDB.readAllData(item)))
.then(results => {
for (let 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 = {});

193
scripts/floTokenAPI.js Normal file

File diff suppressed because one or more lines are too long

1
scripts/floTokenAPI.min.js vendored Normal file

File diff suppressed because one or more lines are too long

9975
scripts/lib.js Normal file

File diff suppressed because it is too large Load Diff

3
scripts/purify.min.js vendored Normal file

File diff suppressed because one or more lines are too long

443
template/css/main.css Normal file
View File

@ -0,0 +1,443 @@
* {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: "Inter", sans-serif;
}
:root {
font-size: clamp(1rem, 1.2vmax, 1.2rem);
}
html,
body {
height: 100%;
scroll-behavior: smooth;
}
body {
color: rgba(var(--text-color), 1);
background: var(--foreground-color);
}
body,
body * {
--accent-color: rgb(0, 156, 78);
--text-color: 36, 36, 36;
--background-color: 248, 248, 248;
--foreground-color: rgb(255, 255, 255);
--danger-color: rgb(255, 75, 75);
--like-color: #e91e63;
scrollbar-width: thin;
}
body[data-theme=dark],
body[data-theme=dark] * {
--accent-color: rgb(14, 230, 122);
--text-color: 230, 230, 230;
--text-color-light: 170, 170, 170;
--background-color: 10, 10, 10;
--foreground-color: rgb(24, 24, 24);
--danger-color: rgb(255, 106, 106);
}
body[data-theme=dark] sm-popup::part(popup) {
background-color: var(--foreground-color);
}
p {
font-size: 0.9rem;
max-width: 70ch;
color: rgba(var(--text-color), 0.8);
}
p * {
font-family: inherit;
}
.flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.direction-column {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
}
.grid {
display: grid;
}
.flow-column {
grid-auto-flow: column;
}
.gap-0-5 {
gap: 0.5rem;
}
.gap-1 {
gap: 1rem;
}
.gap-1-5 {
gap: 1.5rem;
}
.gap-2 {
gap: 2rem;
}
.gap-3 {
gap: 3rem;
}
.justify-self-center {
justify-self: center;
}
.justify-self-start {
justify-self: start;
}
.justify-self-end {
justify-self: end;
}
.icon {
width: 1.2rem;
height: 1.2rem;
fill: rgba(var(--text-color), 0.8);
-ms-flex-negative: 0;
flex-shrink: 0;
}
button,
.button {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
border: none;
background-color: transparent;
overflow: hidden;
color: inherit;
cursor: pointer;
-webkit-transition: -webkit-transform 0.3s;
transition: -webkit-transform 0.3s;
transition: transform 0.3s;
transition: transform 0.3s, -webkit-transform 0.3s;
-webkit-tap-highlight-color: transparent;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
font-size: 0.9rem;
font-weight: 500;
}
.button {
white-space: nowrap;
padding: 0.6rem 1rem;
border-radius: 0.3rem;
background-color: rgba(var(--text-color), 0.06);
color: rgba(var(--text-color), 0.8);
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.button--primary {
background-color: var(--accent-color);
color: rgba(var(--background-color), 1);
}
button:active,
.button:active,
sm-button:not([disabled]):active,
.interact:active {
-webkit-transform: scale(0.9);
transform: scale(0.9);
}
.popup__header {
display: grid;
gap: 0.5rem;
width: 100%;
padding: 0 1.5rem 0 0.5rem;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
grid-template-columns: auto 1fr auto;
}
.popup__header__close {
padding: 0.5rem;
}
#sign_in,
#sign_up {
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
#sign_in sm-form,
#sign_up sm-form {
margin: 2rem 0;
--gap: 1rem;
}
#sign_in header,
#sign_up header {
padding: 1.5rem 0;
}
#sign_up sm-copy {
font-size: 0.9rem;
}
#sign_up h5 {
font-weight: 500;
color: rgba(var(--text-color), 0.8);
}
.card {
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.04);
}
.warning {
background-color: khaki;
color: rgba(0, 0, 0, 0.7);
padding: 1rem;
border-radius: 0.5rem;
line-height: 1.5;
}
#main_header {
display: grid;
gap: 1rem;
position: sticky;
top: 0;
padding: 1rem;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
grid-template-columns: 1fr auto auto;
background-color: var(--foreground-color);
z-index: 1;
}
.logo {
display: grid;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
width: 100%;
grid-template-columns: auto 1fr;
gap: 0 0.5rem;
margin-right: 1rem;
}
.logo h4 {
text-transform: capitalize;
font-size: 0.9rem;
font-weight: 500;
}
.main-logo {
height: 1.4rem;
width: 1.4rem;
fill: rgba(var(--text-color), 1);
stroke: none;
}
article {
position: relative;
padding-bottom: 3rem;
gap: 1rem;
}
article::after {
justify-self: center;
position: absolute;
bottom: 0;
content: "";
width: 4rem;
height: 0.3rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.5);
}
article p {
font-family: "noto serif", serif;
line-height: 1.8;
font-size: 1rem;
}
.page-layout {
display: grid;
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
}
.page-layout > * {
grid-column: 2/3;
}
.hero-section {
display: grid;
grid-template-columns: minmax(0, 1fr);
margin-bottom: 1.5rem;
padding-top: 1.5rem;
}
img {
-o-object-fit: cover;
object-fit: cover;
width: 100%;
height: 40vh;
}
time {
font-size: 0.8rem;
}
h1 {
font-size: 1.4rem;
margin-bottom: 1rem;
}
h3:not(:first-of-type) {
margin-top: 2rem;
}
.full-bleed {
grid-column: 1/-1;
}
.quote-template {
position: relative;
padding: 1rem;
margin: 1rem 0;
border-radius: 0.2rem;
border: solid thin rgba(var(--text-color), 0.3);
-webkit-box-shadow: 0.3rem 0.5rem 0 0.1rem rgba(var(--text-color), 0.8);
box-shadow: 0.3rem 0.5rem 0 0.1rem rgba(var(--text-color), 0.8);
overflow: hidden;
justify-self: center;
padding-left: 1.3rem;
}
.quote-template figcaption {
margin-top: 0.5rem;
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
margin-left: auto;
}
.upvote {
display: grid;
grid-template-columns: auto 1fr;
position: relative;
padding: 0.8rem;
border-radius: 2rem;
background-color: var(--foreground-color);
-webkit-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
border: solid rgba(var(--text-color), 0.2) thin;
}
.upvote > * {
pointer-events: none;
}
.upvote:active {
-webkit-transform: none;
transform: none;
}
.upvote:active .icon {
-webkit-transform: scale(0.7);
transform: scale(0.7);
}
.upvote.liked {
background-color: var(--like-color);
color: white;
}
.upvote.liked .icon {
fill: white;
}
.expanding-heart,
.ring {
grid-area: 1/1;
}
.ring {
border: 0.1rem solid var(--like-color);
border-radius: 50%;
height: 0.5rem;
width: 0.5rem;
justify-self: center;
}
.upvote .icon {
grid-area: 1/1;
fill: var(--like-color);
height: 1.5rem;
width: 1.5rem;
-webkit-transition: -webkit-transform 0.2s;
transition: -webkit-transform 0.2s;
transition: transform 0.2s;
transition: transform 0.2s, -webkit-transform 0.2s;
}
.temp-count,
#like_count {
grid-area: 1/2;
}
.temp-count:not(:empty),
#like_count:not(:empty) {
margin-left: 0.4rem;
}
footer {
padding: 3rem 1.5rem;
justify-items: center;
}
@media screen and (min-width: 40rem) {
sm-popup {
--width: 24rem;
}
.popup__header {
padding: 1rem 1.5rem 0 1rem;
}
.page-layout {
grid-template-columns: 1fr 60ch 1fr;
}
h1 {
font-size: 2rem;
}
}
@media (any-hover: hover) {
::-webkit-scrollbar {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(var(--text-color), 0.5);
}
}
.hide-completely {
display: none;
}

1
template/css/main.min.css vendored Normal file

File diff suppressed because one or more lines are too long

396
template/css/main.scss Normal file
View File

@ -0,0 +1,396 @@
* {
padding: 0;
margin: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
font-family: "Inter", sans-serif;
}
:root {
font-size: clamp(1rem, 1.2vmax, 1.2rem);
}
html,
body {
height: 100%;
scroll-behavior: smooth;
}
body {
color: rgba(var(--text-color), 1);
background: var(--foreground-color);
}
body,
body * {
--accent-color: rgb(0, 156, 78);
--text-color: 36, 36, 36;
--background-color: 248, 248, 248;
--foreground-color: rgb(255, 255, 255);
--danger-color: rgb(255, 75, 75);
--like-color: #e91e63;
scrollbar-width: thin;
}
body[data-theme="dark"],
body[data-theme="dark"] * {
--accent-color: rgb(14, 230, 122);
--text-color: 230, 230, 230;
--text-color-light: 170, 170, 170;
--background-color: 10, 10, 10;
--foreground-color: rgb(24, 24, 24);
--danger-color: rgb(255, 106, 106);
}
body[data-theme="dark"] sm-popup::part(popup) {
background-color: var(--foreground-color);
}
p {
font-size: 0.9rem;
max-width: 70ch;
color: rgba(var(--text-color), 0.8);
}
p * {
font-family: inherit;
}
.flex {
display: flex;
}
.direction-column {
flex-direction: column;
}
.grid {
display: grid;
}
.flow-column {
grid-auto-flow: column;
}
.gap-0-5 {
gap: 0.5rem;
}
.gap-1 {
gap: 1rem;
}
.gap-1-5 {
gap: 1.5rem;
}
.gap-2 {
gap: 2rem;
}
.gap-3 {
gap: 3rem;
}
.justify-self-center {
justify-self: center;
}
.justify-self-start {
justify-self: start;
}
.justify-self-end {
justify-self: end;
}
.icon {
width: 1.2rem;
height: 1.2rem;
fill: rgba(var(--text-color), 0.8);
flex-shrink: 0;
}
button,
.button {
user-select: none;
position: relative;
display: inline-flex;
border: none;
background-color: transparent;
overflow: hidden;
color: inherit;
cursor: pointer;
transition: transform 0.3s;
-webkit-tap-highlight-color: transparent;
align-items: center;
font-size: 0.9rem;
font-weight: 500;
}
.button {
white-space: nowrap;
padding: 0.6rem 1rem;
border-radius: 0.3rem;
background-color: rgba(var(--text-color), 0.06);
color: rgba(var(--text-color), 0.8);
justify-content: center;
&--primary {
background-color: var(--accent-color);
color: rgba(var(--background-color), 1);
}
}
button:active,
.button:active,
sm-button:not([disabled]):active,
.interact:active {
transform: scale(0.9);
}
.popup__header {
display: grid;
gap: 0.5rem;
width: 100%;
padding: 0 1.5rem 0 0.5rem;
align-items: center;
grid-template-columns: auto 1fr auto;
}
.popup__header__close {
padding: 0.5rem;
}
#sign_in,
#sign_up {
align-items: center;
sm-form {
margin: 2rem 0;
--gap: 1rem;
}
header {
padding: 1.5rem 0;
}
}
#sign_up {
sm-copy {
font-size: 0.9rem;
}
h5 {
font-weight: 500;
color: rgba(var(--text-color), 0.8);
}
}
.card {
padding: 1rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.04);
}
.warning {
background-color: khaki;
color: rgba(0, 0, 0, 0.7);
padding: 1rem;
border-radius: 0.5rem;
line-height: 1.5;
}
#main_header {
display: grid;
gap: 1rem;
position: sticky;
top: 0;
padding: 1rem;
align-items: center;
grid-template-columns: 1fr auto auto;
background-color: var(--foreground-color);
z-index: 1;
}
.logo {
display: grid;
align-items: center;
width: 100%;
grid-template-columns: auto 1fr;
gap: 0 0.5rem;
margin-right: 1rem;
}
.logo h4 {
text-transform: capitalize;
font-size: 0.9rem;
font-weight: 500;
}
.main-logo {
height: 1.4rem;
width: 1.4rem;
fill: rgba(var(--text-color), 1);
stroke: none;
}
article {
position: relative;
padding-bottom: 3rem;
gap: 1rem;
&::after {
justify-self: center;
position: absolute;
bottom: 0;
content: "";
width: 4rem;
height: 0.3rem;
border-radius: 0.5rem;
background-color: rgba(var(--text-color), 0.5);
}
p {
font-family: "noto serif", serif;
line-height: 1.8;
font-size: 1rem;
}
}
.page-layout {
display: grid;
grid-template-columns: 1rem minmax(0, 1fr) 1rem;
}
.page-layout > * {
grid-column: 2/3;
}
.hero-section {
display: grid;
grid-template-columns: minmax(0, 1fr);
margin-bottom: 1.5rem;
padding-top: 1.5rem;
}
img {
object-fit: cover;
width: 100%;
height: 40vh;
}
time {
font-size: 0.8rem;
}
h1 {
font-size: 1.4rem;
margin-bottom: 1rem;
}
h3:not(:first-of-type) {
margin-top: 2rem;
}
.full-bleed {
grid-column: 1/-1;
}
.quote-template {
position: relative;
padding: 1rem;
margin: 1rem 0;
border-radius: 0.2rem;
border: solid thin rgba(var(--text-color), 0.3);
box-shadow: 0.3rem 0.5rem 0 0.1rem rgba(var(--text-color), 0.8);
overflow: hidden;
justify-self: center;
padding-left: 1.3rem;
}
.quote-template figcaption {
margin-top: 0.5rem;
color: rgba(var(--text-color), 0.8);
font-size: 0.8rem;
margin-left: auto;
}
.upvote {
display: grid;
grid-template-columns: auto 1fr;
position: relative;
padding: 0.8rem;
border-radius: 2rem;
background-color: var(--foreground-color);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.1);
border: solid rgba(var(--text-color), 0.2) thin;
& > * {
pointer-events: none;
}
&:active {
transform: none;
.icon {
transform: scale(0.7);
}
}
&.liked {
background-color: var(--like-color);
color: white;
.icon {
fill: white;
}
}
}
.expanding-heart,
.ring {
grid-area: 1/1;
}
.ring {
border: 0.1rem solid var(--like-color);
border-radius: 50%;
height: 0.5rem;
width: 0.5rem;
justify-self: center;
}
.upvote .icon {
grid-area: 1/1;
fill: var(--like-color);
height: 1.5rem;
width: 1.5rem;
transition: transform 0.2s;
}
.temp-count,
#like_count {
grid-area: 1/2;
&:not(:empty) {
margin-left: 0.4rem;
}
}
footer {
padding: 3rem 1.5rem;
justify-items: center;
}
@media screen and (min-width: 40rem) {
sm-popup {
--width: 24rem;
}
.popup__header {
padding: 1rem 1.5rem 0 1rem;
}
.page-layout {
grid-template-columns: 1fr 60ch 1fr;
}
h1 {
font-size: 2rem;
}
}
@media (any-hover: hover) {
::-webkit-scrollbar {
width: 0.5rem;
height: 0.5rem;
}
::-webkit-scrollbar-thumb {
background: rgba(var(--text-color), 0.3);
border-radius: 1rem;
&:hover {
background: rgba(var(--text-color), 0.5);
}
}
}
// highest priority styles
.hide-completely {
display: none;
}

11445
template/index.html Normal file

File diff suppressed because it is too large Load Diff