From 750a54c15fa2d8fa54a2770591a89f98034e8f5c Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Fri, 12 Jul 2019 20:45:46 +0800 Subject: [PATCH] update code --- LICENSE | 23 + README.md | 232 +++ Source/HTML/123 | 162 ++ Source/HTML/CSS/blockviewer.css | 440 ++++ Source/HTML/CSS/buttons.css | 201 ++ Source/HTML/CSS/codes.css | 90 + Source/HTML/CSS/history.css | 440 ++++ Source/HTML/CSS/mobile-wallet.css | 2755 ++++++++++++++++++++++++++ Source/HTML/CSS/style.css | 180 ++ Source/HTML/CSS/wallet.css | 535 +++++ Source/HTML/JS/client-electron.js | 32 + Source/HTML/JS/client.js | 1944 ++++++++++++++++++ Source/HTML/JS/coinlib.js | 112 ++ Source/HTML/JS/crypto-client.js | 372 ++++ Source/HTML/JS/dapp-inner.js | 521 +++++ Source/HTML/JS/diagram.js | 455 +++++ Source/HTML/JS/highlight-html.js | 109 + Source/HTML/JS/highlight-js.js | 174 ++ Source/HTML/JS/highlight.js | 836 ++++++++ Source/HTML/JS/lexer.js | 1686 ++++++++++++++++ Source/HTML/JS/marked.js | 1604 +++++++++++++++ Source/HTML/JS/mobile-wallet.js | 1165 +++++++++++ Source/HTML/JS/sec256k1.js | 190 ++ Source/HTML/JS/sha3.js | 803 ++++++++ Source/HTML/JS/sign-lib-min.js | 250 +++ Source/HTML/JS/terahashlib.js | 480 +++++ Source/HTML/JS/tx-lib.js | 10 + Source/HTML/JS/wallet-lib.js | 722 +++++++ Source/HTML/JS/wallet-node.js | 542 +++++ Source/HTML/JS/wallet-web.js | 275 +++ Source/HTML/PIC/B.svg | 4 + Source/HTML/PIC/T.svg | 3 + Source/HTML/PIC/TeraLogo.svg | 13 + Source/HTML/PIC/Tera_logo.svg | 6 + Source/HTML/PIC/Tera_logo2.svg | 6 + Source/HTML/PIC/add-icon.svg | 4 + Source/HTML/PIC/address_book.png | Bin 0 -> 2608 bytes Source/HTML/PIC/blank.svg | 4 + Source/HTML/PIC/chains.png | Bin 0 -> 1617 bytes Source/HTML/PIC/check.svg | 3 + Source/HTML/PIC/console.png | Bin 0 -> 821 bytes Source/HTML/PIC/console0.png | Bin 0 -> 1225 bytes Source/HTML/PIC/counters.png | Bin 0 -> 3025 bytes Source/HTML/PIC/dapp.png | Bin 0 -> 1174 bytes Source/HTML/PIC/down-arrow.svg | 3 + Source/HTML/PIC/down.png | Bin 0 -> 1785 bytes Source/HTML/PIC/glass.svg | 3 + Source/HTML/PIC/info.svg | 3 + Source/HTML/PIC/invoice.png | Bin 0 -> 1174 bytes Source/HTML/PIC/invoice2.png | Bin 0 -> 1130 bytes Source/HTML/PIC/key.png | Bin 0 -> 1621 bytes Source/HTML/PIC/lock_closed.png | Bin 0 -> 5988 bytes Source/HTML/PIC/lock_open.png | Bin 0 -> 6010 bytes Source/HTML/PIC/monitor.png | Bin 0 -> 4078 bytes Source/HTML/PIC/network.png | Bin 0 -> 1396 bytes Source/HTML/PIC/reload.svg | 4 + Source/HTML/PIC/right-arrow.svg | 3 + Source/HTML/PIC/smart.png | Bin 0 -> 7693 bytes Source/HTML/PIC/table.png | Bin 0 -> 494 bytes Source/HTML/PIC/tera-sign.svg | 3 + Source/HTML/PIC/tera.ico | Bin 0 -> 9662 bytes Source/HTML/PIC/up.png | Bin 0 -> 1632 bytes Source/HTML/PIC/viewer.png | Bin 0 -> 2652 bytes Source/HTML/PIC/wallet.png | Bin 0 -> 5515 bytes Source/HTML/PIC/wallet16.png | Bin 0 -> 461 bytes Source/HTML/SOUND/click.mp3 | Bin 0 -> 1088 bytes Source/HTML/SOUND/coin.mp3 | Bin 0 -> 4347 bytes Source/HTML/SOUND/soundbt.mp3 | Bin 0 -> 1197 bytes Source/HTML/blockviewer.html | 174 ++ Source/HTML/chains.html | 1457 ++++++++++++++ Source/HTML/console.html | 523 +++++ Source/HTML/dapp-edit.html | 1048 ++++++++++ Source/HTML/dapp-frame.html | 594 ++++++ Source/HTML/history.html | 308 +++ Source/HTML/mobile-wallet.html | 716 +++++++ Source/HTML/monitor.html | 832 ++++++++ Source/HTML/network.html | 530 +++++ Source/HTML/password.html | 339 ++++ Source/HTML/stat.html | 382 ++++ Source/HTML/tera.ico | Bin 0 -> 9662 bytes Source/HTML/wallet.html | 2062 +++++++++++++++++++ Source/HTML/web-wallet.html | 669 +++++++ Source/core/base.js | 440 ++++ Source/core/block-exchange.js | 1644 +++++++++++++++ Source/core/block-loader-const.js | 16 + Source/core/block-loader.js | 1503 ++++++++++++++ Source/core/buffer.js | 364 ++++ Source/core/code.js | 245 +++ Source/core/connect.js | 1595 +++++++++++++++ Source/core/constant.js | 330 +++ Source/core/crypto-library.js | 1106 +++++++++++ Source/core/db/block-db.js | 1193 +++++++++++ Source/core/db/db-row.js | 256 +++ Source/core/db/db.js | 120 ++ Source/core/geo.js | 87 + Source/core/html-server.js | 1987 +++++++++++++++++++ Source/core/library.js | 483 +++++ Source/core/log-strict.js | 38 + Source/core/log.js | 267 +++ Source/core/node.js | 614 ++++++ Source/core/rest-loader.js | 503 +++++ Source/core/rest_tables.js | 73 + Source/core/server.js | 1239 ++++++++++++ Source/core/startlib.js | 50 + Source/core/terahashmining.js | 227 +++ Source/core/transaction-validator.js | 368 ++++ Source/core/transfer-msg.js | 258 +++ Source/core/update.js | 189 ++ Source/core/wallet.js | 269 +++ Source/package.json | 27 + Source/process/api-exchange.js | 412 ++++ Source/process/dogs.js | 10 + Source/process/main-process.js | 789 ++++++++ Source/process/pow-process.js | 97 + Source/process/static-process.js | 425 ++++ Source/process/tx-process.js | 411 ++++ Source/process/web-process.js | 984 +++++++++ Source/run-node.bat | 5 + Source/run-node.js | 10 + Source/run-nw.js | 76 + Source/run-test.js | 13 + Source/set-test.js | 15 + Source/set.js | 14 + Source/system/accounts.js | 1606 +++++++++++++++ Source/system/dapp.js | 78 + Source/system/file.js | 44 + Source/system/messager.js | 143 ++ Source/system/names.js | 40 + Source/system/smart.js | 1431 +++++++++++++ run-node.bat | 7 + run-nw.bat | 2 + 131 files changed, 49139 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Source/HTML/123 create mode 100644 Source/HTML/CSS/blockviewer.css create mode 100644 Source/HTML/CSS/buttons.css create mode 100644 Source/HTML/CSS/codes.css create mode 100644 Source/HTML/CSS/history.css create mode 100644 Source/HTML/CSS/mobile-wallet.css create mode 100644 Source/HTML/CSS/style.css create mode 100644 Source/HTML/CSS/wallet.css create mode 100644 Source/HTML/JS/client-electron.js create mode 100644 Source/HTML/JS/client.js create mode 100644 Source/HTML/JS/coinlib.js create mode 100644 Source/HTML/JS/crypto-client.js create mode 100644 Source/HTML/JS/dapp-inner.js create mode 100644 Source/HTML/JS/diagram.js create mode 100644 Source/HTML/JS/highlight-html.js create mode 100644 Source/HTML/JS/highlight-js.js create mode 100644 Source/HTML/JS/highlight.js create mode 100644 Source/HTML/JS/lexer.js create mode 100644 Source/HTML/JS/marked.js create mode 100644 Source/HTML/JS/mobile-wallet.js create mode 100644 Source/HTML/JS/sec256k1.js create mode 100644 Source/HTML/JS/sha3.js create mode 100644 Source/HTML/JS/sign-lib-min.js create mode 100644 Source/HTML/JS/terahashlib.js create mode 100644 Source/HTML/JS/tx-lib.js create mode 100644 Source/HTML/JS/wallet-lib.js create mode 100644 Source/HTML/JS/wallet-node.js create mode 100644 Source/HTML/JS/wallet-web.js create mode 100644 Source/HTML/PIC/B.svg create mode 100644 Source/HTML/PIC/T.svg create mode 100644 Source/HTML/PIC/TeraLogo.svg create mode 100644 Source/HTML/PIC/Tera_logo.svg create mode 100644 Source/HTML/PIC/Tera_logo2.svg create mode 100644 Source/HTML/PIC/add-icon.svg create mode 100644 Source/HTML/PIC/address_book.png create mode 100644 Source/HTML/PIC/blank.svg create mode 100644 Source/HTML/PIC/chains.png create mode 100644 Source/HTML/PIC/check.svg create mode 100644 Source/HTML/PIC/console.png create mode 100644 Source/HTML/PIC/console0.png create mode 100644 Source/HTML/PIC/counters.png create mode 100644 Source/HTML/PIC/dapp.png create mode 100644 Source/HTML/PIC/down-arrow.svg create mode 100644 Source/HTML/PIC/down.png create mode 100644 Source/HTML/PIC/glass.svg create mode 100644 Source/HTML/PIC/info.svg create mode 100644 Source/HTML/PIC/invoice.png create mode 100644 Source/HTML/PIC/invoice2.png create mode 100644 Source/HTML/PIC/key.png create mode 100644 Source/HTML/PIC/lock_closed.png create mode 100644 Source/HTML/PIC/lock_open.png create mode 100644 Source/HTML/PIC/monitor.png create mode 100644 Source/HTML/PIC/network.png create mode 100644 Source/HTML/PIC/reload.svg create mode 100644 Source/HTML/PIC/right-arrow.svg create mode 100644 Source/HTML/PIC/smart.png create mode 100644 Source/HTML/PIC/table.png create mode 100644 Source/HTML/PIC/tera-sign.svg create mode 100644 Source/HTML/PIC/tera.ico create mode 100644 Source/HTML/PIC/up.png create mode 100644 Source/HTML/PIC/viewer.png create mode 100644 Source/HTML/PIC/wallet.png create mode 100644 Source/HTML/PIC/wallet16.png create mode 100644 Source/HTML/SOUND/click.mp3 create mode 100644 Source/HTML/SOUND/coin.mp3 create mode 100644 Source/HTML/SOUND/soundbt.mp3 create mode 100644 Source/HTML/blockviewer.html create mode 100644 Source/HTML/chains.html create mode 100644 Source/HTML/console.html create mode 100644 Source/HTML/dapp-edit.html create mode 100644 Source/HTML/dapp-frame.html create mode 100644 Source/HTML/history.html create mode 100644 Source/HTML/mobile-wallet.html create mode 100644 Source/HTML/monitor.html create mode 100644 Source/HTML/network.html create mode 100644 Source/HTML/password.html create mode 100644 Source/HTML/stat.html create mode 100644 Source/HTML/tera.ico create mode 100644 Source/HTML/wallet.html create mode 100644 Source/HTML/web-wallet.html create mode 100644 Source/core/base.js create mode 100644 Source/core/block-exchange.js create mode 100644 Source/core/block-loader-const.js create mode 100644 Source/core/block-loader.js create mode 100644 Source/core/buffer.js create mode 100644 Source/core/code.js create mode 100644 Source/core/connect.js create mode 100644 Source/core/constant.js create mode 100644 Source/core/crypto-library.js create mode 100644 Source/core/db/block-db.js create mode 100644 Source/core/db/db-row.js create mode 100644 Source/core/db/db.js create mode 100644 Source/core/geo.js create mode 100644 Source/core/html-server.js create mode 100644 Source/core/library.js create mode 100644 Source/core/log-strict.js create mode 100644 Source/core/log.js create mode 100644 Source/core/node.js create mode 100644 Source/core/rest-loader.js create mode 100644 Source/core/rest_tables.js create mode 100644 Source/core/server.js create mode 100644 Source/core/startlib.js create mode 100644 Source/core/terahashmining.js create mode 100644 Source/core/transaction-validator.js create mode 100644 Source/core/transfer-msg.js create mode 100644 Source/core/update.js create mode 100644 Source/core/wallet.js create mode 100644 Source/package.json create mode 100644 Source/process/api-exchange.js create mode 100644 Source/process/dogs.js create mode 100644 Source/process/main-process.js create mode 100644 Source/process/pow-process.js create mode 100644 Source/process/static-process.js create mode 100644 Source/process/tx-process.js create mode 100644 Source/process/web-process.js create mode 100644 Source/run-node.bat create mode 100644 Source/run-node.js create mode 100644 Source/run-nw.js create mode 100644 Source/run-test.js create mode 100644 Source/set-test.js create mode 100644 Source/set.js create mode 100644 Source/system/accounts.js create mode 100644 Source/system/dapp.js create mode 100644 Source/system/file.js create mode 100644 Source/system/messager.js create mode 100644 Source/system/names.js create mode 100644 Source/system/smart.js create mode 100644 run-node.bat create mode 100644 run-nw.bat diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b63c084 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2017-2018 The Tera Core developers (progr76@gmail.com) + +Not for evil. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..05131d2 --- /dev/null +++ b/README.md @@ -0,0 +1,232 @@ +# TERA PLATFORM + +* What is the project and what he does we recommend you to read this review article: [Decentralized applications on TERA platform](https://medium.com/@evkara777/decentralized-applications-on-tera-platform-2aa56b597ae9) +* The following describes how to install it on your computer + + +## Light-wallet (web-version) +https://terafoundation.org/web-wallet.html +* Note: the light wallet has a decentralized core - i.e. it works with all available nodes in the network + + +## Installing light wallet from setup on Windows: +* https://gitlab.com/terafoundation/tera/raw/master/Bin/Light/tera_light_setup.exe +* [Light client (zip)](https://gitlab.com/terafoundation/tera/raw/master/Bin/Light/Tera-light.zip) + + +## Installing full node from setup on Windows: +https://gitlab.com/terafoundation/tera/raw/master/Bin/Full/tera_full_setup.exe + + +## Installing full node from source code by steps: + +Attention: +* After the installation shown below, enter the address your server in the browser. Example: 12.34.56.78:8080 +* For mining You must have a static (public) IP address and an open port. +* We recommend not storing private keys on remote servers. +* We recommend putting an additional password on the private key ("Set password" button) - in this case the private key will be stored in file in encrypted form. +* If you do not set http-password, you can access only from the local address: 127.0.0.1:8080 +* For remote access to the node only from the specified computer set the HTTP_IP_CONNECT constant (for example: "HTTP_IP_CONNECT": "122.22.33.11") +* When installing, pay attention to the **secp256k1** cryptographic library. There should be no errors when compiling it (with command: npm install) + + + +## Installing on Windows: + +1. Download and install Nodejs https://nodejs.org +2. Download and install git https://git-scm.com/download/win +3. Then run the commands (in program: cmd or PowerShell): + +``` +cd ..\..\..\ +git clone https://gitlab.com/terafoundation/tera.git wallet +npm install --global --production windows-build-tools +npm install -g node-gyp +cd wallet/Source +npm install +node set httpport:8080 password: +run-node.bat + +``` +If you want to run the wallet as a background process, then instead of the last command (run-node.bat), do the following: +``` +npm install pm2 -g +pm2 start run-node.js +``` + +### Opening ports: +``` +netsh advfirewall firewall add rule name="Open 30000 port" protocol=TCP localport=30000 action=allow dir=IN +``` + +### Updates + +``` +cd wallet +git reset --hard +git clean -f +git pull +``` + + + +## Installation on Linux + +### CentOS 7: + + +``` +yum install -y git +curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash - +yum install -y nodejs +yum install gcc gcc-c++ +npm install pm2 -g +git clone https://gitlab.com/terafoundation/tera.git wallet +cd wallet/Source +npm install +node set httpport:8080 password: +pm2 start run-node.js +``` + +### open ports (all): +``` +systemctl stop firewalld +systemctl disable firewalld +``` + +### Updates + +``` +cd wallet +sudo git reset --hard +sudo git clean -f +sudo git pull +``` + + + +### UBUNTU 18.4: + +``` +apt-get install -y git +apt-get install -y nodejs +apt-get install -y npm +npm install pm2 -g +clone https://gitlab.com/terafoundation/tera.git wallet +apt install build-essential +apt group install "Development Tools" +cd wallet/Source +npm install +node set httpport:8080 password: +pm2 start run-node.js +``` + +### open ports: + +``` +sudo ufw allow 30000/tcp +sudo ufw allow 8080/tcp +sudo ufw allow 80/tcp +``` + + + + +### Updates + +``` +cd wallet +git reset --hard +git pull +``` + +## MAIN NETWORK +Default values: +``` +port:30000 +httpport:8080 +``` + + + +## TEST NETWORK +Default values: +``` +port:40000 +httpport:8080 +``` +Launch: +``` +cp -a Source SourceTest +cd SourceTest +node set-test httpport:8080 password:SecretWord +pm2 start run-test.js +``` + + + + + + + + +## Specification + +* Name: TERA +* Consensus: PoW +* Algorithm:  Terahash (sha3 + Optimize RAM hashing) +* Total suplay: 1 Bln +* Reward for block (befor 43 mln blocks): 1-20 coins, depends on network power (one billionth of the remainder of undistributed amount of coins and multiplied by the hundredth part of the square of the logarithm of the network power). With a block of 22.5 million, the power for the reward is limited to a constant of 43. +* Block size 130 KB +* Premine: 5% +* Development fund: 1% of the mining amount +* Block generation time: 1 second +* Block confirmation time: 8 seconds +* Speed: from 1000 transactions per second +* Commission: free of charge +* Cryptography: sha3, secp256k1 +* Protection against DDoS: PoW (hash calculation) +* Platform: Node.JS + + +# FAQs + +## Mining is possible only on a public IP +* Check the presence of a direct ip-address (order from the provider) +* Check if the port is routed from the router to your computer +* Check the firewall (port must open on the computer) + + + +## Refs: +* Web: http://terafoundation.org +* Btt: https://bitcointalk.org/index.php?topic=4573801.0 +* Twitter: https://twitter.com/terafoundation +* Telegram: https://web.telegram.org/#/im?p=@terafoundation +* Discord: https://discord.gg/CvwrbeG +* [White Paper](https://docs.google.com/document/d/1EaqFg1ncIxsrNE2M9xJOSzQu8z3ANwMuNyTX0z_A1ow/edit?usp=sharing) +* [DApp Paper](https://docs.google.com/document/d/1PXVBbMKdpsAKPkO9UNB5B-LMwIDjylWoHvAAzzrXjvU/edit?usp=sharing) +* [DEX-guide](https://docs.google.com/document/d/1qvVRfLq3qcYYF6dcsAAAqoGyBFF4njXUYZXZfTPWd2w/edit?usp=sharing) +* [BTC for DEX](https://docs.google.com/document/d/19vRY6tkbTP8tubZxM01llwnMyz4P6IzY0zvnargrU6k/edit?usp=sharing) +* [API](https://gitlab.com/terafoundation/tera/blob/master/Doc/Eng/API.md) +* [API-2 for Exchanges](https://gitlab.com/terafoundation/tera/blob/master/Doc/Eng/API2.md) +* [CONSTANTS](https://gitlab.com/terafoundation/tera/blob/master/Doc/Eng/CONSTANTS.MD) + + + +## Articles: +* [Decentralized applications on TERA platform](https://medium.com/@evkara777/decentralized-applications-on-tera-platform-2aa56b597ae9) +* [How does TERA platform work](https://medium.com/@Blockchainize1/how-does-tera-platform-work-cbfbeefdfc12) +* [More articles...](https://terafoundation.org/blog/) + + + +## Chinese +* [Mining guide (chinese PDF)](https://gitlab.com/terafoundation/tera/raw/master/Doc/Chinese/Mining.pdf?inline=false) +* [Tera White Paper (chinese PDF)](https://gitlab.com/terafoundation/tera/raw/master/Doc/Chinese/WP_chinese.pdf?inline=false) +* [Decentralized applications on TERA platform - Chinese](https://medium.com/@Blockchainize1/tera%E5%B9%B3%E5%8F%B0%E4%B8%8A%E7%9A%84%E5%8E%BB%E4%B8%AD%E5%BF%83%E5%8C%96%E5%BA%94%E7%94%A8-590f7663ecaf) + + +## RUS + [Эта же страница на русском](https://gitlab.com/terafoundation/tera/tree/master/Doc/Rus) + diff --git a/Source/HTML/123 b/Source/HTML/123 new file mode 100644 index 0000000..c3b1ac9 --- /dev/null +++ b/Source/HTML/123 @@ -0,0 +1,162 @@ +ࠢ 䠩 mobile-wallet.html _MOBILE-WALLET.HTML +***** mobile-wallet.html + + + +***** _MOBILE-WALLET.HTML + + + +***** + +***** mobile-wallet.html +
+
+

From:

+ + +***** + +***** mobile-wallet.html + + + +***** _MOBILE-WALLET.HTML + + + +***** + +***** mobile-wallet.html + + 2 000 000 000,00000000--> + + Description: + +***** _MOBILE-WALLET.HTML + + +***** + +***** mobile-wallet.html +
+ + +***** _MOBILE-WALLET.HTML +
+ + +***** + +***** mobile-wallet.html + + +
+***** _MOBILE-WALLET.HTML + + +
+***** + +***** mobile-wallet.html +
+ + +***** _MOBILE-WALLET.HTML +
+
+ +***** + +***** mobile-wallet.html +
+ Item.Name + +***** _MOBILE-WALLET.HTML +
+ Item.Name + +***** + +***** mobile-wallet.html +
+ dApp
+ logo +
+***** _MOBILE-WALLET.HTML +
+ dApp logo +
+***** + diff --git a/Source/HTML/CSS/blockviewer.css b/Source/HTML/CSS/blockviewer.css new file mode 100644 index 0000000..8dd89b8 --- /dev/null +++ b/Source/HTML/CSS/blockviewer.css @@ -0,0 +1,440 @@ +@import url('https://fonts.googleapis.com/css?family=Roboto:400,500,700&subset=cyrillic'); + +:root { + --blue-grey: #445368; + --light-blue: #718CAF; +} + +body { + margin: 0 auto; + box-sizing: border-box; + font-family: "Roboto", "Arial", sans-serif; + + color: #445368; + background: #f2f2f2; +} +main { + max-width: 1200px; + margin: 0 auto; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +*::placeholder { + color: rgba(0, 0, 0, 0.5); + font-family: "Roboto"; + font-size: 16px; +} + +a { + text-decoration: none; + color: #445368; +} + +a:hover, +a:focus { + color: #718CAF; +} + +.hidden { + display: none; +} + +.btn { + color: #fff; + background: #445368; + + display: block; + padding: 15px 25px; + border: none; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.3); + border-radius: 4px; + + text-decoration: none; + text-transform: uppercase; + font-family: inherit; + font-weight: 500; + font-size: 18px; + line-height: 21px; + cursor: pointer; + text-align: center; +} + +.btn:hover, +.btn:focus { + background: #718CAF; + color: #fff; +} + +.btn[disabled], +.btn.disabled { + opacity: .3; + cursor: not-allowed; +} + + +.reconnect { + background-image: url("./../PIC/reload.svg"); + background-repeat: no-repeat; + background-size: 18px 18px; + background-position: center center; + padding: 5px; + width: 27px; + margin-left: 5px; +} +.reconnect span { + display: none; +} +.grey-btn { + border: none; + background: #F2F2F2; + border-radius: 3px; + font-family: inherit; + font-size: 14px; + line-height: 16px; + color: #000; + height: 27px; + white-space: nowrap; +} + +.header { + background: linear-gradient(270deg, #3D4C61 0%, #445368 100%); + color: #fff; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15); +} +.header__wrapper { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + height: 100%; +} + +.header__logo { + margin-right: 2px; + padding: 0 15px; +} +.header__logo-link:hover { + opacity: .5; + cursor: pointer; +} +.header__right { + display: flex; + align-items: center; + position: relative; +} +.header__right select { + /* background: var(--blue-grey); */ + background: transparent; + border: none; + color: #fff; + padding: 5px; + width: 55px; + font-family: inherit; + font-size: 12px; + line-height: 14px; +} +.white-select { + display: none; +} +.header__nav { + width: 100%; +} +.page-pagination { + display: flex; + justify-content: center; + padding-bottom: 15px; + padding-right: 15px; +} +.page-pagination__num { + width: 33%; + margin: 0 5px; +} +.page-pagination .btn { + padding: 0; + white-space: nowrap; + width: 45px; + height: 40px; + margin: 0 5px; +} +.page-pagination .btn:first-of-type { + margin-left: 0; +} +.page-pagination .btn:last-of-type { + margin-right: 0; +} +.back-link { + cursor: pointer; + position: relative; + width: 30px; +} +.back-link span { + display: none; +} +.back-link::before { + content: ''; + display: block; + color: var(--blue-grey); + background: url("./../PIC/right-arrow.svg") no-repeat; + background-size: 18px 18px; + transform: translateY(-50%) rotate(180deg); + height: 18px; + width: 18px; + position: absolute; + left: 15px; + top: 50%; +} +.back-link:hover::before, +.back-link:focus::before { + opacity: .5; +} + + + + + +.back-link--history { + position: absolute; + top: 15px; + font-size: 16px; + line-height: 19px; + font-weight: 400; + text-decoration: none; +} + +.back-link--history::before { + left: 0; +} + +.back-link--history span { + display: block; + padding-left: 23px; +} + +.header__logo--history { + padding: 10px 15px; +} + +.history-page { + position: relative; + padding-bottom: 110px; +} +.history__text-block { + padding: 65px 15px 10px; +} +.history-page__id { + margin-bottom: 7px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + color: #445368; +} +.history-page__id-count { + max-width: 140px; + margin-left: 2px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + text-align-last: left; + color: #445368; + border: none; + background: #f2f2f2; +} +.history-page__description { + margin: 0; + margin-bottom: 17px; + font-size: 12px; + line-height: 14px; + color: rgba(0, 0, 0, 0.5); +} +.history-page__balance { + display: flex; + align-items: center; + + font-size: 14px; + line-height: 16px; + font-weight: 700; +} +.history-page__tera-icon { + margin-left: 5px; +} +.history-page__table { + padding: 15px 0 15px 15px; + background-color: #ffffff; +} +.history-page__table .grid { + width: 100%; + border-collapse: collapse; +} +.history-page__table-wrap { + overflow-x: auto; +} +.history-page__table th { + background: var(--blue-grey); + color: #fff; + border-right: 1px solid rgba(0, 0, 0, 0.1); + height: 30px; + padding: 0 20px 0 10px; + font-size: 16px; + line-height: 19px; + font-weight: 400; + white-space: nowrap; + text-align-last: left; +} +.history-page__table th:first-child { + border-top-left-radius: 5px; +} +.history-page__table th:last-child { + border-top-right-radius: 5px; +} +.history-page__table .grid tr td { + padding: 8px 10px 6px; + font-size: 14px; + line-height: 16px; + white-space: nowrap; + color: #000000; + background-color: #ffffff; + border: 1px solid #C4C4C4; +} +.history-page__table .grid tr td:first-of-type { + text-align: center; +} +.history-page__table .grid tr td.code +{ + white-space: normal; + width: 340px; + min-width:200px; + font-family: "courier new", "times new roman", monospace; + font-size: small; + word-break: break-all; +} + +.history-page__table .grid tr td.hash +{ + white-space: normal; + width: 160px; + font-family: "courier new", "times new roman", monospace; + font-size: small; + word-break: break-all; +} + + + +@media (min-width: 576px) { + .history__text-block { + padding: 65px 33px 10px; + } + .history-page__table { + padding: 15px 33px; + } + .back-link::before { + left: 0; + } +} + +@media (min-width: 768px) { + .header { + height: 60px; + } + .fixed-status .grey-btn { + height: 40px; + padding: 10px; + } + .reconnect { + width: 40px; + margin-left: 15px; + background-position: 11px 11px; + } + .main-logo { + width: 125px; + height: 30px; + } + .back-link { + display: block; + padding-left: 34px; + } + .back-link span { + display: inline; + text-decoration: underline; + } + .back-link::before { + left: 0; + } + .back-link--history span { + padding-left: 0; + } +} + +@media (min-width: 960px) { + .header__logo { + padding-left: 20px; + } + .reconnect.grey-btn { + width: 130px; + margin-left: 15px; + padding-top: 12px; + padding-left: 39px; + background-position: 11px 11px; + } + .reconnect span { + display: inline; + } + .history-page { + padding-right: 40px; + padding-left: 40px; + } + .history__text-block { + padding: 65px 0 10px; + } + .history-page__table { + padding: 15px 0 10px; + } +} + +@media (min-width: 1200px) { + .header__wrapper { + max-width: 1150px; + margin: 0 auto; + } + .history__text-block { + padding-top: 60px; + padding-bottom: 0; + } + .history-page__id { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__id-count { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__description { + margin-bottom: 30px; + font-size: 16px; + line-height: 19px; + text-align: center; + } + .history-page__balance { + justify-content: center; + margin-bottom: 30px; + font-size: 16px; + line-height: 19px; + } + .history-page__table { + padding: 30px 15px; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + background-color: #ffffff; + } + .page-pagination { + padding-bottom: 20px; + } +} diff --git a/Source/HTML/CSS/buttons.css b/Source/HTML/CSS/buttons.css new file mode 100644 index 0000000..5ddad06 --- /dev/null +++ b/Source/HTML/CSS/buttons.css @@ -0,0 +1,201 @@ +/*Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons Buttons*/ + +/*TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader TabHeader*/ +#TabHeader th +{ + /*border: 1px solid black;*/ + width:205px; + + color: white; + background-color: #22547f; + margin: 0; + padding: 0; + height: 34px; +} +.bttab +{ + background-color: black; + vertical-align: middle; + cursor: pointer; + color: white; + width: 100%; + margin: 0; + padding-top: 7px; + height: 100%; +} + + + + + +/*User action*/ +.btdoit +{ + width: 130px; + height: 32px; + line-height: 20px; + margin: 10px; + cursor: pointer; + font-family: monospace; +} + + +.btdoitm +{ + width: 40px; + height: 32px; + line-height: 20px; + margin: 10px; + cursor: pointer; + font-family: monospace; +} + + +/*All buttons*/ +.bt +{ + border: 1px solid black; + background: transparent; + font-weight: 400; + cursor: pointer; +} +.bt:hover,button:hover +{ + filter: brightness(120%); + cursor: pointer; +} + + +.bt:active +{ + cursor: progress; +} + + + + +.btlong +{ + width: 160px; +} + +.btopen +{ + width: 136px; +} + +.btsmall +{ + width: 10px; +} + +#idSendList +{ + width: 800px; + border: 0 solid black; + font-weight: 700; +} + +#PaiListInfo +{ + width: 800px; + height:20px; + text-align: center; +} + +.btinvoice +{ + width: 133px; + min-width: 133px; + max-width: 133px; + height:40px; + white-space: pre-line; + background-image: url('/HTML/PIC/invoice.png'); + background-repeat: no-repeat; + background-size: 16px; +} +.btinvoice_use +{ + width: 110px; + min-width: 110px; + max-width: 110px; + height:62px; +} + + + +#idCounters +{ + background-image: url('/HTML/PIC/counters.png'); + background-repeat: no-repeat; + background-size: 16px; +} + +#idChains +{ + background-image: url('/HTML/PIC/chains.png'); + background-repeat: no-repeat; + background-size: 16px; +} + + + + +#idMonitor +{ + background-image: url('/HTML/PIC/monitor.png'); + background-repeat: no-repeat; + background-size: 16px; +} + +#idConsole +{ + background-image: url('/HTML/PIC/console.png'); + background-repeat: no-repeat; + background-size:16px; +} +#idOpenSmart +{ + background-image: url('/HTML/PIC/smart.png'); + background-repeat: no-repeat; + background-size:16px; +} + +#idNetwork +{ + background-image: url('/HTML/PIC/network.png'); + background-repeat: no-repeat; + background-size:16px; +} + +#idDown +{ + background-image: url('/HTML/PIC/down.png'); + background-repeat: no-repeat; + background-size: 16px; + background-position: right; +} + +#idUp +{ + background-image: url('/HTML/PIC/up.png'); + background-repeat: no-repeat; + background-size: 16px; + background-position: right; + + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(82, 168, 236, 1); +} + + + + +.btexlporer +{ + width: 136px; +} + + +input[type="checkbox"] +{ + vertical-align: middle; +} diff --git a/Source/HTML/CSS/codes.css b/Source/HTML/CSS/codes.css new file mode 100644 index 0000000..7766620 --- /dev/null +++ b/Source/HTML/CSS/codes.css @@ -0,0 +1,90 @@ +/* + +Google Code style (c) Aahan Krish +Some correct by vtools + +*/ + +.hljs { + display: block; + overflow-x: auto; + padding: 0.5em; + background: #eff5eb; + color: black; +} + +.hljs-comment, +.hljs-quote { + color: #800; +} + +.hljs-keyword, +.hljs-selector-tag, +.hljs-section, +.hljs-title, +.hljs-name { + color: #008; +} + +.hljs-variable, +.hljs-template-variable { + color: #660; +} + +.hljs-string, +.hljs-selector-attr, +.hljs-selector-pseudo, +.hljs-regexp { + color: #080; +} + +.hljs-literal, +.hljs-symbol, +.hljs-bullet, +.hljs-meta, +.hljs-number, +.hljs-link { + color: #066; +} + +.hljs-title, +.hljs-doctag, +.hljs-type, +.hljs-attr, +.hljs-built_in, +.hljs-builtin-name, +.hljs-params { + color: #606; +} + +.hljs-attribute, +.hljs-subst { + color: #000; +} + +.hljs-formula { + background-color: #eee; + font-style: italic; +} + +.hljs-selector-id, +.hljs-selector-class { + color: #9B703F +} + +.hljs-addition { + background-color: #baeeba; +} + +.hljs-deletion { + background-color: #ffc8bd; +} + +.hljs-doctag, +.hljs-strong { + font-weight: bold; +} + +.hljs-emphasis { + font-style: italic; +} diff --git a/Source/HTML/CSS/history.css b/Source/HTML/CSS/history.css new file mode 100644 index 0000000..eb2382e --- /dev/null +++ b/Source/HTML/CSS/history.css @@ -0,0 +1,440 @@ +@import url('https://fonts.googleapis.com/css?family=Roboto:400,500,700&subset=cyrillic'); + +:root { + --blue-grey: #445368; + --light-blue: #718CAF; +} + +body { + margin: 0 auto; + box-sizing: border-box; + font-family: "Roboto", "Arial", sans-serif; + + color: #445368; + background: #f2f2f2; +} +main { + max-width: 1200px; + margin: 0 auto; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +*::placeholder { + color: rgba(0, 0, 0, 0.5); + font-family: "Roboto"; + font-size: 16px; +} + +a { + text-decoration: none; + color: #445368; +} + +a:hover, +a:focus { + color: #718CAF; +} + +.hidden { + display: none; +} + +.btn { + color: #fff; + background: #445368; + + display: block; + padding: 15px 25px; + border: none; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.3); + border-radius: 4px; + + text-decoration: none; + text-transform: uppercase; + font-family: inherit; + font-weight: 500; + font-size: 18px; + line-height: 21px; + cursor: pointer; + text-align: center; +} + +.btn:hover, +.btn:focus { + background: #718CAF; + color: #fff; +} + +.btn[disabled], +.btn.disabled { + opacity: .3; + cursor: not-allowed; +} + + +.reconnect { + background-image: url("./../PIC/reload.svg"); + background-repeat: no-repeat; + background-size: 18px 18px; + background-position: center center; + padding: 5px; + width: 27px; + margin-left: 5px; +} +.reconnect span { + display: none; +} +.grey-btn { + border: none; + background: #F2F2F2; + border-radius: 3px; + font-family: inherit; + font-size: 14px; + line-height: 16px; + color: #000; + height: 27px; + white-space: nowrap; +} + +.header { + background: linear-gradient(270deg, #3D4C61 0%, #445368 100%); + color: #fff; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15); +} +.header__wrapper { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + height: 100%; +} + +.header__logo { + margin-right: 2px; + padding: 0 15px; +} +.header__logo-link:hover { + opacity: .5; + cursor: pointer; +} +.header__right { + display: flex; + align-items: center; + position: relative; +} +.header__right select { + /* background: var(--blue-grey); */ + background: transparent; + border: none; + color: #fff; + padding: 5px; + width: 55px; + font-family: inherit; + font-size: 12px; + line-height: 14px; +} +.white-select { + display: none; +} +.header__nav { + width: 100%; +} +.header__wallet-set { + margin-left: 5px; +} +.header__wallet-link { + color: rgba(255, 255, 255, 0.6); + display: block; + padding: 13px 15px 9px 15px; +} +.header__wallet-link:hover, +.header__wallet-link:focus { + background: #55657C; +} +.header__wallet-link span { + display: none; +} +.header__wallet-link:active, +.header__wallet-link.active { + box-shadow: inset 0px 4px 0px rgba(255, 255, 255, 0.25); +} +.dapps-page__pagination, +.page-pagination { + display: flex; + justify-content: center; + padding-bottom: 15px; + padding-right: 15px; +} +.page-pagination__num { + width: 33%; + margin: 0 5px; +} +.page-pagination .btn { + padding: 0; + white-space: nowrap; + width: 45px; + height: 40px; + margin: 0 5px; +} +.page-pagination .btn:first-of-type { + margin-left: 0; +} +.page-pagination .btn:last-of-type { + margin-right: 0; +} + +.back-link { + position: relative; + width: 30px; + cursor: pointer; +} +.back-link span { + display: none; +} +.back-link::before { + content: ''; + display: block; + color: var(--blue-grey); + background: url("./../PIC/right-arrow.svg") no-repeat; + background-size: 18px 18px; + transform: translateY(-50%) rotate(180deg); + height: 18px; + width: 18px; + position: absolute; + left: 15px; + top: 50%; +} +.back-link:hover::before, +.back-link:focus::before { + opacity: .5; +} + + + +.header__logo--history { + padding: 10px 15px; +} + +.back-link--history { + position: absolute; + top: 15px; + font-size: 16px; + line-height: 19px; + line-height: 19px; + font-weight: 400; + text-decoration: none; +} + +.back-link--history::before { + left: 0; +} + +.back-link--history span { + display: block; + padding-left: 23px; +} + +.history-page { + padding-bottom: 110px; +} +.history__text-block { + position: relative; + padding: 10px 15px 10px; +} +.history-page__id { + margin-bottom: 7px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + color: #445368; +} +.history-page__id-count { + max-width: 140px; + margin-left: 2px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + text-align-last: left; + color: #445368; + border: none; + background: #f2f2f2; +} +.history-page__description { + margin: 0; + margin-bottom: 17px; + font-size: 12px; + line-height: 14px; + color: rgba(0, 0, 0, 0.5); +} +.history-page__balance { + display: flex; + align-items: center; + + font-size: 14px; + line-height: 16px; + font-weight: 700; +} +.history-page__tera-icon { + margin-left: 5px; +} +.history-page__table { + padding: 15px 0 15px 15px; +} +.history-page__table .grid { + width: 100%; + border-collapse: collapse; +} +.history-page__table-wrap { + overflow-x: auto; +} +.history-page__table th { + background: var(--blue-grey); + color: #fff; + border-right: 1px solid rgba(0, 0, 0, 0.1); + height: 30px; + padding: 0 20px 0 10px; + font-size: 16px; + line-height: 19px; + font-weight: 400; + white-space: nowrap; + text-align-last: left; +} +.history-page__table th:first-child { + border-top-left-radius: 5px; +} +.history-page__table th:last-child { + border-top-right-radius: 5px; +} +.history-page__table .grid tr td { + padding: 2px 10px 0px; + font-size: 14px; + line-height: 16px; + color: #000000; + background-color: #ffffff; + border: 1px solid #C4C4C4; +} + +.history-page__table .grid tr th.direct { + max-width:30px; +} + +.history-page__table .grid tr td.date +{ + white-space: nowrap; +} + + +.history-page__table .grid tr td:first-of-type { + text-align: center; +} + +.history-page__table .grid tr td:first-of-type b { + font-size: 16px!important; + font-weight: 700!important; +} +.history-page__table .grid tr td.desc { + max-width: 170px; + word-break: break-all; +} + +@media (min-width: 576px) { + .history__text-block { + padding: 10px 33px 10px; + } + .history-page__table { + padding: 15px 33px; + } + .back-link::before { + left: 0; + } +} + +@media (min-width: 768px) { + .header { + height: 60px; + } + .main-logo { + width: 125px; + height: 30px; + } + .back-link { + display: block; + padding-left: 34px; + } + .back-link span { + display: inline; + text-decoration: underline; + } + .back-link::before { + left: 0; + } + .back-link--history span { + padding-left: 0; + } +} + +@media (min-width: 960px) { + .header__logo { + padding-left: 20px; + } + .history-page { + padding-right: 40px; + padding-left: 40px; + } + .history__text-block { + padding: 10px 0 10px; + } + .history-page__table { + padding: 15px 0 10px; + } +} + +@media (min-width: 1200px) { + .header__wrapper { + max-width: 1150px; + margin: 0 auto; + } + .history__text-block { + padding-top: 10px; + padding-bottom: 0; + } + .history-page__id { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__id-count { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__description { + margin-bottom: 20px; + font-size: 16px; + line-height: 19px; + min-height: 20px; + text-align: center; + } + .history-page__balance { + justify-content: center; + margin-bottom: 30px; + font-size: 16px; + line-height: 19px; + } + .history-page__table { + padding: 30px 15px; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + background-color: #ffffff; + } + .page-pagination { + padding-bottom: 20px; + } +} \ No newline at end of file diff --git a/Source/HTML/CSS/mobile-wallet.css b/Source/HTML/CSS/mobile-wallet.css new file mode 100644 index 0000000..a0a22ec --- /dev/null +++ b/Source/HTML/CSS/mobile-wallet.css @@ -0,0 +1,2755 @@ +@import url('https://fonts.googleapis.com/css?family=Roboto:400,500,700&subset=cyrillic'); + +:root { + --blue-grey: #445368; + --light-blue: #718CAF; + --fill--password:#000; +} + + + + +body { + margin: 0 auto; + box-sizing: border-box; + font-family: "Roboto", "Arial", sans-serif; + + color: #445368; + background: #f2f2f2; +} +main { + max-width: 1200px; + margin: 0 auto; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} + +*::placeholder { + color: rgba(0, 0, 0, 0.5); + font-family: "Roboto"; + font-size: 16px; +} + +a { + text-decoration: none; + color: #445368; +} + +a:hover, +a:focus { + color: #718CAF; +} + +.hidden { + display: none; +} +.small-text { + font-size: 10px; + line-height: 12px; +} +.text-center { + text-align: center; +} +#overlay { + background: rgba(68, 83, 104, 0.65); + position: fixed; + width: 100%; + height: 100vh; + top: 0; + display: none; +} + +label[for=load-key] { + font-weight: 500; +} +textarea { + resize: none; +} +.currentBlockNum { + font-size: 14px; + padding: 15px; + padding-bottom: 0; + margin-bottom: 15px; +} +.currentBlockNum span { + display: inline-block; + min-width: 220px; +} +.currentBlockNum p { + display: flex; + justify-content: space-between; + margin-top: 8px; + margin-bottom: 8px; +} +.currentBlockNum p:first-child { + margin-top: 0; +} +.currentBlockNum p:last-child { + margin-bottom: 0; +} +.btn { + color: #fff; + background: #445368; + + display: block; + padding: 15px 25px; + border: none; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.3); + border-radius: 4px; + + text-decoration: none; + text-transform: uppercase; + font-family: inherit; + font-weight: 500; + font-size: 18px; + line-height: 21px; + cursor: pointer; + text-align: center; +} + +.btn:hover, +.btn:focus { + background: #718CAF; + color: #fff; +} + +.btn[disabled], +.btn.disabled { + opacity: .3; + cursor: not-allowed; +} + +.btn span { + opacity: .4; +} + +.btn--white { + background: #fff; + color: #445368; +} + +.btn--white:hover, +.btn--white:focus { + background: #F5F5F5; + color: #445368; +} + +.btn--full-width { + width: 100%; + border-radius: 0px; +} +.btn--270 { + max-width: 270px; + border-radius: 4px; +} + +.btn--450 { + max-width: 450px; + margin: 0 auto; +} + +.btn--center { + margin: 0 auto; +} + +.grey-btn { + border: none; + background: #F2F2F2; + border-radius: 3px; + font-family: inherit; + font-size: 14px; + line-height: 16px; + color: #000; + height: 27px; + + white-space: nowrap; +} + +.btn-no-bg { + color: var(--blue-grey); + background: none; + border: none; + text-transform: uppercase; + font-weight: 500; + font-size: 16px; + line-height: 19px; +} + +.prod-card__drop-btn:hover, +.prod-card__drop-btn:focus { + background: rgba(0, 0, 0, 0.1); +} + +.title { + font-weight: 700; + font-size: 20px; + line-height: 23px; +} + +.welcome, +.wallet-settings, +.wallet-settings--info, +.accounts-info, +.accounts-info__add, +.accounts-info__empty, +.send-page, +.dapps-page, +.explorer-page { + padding-bottom: 50px; +} + +.explorer-page { + padding-bottom: 100px; +} + +.header { + background: linear-gradient(270deg, #3D4C61 0%, #445368 100%); + color: #fff; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15); +} +.header__wrapper { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; +} + +.header__logo { + margin-right: 2px; + padding: 0 15px; +} +.header__logo-link:hover { + opacity: .5; + cursor: pointer; +} +.header__right { + display: flex; + align-items: center; + position: relative; +} +.header__right select { + /* background: var(--blue-grey); */ + background: transparent; + border: none; + color: #fff; + padding: 5px; + width: 55px; + font-family: inherit; + font-size: 12px; + line-height: 14px; +} +.white-select { + display: none; +} +.header__nav { + width: 100%; +} +.header__wallet-set { + margin-left: 5px; +} +.header__wallet-link { + color: rgba(255, 255, 255, 0.6); + display: block; + padding: 13px 15px 9px 15px; +} +.header__wallet-link:hover, +.header__wallet-link:focus { + background: #55657C; +} +.header__wallet-link span { + display: none; +} +.header__wallet-link:active, +.header__wallet-link.active { + box-shadow: inset 0px 4px 0px rgba(255, 255, 255, 0.25); + } + + +.nav-tabs__list { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + justify-content: space-between; + + font-size: 13px; +} + +.nav-tabs__tab { + background: linear-gradient(270deg, #3D4C61 0%, #445368 100%); + width: 100%; + text-align: center; +} + +.nav-tabs__link { + color: rgba(255, 255, 255, 0.6); + text-decoration: none; + padding: 17px 14px 18px 14px; + display: block; +} + +.tab-link:hover { + background: linear-gradient(180deg, #3D4C61 0%, #445368 100%); + opacity: .6; + cursor: pointer; +} + +.tab-link.active, +.tab-link:focus { + color: #fff; + background: #55657C; + box-shadow: inset 0px 4px 0px rgba(255, 255, 255, 0.25); + cursor: pointer; +} + +.welcome { + text-align: center; + padding: 50px 15px 35px; +} + +.welcome__link { + max-width: 270px; + margin: 0 auto; +} + +.social-list { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: 0 auto; + max-width: 300px; + list-style-type: none; + padding: 50px 0 80px; + font-weight: 500; +} + +.social-list__item { + text-align: center; + padding: 10px 0; + +} + +.social-list__item a { + padding: 0 15px; + border-right: 1px solid rgba(68, 83, 104, .2); +} + +.social-list__item:nth-child(3n) a { + border-right: none; +} + +.fixed-status { + background: var(--blue-grey); + color: rgba(255, 255, 255, 1); + font-size: 12px; + padding: 12px 10px 11px 10px; + padding-left: 17px; + + position: fixed; + bottom: 0; + left: 0; + + width: 100%; + font-family: "Roboto", sans-serif; +} +.fixed-status__wrap { + display: flex; + align-items: center; + max-width: 1170px; + margin: 0 auto; +} + +.fixed-status__status { + max-width: 330px; + margin-right: auto; + padding-right: 10px; +} +.fixed-status .grey-btn { + padding-left: 3px; + font-size: 12px; + line-height: 14px; +} + +.reconnect { + background-image: url("./../PIC/reload.svg"); + background-repeat: no-repeat; + background-size: 18px 18px; + background-position: center center; + padding: 5px; + width: 27px; + margin-left: 5px; +} +.reconnect span { + display: none; +} + + +.key-field { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + font-size: 16px; + line-height: 18px; + padding-bottom: 16px; + color: #445368; +} + +.key-field:first-of-type { + padding-top: 15px; +} + +.key-field__title { + font-weight: 500; + margin: 0 auto 0 0; + padding-left: 15px; +} + +.key-field__title span { + opacity: .6; +} +@media (max-width: 445px) +{ + .key-field__title--pay { + margin-bottom: 5px; + } +} + +.key-field__input { + width: 100%; + padding: 15px 15px 13px 14px; + margin: 10px 0 0; + border: 1px solid rgba(0, 0, 0, 0.1); + font-family: inherit; + font-size: 16px; + line-height: 18px; + color: #445368; +} +.key-field__input--select { + padding: 11px 15px 11px 10px; + font-size: 18px; + line-height: 21px; +} +.key-field__input--enter { + padding-top: 20px; + padding-bottom: 18px; +} + +.key-field__btn { + margin-right: 15px; +} + +.key-field__btn--with-icon { + display: flex; + align-items: center; +} +.key-field__btn--with-icon svg { + margin-left: 10px; +} + +.key-field__textarea { + width: 100%; + background: #FFFFFF; + border: 1px solid rgba(0, 0, 0, 0.1); + min-height: 50px; + padding: 21px 15px 14px 15px; + margin-top: 8px; + font-family: "Roboto", "Arial", sans-serif; + font-size: 16px; + line-height: 18px; + color: #445368; +} +.key-field__textarea--mobile { + max-height: 50px; + padding-top: 15px; +} + +.key-field__select::placeholder, +.key-field__input::placeholder, +.key-field__textarea::placeholder { + color: rgba(68, 83, 104, .5); + font-size: 16px; + font-family: "Roboto", "Arial", sans-serif; + color: #445368; + opacity: 0.5; +} + +.key-field__select { + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.1); + padding: 12px 15px; + margin-top: 5px; + background: #fff; + font-family: inherit; + font-size: 16px; + line-height: 18px; + color: #445368; +} +.key-field__key-place { + word-wrap: break-word; + padding: 10px 15px; + width: 100%; + margin-bottom: 0; +} +.back-link { + position: relative; + width: 30px; +} +.back-link span { + display: none; +} +.back-link::before { + content: ''; + display: block; + color: var(--blue-grey); + background: url("./../PIC/right-arrow.svg") no-repeat; + background-size: 18px 18px; + transform: translateY(-50%) rotate(180deg); + height: 18px; + width: 18px; + position: absolute; + left: 15px; + top: 50%; +} +.back-link:hover::before, +.back-link:focus::before { + opacity: .5; +} +.wallet-settings__title-wrap { + position: relative; + display: flex; + align-items: center; + margin-bottom: 15px; +} +.wallet-settings__title, +.accounts-info__title { + display: block; + padding: 15px 20px; + text-align: center; + margin: 0 auto; +} +.accounts-info__title { + margin-left: calc(50% - 133px); +} +.wallet-settings__btns-wrapper { + display: flex; + /*flex-wrap: wrap;*/ + align-items: center; + justify-content: center; + padding: 0 20px 20px; +} +.wallet-settings__ok-btns { + padding-top: 30px; + flex-wrap: wrap; +} +.wallet-settings__ok-btns .btn { + width: 100%; +} + +.wallet-settings__btns-wrapper .btn { + margin: 10px 10px; + white-space: nowrap; + min-width: 125px; + text-align: center; +} + +.wallet-settings__go-btn { + margin: 0 auto; + min-width: 270px; + text-align: center; + + margin-bottom: 60px; + margin-top: auto; +} + +.wallet-settings__hidden-field { + border-top: 1px dotted var(--blue-grey); + width: 100%; + margin: 20px 15px 5px; +} + +.wallet-settings__btn-270.btn { + max-width: 270px; + margin: 0 auto; + border-radius: 4px; +} + +.accounts-info__counter { + text-transform: uppercase; + padding: 15px; + margin: 0; +} + +.accounts-info__btn { + margin: 30px auto 50px; +} + +.accounts-info__add-btn { + margin-top: 44px; + max-width: 330px; +} + +.accounts-info__to-send-link { + padding: 15px; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + display: block; + position: relative; + width: 100%; + margin-bottom: 15px; + font-family: inherit; + font-size: 16px; + line-height: 18px; +} + +.accounts-info__to-send-link::after { + content: ''; + color: var(--blue-grey); + background: url("./../PIC/right-arrow.svg") no-repeat; + position: absolute; + background-size: 17px 17px; + transform: translateY(-50%); + height: 17px; + width: 17px; + right: 15px; + top: 50%; +} + +.accounts-info__to-send-link:hover::after, +.accounts-info__to-send-link:focus::after { + opacity: .5; +} + +.accounts-info__counter-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + -ms-align-items: center; + align-items: center; + padding: 0 15px 15px; +} + +.accounts-info__counter-wrapper .title { + margin: 0; + margin-right: -30px; +} + +.accounts-info__empty img { + width: 100%; + display: none; +} +.accounts-info__empty-img img:first-child { + display: block; +} + +.prod-list { + list-style-type: none; + padding: 0; + margin: 0; +} +.prod-card { + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + background: #FFFFFF; + margin-bottom: 5px; +} +.prod-card__dapp-icon { + margin-right: 2px; +} + +.prod-card__first-line, +.prod-card__second-line, +.prod-card__footer { + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: -o-flex; + display: flex; + justify-content: space-between; + white-space: nowrap; +} + +.prod-card__first-line { + padding: 15px; +} + +.prod-card__second-line { + padding: 0 15px 15px; +} + +.prod-card__footer { + padding: 15px; +} + +.prod-card__id { + color: #445368; + opacity: 0.5; + margin-right: 15px; + min-width: 56px; +} +.prod-card__id:first-of-type { + min-width: 45px; +} +.prod-card__id--desktop { + display: none; +} +.prod-card__id--mobile { + display: none; +} +.prod-card__id--mobile-active { + display: none; +} +@media (max-width: 960px) { + .prod-card__id--mobile { + display: block; + } +} +@media (min-width: 960px) { + .prod-card__id--desktop { + display: block; + } +} +.prod-card__descr { + width: 100%; + text-overflow: ellipsis; + overflow: hidden; +} + +.prod-card__heading { + font-size: 21px; + line-height: 25px; + font-weight: bold; + color: var(--blue-grey); + + display: flex; + -ms-align-items: center; + align-items: center; + justify-content: flex-end; + width: 100%; + /*padding-bottom: 15px;*/ +} +.prod-card__currency { + margin-left: 5px; + position: relative; +} +.prod-card__currency--with-dot:after { + content: ''; + position: absolute; + top: 0; + right: 0; + background: var(--blue-grey); + width: 5px; + height: 5px; + border-radius: 50%; +} +.prod-card__footer-empty { + position: relative; + padding-left: 40px; + color: rgba(0, 0, 0, 0.2);; +} +.prod-card__footer-empty:before { + content: ''; + position: absolute; + background-image: url("./../PIC/add-icon.svg"); + background-size: 30px 30px; + top: 50%; + transform: translateY(-50%); + left: 0; + width: 30px; + height: 30px; + border-radius: 50%; +} +.prod-card__heading sup { + font-size: 13px; + line-height: 15px; + vertical-align: unset; +} + +.prod-card__dropdown { + cursor: pointer; + position: absolute; + right: 38px; + top: 10px; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.15); + background: #fff; + border-radius: 4px; + width: 197px; + z-index: 20; +} +.prod-card__dropdown a { + display: block; + padding: 10px 15px; +} +.prod-card__drop-btn +{ + cursor: pointer; + padding: 6px 12px; + border-radius: 4px; +} +.accounts-info__accounts { + padding: 0 15px; + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + background: #FFFFFF; +} +.accounts-info__accounts .prod-card { + /*padding: 0 15px;*/ + box-shadow: none; + background: transparent; + font-size: 12px; + line-height: 14px; +} +.accounts-info__accounts .prod-card__heading { + width: 50%; +} +.prod-card__coins-count { + font-weight: normal; + font-size: 14px; + line-height: 16px; + padding: 0 15px; + display: inline; + text-align: right; +} +.accounts-info__accounts .prod-card__id { + opacity: 1; + text-decoration: underline; + margin-right: auto; +} +.accounts-info__accounts .prod-card__first-line { + flex-wrap: wrap; +} +.accounts-info__accounts .prod-card__second-line { + padding: 15px; +} +.prod-card__footer { + border-top: 1px solid rgba(68, 83, 104, 0.1); + position: relative; +} + +.prod-card__link +{ + cursor: pointer; + display: flex; + -ms-align-items: center; + align-items: center; + width: 100%; +} + +.prod-card__link:hover:after { + opacity: .5; +} + +.prod-card__link--empty { + color: rgba(68, 83, 104, 0.5); +} + +.prod-card__link--empty:before { + background: url("./../PIC/chain-off.svg") no-repeat; + opacity: .2; +} +.prod-card__link--info::after { + display: none; +} +.prod-card__link--info::before { + content: ""; + position: absolute; + top: 0; + left: -28px; + background: url("./../PIC/info.svg") no-repeat; + width: 18px; + height: 18px; +} +.prod-card__link--info { + position: relative; + width: auto; + margin-left: auto; +} +.accounts-info__total { + padding: 20px 15px 30px; +} + +.prod-card--line { + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: -o-flex; + display: flex; + justify-content: space-between; + padding: 10px 0; + margin-bottom: 0; +} +.prod-card--line:not(:last-child) { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} +.prod-card--line .prod-card__first-line { + width: 100%; + padding: 0 15px 0 0; +} +.prod-card--line .prod-card__footer { + border: 0; + border-left: 1px solid rgba(0, 0, 0, 0.1); + padding: 0 0 0 15px; + display: flex; + align-items: center; + +} +.prod-card--line .prod-card__footer-wrap { + width: 100%; + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: -o-flex; + display: flex; + -ms-align-items: center; + align-items: center; + justify-content: flex-start; +} + +.prod-card__dapp-name { + margin-left: 10px; + margin-right: auto; +} + +.prod-card__dapp-text { + font-size: 14px; + line-height: 16px; + color: rgba(0, 0, 0, 0.2); +} + +.total-info__item { + display: -webkit-flex; + display: -moz-flex; + display: -ms-flex; + display: -o-flex; + display: flex; + justify-content: space-between; + width: 100%; + font-size: 14px; + line-height: 16px; + padding: 5px 0; +} + +.total-info__item dd { + font-weight: bold; +} +.send-page__send-btn { + border-radius: 4px; +} + +.send-page__send-btn-wrp { + padding: 0 15px 80px; +} +.send-page__setting .title, +.dapps-page .title { + display: none; +} + +.light-grey-text { + color: rgba(0, 0, 0, 0.5); +} +.lighter-grey-text { + color: rgba(0, 0, 0, 0.2); +} +.grey-blue-text { + color: var(--blue-grey); +} +.confirm-info { + font-size: 16px; + max-width: 950px; +} +.confirm-info__line { + display: flex; + align-items: flex-start; + padding: 15px; +} +.confirm-info__line .btn-no-bg { + margin-left: auto; +} +.confirm-info__line p { + margin: 0; +} +.confirm-info__title { + min-width: 90px; + text-align: right; + font-weight: 500; +} +.confirm-info__descr { + text-align: left; + margin-left: 3%; + font-weight: 500; +} +.confirm-info__descr p { + padding-top: 10px; +} +.send-page__btns-wrap { + display: flex; + justify-content: space-between; + padding: 20px 15px 80px; +} +.send-page__btns-wrap .btn { + width: 48%; + margin: 0; +} + +.dapps-page__field.key-field { + padding-top: 0; + margin-top: 5px; +} +.dapps-page__field select { + padding: 15px; + background: #fff; +} +.dapps-page__pagination, +.page-pagination { + display: flex; + justify-content: center; + padding-bottom: 15px; + padding-right: 15px; +} +.page-pagination__num { + width: 33%; + margin: 0 5px; +} +.dapps-page__pagination input, +.page-pagination input { + margin: 0 5px; + border: 1px solid rgba(0, 0, 0, 0.1); +} +.dapps-page__page-btns, +.page-pagination .btn { + padding: 0; + white-space: nowrap; + width: 45px; + height: 40px; + margin: 0 5px; +} +.page-pagination .btn:first-of-type { + margin-left: 0; +} +.page-pagination .btn:last-of-type { + margin-right: 0; +} + + +.prod-card__second-line-left ol { + margin-top: 10px; + border-radius: 3px; + padding: 0; + list-style-position: inside; + display: flex; + flex-wrap: wrap; +} +.prod-card__second-line-left li { + border: 1px solid rgba(68, 83, 104, 0.1); + padding: 5px 10px; + margin: 0 10px 10px 0; + border-radius: 4px; +} +.prod-card__second-line-left p { + white-space: normal; + display: none; + padding-top: 5px; + margin-top: 0; +} +.dapp-modal__ok-token:before { + content: ""; + background: url("./../PIC/check.svg") no-repeat; + background-size: 20px 15px; + width: 20px; + height: 15px; + display: inline-block; + margin-right: 10px; +} +@media (max-width: 440px) { + .dapp-modal .modal__close { + left: -15px; + top: -15px; + } +} +@media (max-width: 960px) { + .modal__title-wrap { + margin-right: auto; + } +} + +.dapp-modal { + padding-bottom: 100px; +} + +.modal { + padding: 30px; + background: #fff; + border-radius: 3px; + color: #000; + position: fixed; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.15); + margin: 0 auto; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + max-width: 570px; + width: 87%; + z-index: 10; +} +.dapp-modal { + top: calc(50% - 210px); + left: 20px; + right: 20px; + transform: none; +} +.modal h3 { + font-size: 20px; + line-height: 23px; + letter-spacing: 0.02em; + margin: 15px 0 10px; + color: var(--blue-grey); +} +.modal__header { + display: flex; +} +.password-modal { + display: none; + padding: 15px 15px 45px 15px; + padding-top: 15px; + padding-bottom: 45px; + border-radius: 5px; +} +.password-modal__title { + width: 100%; + margin-top: 0; + margin-bottom: 10px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + text-align: center; + text-transform: uppercase; + color: #445368; +} +.password-modal__subtitle { + width: 100%; + margin-top: 0; + margin-bottom: 30px; + + font-size: 16px; + line-height: 18px; + text-align: center; + color: rgba(0, 0, 0, 0.5); +} +.password-modal--change .password-modal__input--margin { + margin-bottom: 30px; +} +.password-modal--set .btn, +.password-modal--change .btn { + max-width: 48%; + width: 48%; +} +.password-modal__buttons--one .btn { + width: 100%; +} +.password-modal__subtitle--red { + color: #eb5757; +} +.password-modal__input { + width: 100%; + margin-bottom: 10px; + padding: 11px 15px; + font-size: 18px; + line-height: 21px; + color: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 4px; +} +.password-modal__input:last-of-type { + margin-bottom: 30px; +} +.password-modal__buttons { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} +.password-modal__buttons--one { + justify-content: center; +} +.password-modal__buttons .btn { + font-size: 16px; + line-height: 19px; +} +.password-modal__link { + display: block; + width: 100%; + margin-top: 30px; + font-size: 16px; + line-height: 19px; + font-weight: 500; + text-align: center; + text-transform: uppercase; + text-decoration: underline; + color: rgba(0, 0, 0, 0.5); +} +@media (max-width: 359px) { + .password-modal__buttons .btn { + padding: 15px 20px; + } + .accounts-info__add-btn { + max-width: 320px; + } +} +@media (min-width: 500px) { + .password-modal__buttons .btn { + width: 100%; + } +} +.modal__ol { + list-style-position: inside; + display: flex; + flex-wrap: wrap; + padding: 0; +} +.modal__category { + padding: 5px 10px; + border: 1px solid rgba(68, 83, 104, 0.1); + margin: 0 10px 10px 0; +} +.dapp-modal__token-status { + margin: 15px 0 0; +} +.dapp-modal__token-status .light-grey-text { + color: rgba(0, 0, 0, 0.2); +} +.modal__img-wrap { + padding: 10px 15px; + text-align: center; +} +.modal__img-wrap img { + display: block; + margin-bottom: 5px; +} + +.modal__btns-wrap { + display: flex; + justify-content: center; + padding: 30px 0 0; +} +.modal__close { + position: absolute; + width: 50px; + height: 50px; + box-shadow: 0px 5px 10px rgba(68, 83, 104, 0.15); + top: -25px; + left: -25px; + border: 0; + border-radius: 50%; +} +.modal__close::before, +.modal__close::after { + content: ""; + position: absolute; + left: 24px; + height: 35px; + width: 2px; + top: 7px; + background-color: var(--blue-grey); +} + +.modal__close:before { + transform: rotate(45deg); +} + +.modal__close:after { + transform: rotate(-45deg); +} +.modal__btns-wrap .btn { + margin: 0 15px; + padding: 14px 10px; + min-width: 125px; +} +.modal-lock { + position: absolute; + top: 17px; + right: 26px; + display: block; + width: 14px; + height: 18px; +} +.modal-lock2 { + position: absolute; + top: 18px; + right: 10px; + display: block; + width: 10px; + height: 20px; +} + + +.def-list__item { + display: flex; + align-items: center; +} +.def-list__item dt { + min-width: 90px; + margin-right: 30px; +} + +.def-list__item a { + text-decoration: underline; +} +.def-list__item dd { + margin: 5px 0; +} + +.explorer-page__explore { + margin-top: 10px; + text-align: left; + position: relative; + font-weight: bold; + font-size: 20px; + line-height: 23px; + text-transform: none; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + padding: 18px 12px 17px 15px; +} + +.explorer-page__explore:last-of-type { + margin-bottom: 20px; +} + +.explorer-page__explore::after { + content: ""; + display: block; + background: url("./../PIC/down-arrow.svg") no-repeat; + background-size: 20px 15px; + width: 20px; + height: 15px; + position: absolute; + top: 50%; + right: 13px; + transform: translateY(-50%); +} +.explorer-page__explore.btpress { + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: none; +} +.explorer-page__explore.btpress::after { + transform: translateY(-50%) rotate(180deg); +} + +.dapps__table { + width: 100%; +} +.dapps__table tr { + margin-bottom: 5px; +} +.dapps__table .prod-card__link--info { + font-size: 14px; + line-height: 16px; +} +.dapps__table .prod-card__id { + font-size: 16px; + line-height: 19px; + opacity: 1; +} +.dapps__table .prod-card__heading { + font-size: 20px; + line-height: 23px; +} +.dapps__table tr:first-of-type .dapp_card { + padding-bottom: 15px; +} + +.explorer-page__table { + background: #fff; + padding: 15px 0 15px 15px; + display: none; +} +.explorer-page__table-wrap { + overflow-x: scroll; +} +.dapps-page .explorer-page__table-wrap { + overflow-x: hidden; +} +.explorer-page__explore.btpress + .explorer-page__table { + display: block; +} +.explorer-page__table .grid { + border-collapse: collapse; + border-spacing: 1px; +} +.explorer-page__table th { + background: var(--blue-grey); + color: #fff; + border-right: 1px solid rgba(0, 0, 0, 0.1); + height: 30px; + padding: 0 40px; + font-size: 16px; + line-height: 19px; + font-weight: 400; + white-space: nowrap; +} +.explorer-page__table th:first-child { + border-top-left-radius: 5px; +} +.explorer-page__table th:last-child { + border-top-right-radius: 5px; +} + +.explorer-page__table td { + padding: 5px; + border: 1px solid #C4C4C4; + text-align: center; + font-size: 14px; + line-height: 16px; + letter-spacing: 0.02em; +} + +.explorer-page__table td:nth-child(2) { + text-align: right; +} +.explorer-page__table td:nth-child(4) { + text-align: left; +} +td.hash { + font-size: 10px; + line-height: 12px; + letter-spacing: 0.02em; + width: 220px; + min-width: 120px; + word-break: break-all; +} +.history-page__table .grid tr td.code +{ + white-space: normal; + width: 340px; + min-width:200px; + font-family: "courier new", "times new roman", monospace; + font-size: small; + word-break: break-all; +} +.explorer-page__table td.pubkey { + width: 215px; + min-width: 215px; +} + +.explorer-page__table td a { + text-decoration: underline; +} + +.explorer-page__table .num button { + background: none; + border: none; + color: var(--blue-grey); + text-decoration: underline; +} + +.explorer-page__diagramms canvas { + width: 100%; +} +.explorer-page__diagramms b { + padding-left: 15px; +} +.explorer-page__diagramms .delete { + margin-left: 10px; + margin-bottom: 10px; + background: #fff; + border: none; + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); +} +.addresses-modal { + padding-bottom: 0; +} +.addresses-modal .title { + text-transform: uppercase; + text-align: center; + color: #445368; + margin: 0; + padding-bottom: 15px; + font-size: 20px; +} +.addresses-modal__input { + padding: 14px 15px; + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.1); + + background: url("./../PIC/glass.svg") no-repeat; + -webkit-background-size: 18px 18px; + background-size: 18px 18px; + background-position: 98% 13px; +} +.addresses-modal__list { + padding: 0 15px; + margin: 0; + list-style-type: none; + max-height: 350px; + overflow-y: auto; +} +.addresses-modal__item { + padding: 10px 0; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + display: flex; + position: relative; +} +.addresses-modal__text-wrap { + display: flex; + justify-content: space-between; + -ms-align-items: center; + align-items: center; + width: 100%; + padding-right: 30px; +} +.addresses-modal__btns-wrapper { + display: flex; + justify-content: flex-end; + padding-right: 5px; + position: absolute; + top: 4px; + width: 100%; + background: rgba(255,255,255,.6); +} +.addresses-modal__delete-item { + position: absolute; + right: 0; + top: 21px; + height: 15px; + width: 15px; + background: none; + border: none; +} +.addresses-modal__delete-item::before, +.addresses-modal__delete-item::after { + content: ""; + position: absolute; + left: 9px; + height: 15px; + width: 2px; + top: 0; + background-color: var(--blue-grey); +} + +.addresses-modal__delete-item:before { + transform: rotate(45deg); +} + +.addresses-modal__delete-item:after { + transform: rotate(-45deg); +} +.addresses-modal__btns-wrapper .btn { + width: 125px; + margin: 5px 10px; + padding: 10px 25px; +} +.addresses-modal__choosing { + display: flex; + justify-content: space-between; + padding-top: 30px; + padding-bottom: 30px; +} +.addresses-modal__choosing .btn { + width: 48%; + padding: 15px 0; +} +.wallet-settings__generate { + margin-bottom: 20px; +} + +.welcome__img .tablet { + display: none; + margin: 0 auto; +} + +.prod-card--line .mob-hidden { + display: none; +} + +@media (max-width: 1000px) +{ + .nomobile + { + display: none; + } +} + +@media (min-width: 576px) { + .welcome, + .wallet-settings--info, + .wallet-settings, + .accounts-info, + .send-page, + .dapps-page { + padding-left: 33px; + padding-right: 33px; + } + .welcome { + padding: 20px 0 0 0; + } + .wallet-settings__title { + padding: 15px 0; + } + .wallet-settings__title:before { + left: 5px; + } + .key-field__title { + padding-left: 0; + } + .wallet-settings__generate { + border-radius: 4px; + } + .social-list { + max-width: 100%; + padding: 102px 0 20px; + } + .accounts-info__counter-wrapper, + .accounts-info__total { + padding-left: 0; + padding-right: 0; + } + + .accounts-info__total h3 { + margin-top: 5px; + margin-bottom: 5px; + } + + .accounts-info__total .total-info { + margin-top: 10px; + } + .key-field__input { + padding-top: 20px; + padding-bottom: 18px; + } + .key-field { + padding-bottom: 30px; + } + .key-field:first-of-type { + padding-bottom: 16px; + } + .send-page__send-btn-wrp { + max-width: 330px; + width: 330px; + margin: 0 auto; + margin-top: 30px; + padding-left: 0; + padding-right: 0; + padding-bottom: 30px; + } + .dapps-page__field select { + padding-left: 10px; + } + /* .dapps-page__field select::placeholder { + color: rgba(0, 0, 0, 0.2); + } */ + .dapps-page__pagination, + .page-pagination { + max-width: 576px; + margin: 0 auto; + justify-content: space-between; + } + .explorer-page__explore.btpress + .explorer-page__table { + padding-left: 34px; + padding-right: 34px; + } + .dapps-page__page-btns, + .page-pagination .btn { + width: 60px; + height: 40px; + } + .dapps-page__page-btns:first-of-type { + margin-left: 0; + } + .page-pagination .btn:first-of-type { + margin-left: 0; + } + .dapps-page__page-btns:last-of-type { + margin-right: 0; + } + .page-pagination .btn:last-of-type { + margin-right: 0; + } + .dapps-page__pagination input, + .page-pagination input { + width: 150px; + height: 40px; + } + .dapps-page .key-field:first-of-type { + padding-bottom: 30px; + } + .dapps-page { + padding-bottom: 75px; + } + .explorer-page__explore { + padding-right: 33px; + padding-left: 33px; + } + .currentBlockNum { + padding-left: 33px; + padding-right: 33px; + } + .currentBlockNum p { + justify-content: flex-start; + } + .currentBlockNum p span:first-of-type { + min-width: 180px; + } + .explorer-page__explore::after { + right: 30px; + } + .explorer-page__table th.cur { + text-transform: uppercase; + } + .wallet-settings__field { + padding-bottom: 0; + } + .wallet-settings__generate { + margin: 0 auto; + margin-bottom: 5px; + max-width: 285px; + } + .back-link::before { + left: 0; + } + .accounts-info__add .key-field { + padding-bottom: 15px; + } + + .accounts-info__add .key-field__input { + max-height: 48px; + height: 48px; + padding-top: 15px; + padding-bottom: 14px; + } + .accounts-info__add .key-field__input--select { + padding-top: 10px; + padding-bottom: 10px; + } + .accounts-info__add-btn { + margin-top: 15px; + } + .dapp_card { + padding: 0; + } + .dapps-page__field.key-field { + margin-top: 10px; + } + .accounts-info__to-send-link { + padding-left: 0; + } +} + + +@media (min-width: 768px) { + .main-logo { + width: 125px; + height: 30px; + } + .no-mr { + margin-right: 0; + } + .accounts-info__counter-wrapper .title { + font-size: 28px!important; + line-height: 33px; + } + .header__logo { + margin-top: 3px; + } + .header__logo-link img { + height: 30px; + width: 125px; + } + .prod-card { + border-radius: 4px; + } + .welcome__img .mobile { + display: none; + } + .welcome__img .tablet { + display: block; + width: 410px; + height: 410px; + } + .wallet-settings__generate { + margin-bottom: 0; + } + .title { + font-size: 28px; + line-height: 33px; + } + .welcome, + .wallet-settings--info, + .wallet-settings, + .accounts-info, + .send-page, + .dapps-page { + padding-top: 45px; + padding-left: 39px; + padding-right: 39px; + } + .send-page { + padding-top: 25px; + } + .welcome { + padding-top: 53px; + } + .welcome__title.title { + margin-top: -8px; + margin-bottom: 30px; + } + .header { + height: 60px; + } + .header__wrapper { + flex-wrap: nowrap; + } + .header__right { + order: 2; + } + .header__right select { + text-decoration: underline; + width: 60px; + } + .header__wallet-link { + display: flex; + align-items: center; + padding: 18px 17px; + } + .header__wallet-link span { + white-space: nowrap; + margin-left: 10px; + display: block; + text-transform: uppercase; + } + .header__nav { + max-width: 350px; + } + .nav-tabs__list { + font-size: 16px; + line-height: 18px; + } + .nav-tabs__tab { + background: none; + } + .nav-tabs__link { + padding: 21px 15px; + + } + .welcome__link { + margin: 60px auto; + } + .social-list { + flex-wrap: wrap; + max-width: 550px; + padding-top: 0; + margin-top: 180px; + } + .header__right select { + position: absolute; + font-size: 14px; + line-height: 16px; + bottom: -38px; + right: 7px; + width: 60px; + border-radius: 3px; + background: transparent; + color: var(--blue-grey); + } + .white-select { + display: block; + border: 0; + padding: 10px; + margin: 10px 15px 10px auto; + } + .back-link { + display: block; + padding-left: 34px; + } + .back-link span { + display: inline; + text-decoration: underline; + } + .back-link::before { + left: 0; + } + .wallet-settings__title { + text-align: left; + padding: 30px 0; + } + .wallet-settings__ok-btns { + display: flex; + flex-wrap: nowrap; + } + .wallet-settings__btns-wrapper, + .wallet-settings__ok-btns { + padding: 30px 0; + justify-content: space-between; + } + .wallet-settings__btns-wrapper .btn, + .wallet-settings__ok-btns .btn { + margin: 0; + width: calc(50% - 15px); + max-width: none; + } + .wallet-settings__go-btn { + margin: 100px auto 150px; + width: 100%; + } + .accounts-info__empty-img { + display: flex; + justify-content: space-between; + padding-bottom: 100px; + } + .accounts-info__empty-img img { + width: 48%; + } + .accounts-info__empty-img img:nth-child(2) { + display: block; + } + .accounts-info__counter { + text-align: center; + font-size: 28px; + line-height: 33px; + } + .accounts-info__counter-wrapper { + padding: 15px 0 60px; + } + .accounts-info__to-send-link { + order: 2; + border-bottom: 0; + width: auto; + margin-bottom: 0; + padding-right: 35px; + } + .accounts-info__accounts .prod-card { + font-size: 16px; + line-height: 19px; + padding: 18px 0; + } + .accounts-info__to-send-link::after { + right: 0; + } + .accounts-info__counter-wrapper p { + order: 1; + + } + .accounts-info__counter-wrapper .btn { + order: 0; + } + .accounts-info__total { + padding-left: 0; + } + .total-info { + max-width: 330px; + } + .send-page .title, + .dapps-page .title { + display: block; + margin: 0 auto; + text-align: center; + padding-bottom: 0; + padding-top: 30px; + } + .send-page .title { + margin-bottom: 25px; + } + .send-page__send-btn-wrp { + padding-top: 75px; + } + .confirm-info__title { + min-width: 180px; + } + .confirm-info__descr { + margin-left: 30px; + } + .confirm-info__line .btn-no-bg { + margin-left: 0; + } + .send-page__btns-wrap { + padding-left: 0; + padding-right: 0; + justify-content: space-between; + padding-top: 75px; + } + .page-pagination { + justify-content: space-between; + max-width: 690px; + margin: 0 auto; + } + .page-pagination .btn { + width: 90px; + margin-left: 30px; + margin-right: 0; + } + .page-pagination .btn:first-child { + margin-left: 0; + } + .page-pagination input { + min-width: 210px; + margin-left: 30px; + } + .prod-card__second-line-left p { + white-space: normal; + display: block; + max-width: 550px; + } + .dapps-page__card .prod-card__heading { + justify-content: flex-start; + } + .prod-card__heading span { + display: inline; + } + .modal__btns-wrap .btn { + margin: 0 15px; + padding: 15px 10px; + min-width: 210px; + } + .accounts-info__accounts .prod-card__descr { + margin-top: 10px; + max-width: none; + } + .addresses-modal__item { + padding: 15px 0; + } + .addresses-modal__item-name { + display: flex; + width: 100%; + } + .addresses-modal__item-name .grey-blue-text { + width: 50%; + } + .addresses-modal__btns-wrapper .btn { + padding: 6px 30px; + } + .key-field__key-place { + margin: 0; + padding: 10px 0; + } + .fixed-status .grey-btn { + height: 40px; + padding: 10px; + } + .fixed-status .grey-btn--select { + padding-right: 0; + } + .reconnect { + width: 40px; + margin-left: 15px; + background-position: 11px 11px; + } + .fixed-status__status { + font-size: 14px; + max-width: none; + } + .currentBlockNum { + padding: 60px 30px 20px; + } + .key-field__title { + margin-bottom: 5px; + } + +.prod-card--line .prod-card__first-line { + flex-wrap: nowrap; + justify-content: flex-start; + -ms-align-items: center; + align-items: center; + } + .accounts-info__accounts .prod-card__id { + width: auto; + margin-right: 20px; + } + .prod-card--line .prod-card__descr { + order: 1; + margin: 0; + } + .prod-card--line .prod-card__heading { + order: 2; + margin-left: auto; + } + .send-page__field .key-field__input, + .key-field__select { + max-height: 58px; + height: 58px; + } + .key-field:first-of-type { + padding-bottom: 30px; + } + .send-page__send-btn-wrp { + max-width: 450px; + width: 450px; + padding-top: 25px; + } + .send-page__confirm .title { + margin-bottom: 40px; + } + .dapps-page .title { + margin-bottom: 10px; + padding-top: 20px; + } + .dapps-page .key-field:first-of-type { + padding-bottom: 15px; + } + .wallet-settings__title { + padding: 10px; + margin-bottom: 30px; + } + .wallet-settings__generate { + max-width: 100%; + width: 100%; + } + .wallet-wrapper { + margin-top: 90px; + } + .accounts-info__add .key-field { + padding-bottom: 25px; + } + .accounts-info__add-btn { + margin-top: 65px; + } + +} + + + +@media (max-width: 960px) { + /* .prod-card--line:hover { + transform: scale(1.05, 1.2); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); + padding:5px 15px; + background: #fff; + display: block; + transition: all ease .5s; + } + .prod-card--line:hover .mob-hidden { + display: inline-block; + } + .prod-card--line:hover .prod-card__footer { + padding: 5px; + padding-left: 0; + border: 0; + } + .prod-card--line:hover .prod-card__footer-empty { + padding: 10px 0 10px 40px; + } + .prod-card--line:hover .prod-card__coins-count { + padding-top: 5px; + } + .prod-card--line:hover .prod-card__coins-count, + .prod-card--line:hover .prod-card__id { + font-weight: 500; + } + .prod-card--line:hover .small-text { + font-weight: normal; + } + .prod-card--line:hover .id-sign { + display: inline-block; + font-weight: 500; + } + .prod-card--line:hover .prod-card__first-line { + padding-right: 0; + } */ + + .prod-card--active { + /* transform: scale(1.05, 1.2); + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); + padding:5px 15px; */ + background: #fff; + display: block; + transition: all ease .5s; + } + .prod-card--active .mob-hidden { + display: inline-block; + } + .prod-card--active .prod-card__footer { + padding: 5px; + padding-left: 0; + border: 0; + } + .prod-card--active .prod-card__footer-empty { + padding: 10px 0 10px 40px; + } + .prod-card--active .prod-card__coins-count { + padding-top: 5px; + } + .accounts-info__accounts .prod-card__id { + text-decoration: none; + } + .prod-card--active .prod-card__coins-count, + .prod-card--active .prod-card__id { + font-weight: 500; + } + .prod-card--active .prod-card__id { + text-decoration: underline; + } + .prod-card--active .prod-card__id--mobile { + display: none; + } + .prod-card--active .prod-card__id--mobile-active { + display: block; + } + .prod-card--active .small-text { + font-weight: normal; + } + .prod-card--active .id-sign { + display: inline-block; + font-weight: 500; + } + .prod-card--active .prod-card__first-line { + padding-right: 0; + } +} + +@media (min-width: 960px) { + .welcome { + padding-top: 22px; + } + .social-list { + margin-top: 160px; + } + .header__logo { + padding-left: 42px; + } + .header__wallet-set { + margin-right: 39px; + margin-left: 0; + } + .header__right select { + right: 37px; + } + .send-page__send-btn-wrp { + max-width: 100%; + width: 100%; + padding-top: 0; + padding-bottom: 70px; + } + .accounts-info__counter-wrapper .btn { + width: 210px; + } + .accounts-info__counter-wrapper { + padding-bottom: 20px; + } + .prod-card__dapp-icon { + margin-right: 10px; + } + .prod-card--line .mob-hidden { + display: inline-block; + } + .accounts-info__accounts .prod-card { + padding: 10px 0 9px 0; + } + .addresses-modal { + padding: 15px 15px 0; + } + .accounts-info__btn { + max-width: 350px; + } + .prod-card--line .prod-card__footer { + min-width: 300px; + } + .welcome__img .tablet { + width: 225px; + height: 225px; + } + .addresses-modal__list { + max-height: 480px; + } + .accounts-info__empty-img { + padding-bottom: 60px; + } + .accounts-info__empty-img img { + display: block; + width: 32%; + } + .send-page__send-btn-wrp .btn { + margin-right: 0; + max-width: 210px; + border-radius: 4px; + } + .wallet-settings__btns-wrapper { + padding: 0; + justify-content: flex-end; + } + .wallet-settings__ok-btns { + padding-top: 100px; + justify-content: flex-end; + } + .send-page__btns-wrap { + padding-left: 0; + padding-right: 0; + padding-top: 15px; + justify-content: flex-end; + } + .dapps-page .title { + margin-bottom: 30px; + } + .accounts-info__add .key-field { + max-width: 770px; + margin: 0 auto; + } + .accounts-info__add-btn { + max-width: 210px; + width: 210px; + margin-left: auto; + margin-right: 0; + } + .wallet-settings__go-btn { + max-width: 290px; + } + .send-page__btns-wrap .btn, + .wallet-settings__ok-btns .btn { + margin-left: 30px; + max-width: 210px; + } + .wallet-settings__btns-wrapper .btn { + margin-left: 30px; + max-width: 130px; + } + .send-page__field, + .key-field__select, + .confirm-info__line, + .dapps-page__card { + max-width: 770px; + margin: 0 auto; + } + .dapps-page__card { + margin-bottom: 10px; + } + .page-pagination { + justify-content: space-between; + max-width: 770px; + margin: 0 auto; + } + .page-pagination .btn { + width: 50px; + } + .page-pagination input { + min-width: 450px; + } + .prod-card__second-line-left p { + max-width: 595px; + } + .modal { + max-width: 770px; + } + .modal__title-wrap { + width: 100%; + } + .modal__def-list { + display: flex; + } + .def-list__item { + margin-right: 30px; + } + .def-list__item dt { + min-width: auto; + margin-right: 15px; + } + .addresses-modal__choosing { + justify-content: flex-end; + } + + .addresses-modal__choosing .btn { + max-width: 210px; + margin-left: 30px; + } + .wallet-settings__generate { + float: left; + width: auto; + } + .wallet-settings__field { + position: relative; + justify-content: flex-start; + max-width: 770px; + margin: 0 auto; + } + .key-field__title { + margin-right: 30px; + } + .send-page__field .key-field__title { + margin-right: auto; + } + .wallet-settings__field .key-field__btn:not(.wallet-settings__hiding) { + position: absolute; + right: 0; + bottom: 42px; + } + .wallet-wrapper { + max-width: 770px; + margin: 0 auto; + padding-top: 15px; + } + .wallet-settings__generate { + width: 210px; + } + .reconnect span { + display: inline; + } + .reconnect.grey-btn { + width: 130px; + margin-left: 15px; + padding-top: 12px; + padding-left: 39px; + background-position: 11px 11px; + } + .explorer-page { + padding-right: 40px; + padding-left: 40px; + } + .currentBlockNum { + font-size: 16px; + padding-left: 0; + } + .currentBlockNum p { + margin-top: 10px; + margin-bottom: 0; + } + .key-field__textarea { + max-height: 58px; + padding-top: 21px; + padding-bottom: 16px; + } + .wallet-settings__hiding { + margin-bottom: 8px; + } + .password-modal { + padding-top: 45px; + } + .explorer-page__explore::after { + right: 10px; + } + .explorer-page__explore { + padding-right: 15px; + padding-left: 15px; + } + .explorer-page__explore.btpress + .explorer-page__table { + padding-right: 15px; + padding-left: 15px; + } +} + +@media (max-height: 700px) { + .social-list { + margin-top: 100px; + } +} + +@media (min-width: 1200px) { + .header__logo { + padding-left: 20px; + } + .header__wallet-set { + margin-right: 18px; + } + .header__right select { + right: 10px; + } + .accounts-info__add .key-field { + min-width: 920px; + width: 920px; + } + .wallet-settings__title-wrap { + margin-bottom: 50px; + } + .welcome__img svg { + width: 180px; + height: 150px; + } + .header__wrapper { + max-width: 1150px; + margin: 0 auto; + } + .total-info { + max-width: 360px; + } + .send-page__send-btn-wrp .btn, + .send-page__btns-wrap .btn, + .wallet-settings__ok-btns .btn { + max-width: 250px; + } + + .wallet-settings__btns-wrapper .btn { + max-width: 160px; + } + .send-page__field, + .key-field__select, + .dapps-page__card { + max-width: 920px; + } + .key-field__select { + min-height: 58px; + } + .key-field { + margin-bottom: 0; + } + .currentBlockNum p span:first-of-type { + min-width: 250px; + } + .page-pagination { + justify-content: space-between; + max-width: 920px; + margin: 0 auto; + } + .page-pagination .btn { + width: 65px; + } + .page-pagination input { + min-width: 540px; + } + .prod-card__second-line-left p { + max-width: 715px; + } + .wallet-settings .wallet-settings__field, + .wallet-settings .btn--full-width, + .wallet-wrapper { + max-width: 920px; + margin-left: auto; + margin-right: auto; + } + .wallet-settings__go-btn { + max-width: 350px; + margin-left: auto; + margin-right: auto; + } + + .wallet-settings__ok-btns { + padding-top: 150px; + } + .wallet-settings__generate { + width: 250px; + } + .wallet-settings__info { + margin-bottom: 35px; + } + +} + +.delete +{ + position: absolute; + display: none; +} + +.myhidden +{ + display: none; +} + + + +.nodapp .editdapp +{ + display: none; +} + +.dapp_card +{ + width: 100%; +} + + + + + +/* правила стилей для history и explorer */ +.header__logo--history { + padding: 10px 15px; +} + +.history-page { + padding-bottom: 110px; +} +.history__text-block { + padding: 10px 15px 10px; +} +.history-page__id { + margin-bottom: 7px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + color: #445368; +} +.history-page__id-count { + max-width: 140px; + margin-left: 2px; + + font-size: 20px; + line-height: 23px; + font-weight: 700; + text-align-last: left; + color: #445368; + border: none; + background: #f2f2f2; +} +.history-page__description { + margin: 0; + margin-bottom: 17px; + font-size: 12px; + line-height: 14px; + color: rgba(0, 0, 0, 0.5); +} +.history-page__balance { + display: flex; + align-items: center; + + font-size: 14px; + line-height: 16px; + font-weight: 700; +} +.history-page__tera-icon { + margin-left: 5px; +} +.history-page__table { + padding: 15px 0 15px 15px; +} +.history-page__table .grid { + width: 100%; + border-collapse: collapse; +} +.history-page__table-wrap { + overflow-x: scroll; +} +.history-page__table th { + background: var(--blue-grey); + color: #fff; + border-right: 1px solid rgba(0, 0, 0, 0.1); + height: 30px; + padding: 0 20px 0 10px; + font-size: 16px; + line-height: 19px; + font-weight: 400; + white-space: nowrap; + text-align-last: left; +} +.history-page__table th:first-child { + border-top-left-radius: 5px; +} +.history-page__table th:last-child { + border-top-right-radius: 5px; +} +.history-page__table .grid tr td { + padding: 2px 10px 0px; + font-size: 14px; + line-height: 16px; + color: #000000; + background-color: #ffffff; + border: 1px solid #C4C4C4; +} +.history-page__table .grid tr td:first-of-type { + text-align: center; +} +.history-page__table .grid tr td.desc { + max-width: 170px; + word-break: break-all; +} + +@media (min-width: 576px) { + .history__text-block { + padding: 10px 33px 10px; + } + .history-page__table { + padding: 15px 33px; + } +} + +@media (min-width: 960px) { + .history-page { + padding-right: 40px; + padding-left: 40px; + } + .history__text-block { + padding: 10px 0 10px; + } + .history-page__table { + padding: 15px 0 10px; + } +} + +@media (min-width: 1200px) { + .history__text-block { + padding-top: 10px; + padding-bottom: 0; + } + .history-page__id { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__id-count { + font-size: 28px; + line-height: 33px; + text-align: center; + } + .history-page__description { + margin-bottom: 30px; + font-size: 16px; + line-height: 19px; + text-align: center; + } + .history-page__balance { + justify-content: center; + margin-bottom: 30px; + font-size: 16px; + line-height: 19px; + } + .history-page__table { + padding: 30px 15px; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0px 3px 5px rgba(0, 0, 0, 0.1); + background-color: #ffffff; + } +} + +/* vtools added*/ + +th.minwidth +{ + padding: 5px; +} +.minwidth +{ + width: 100px; +} +.accname +{ + width: 200px; + max-width: 200px; + word-break: break-all; +} + +.openblock +{ + cursor:pointer; +} + +.header__logo-name { + margin: 0; + margin-top: 3px; + font-size: 14px; + line-height: 18px; + font-weight: 300; + letter-spacing: 0.17em; + text-transform: uppercase; + color: #ffffff; +} + +.header__wrapper { + height: 100%; +} + +.header__logo-link { + display: flex; + flex-direction: column; +} + +iframe +{ + left:0; + top: 0px; + width: 100%; + height: 97vh; + border: 0; +} + +.pointer,.olink +{ + cursor: pointer; +} + diff --git a/Source/HTML/CSS/style.css b/Source/HTML/CSS/style.css new file mode 100644 index 0000000..649e8ae --- /dev/null +++ b/Source/HTML/CSS/style.css @@ -0,0 +1,180 @@ +/*STYLE STYLE STYLE*/ +/*STYLE STYLE STYLE*/ +/*STYLE STYLE STYLE*/ +/*STYLE STYLE STYLE*/ + + +root +{ + --colorText:#000; +} + + +body +{ + + font-family: Arial, sans-serif; + font-size: 15px; + font-weight: 300; +} + +body.styleBrown +{ + --color0:#503F13; + --color1:#86754A; + --color2:#F5E5BB; + --color3:#F5F0E3; +} + + +body.styleGreen +{ + --color0:#204410; + --color1:#4F7340; + --color2:#CBF4BA; + --color3:#E7F4E1; +} + +body.styleBlue +{ + --color0:#0D1B35; + --color1:#354259; + --color2:#BED0F2; + --color3:#E1E7F2; +} + +body.styleGray +{ + --color0:#424a5D; + --color1:#5b6775; + --color2:#FFFFFF; + --color3:#f3f3f4; +} + +body.styleContrast1 +{ + --color0:#101010; + --color1:#303030; + --color2: #b3cee8; + --color3:#FFFFFF; +} + +body.styleContrast2 +{ + --color0:#101010; + --color1:#303030; + --color2: #cee8df; + --color3:#FFFFFF; +} + +body.styleContrast3 +{ + --color0:#101010; + --color1:#303030; + --color2: #e8e0d4; + --color3:#FFFFFF; +} + + +body.univers .bt +{ + background: var(--color1); + color: var(--color2); +} +body.univers +{ + background: var(--color2); +} + +body.univers table.grid +{ + background:var(--color3); +} + +body.univers .bttab +{ + background: var(--color1); + color:white; +} + +body.univers .current +{ + background: var(--color0); + color:white; +} + +body.univers input,textarea,select +{ + background:white; + color: black; +} + + + + +/*dark*/ +body.styleDark +{ + background: #282828; + --colorText:white; + color: white; +} + +body.styleDark .bt +{ + background: #4c4c4c; + color: white; + border: 1px solid #5a5a5a; +} + +body.styleDark .header_tr +{ + color:white; +} + + +body.styleDark table.grid +{ + background: #565656; +} + +body.styleDark .bttab +{ + background: #202225; + color:white; +} + +body.styleDark .current +{ + background: #565b61; + color:white; +} + +body.styleDark input,textarea,select +{ + background:white; + color: black; +} + +/*bt*/ +body.styleDark button +{ + background: #939393; + border: 1px solid #282828; + color: white; +} + +body.styleDark .bold +{ + font-weight: 300; +} + +body.styleDark #idStatus +{ + color: #fffabd; +} + +body.styleDark #idOpenWallet +{ + border: 1px solid darkgrey; +} diff --git a/Source/HTML/CSS/wallet.css b/Source/HTML/CSS/wallet.css new file mode 100644 index 0000000..9247324 --- /dev/null +++ b/Source/HTML/CSS/wallet.css @@ -0,0 +1,535 @@ + +/*Списки*/ +table +{ + border-collapse: collapse; + width:100%; + max-width: 800px +} +.grid th, .grid td +{ + border: 1px solid #264378; + padding: 4px; +} +.grid td +{ + width: 60px; + vertical-align:top; +} + +.bold +{ + font-weight: 700; +} + +.smallbold +{ + font-weight: 600; +} + + + +td.sum +{ + text-align: right; + width: 120px; +} + + + +td.num +{ + text-align: right; + width: 50px; +} + +td.cur +{ + text-align: left; + width: 50px; + font-size:smaller; +} +td.snum +{ + text-align: center; + width: 20px; +} + +td.direct +{ + text-align: center; + vertical-align: middle; + width: 10px; + font-weight: 700; + font-size:x-large; +} +td.smart +{ + text-align: right; + width: 150px; + max-width: 200px; +} + + +td.txt +{ + text-align: left; + width: 100px; +} +td.desc +{ + text-align: left; + width: 200px; + max-width: 200px; + word-break: break-all; +} +td.name +{ + text-align: left; + width: 100px; +} + +td.date +{ + text-align: left; + width: 90px; + min-width: 70px; + font-size: small; +} + + + + + + +/*SEND SEND SEND SEND SEND SEND SEND SEND SEND SEND */ + +table.form_input +{ + border-collapse: collapse; + width: 600px; +} +.form_input td +{ + width: 20%; +} +.form_input td:nth-child(2) +{ + width: 80%; +} + +.form_input input, .form_input select +{ + width: 98%; + text-align: right; +} + +.form_input textarea +{ + width: 98%; + text-align: left; +} +.form_input .bsend +{ + width: 33%; + height: 30px; + text-align: center; + float: left; +} +.short_input input +{ + width: 120px; +} + + + + +td.code +{ + width: 240px; + font-family: "courier new", "times new roman", monospace; + font-size: small; + align-content: left; + text-align: left; + word-break: break-all; +} +td.code2 +{ + width: 340px; + font-family: "courier new", "times new roman", monospace; + font-size: small; + align-content: left; + text-align: left; + word-break: break-all; +} +.hash +{ + width: 250px; + min-width: 100px; + font-family: "courier new", "times new roman", monospace; + font-size: 60%; + align-content: left; + text-align: left; + word-break: break-all; +} +.pubkey +{ + width: 180px; + font-family: "courier new", "times new roman", monospace; + font-size: 75%; + align-content: left; + text-align: left; + word-break: break-all; +} + +.accname +{ + width: 180px; + font-family: Cambria,sans-serif; + align-content: left; + text-align: left; + word-break: break-all; +} + + +.verify +{ + +} + + + +/*CHECKBOX*/ + +.checkbox +{ + position: absolute; + z-index: -1; + opacity: 0; + margin: 10px 0 0 20px; +} +.checkbox + label +{ + position: relative; + padding: 0 20px 0 55px; + cursor: pointer; +} +.checkbox + label:before +{ + content: ''; + position: absolute; + top: -4px; + left: 0; + width: 50px; + height: 26px; + border-radius: 13px; + background: #CDD1DA; + box-shadow: inset 0 2px 3px rgba(0,0,0,.2); + transition: .2s; + +} +.checkbox + label:after +{ + content: ''; + position: absolute; + top: -2px; + left: 2px; + width: 22px; + height: 22px; + border-radius: 10px; + background: #FFF; + box-shadow: 0 2px 5px rgba(0,0,0,.3); + transition: .2s; +} +.checkbox:checked + label:before +{ + background: #46b319; +} +.checkbox_red:checked + label:before +{ + background: darkred; +} + +.checkbox_alert + label:before +{ + background: #f0b323; +} + +.checkbox:checked + label:after +{ + left: 26px; +} +.checkbox:focus + label:before +{ + box-shadow: inset 0 2px 3px rgba(0,0,0,.2), 0 0 0 3px rgba(217, 217, 217, 0.7); +} + + + + + + + + +#idPercentMining +{ + width: 40px; + text-align: center; + display:inline-block; +} + + + +#idNodeWhiteList +{ + width: 780px; +} + + +#idTotalSum +{ + text-align: left; +} + +/*CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG CONFIG*/ +/*Просмотр ключей*/ +table.keys +{ + border-collapse: collapse; + width: 800px; +} +/*Редактирование приватного ключа*/ +#grid_edit_key .bt +{ + width: 80px; + height: 30px; + line-height: 20px; + margin: 10px; + text-align: center; +} +#idTypeKey,#idShortNameText +{ + width: 250px; + height: 20px; + text-align: left; +} + +/*Редактирование new acc Name*/ +#grid_edit_newacc .bt, #grid_mining_set .bt, #idTablePassword1 .bt, #idTablePassword2 .bt +{ + width: 80px; + height: 30px; + line-height: 20px; + margin: 10px; + text-align: center; +} +#idAutoSetMining +{ + width: 16px; + height: 16px; + text-align: left; + top: 3px; + position:relative; +} + +#grid_accounts_all, #grid_history +{ + font-size: smaller; +} +#grid_history .red td +{ + color: darkred; +} +#grid_history .blue td +{ + color: darkblue; +} +#grid_history .green td +{ + color: darkgreen; +} + + + +/*idStatus Log operations*/ +#idStatus +{ + z-index:1000; + position:relative; + top:40px; + left:10px; + text-align: left; + width:800px; /* ширина блока */ + color:blue; +} + +#idServerLog +{ + width:100%; + max-width: 800px; + text-align: left; + + border: 1px solid black; + height: 220px; + padding: 1px; + margin: 0px; + + + font-family: "courier new", "times new roman", monospace; + font-size: 80%; + align-content: left; + word-break: break-all; + +} + +#idTransaction +{ + width:100%; + min-width: 795px; + max-width: 795px; + text-align: left; + color:#204410; +} + +#idDescription +{ + width: 95%; + min-width: 560px; + max-width: 560px; +} + + +#idAccount +{ + width: 100%; + text-align: left; + align-content: left; +} +#idTo +{ + width: 99%; + text-align: left; + align-content: left; +} + +#idSumSend +{ + background-color: #f0ffb9; + font-weight: 700; + width: 75%; +} + +#view_header +{ + width: 800px; + font-weight: 600; + color: black; + text-align: center; +} +.header_tr +{ + width: 600px; + font-weight: 600; + text-align: center; + color:black; +} + + +#idPaginationHistory, #idPaginationBlock, #idPaginationAccount, #idPaginationAct, #idPaginationHash, #idUtilView, #idPaginationDapps +{ + width: 800px; + align-content: center; + text-align: center; +} + + +#MAIN +{ + align-content: left; +} + +#idStableScroll +{ + z-index:0; + position:absolute; + top:800px; + height: 10px; + color: darkred; + width: 97%; + margin: auto; +} + + + + +#idBlockOnSend +{ + z-index:100; + position:absolute; + height:170px; + width:380px; + top:20%; + left:0; + transform: translateX(50vw) translateX(-190px); + + font-size: larger; + padding: 10px; + background: #2b372d; + color: white; + text-align: left; + border: 1px solid gray; + box-shadow: 0 0 0 2px darkgrey; + border-radius: 8px; +} + + +#idCheckOnSend +{ + font-size: smaller; + position:absolute; + bottom: 10px; +} + +.radius +{ + border-radius: 4px; + max-height: 34px; + height: 34px; +} +button.radius +{ + color:white; + background-color: #337ab7; + border-color: #2e6da4; + width: 100px; + margin: 20px; +} + + + +.bt_open_dapp +{ + min-width: 160px; + max-width: 160px; + height: 40px; + min-height: 40px; + vertical-align:middle; + margin: 0; +} + +.setsmart +{ + color:saddlebrown; + margin: 0; +} + +.olink +{ + text-decoration: none; + color: var(--colorText); +} +.olink:hover +{ + font-weight: bold; +} + diff --git a/Source/HTML/JS/client-electron.js b/Source/HTML/JS/client-electron.js new file mode 100644 index 0000000..42ae61f --- /dev/null +++ b/Source/HTML/JS/client-electron.js @@ -0,0 +1,32 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +const ipcRenderer = require('electron').ipcRenderer; + +function GetDataElectron(Method,ObjPost,Func) +{ + if(Func === undefined) + { + Func = ObjPost; + ObjPost = null; + } + var reply; + try + { + reply = ipcRenderer.sendSync('GetData', {path:Method, obj:ObjPost}); + } + catch(e) + { + reply = undefined; + } + if(Func) + Func(reply); +}; +window.GetData = GetDataElectron; diff --git a/Source/HTML/JS/client.js b/Source/HTML/JS/client.js new file mode 100644 index 0000000..1b4c0b3 --- /dev/null +++ b/Source/HTML/JS/client.js @@ -0,0 +1,1944 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + + +function $(id) +{ + return document.getElementById(id); +}; +var WALLET_KEY_NAME = "WALLET_KEY"; +var WALLET_PUB_KEY_NAME = "WALLET_PUB_KEY"; +if(!Math.log2) + Math.log2 = Math.log2 || function (x) + { + return Math.log(x) * Math.LOG2E; + }; +if(!window.crypto) + window.crypto = window.msCrypto; +if(!window.toStaticHTML) + toStaticHTML = function (Str) + { + return Str; + }; +if(!String.prototype.padStart) +{ + window.BrowserIE = 1; + String.prototype.padStart = function padStart(targetLength,padString) + { + targetLength = targetLength >> 0; + padString = String((typeof padString !== 'undefined' ? padString : ' ')); + if(this.length > targetLength) + { + return String(this); + } + else + { + targetLength = targetLength - this.length; + if(targetLength > padString.length) + { + padString += padString.repeat(targetLength / padString.length); + } + return padString.slice(0, targetLength) + String(this); + } + }; +} +window.Storage = {}; +window.Storage.setItem = function (Key,Value) +{ + if(window.localStorage) + localStorage.setItem(Key, Value); +}; +window.Storage.getItem = function (Key) +{ + if(window.localStorage) + return localStorage.getItem(Key); +}; +window.IsLocalClient = function () +{ + return (window.location.protocol.substr(0, 4) !== "http"); +}; +var ServerHTTP; +var MainServer; +if(window.nw) +{ + window.Open = function (path,iconname,width,height) + { + width = width || 840; + height = height || 1000; + var params = {width:width, height:height}; + if(iconname) + params.icon = "../HTML/PIC/" + iconname + ".png"; + window.nw.Window.open(path, params, function (win) + { + }); + }; + window.GetData = function (Method,ObjPost,Func) + { + window.nw.global.RunRPC({path:Method, obj:ObjPost}, Func); + }; + global.RunRPC = function (message,Func) + { + if(!ServerHTTP) + ServerHTTP = require('../core/html-server'); + var reply = ServerHTTP.SendData(message); + if(Func) + { + Func(reply); + } + }; +} +else +{ + window.Open = function (path,iconname,width,height) + { + if(!window.NWMODE) + { + var win = window.open(path); + } + else + { + width = width || 840; + height = height || 1000; + var left = (screen.width - width) / 2; + var params = "left=" + left + ",top=24,menubar=no,location=no,resizable=yes,scrollbars=no,status=no"; + params += ",width=" + width; + params += ",height=" + height; + var win = window.open(path, undefined, params); + } + }; + window.GetData = function (Method,ObjPost,Func) + { + if(Method.substr(0, 4) !== "http") + { + if(Method.substr(0, 1) !== "/") + Method = "/" + Method; + if(MainServer) + { + Method = GetProtocolServerPath(MainServer) + Method; + } + else + { + if(IsLocalClient()) + return ; + } + } + var StrPost = null; + var serv = new XMLHttpRequest(); + if(ObjPost !== null) + { + StrPost = JSON.stringify(ObjPost); + serv.open("POST", Method, true); + } + else + { + throw "ERROR GET-TYPE"; + } + var STACK = "" + new Error().stack; + serv.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + serv.onreadystatechange = function () + { + if(serv.readyState == 4) + { + if(serv.status == 200) + { + if(Func) + { + var Data; + try + { + Data = JSON.parse(serv.responseText); + } + catch(e) + { + console.log("Error parsing: " + e); + console.log(serv.responseText); + console.log(STACK); + } + Func(Data, serv.responseText); + } + } + else + { + if(Func) + Func(undefined, undefined); + } + } + }; + serv.send(StrPost); + }; +} + +function IsIPAddres(Str) +{ + var arr = Str.split("."); + if(arr.length !== 3) + return 0; + for(var i = 0; i < arr.length; i++) + if(arr[i] !== "" + ParseNum(arr[i])) + return 0; + return 1; +}; + +function GetProtocolServerPath(Item) +{ + if(Item.port === 443) + return "https://" + Item.ip; + else + if(Item.port === 80) + return "http://" + Item.ip; + else + return "http://" + Item.ip + ":" + Item.port; +}; + +function SUM_TO_STRING(Value,Currency,bFloat,bLocal) +{ + var Str; + if(Value.SumCOIN || Value.SumCENT) + if(bFloat) + { + Str = "" + FLOAT_FROM_COIN(Value).toStringF(); + } + else + { + var SumCOIN = Value.SumCOIN; + if(bLocal) + SumCOIN = SumCOIN.toLocaleString(); + Str = "" + SumCOIN + "." + Rigth("000000000" + Value.SumCENT, 9); + } + else + Str = ""; + if(Currency !== undefined) + { + if(Str === "") + Str = "0"; + Str += " " + CurrencyName(Currency); + } + return Str; +}; + +function GetArrFromHex(Str) +{ + var array = []; + for(var i = 0; Str && i < Str.length / 2; i++) + { + array[i] = parseInt(Str.substr(i * 2, 2), 16); + } + return array; +}; + +function GetHexFromArr(arr) +{ + if(!(arr instanceof Array) && arr.data) + arr = arr.data; + var Str = ""; + for(var i = 0; arr && i < arr.length; i++) + { + if(!arr[i]) + Str += "00"; + else + { + var Val = arr[i] & 255; + var A = Val.toString(16); + if(A.length === 1) + A = "0" + A; + Str = Str + A; + } + } + return Str.toUpperCase(); +}; + +function GetStrFromAddr(arr) +{ + return GetHexFromArr(arr); +}; + +function GetHexFromArrBlock(Arr,LenBlock) +{ + var Str = ""; + var Arr2 = []; + for(var i = 0; i < Arr.length; i++) + { + Arr2[i % LenBlock] = Arr[i]; + if(Arr2.length >= LenBlock) + { + Str += GetHexFromArr(Arr2) + "\n"; + Arr2 = []; + } + } + if(Arr2.length) + { + Str += GetHexFromArr(Arr2); + } + return Str; +}; + +function Rigth(Str,Count) +{ + if(Str.length < Count) + return Str; + else + return Str.substr(Str.length - Count); +}; + +function toUTF8Array(str) +{ + var utf8 = []; + for(var i = 0; i < str.length; i++) + { + var charcode = str.charCodeAt(i); + if(charcode < 0x80) + utf8.push(charcode); + else + if(charcode < 0x800) + { + utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f)); + } + else + if(charcode < 0xd800 || charcode >= 0xe000) + { + utf8.push(0xe0 | (charcode >> 12), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f)); + } + else + { + i++; + charcode = 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff)); + utf8.push(0xf0 | (charcode >> 18), 0x80 | ((charcode >> 12) & 0x3f), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f)); + } + } + return utf8; +}; + +function Utf8ArrayToStr(array) +{ + var out, i, len, c; + var char2, char3; + out = ""; + len = array.length; + i = 0; + while(i < len) + { + c = array[i++]; + switch(c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + out += String.fromCharCode(c); + break; + case 12: + case 13: + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + } + } + for(var i = 0; i < out.length; i++) + { + if(out.charCodeAt(i) === 0) + { + out = out.substr(0, i); + break; + } + } + return out; +}; + +function GetArr32FromStr(Str) +{ + return GetArrFromStr(Str, 32); +}; + +function GetArrFromStr(Str,Len) +{ + var arr = toUTF8Array(Str); + for(var i = arr.length; i < Len; i++) + { + arr[i] = 0; + } + return arr.slice(0, Len); +}; + +function WriteByte(arr,Num) +{ + arr[arr.length] = Num & 0xFF; +}; + +function WriteUint(arr,Num) +{ + var len = arr.length; + arr[len] = Num & 0xFF; + arr[len + 1] = (Num >>> 8) & 0xFF; + arr[len + 2] = (Num >>> 16) & 0xFF; + arr[len + 3] = (Num >>> 24) & 0xFF; + var NumH = Math.floor(Num / 4294967296); + arr[len + 4] = NumH & 0xFF; + arr[len + 5] = (NumH >>> 8) & 0xFF; +}; + +function WriteUint16(arr,Num) +{ + var len = arr.length; + arr[len] = Num & 0xFF; + arr[len + 1] = (Num >>> 8) & 0xFF; +}; + +function WriteUint32(arr,Num) +{ + var len = arr.length; + arr[len] = Num & 0xFF; + arr[len + 1] = (Num >>> 8) & 0xFF; + arr[len + 2] = (Num >>> 16) & 0xFF; + arr[len + 3] = (Num >>> 24) & 0xFF; +}; + +function WriteStr(arr,Str,ConstLength) +{ + if(!Str) + Str = ""; + var arrStr = toUTF8Array(Str); + var length; + var len = arr.length; + if(ConstLength) + { + length = ConstLength; + } + else + { + length = arrStr.length; + if(length > 65535) + length = 65535; + arr[len] = length & 0xFF; + arr[len + 1] = (length >>> 8) & 0xFF; + len += 2; + } + for(var i = 0; i < length; i++) + { + arr[len + i] = arrStr[i]; + } +}; + +function WriteArr(arr,arr2,ConstLength) +{ + var len = arr.length; + for(var i = 0; i < ConstLength; i++) + { + arr[len + i] = arr2[i]; + } +}; + +function WriteTr(arr,arr2) +{ + var len2 = arr2.length; + var len = arr.length; + arr[len] = len2 & 0xFF; + arr[len + 1] = (len2 >>> 8) & 0xFF; + len += 2; + for(var i = 0; i < len2; i++) + { + arr[len + i] = arr2[i]; + } +}; + +function ReadUintFromArr(arr,len) +{ + if(len === undefined) + { + len = arr.len; + arr.len += 6; + } + var value = (arr[len + 5] << 23) * 2 + (arr[len + 4] << 16) + (arr[len + 3] << 8) + arr[len + 2]; + value = value * 256 + arr[len + 1]; + value = value * 256 + arr[len]; + return value; +}; + +function ReadUint32FromArr(arr,len) +{ + if(len === undefined) + { + len = arr.len; + arr.len += 4; + } + var value = (arr[len + 3] << 23) * 2 + (arr[len + 3] << 16) + (arr[len + 1] << 8) + arr[len]; + return value; +}; + +function ReadArr(arr,length) +{ + var Ret = []; + var len = arr.len; + for(var i = 0; i < length; i++) + { + Ret[i] = arr[len + i]; + } + arr.len += length; + return Ret; +}; + +function ReadStr(arr) +{ + var length = arr[arr.len] + arr[arr.len + 1] * 256; + arr.len += 2; + var arr2 = arr.slice(arr.len, arr.len + length); + var Str = Utf8ArrayToStr(arr2); + arr.len += length; + return Str; +}; + +function ParseNum(Str) +{ + var Res = parseInt(Str); + if(isNaN(Res)) + Res = 0; + if(!Res) + Res = 0; + if(Res < 0) + Res = 0; + return Res; +}; + +function parseUint(Str) +{ + var Res = parseInt(Str); + if(isNaN(Res)) + Res = 0; + if(!Res) + Res = 0; + if(Res < 0) + Res = 0; + return Res; +}; + +function CopyObjKeys(dest,src) +{ + for(var key in src) + { + dest[key] = src[key]; + } +}; + +function SaveToArr(Arr,Obj) +{ + for(var key in Obj) + { + Arr[0]++; + var Value = Obj[key]; + switch(typeof Value) + { + case "number": + WriteByte(Arr, 241); + WriteUint(Arr, Value); + break; + case "string": + WriteByte(Arr, 242); + WriteStr(Arr, Value); + break; + case "object": + if(Value && (Value.length > 0 || Value.length === 0) && Value.length <= 240) + { + WriteByte(Arr, Value.length); + WriteArr(Arr, Value, Value.length); + break; + } + default: + WriteByte(Arr, 250); + } + } +}; + +function LoadFromArr(Arr,Obj) +{ + if(!Arr.length) + return false; + var Count = Arr[0]; + Arr.len = 1; + for(var key in Obj) + { + if(!Count) + break; + Count--; + var Type = Arr[Arr.len]; + Arr.len++; + switch(Type) + { + case 241: + Obj[key] = ReadUintFromArr(Arr); + break; + case 242: + Obj[key] = ReadStr(Arr); + break; + default: + if(Type <= 240) + { + var length = Type; + Obj[key] = ReadArr(Arr, length); + break; + } + else + { + Obj[key] = undefined; + } + } + } + if(Arr[0]) + return true; + else + return false; +}; +var entityMap = {"&":"&", "<":"<", ">":">", '"':'"', "'":''', "/":'/', "\n":'
', " ":' ', }; + +function escapeHtml(string) +{ + string = string.replace(/\\n/g, "\n"); + string = string.replace(/\\"/g, "\""); + return String(string).replace(/[\s\n&<>"'\/]/g, function (s) + { + return entityMap[s]; + }); +}; + +function InsertAfter(elem,refElem) +{ + var parent = refElem.parentNode; + var next = refElem.nextSibling; + if(next) + { + return parent.insertBefore(elem, next); + } + else + { + return parent.appendChild(elem); + } +}; + +function MoveUp(elem) +{ + var parent = elem.parentNode; + for(var i = 0; i < parent.children.length; i++) + { + var item = parent.children[i]; + if(item.id && item.id !== undefined) + { + return parent.insertBefore(elem, item); + } + } +}; + +function ViewGrid(APIName,Params,nameid,bClear,TotalSum) +{ + GetData(APIName, Params, function (Data) + { + if(!Data || !Data.result) + return ; + SetGridData(Data.arr, nameid, TotalSum, bClear); + }); +}; + +function CheckNewSearch(Def) +{ + var Str = $(Def.FilterName).value; + if(Str) + { + $(Def.NumName).value = "0"; + } +}; + +function ViewCurrent(Def,flag,This) +{ + if(Def.BlockName) + { + var element = $(Def.BlockName); + if(flag) + { + var bVisible = IsVisibleBlock(Def.BlockName); + if(!bVisible) + MoveUp(element); + SetVisibleBlock(Def.BlockName, !bVisible); + } + else + { + SetVisibleBlock(Def.BlockName, true); + } + var ResVisible = IsVisibleBlock(Def.BlockName); + if(This && This.className) + { + This.className = This.className.replace("btpress", ""); + if(ResVisible) + This.className += " btpress"; + } + if(!ResVisible) + return ; + } + var item = $(Def.NumName); + var Filter = "", Filter2 = ""; + if(Def.FilterName) + { + Filter = $(Def.FilterName).value; + } + if(Def.FilterName2) + { + Filter2 = $(Def.FilterName2).value; + } + if(!Def.Param3) + Def.Param3 = ""; + ViewGrid(Def.APIName, {StartNum:ParseNum(item.value), CountNum:GetCountViewRows(Def), Param3:Def.Param3, Filter:Filter, Filter2:Filter2, + }, Def.TabName, 1, Def.TotalSum); + SaveValues(); + if(This) + SetImg(This, Def.BlockName); +}; + +function ViewPrev(Def) +{ + var item = document.getElementById(Def.NumName); + var Num = ParseNum(item.value); + Num -= GetCountViewRows(Def); + if(Num < 0) + Num = 0; + item.value = Num; + ViewCurrent(Def); +}; + +function ViewNext(Def,MaxNum) +{ + var item = document.getElementById(Def.NumName); + var Num = ParseNum(item.value); + Num += GetCountViewRows(Def); + if(Def.FilterName) + { + if(document.getElementById(Def.FilterName).value) + { + Num = document.getElementById(Def.TabName).MaxNum + 1; + } + } + if(Num < MaxNum) + { + item.value = Num; + } + else + { + item.value = MaxNum - MaxNum % GetCountViewRows(Def); + } + ViewCurrent(Def); +}; + +function ViewBegin(Def) +{ + document.getElementById(Def.NumName).value = 0; + ViewCurrent(Def); +}; + +function ViewEnd(Def,MaxNum,bInitOnly) +{ + document.getElementById(Def.NumName).value = MaxNum - MaxNum % GetCountViewRows(Def); + if(bInitOnly) + return ; + ViewCurrent(Def); +}; + +function GetCountViewRows(Def) +{ + if(Def.CountViewRows) + return Def.CountViewRows; + else + return CountViewRows; +}; + +function DoStableScroll() +{ + var item = $("idStableScroll"); + if(!item) + return ; + var scrollHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, + document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight); + var itemlHeight = Math.max(item.scrollHeight, item.offsetHeight, item.clientHeight); + scrollHeight = scrollHeight - itemlHeight; + item.style.top = "" + scrollHeight + "px"; +}; +var glEvalMap = {}; + +function CreateEval(formula,StrParams) +{ + var Ret = glEvalMap[formula]; + if(!Ret) + { + eval("function M(" + StrParams + "){return " + formula + "}; Ret=M;"); + glEvalMap[formula] = Ret; + } + return Ret; +}; +var glWorkNum = 0; +var CUR_ROW; + +function SetGridData(arr,id_name,TotalSum,bclear,revert) +{ + var htmlTable = document.getElementById(id_name); + if(!htmlTable) + { + console.log("Error id_name: " + id_name); + return ; + } + if(bclear) + ClearTable(htmlTable); + if(!htmlTable.ItemsMap) + { + htmlTable.ItemsMap = {}; + htmlTable.RowCount = 0; + } + var map = htmlTable.ItemsMap; + glWorkNum++; + var ValueTotal = {SumCOIN:0, SumCENT:0}; + var row0 = htmlTable.rows[0]; + var row0cells = row0.cells; + var colcount = row0cells.length; + for(var i = 0; arr && i < arr.length; i++) + { + var Item = arr[i]; + var ID = Item.Num; + htmlTable.MaxNum = Item.Num; + var row = map[ID]; + if(!row) + { + htmlTable.RowCount++; + if(revert) + row = htmlTable.insertRow(1); + else + row = htmlTable.insertRow( - 1); + map[ID] = row; + for(var n = 0; n < colcount; n++) + { + var cell0 = row0cells[n]; + if(cell0.innerText == "") + continue; + cell0.F = CreateEval(cell0.id, "Item"); + if(cell0.id.substr(0, 1) === "(") + cell0.H = 1; + var cell = row.insertCell(n); + cell.className = cell0.className; + } + } + row.Work = glWorkNum; + CUR_ROW = row; + for(var n = 0; n < colcount; n++) + { + var cell = row.cells[n]; + if(!cell) + continue; + var cell0 = row0cells[n]; + if(cell0.H) + { + var text = "" + cell0.F(Item); + text = toStaticHTML(text.trim()); + if(cell.innerHTML !== text) + cell.innerHTML = text; + } + else + { + var text = "" + cell0.F(Item); + text.trim(); + if(cell.innerText !== text) + cell.innerText = text; + } + } + if(TotalSum && Item.Currency === 0) + ADD(ValueTotal, Item.Value); + } + for(var key in map) + { + var row = map[key]; + if(row.Work !== glWorkNum) + { + htmlTable.deleteRow(row.rowIndex); + delete map[key]; + } + } + if(TotalSum) + { + var id = document.getElementById(TotalSum); + if(id) + { + if(!ISZERO(ValueTotal)) + id.innerText = "Total on page: " + SUM_TO_STRING(ValueTotal, 0, 0, 1); + else + id.innerText = ""; + } + } + DoStableScroll(); +}; + +function ClearTable(htmlTable) +{ + for(var i = htmlTable.rows.length - 1; i > 0; i--) + htmlTable.deleteRow(i); + htmlTable.ItemsMap = {}; + htmlTable.RowCount = 0; +}; + +function RetOpenBlock(BlockNum,bTrDataLen) +{ + if(BlockNum && bTrDataLen) + { + if(bTrDataLen === 2) + { + return '' + BlockNum + ''; + } + else + { + return ''; + } + } + else + return '' + BlockNum + ''; +}; + +function RetBool(Value) +{ + if(Value) + return "✔"; + else + return ""; +}; + +function RetNumDapp(Item) +{ + return Item.Num; +}; + +function RetIconPath(Item,bCurrency) +{ + if(bCurrency && MapCurrencyIcon[Item.Num]) + { + return MapCurrencyIcon[Item.Num]; + } + var StrPath = ""; + if(MainServer) + { + StrPath = GetProtocolServerPath(MainServer); + } + if(Item.IconBlockNum) + { + return StrPath + '/file/' + Item.IconBlockNum + '/' + Item.IconTrNum; + } + else + return StrPath + "./PIC/blank.svg"; +}; + +function RetIconDapp(Item) +{ + if(Item.IconBlockNum) + { + return ' '; + } + else + return ""; +}; + +function RetOpenDapps(Item,bNum,AccountNum) +{ + var Name = escapeHtml(Item.Name); + if(bNum) + Name = "" + Item.Num + "." + Name; + if(Item.HTMLLength > 0) + { + var StrText = RetIconDapp(Item) + Name; + return ''; + } + else + return RetIconDapp(Item) + Name; +}; + +function RetDirect(Value) +{ + if(Value === "-") + { + return "-"; + } + else + if(Value === "+") + { + return "+"; + } + else + return ""; +}; + +function RetCategory(Item) +{ + var Str = ""; + var Num = 0; + if(Item.Category1 && MapCategory[Item.Category1]) + { + Num++; + Str += "" + Num + "." + MapCategory[Item.Category1] + "
"; + } + if(Item.Category2 && MapCategory[Item.Category2]) + { + Num++; + Str += "" + Num + "." + MapCategory[Item.Category2] + "
"; + } + if(Item.Category3 && MapCategory[Item.Category3]) + { + Num++; + Str += "" + Num + "." + MapCategory[Item.Category3] + "
"; + } + Str = Str.substr(0, Str.length - 4); + return Str; +}; + +function RetChangeSmart(Item) +{ + var Name = ""; + var State = ""; + var bOpen = 0; + if(Item.SmartObj) + { + if(Item.SmartObj.HTMLLength) + { + Name = RetOpenDapps(Item.SmartObj, 1, Item.Num); + bOpen = 1; + } + else + Name = "" + Item.SmartObj.Num + "." + escapeHtml(Item.SmartObj.Name) + "
"; + if(window.DEBUG_WALLET) + State = "
State:" + JSON.stringify(Item.SmartState); + } + var Height = 20; + if(bOpen) + Height = 40; + return '
' + Name + '' + State + '
'; +}; + +function RetHistoryAccount(Item,Name) +{ + var Num; + if(Name) + Num = Item[Name]; + else + Num = Item.Num; + if(Num < 1) + return "" + Num; + var Str; + if(IsLocalClient()) + Str = "" + Num + ""; + else + Str = "" + Num + ""; + return Str; +}; + +function RetBaseAccount(Item) +{ + var Str = RetHistoryAccount(Item, "Account"); + if(Item.AccountLength > 1) + Str += "-" + (Item.Account + Item.AccountLength - 1); + return Str; +}; + +function ViewTransaction(BlockNum) +{ + if(IsLocalClient()) + OpenBlockViewerPage(BlockNum); + else + window.Open('./blockviewer.html#' + BlockNum, 'viewer', 800, 800); +}; + +function formatDate(now) +{ + var year = now.getFullYear(); + var month = now.getMonth() + 1; + var date = now.getDate(); + var hour = now.getHours(); + var minute = now.getMinutes(); + var second = now.getSeconds(); + return year + "-" + String(month).padStart(2, "0") + "-" + String(date).padStart(2, "0") + " " + String(hour).padStart(2, + "0") + ":" + String(minute).padStart(2, "0") + ":" + String(second).padStart(2, "0"); +}; + +function DateFromBlock(BlockNum) +{ + var Str; + if(window.FIRST_TIME_BLOCK) + { + var now = new Date(window.FIRST_TIME_BLOCK + BlockNum * 1000); + Str = formatDate(now); + } + else + { + Str = ""; + } + return Str; +}; + +function SetCheckPoint(BlockNum) +{ + if(!BlockNum) + { + SetError("Not set BlockNum"); + return ; + } + GetData("SetCheckPoint", BlockNum, function (Data) + { + if(Data) + { + SetStatus(Data.text, !Data.result); + } + }); +}; + +function AddDiagramToArr(Arr,Item) +{ + var bWas = 0; + for(var i = 0; i < Arr.length; i++) + { + if(Arr[i].name === Item.name) + { + Item.Delete = 0; + Arr[i] = Item; + bWas = 1; + break; + } + } + if(!bWas) + { + Item.num = Arr.length; + Arr.push(Item); + } +}; + +function SetVisibleBlock(name,bSet) +{ + var Item = document.getElementById(name); + if(!Item) + return ; + if(bSet && typeof bSet === "string") + Item.style.display = bSet; + else + if(bSet) + { + Item.style.display = 'block'; + DoStableScroll(); + } + else + { + Item.style.display = 'none'; + } + return Item; +}; + +function IsVisibleBlock(name) +{ + var Item = document.getElementById(name); + if(Item && (Item.style.display === 'block' || Item.style.display === "table-row")) + return true; + else + return false; +}; + +function SetVisibleClass(Arr,Visible) +{ + if(typeof Arr === "string") + Arr = [Arr]; + for(var i = 0; i < Arr.length; i++) + { + var item = document.querySelector(Arr[i]); + if(!item) + { + ToLog("Error class name: " + Arr[i]); + continue; + } + if(!Visible) + item.classList.add("hidden"); + else + item.classList.remove("hidden"); + } +}; + +function IsVisibleClass(name) +{ + var List = document.querySelector(name); + if(List.className.indexOf(" hidden") >= 0) + return 0; + else + return 1; +}; + +function LoadValuesByArr(Arr,DopStr) +{ + if(!DopStr) + DopStr = ""; + if(Storage.getItem("VerSave") !== "3") + return 0; + for(var i = 0; i < Arr.length; i++) + { + var name = Arr[i]; + var Item = document.getElementById(name); + var name2 = DopStr + name; + if(Item.type === "checkbox") + Item.checked = parseInt(Storage.getItem(name2)); + else + Item.value = Storage.getItem(name2); + } + return 1; +}; + +function SaveValuesByArr(Arr,DopStr) +{ + if(!DopStr) + DopStr = ""; + Storage.setItem("VerSave", "3"); + for(var i = 0; i < Arr.length; i++) + { + var name = Arr[i]; + var name2 = DopStr + name; + var Item = $(name); + if(Item.type === "checkbox") + window.Storage.setItem(name2, 0 + Item.checked); + else + window.Storage.setItem(name2, Item.value); + } +}; +var MapCurrency = {}; +MapCurrency[0] = "TERA"; +MapCurrency[16] = "BTC"; +var MapCurrencyIcon = {}; +MapCurrencyIcon[0] = "./PIC/T.svg"; +MapCurrencyIcon[16] = "./PIC/B.svg"; +var MapCategory = {}; +MapCategory[0] = "-"; +MapCategory[1] = "Art & Music"; +MapCategory[2] = "Big Data & AI"; +MapCategory[3] = "Business"; +MapCategory[4] = "Commerce & Advertising"; +MapCategory[5] = "Communications"; +MapCategory[6] = "Content Management"; +MapCategory[7] = "Crowdfunding"; +MapCategory[8] = "Data Storage"; +MapCategory[9] = "Drugs & Healthcare"; +MapCategory[10] = "Education"; +MapCategory[11] = "Energy & Utilities"; +MapCategory[12] = "Events & Entertainment"; +MapCategory[13] = "eСommerce"; +MapCategory[14] = "Finance"; +MapCategory[15] = "Gambling & Betting"; +MapCategory[16] = "Gaming & VR"; +MapCategory[17] = "Healthcare"; +MapCategory[18] = "Identity & Reputation"; +MapCategory[19] = "Industry"; +MapCategory[20] = "Infrastructure"; +MapCategory[21] = "Investment"; +MapCategory[22] = "Live Streaming"; +MapCategory[23] = "Machine Learning & AI"; +MapCategory[24] = "Marketing"; +MapCategory[25] = "Media"; +MapCategory[26] = "Mining"; +MapCategory[27] = "Payments"; +MapCategory[28] = "Platform"; +MapCategory[29] = "Provenance & Notary"; +MapCategory[30] = "Real Estate"; +MapCategory[31] = "Recruitment"; +MapCategory[32] = "Service"; +MapCategory[33] = "Social Network"; +MapCategory[34] = "Social project"; +MapCategory[35] = "Supply & Logistics"; +MapCategory[36] = "Trading & Investing"; +MapCategory[37] = "Transport"; +MapCategory[38] = "Travel & Tourisim"; +MapCategory[39] = "Bounty"; +MapCategory[40] = "Code-library"; +MapCategory[41] = "Development"; +MapCategory[42] = "Exchanges"; +MapCategory[43] = "Security"; +MapCategory[44] = "Governance"; +MapCategory[45] = "Property"; +MapCategory[46] = "Insurance"; + +function GetTokenName(Num,Name) +{ + if(!Name) + Name = "Token"; + return "(" + Num + "." + Name + ")"; + return "{" + Num + "." + Name + "}"; +}; + +function CurrencyNameItem(Item) +{ + var Name = MapCurrency[Item.Currency]; + if(!Name) + { + if(Item.CurrencyObj) + Name = GetTokenName(Item.Currency, Item.CurrencyObj.ShortName); + else + Name = GetTokenName(Item.Currency, ""); + MapCurrency[Item.Currency] = Name; + } + return Name; +}; + +function CurrencyName(Num) +{ + var Name = MapCurrency[Num]; + if(!Name) + { + GetData("GetDappList", {StartNum:Num, CountNum:1}, function (Data) + { + if(Data && Data.result) + { + var Smart = Data.arr[0]; + Name = GetTokenName(Smart.Num, Smart.ShortName); + MapCurrency[Smart.Num] = Name; + } + }); + Name = GetTokenName(Num, ""); + } + return Name; +}; + +function FillCurrencyAsync(IdName,StartNum) +{ + if(!StartNum) + StartNum = 8; + var MaxCountViewRows = 10; + GetData("DappSmartList", {StartNum:StartNum, CountNum:MaxCountViewRows, TokenGenerate:1}, function (Data) + { + if(Data && Data.result && Data.arr) + { + var MaxNum = 0; + for(var i = 0; i < Data.arr.length; i++) + { + var Smart = Data.arr[i]; + if(!MapCurrency[Smart.Num]) + { + Name = GetTokenName(Smart.Num, Smart.ShortName); + MapCurrency[Smart.Num] = Name; + } + if(Smart.Num > MaxNum) + MaxNum = Smart.Num; + } + FillSelect(IdName, MapCurrency, 1); + if(Data.arr.length === MaxCountViewRows && MaxNum) + { + FillCurrencyAsync(IdName, MaxNum + 1); + } + } + }); +}; + +function FillSelect(IdName,arr,bNatural) +{ + var Select = $(IdName); + var Value = Select.value; + var Options = Select.options; + var strJSON = JSON.stringify(arr); + if(Select.strJSON === strJSON) + return ; + Select.strJSON = strJSON; + var Value = Select.value; + if(bNatural) + { + Options.length = 0; + for(var key in arr) + { + var name; + if(bNatural === "KEY") + name = key; + else + name = arr[key]; + Options[Options.length] = new Option(name, key); + if(key == Value) + Select.value = key; + } + } + else + { + Options.length = 0; + for(var i = 0; i < arr.length; i++) + { + var item = arr[i]; + Options[Options.length] = new Option(item.text, item.value); + if(item.value == Value) + Select.value = item.value; + } + if(!arr.length) + for(var key in arr) + { + var item = arr[key]; + Options[Options.length] = new Option(item.text, item.value); + if(item.value == Value) + Select.value = item.value; + } + } +}; + +function GetArrFromSelect(IdName) +{ + var Select = $(IdName); + var Options = Select.options; + var arr = []; + for(var i = 0; i < Options.length; i++) + { + var item = Options[i]; + arr.push({text:item.text, value:item.value}); + } + return arr; +}; + +function FillCategory(IdName) +{ + var arr = []; + for(var key in MapCategory) + { + arr.push({sort:MapCategory[key].toUpperCase(), text:MapCategory[key], value:key}); + } + FillCategoryAndSort(IdName, arr); +}; + +function FillCategoryAndSort(IdName,arr) +{ + arr.sort(function (a,b) + { + if(a.sort < b.sort) + return - 1; + if(a.sort > b.sort) + return 1; + return 0; + }); + FillSelect(IdName, arr); +}; + +function AddToInvoiceList(Item) +{ + var arr; + var Str = Storage.getItem("InvoiceList"); + if(Str) + { + arr = JSON.parse(Str); + } + else + { + arr = []; + } + arr.unshift(Item); + Storage.setItem("InvoiceList", JSON.stringify(arr)); +}; + +function OpenDapps(Num,AccountNum) +{ + if(!Num) + return ; + var StrPath = '/dapp/' + Num; + if(IsLocalClient()) + { + StrPath = "./dapp-frame.html?dapp=" + Num; + } + if(AccountNum) + StrPath += '#' + AccountNum; + window.Open(StrPath, 'dapp', 1200); +}; + +function ParseFileName(Str) +{ + var Ret = {BlockNum:0, TrNum:0}; + var index1 = Str.indexOf("file/"); + if(index1) + { + var index2 = Str.indexOf("/", index1 + 6); + Ret.BlockNum = parseInt(Str.substr(index1 + 5, index2 - index1 - 5)); + Ret.TrNum = parseInt(Str.substr(index2 + 1)); + } + return Ret; +}; +window.MapSendTransaction = {}; + +function SendTransaction(Body,TR,SumPow,F) +{ + if(Body.length > 16000) + { + if(window.SetStatus) + SetStatus("Error length transaction =" + Body.length + " (max size=16000)"); + if(F) + F(1, TR, Body); + return ; + } + if(window.SetStatus) + SetStatus("Prepare to sending..."); + CreateNonceAndSend(1, 0, 0); + +function CreateNonceAndSend(bCreateNonce,startnonce,NumNext) + { + if(!NumNext) + NumNext = 0; + var nonce = startnonce; + if(bCreateNonce) + nonce = CreateHashBodyPOWInnerMinPower(Body, SumPow, startnonce); + var StrHex = GetHexFromArr(Body); + if(NumNext > 10) + { + SetError("Not sending. Cannt calc pow."); + return ; + } + GetData("SendTransactionHex", {Hex:StrHex}, function (Data) + { + if(Data) + { + var key = GetHexFromArr(sha3(Body)); + if(window.SetStatus) + SetStatus("Send '" + key.substr(0, 16) + "' result:" + Data.text); + if(Data.text === "Not add") + { + CreateNonceAndSend(1, nonce + 1, NumNext + 1); + } + else + if(Data.text === "Bad time") + { + if(window.DELTA_FOR_TIME_TX < 6) + { + window.DELTA_FOR_TIME_TX++; + console.log("New set Delta time: " + window.DELTA_FOR_TIME_TX); + CreateNonceAndSend(1, 0, NumNext + 1); + } + } + else + { + var key = GetHexFromArr(sha3(Body)); + MapSendTransaction[key] = TR; + if(F) + F(0, TR, Body); + } + } + else + { + if(window.SetStatus) + SetStatus("Error Data"); + } + }); + }; +}; +var MapSendID = {}; + +function SendCallMethod(Account,MethodName,Params,FromNum,FromSmartNum) +{ + var TR = {Type:135}; + var Body = [TR.Type]; + WriteUint(Body, Account); + WriteStr(Body, MethodName); + WriteStr(Body, JSON.stringify(Params)); + WriteUint(Body, FromNum); + if(FromNum) + { + GetData("GetAccount", Account, function (Data) + { + if(!Data || Data.result !== 1 || !Data.Item) + { + SetStatus("Error account number: " + Account); + return ; + } + if(Data.Item.Value.Smart !== FromSmartNum) + { + SetStatus("Error - The account:" + Account + " does not belong to a smart contract:" + FromSmartNum + " (have: " + Data.Item.Value.Smart + ")"); + return ; + } + GetData("GetAccount", FromNum, function (Data) + { + if(!Data || Data.result !== 1 || !Data.Item) + { + SetStatus("Error account number: " + FromNum); + return ; + } + if(Data.Item.Num != FromNum) + { + SetStatus("Error read from account number: " + FromNum + " read data=" + Data.Item.Num); + return ; + } + var OperationID; + if(!MapSendID[FromNum]) + { + OperationID = Data.Item.Value.OperationID + 10; + MapSendID[FromNum] = {}; + } + else + { + OperationID = MapSendID[FromNum].OperationID; + if((new Date() - MapSendID[FromNum].Date) > 8 * 1000) + { + OperationID += 20; + } + } + OperationID++; + OperationID++; + MapSendID[FromNum].OperationID = OperationID; + MapSendID[FromNum].Date = Date.now(); + WriteUint(Body, OperationID); + Body.length += 10; + SendTrArrayWithSign(Body, FromNum, TR); + }); + }); + } + else + { + WriteUint(Body, 0); + Body.length += 10; + Body.length += 64; + Body.length += 12; + SendTransaction(Body, TR); + } +}; + +function SendTrArrayWithSign(Body,Account,TR) +{ + if(MainServer || CanClientSign()) + { + var Sign = GetSignFromArr(Body); + var Arr = GetArrFromHex(Sign); + WriteArr(Body, Arr, 64); + Body.length += 12; + SendTransaction(Body, TR); + } + else + { + var StrHex = GetHexFromArr(Body); + GetData("GetSignFromHEX", {Hex:StrHex, Account:Account}, function (Data) + { + if(Data && Data.result) + { + var Arr = GetArrFromHex(Data.Sign); + WriteArr(Body, Arr, 64); + Body.length += 12; + SendTransaction(Body, TR); + } + }); + } +}; + +function GetTrCreateAcc(Currency,PubKey,Description,Adviser,Smart) +{ + var TR = {Type:TYPE_TRANSACTION_CREATE, Currency:Currency, PubKey:PubKey, Name:Description, Adviser:Adviser, Smart:Smart, }; + return TR; +}; + +function GetBodyCreateAcc(TR) +{ + var Body = []; + WriteByte(Body, TR.Type); + WriteUint(Body, TR.Currency); + WriteArr(Body, GetArrFromHex(TR.PubKey), 33); + WriteStr(Body, TR.Name, 40); + WriteUint(Body, TR.Adviser); + WriteUint32(Body, TR.Smart); + Body.length += 3; + Body.length += 12; + return Body; +}; + +function GetArrFromTR(TR) +{ + MaxBlockNum = GetCurrentBlockNumByTime(); + var Body = []; + WriteByte(Body, TR.Type); + WriteByte(Body, TR.Version); + WriteUint(Body, 0); + WriteUint(Body, TR.FromID); + WriteUint32(Body, TR.To.length); + for(var i = 0; i < TR.To.length; i++) + { + var Item = TR.To[i]; + if(TR.Version >= 3) + WriteTr(Body, Item.PubKey); + WriteUint(Body, Item.ID); + WriteUint(Body, Item.SumCOIN); + WriteUint32(Body, Item.SumCENT); + if(MapAccounts && MapAccounts[Item.ID]) + MapAccounts[Item.ID].MustUpdate = MaxBlockNum + 10; + } + WriteStr(Body, TR.Description); + WriteUint(Body, TR.OperationID); + if(TR.Version >= 3) + { + if(TR.Body) + { + WriteTr(Body, TR.Body); + } + else + { + WriteByte(Body, 0); + WriteByte(Body, 0); + } + } + return Body; +}; + +function GetSignTransaction(TR,StrPrivKey,F) +{ + if(window.SignLib) + { + if(TR.Version === 3) + { + var Arr = []; + var GetCount = 0; + for(var i = 0; i < TR.To.length; i++) + { + var Item = TR.To[i]; + GetData("GetAccountList", {StartNum:Item.ID}, function (Data) + { + if(Data && Data.result === 1 && Data.arr.length) + { + GetCount++; + var DataItem = Data.arr[0]; + var DataPubArr = DataItem.PubKey.data; + for(var j = 0; j < 33; j++) + Arr[Arr.length] = DataPubArr[j]; + if(GetCount === TR.To.length) + { + var Body = GetArrFromTR(TR); + for(var j = 0; j < Body.length; j++) + Arr[Arr.length] = Body[j]; + TR.Sign = GetArrFromHex(GetSignFromArr(Arr, StrPrivKey)); + F(TR); + } + } + }); + } + } + else + { + TR.Sign = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + F(TR); + } + } + else + { + GetData("GetSignTransaction", TR, function (Data) + { + if(Data && Data.result === 1) + { + TR.Sign = GetArrFromHex(Data.Sign); + F(TR); + } + }); + } +}; + +function GetSignFromArr(Arr,StrPrivKey) +{ + if(!StrPrivKey) + StrPrivKey = GetPrivKey(); + if(!IsHexStr(StrPrivKey) || StrPrivKey.length !== 64) + return "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + var PrivKey = GetArrFromHex(StrPrivKey); + var sigObj = SignLib.sign(SHA3BUF(Arr), Buffer.from(PrivKey), null, null); + return GetHexFromArr(sigObj.signature); +}; + +function IsHexStr(Str) +{ + if(!Str) + return false; + var arr = GetArrFromHex(Str); + var Str2 = GetHexFromArr(arr); + if(Str2 === Str.toUpperCase()) + return true; + else + return false; +}; + +function RetJSON(Item) +{ + return JSON.stringify(Item); +}; +Number.prototype.toStringF = function () +{ + var data = String(this).split(/[eE]/); + if(data.length == 1) + return data[0]; + var z = '', sign = this < 0 ? '-' : '', str = data[0].replace('.', ''), mag = Number(data[1]) + 1; + if(mag < 0) + { + z = sign + '0.'; + while(mag++) + z += '0'; + return z + str.replace(/^\-/, ''); + } + mag -= str.length; + while(mag--) + z += '0'; + return str + z; +}; + +function CanClientSign() +{ + var StrPrivKey = GetPrivKey(); + if(!IsHexStr(StrPrivKey) || StrPrivKey.length !== 64) + { + return 0; + } + return 1; +}; + +function random(max) +{ + return Math.floor(Math.random() * max); +}; + +function ToLog(Str) +{ + console.log(Str); +}; + +function InitMainServer() +{ + var Str = localStorage["MainServer"]; + if(Str && !localStorage["BIGWALLET"] && Str.substr(0, 1) === "{") + { + MainServer = JSON.parse(Str); + } +}; + +function IsZeroArr(arr) +{ + if(arr) + for(var i = 0; i < arr.length; i++) + { + if(arr[i]) + return false; + } + return true; +}; +var WALLET_PASSWORD; +var KeyPasswordMap = {}; + +function InitWalletKeyName() +{ + if(!localStorage["WALLET_KEY"]) + { + localStorage["WALLET_KEY"] = localStorage["idPrivKey"]; + } + if(!localStorage["WALLET_PUB_KEY"]) + { + localStorage["WALLET_PUB_KEY"] = localStorage["idPubKey"]; + } +}; + +function OpenWalletKey() +{ + var Key = Storage.getItem(WALLET_KEY_NAME); + if(Key && Key.substr(0, 1) === "!" && WALLET_PASSWORD) + { + Key = Key.substr(1); + var StrKey = WALLET_PASSWORD + "-" + Key; + var RetKey = KeyPasswordMap[StrKey]; + if(!RetKey) + { + var Hash = HashProtect(WALLET_PASSWORD); + RetKey = GetHexFromArr(XORHash(GetArrFromHex(Key), Hash, 32)); + KeyPasswordMap[StrKey] = RetKey; + } + } + else + { + RetKey = Key; + } + var PubKeyStr; + if(RetKey && IsHexStr(RetKey) && RetKey.length === 64) + { + var PrivKey = GetArrFromHex(RetKey); + PubKeyStr = GetHexFromArr(SignLib.publicKeyCreate(PrivKey, 1)); + } + else + { + PubKeyStr = ""; + } + sessionStorage[WALLET_KEY_NAME] = RetKey; + sessionStorage[WALLET_PUB_KEY_NAME] = PubKeyStr; + if(!WALLET_PASSWORD) + Storage.setItem(WALLET_PUB_KEY_NAME, PubKeyStr); + return RetKey; +}; + +function IsLockedWallet() +{ + var Key = Storage.getItem(WALLET_KEY_NAME); + if(Key && Key.substr(0, 1) === "!") + return 1; + else + return 0; +}; + +function GetPrivKey() +{ + var Key = sessionStorage[WALLET_KEY_NAME]; + if(!Key) + Key = Storage.getItem(WALLET_KEY_NAME); + return Key; +}; + +function GetPubKey() +{ + var Key = sessionStorage[WALLET_PUB_KEY_NAME]; + if(!Key) + Key = Storage.getItem(WALLET_PUB_KEY_NAME); + return Key; +}; + +function SetPrivKey(StrPrivKey) +{ + var Key; + if(WALLET_PASSWORD) + { + var Hash = HashProtect(WALLET_PASSWORD); + var KeyXOR = GetHexFromArr(XORHash(GetArrFromHex(StrPrivKey), Hash, 32)); + Key = "!" + KeyXOR; + } + else + { + Key = StrPrivKey; + } + var PrivKey = GetArrFromHex(StrPrivKey); + var StrPubKey = GetHexFromArr(SignLib.publicKeyCreate(PrivKey, 1)); + Storage.setItem(WALLET_KEY_NAME, Key); + Storage.setItem(WALLET_PUB_KEY_NAME, StrPubKey); + Storage.setItem("WALLET_PUB_KEY_MAIN", StrPubKey); + sessionStorage[WALLET_KEY_NAME] = StrPrivKey; + sessionStorage[WALLET_PUB_KEY_NAME] = StrPubKey; +}; + +function SetWalletPassword(Str) +{ + WALLET_PASSWORD = Str; + if(localStorage["idPrivKey"]) + delete localStorage["idPrivKey"]; +}; + +function HashProtect(Str) +{ + var arr = sha3(Str); + for(var i = 0; i < 30000; i++) + { + arr = sha3(arr); + } + return arr; +}; + +function XORHash(arr1,arr2,length) +{ + var arr3 = []; + for(var i = 0; i < length; i++) + { + arr3[i] = arr1[i] ^ arr2[i]; + } + return arr3; +}; diff --git a/Source/HTML/JS/coinlib.js b/Source/HTML/JS/coinlib.js new file mode 100644 index 0000000..2479f46 --- /dev/null +++ b/Source/HTML/JS/coinlib.js @@ -0,0 +1,112 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var MAX_SUM_TER = 1e9; +var MAX_SUM_CENT = 1e9; + +function ADD(Coin,Value2) +{ + Coin.SumCOIN += Value2.SumCOIN; + Coin.SumCENT += Value2.SumCENT; + if(Coin.SumCENT >= MAX_SUM_CENT) + { + Coin.SumCENT -= MAX_SUM_CENT; + Coin.SumCOIN++; + } + return true; +}; + +function SUB(Coin,Value2) +{ + Coin.SumCOIN -= Value2.SumCOIN; + if(Coin.SumCENT >= Value2.SumCENT) + { + Coin.SumCENT -= Value2.SumCENT; + } + else + { + Coin.SumCENT = MAX_SUM_CENT + Coin.SumCENT - Value2.SumCENT; + Coin.SumCOIN--; + } + if(Coin.SumCOIN < 0) + { + return false; + } + return true; +}; + +function DIV(Coin,Value) +{ + Coin.SumCOIN = Coin.SumCOIN / Value; + Coin.SumCENT = Math.floor(Coin.SumCENT / Value); + var SumCOIN = Math.floor(Coin.SumCOIN); + var SumCENT = Math.floor((Coin.SumCOIN - SumCOIN) * MAX_SUM_CENT); + Coin.SumCOIN = SumCOIN; + Coin.SumCENT = Coin.SumCENT + SumCENT; + if(Coin.SumCENT >= MAX_SUM_CENT) + { + Coin.SumCENT -= MAX_SUM_CENT; + Coin.SumCOIN++; + } + return true; +}; + +function FLOAT_FROM_COIN(Coin) +{ + var Sum = Coin.SumCOIN + Coin.SumCENT / MAX_SUM_CENT; + return Sum; +}; + +function STRING_FROM_COIN(Coin) +{ + var Sum = FLOAT_FROM_COIN(Coin); + return Sum.toLocaleString(undefined, {useGrouping:true, style:'decimal', maximumFractionDigits:9}); +}; + +function COIN_FROM_FLOAT(Sum) +{ + var SumCOIN = Math.floor(Sum); + var SumCENT = Math.floor((Sum - SumCOIN) * MAX_SUM_CENT); + var Coin = {SumCOIN:SumCOIN, SumCENT:SumCENT}; + return Coin; +}; + +function COIN_FROM_FLOAT2(Sum) +{ + var SumCOIN = Math.floor(Sum); + var SumCENT = Math.floor(Sum * MAX_SUM_CENT - SumCOIN * MAX_SUM_CENT); + var Coin = {SumCOIN:SumCOIN, SumCENT:SumCENT}; + return Coin; +}; +if(typeof window === "object") + window.COIN_FROM_FLOAT = COIN_FROM_FLOAT2; + +function ISZERO(Coin) +{ + if(Coin.SumCOIN === 0 && Coin.SumCENT === 0) + return true; + else + return false; +}; + +function COIN_FROM_STRING(Str) +{ + throw "TODO: COIN_FROM_STRING"; +}; +if(typeof global === "object") +{ + global.ADD = ADD; + global.SUB = SUB; + global.DIV = DIV; + global.ISZERO = ISZERO; + global.FLOAT_FROM_COIN = FLOAT_FROM_COIN; + global.COIN_FROM_FLOAT = COIN_FROM_FLOAT; + global.COIN_FROM_STRING = COIN_FROM_STRING; +} diff --git a/Source/HTML/JS/crypto-client.js b/Source/HTML/JS/crypto-client.js new file mode 100644 index 0000000..7a467cb --- /dev/null +++ b/Source/HTML/JS/crypto-client.js @@ -0,0 +1,372 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var MAX_SUPER_VALUE_POW = (1 << 30) * 2; +window.TYPE_TRANSACTION_CREATE = 100; + +function GetHashWithValues(hash0,value1,value2,bNotCopy) +{ + var hash; + if(bNotCopy) + hash = hash0; + else + hash = hash0.slice(); + hash[0] = value1 & 0xFF; + hash[1] = (value1 >>> 8) & 0xFF; + hash[2] = (value1 >>> 16) & 0xFF; + hash[3] = (value1 >>> 24) & 0xFF; + hash[4] = value2 & 0xFF; + hash[5] = (value2 >>> 8) & 0xFF; + hash[6] = (value2 >>> 16) & 0xFF; + hash[7] = (value2 >>> 24) & 0xFF; + var arrhash = shaarr(hash); + return arrhash; +}; + +function GetPowPower(arrhash) +{ + var SumBit = 0; + for(var i = 0; i < arrhash.length; i++) + { + var byte = arrhash[i]; + for(var b = 7; b >= 0; b--) + { + if((byte >> b) & 1) + { + return SumBit; + } + else + { + SumBit++; + } + } + } + return SumBit; +}; + +function GetPowValue(arrhash) +{ + var value = (arrhash[0] << 23) * 2 + (arrhash[1] << 16) + (arrhash[2] << 8) + arrhash[3]; + value = value * 256 + arrhash[4]; + value = value * 256 + arrhash[5]; + return value; +}; + +function CreateNoncePOWExtern(arr0,BlockNum,count,startnone) +{ + var arr = []; + for(var i = 0; i < arr0.length; i++) + arr[i] = arr0[i]; + if(!startnone) + startnone = 0; + var maxnonce = 0; + var supervalue = MAX_SUPER_VALUE_POW; + for(var nonce = startnone; nonce <= startnone + count; nonce++) + { + var arrhash = GetHashWithValues(arr, nonce, BlockNum, true); + var value = GetPowValue(arrhash); + if(value < supervalue) + { + maxnonce = nonce; + supervalue = value; + } + } + return maxnonce; +}; +window.TR_TICKET_HASH_LENGTH = 10; + +function CreateHashBody(body,Num,Nonce) +{ + var length = body.length - 12; + body[length + 0] = Num & 0xFF; + body[length + 1] = (Num >>> 8) & 0xFF; + body[length + 2] = (Num >>> 16) & 0xFF; + body[length + 3] = (Num >>> 24) & 0xFF; + body[length + 4] = 0; + body[length + 5] = 0; + length = body.length - 6; + body[length + 0] = Nonce & 0xFF; + body[length + 1] = (Nonce >>> 8) & 0xFF; + body[length + 2] = (Nonce >>> 16) & 0xFF; + body[length + 3] = (Nonce >>> 24) & 0xFF; + body[length + 4] = 0; + body[length + 5] = 0; + var HASH = sha3(body); + var FullHashTicket = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for(var i = 0; i < TR_TICKET_HASH_LENGTH; i++) + FullHashTicket[i] = HASH[i]; + WriteUintToArrOnPos(FullHashTicket, Num, TR_TICKET_HASH_LENGTH); + return sha3(FullHashTicket); +}; +window.DELTA_POWER_POW_TR = 0; +window.DELTA_FOR_TIME_TX = 0; +window.MIN_POWER_POW_TR = 0; +window.CONSENSUS_PERIOD_TIME = 1000; +window.FIRST_TIME_BLOCK = 1530446400000; +window.NEW_SIGN_TIME = 25500000; +window.SetBlockChainConstant = function (Data) +{ + var DeltaServerClient = new Date() - Data.CurTime; + if(!Data.DELTA_CURRENT_TIME) + Data.DELTA_CURRENT_TIME = 0; + window.DELTA_CURRENT_TIME2 = Data.DELTA_CURRENT_TIME - DeltaServerClient; + window.MIN_POWER_POW_TR = DELTA_POWER_POW_TR + Data.MIN_POWER_POW_TR; + window.FIRST_TIME_BLOCK = Data.FIRST_TIME_BLOCK; + window.NEW_SIGN_TIME = Data.NEW_SIGN_TIME; + window.CONSENSUS_PERIOD_TIME = Data.CONSENSUS_PERIOD_TIME; + window.GetCurrentBlockNumByTime = function () + { + var CurrentTime = Date.now() + DELTA_CURRENT_TIME2; + var CurTimeNum = CurrentTime - FIRST_TIME_BLOCK; + var StartBlockNum = Math.floor((CurTimeNum + CONSENSUS_PERIOD_TIME) / CONSENSUS_PERIOD_TIME); + return StartBlockNum; + }; + window.NWMODE = Data.NWMODE; +}; +window.GetCurrentBlockNumByTime = function () +{ + return 0; +}; + +function GetBlockNumTr(arr) +{ + var BlockNum = window.DELTA_FOR_TIME_TX + GetCurrentBlockNumByTime(); + if(arr[0] === TYPE_TRANSACTION_CREATE) + { + var BlockNum2 = Math.floor(BlockNum / 10) * 10; + if(BlockNum2 < BlockNum) + BlockNum2 = BlockNum2 + 10; + BlockNum = BlockNum2; + } + return BlockNum; +}; +var LastCreatePOWTrType = 0; +var LastCreatePOWBlockNum = 0; +var LastCreatePOWHash = [255, 255, 255, 255]; + +function CreateHashBodyPOWInnerMinPower(arr,MinPow,startnonce) +{ + var TrType = arr[0]; + var BlockNum = GetBlockNumTr(arr); + if(MinPow === undefined) + { + MinPow = MIN_POWER_POW_TR + Math.log2(arr.length / 128); + } + var nonce = startnonce; + while(1) + { + var arrhash = CreateHashBody(arr, BlockNum, nonce); + var power = GetPowPower(arrhash); + if(power >= MinPow) + { + if(LastCreatePOWBlockNum === BlockNum && LastCreatePOWTrType === TrType && CompareArr(LastCreatePOWHash, arrhash) > 0) + { + } + else + { + LastCreatePOWBlockNum = BlockNum; + LastCreatePOWTrType = TrType; + LastCreatePOWHash = arrhash; + return nonce; + } + } + nonce++; + if(nonce % 2000 === 0) + { + BlockNum = GetBlockNumTr(arr); + } + } +}; + +function CalcHashFromArray(ArrHashes,bOriginalSeq) +{ + if(bOriginalSeq === undefined) + ArrHashes.sort(CompareArr); + var Buf = []; + for(var i = 0; i < ArrHashes.length; i++) + { + var Value = ArrHashes[i]; + for(var n = 0; n < Value.length; n++) + Buf.push(Value[n]); + } + if(Buf.length === 0) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + else + if(Buf.length === 32) + return Buf; + var Hash = shaarr(Buf); + return Hash; +}; + +function GetArrFromValue(Num) +{ + var arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + arr[0] = Num & 0xFF; + arr[1] = (Num >>> 8) & 0xFF; + arr[2] = (Num >>> 16) & 0xFF; + arr[3] = (Num >>> 24) & 0xFF; + var NumH = Math.floor(Num / 4294967296); + arr[4] = NumH & 0xFF; + arr[5] = (NumH >>> 8) & 0xFF; + return arr; +}; + +function LoadLib(Path) +{ + var item = document.createElement('script'); + item.type = "text/javascript"; + item.src = Path; + document.getElementsByTagName('head')[0].appendChild(item); +}; + +function IsMS() +{ + var ua = window.navigator.userAgent; + var msie = ua.indexOf("MSIE "); + if(msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) + { + return 1; + } + else + { + return 0; + } +}; + +function LoadSignLib() +{ + if(window.SignLib) + return ; + LoadLib("./JS/sign-lib-min.js"); +}; + +function ComputeSecretWithCheck(PubKey,StrPrivKey,F) +{ + if(!window.SignLib) + { + SetError("Error - SignLib not installed"); + return ; + } + if(!IsHexStr(StrPrivKey) || StrPrivKey.length !== 64) + { + SetError("Error set PrivKey"); + return ; + } + var PrivKey = Buffer.from(GetArrFromHex(StrPrivKey)); + if(typeof PubKey === "string") + { + if(!IsHexStr(PubKey) || PubKey.length !== 66) + { + SetError("Error PubKey"); + return ; + } + PubKey = Buffer.from(GetArrFromHex(PubKey)); + } + var Result = SignLib.ecdh(PubKey, PrivKey); + F(sha3(Result)); +}; + +function ComputeSecret(Account,PubKey,F) +{ + if(GetPrivKey()) + { + ComputeSecretWithCheck(PubKey, GetPrivKey(), F); + } + else + { + GetData("GetWalletInfo", {Account:Account}, function (Data) + { + if(!Data || !Data.result) + return ; + ComputeSecretWithCheck(PubKey, Data.PrivateKey, F); + }); + } +}; + +function Encrypt(ArrSecret,StartEncrypt,StrName,StrValue) +{ + var arrRnd = sha3arr2(ArrSecret, sha3(StrName + StartEncrypt)); + var Arr = toUTF8Array(StrValue); + return DoSecret(Arr, arrRnd); +}; + +function Decrypt(ArrSecret,StartEncrypt,StrName,Arr) +{ + if(!ArrSecret) + return "".padEnd(Arr.length / 2, "."); + if(typeof Arr === "string") + Arr = GetArrFromHex(Arr); + var arrRnd = sha3arr2(ArrSecret, sha3(StrName + StartEncrypt)); + var Arr2 = DoSecret(Arr, arrRnd); + var Str = Utf8ArrayToStr(Arr2); + return Str; +}; + +function DoSecret(Arr,arrRnd) +{ + var Arr2 = []; + var CryptID = 0; + var Pos = 0; + while(Pos < Arr.length) + { + CryptID++; + WriteUintToArrOnPos(arrRnd, CryptID, 0); + var CurBuf = sha3(arrRnd); + for(var i = 0; i < 32 && Pos < Arr.length; i++, Pos++) + { + Arr2[Pos] = Arr[Pos] ^ CurBuf[i]; + } + } + return Arr2; +}; +var glEncryptInit = 0; + +function EncryptInit() +{ + glEncryptInit++; + var Time = Date.now() - new Date(2019, 0, 1); + return Math.floor(Time * 100 + Math.random() * 100) * 100 + glEncryptInit; +}; + +function EncryptID(ArrSecret,StartEncrypt,id) +{ + var Value = $(id).value; + Value = Value.padEnd(Value.length + random(5), " "); + return GetHexFromArr(Encrypt(ArrSecret, StartEncrypt, id, Value)); +}; + +function EncryptFields(ArrSecret,Params,ArrName) +{ + if(!Params.Crypto) + Params.Crypto = EncryptInit(); + for(var i = 0; i < ArrName.length; i++) + { + var Name = ArrName[i]; + var Value = Params[Name]; + Value = Value.padEnd(Value.length + random(5), " "); + Params[Name] = GetHexFromArr(Encrypt(ArrSecret, Params.Crypto, Name, Value)); + } +}; + +function DecryptFields(ArrSecret,Params,ArrName) +{ + for(var i = 0; i < ArrName.length; i++) + { + var Name = ArrName[i]; + if(Params[Name]) + { + Params[Name] = Decrypt(ArrSecret, Params.Crypto, Name, GetArrFromHex(Params[Name])); + } + else + { + Params[Name] = ""; + } + } +}; diff --git a/Source/HTML/JS/dapp-inner.js b/Source/HTML/JS/dapp-inner.js new file mode 100644 index 0000000..cc1eb56 --- /dev/null +++ b/Source/HTML/JS/dapp-inner.js @@ -0,0 +1,521 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + + +function SendPay(Data) +{ + Data.cmd = "pay"; + SendData(Data); +}; + +function SetStorage(Key,Value) +{ + var Data = {cmd:"setstorage", Key:Key, Value:Value}; + SendData(Data); +}; + +function GetStorage(Key,F) +{ + var Data = {cmd:"getstorage", Key:Key}; + SendData(Data, F); +}; + +function SetCommon(Key,Value) +{ + var Data = {cmd:"setcommon", Key:Key, Value:Value}; + SendData(Data); +}; + +function GetCommon(Key,F) +{ + var Data = {cmd:"getcommon", Key:Key}; + SendData(Data, F); +}; + +function GetInfo(F,bUseCache) +{ + var Data = {cmd:"DappInfo", AllAccounts:ALL_ACCOUNTS, AllData:!bUseCache}; + SendData(Data, F); +}; + +function Call(Account,MethodName,Params,F) +{ + var Data = {cmd:"DappCall", MethodName:MethodName, Params:Params, Account:Account}; + SendData(Data, F); +}; + +function SendCall(Account,MethodName,Params,FromNum) +{ + if(!INFO.WalletCanSign) + { + SetError("Pls, open wallet"); + return 0; + } + var Data = {cmd:"DappSendCall", MethodName:MethodName, Params:Params, Account:Account, FromNum:FromNum}; + SendData(Data); + return 1; +}; + +function GetWalletAccounts(F) +{ + var Data = {cmd:"DappWalletList"}; + SendData(Data, F); +}; + +function GetAccountList(Params,F) +{ + var Data = {cmd:"DappAccountList", Params:Params}; + SendData(Data, F); +}; + +function GetSmartList(Params,F) +{ + var Data = {cmd:"DappSmartList", Params:Params}; + SendData(Data, F); +}; + +function GetBlockList(Params,F) +{ + var Data = {cmd:"DappBlockList", Params:Params}; + SendData(Data, F); +}; + +function GetTransactionList(Params,F) +{ + var Data = {cmd:"DappTransactionList", Params:Params}; + SendData(Data, F); +}; + +function DappSmartHTMLFile(Smart,F) +{ + var Data = {cmd:"DappSmartHTMLFile", Params:{Smart:Smart}}; + SendData(Data, F); +}; + +function DappBlockFile(BlockNum,TrNum,F) +{ + var Data = {cmd:"DappBlockFile", Params:{BlockNum:BlockNum, TrNum:TrNum}}; + SendData(Data, F); +}; + +function SetStatus(Str) +{ + SendData({cmd:"SetStatus", Message:Str}); +}; + +function SetError(Str) +{ + SendData({cmd:"SetError", Message:Str}); +}; + +function SetLocationPath(Str) +{ + SendData({cmd:"SetLocationHash", Message:Str}); +}; + +function CreateNewAccount(Currency) +{ + SendData({cmd:"CreateNewAccount", Currency:Currency}); +}; + +function OpenLink(Str) +{ + SendData({cmd:"OpenLink", Message:Str}); +}; + +function SetMobileMode() +{ + SendData({cmd:"SetMobileMode"}); +}; + +function ComputeSecret(PubKey,F,Account) +{ + if(!INFO.WalletCanSign) + { + SetError("Pls, open wallet"); + return 0; + } + if(!Account && USER_ACCOUNT.length) + Account = USER_ACCOUNT[0].Num; + if(typeof PubKey === "number") + { + var AccNum = PubKey; + GetAccountList({StartNum:AccNum, CountNum:1}, function (Err,Arr) + { + if(Err) + { + SetError(Err); + } + else + { + SendData({cmd:"ComputeSecret", Account:Account, PubKey:Arr[0].PubKey.data}, F); + } + }); + } + else + { + SendData({cmd:"ComputeSecret", Account:Account, PubKey:PubKey}, F); + } +}; + +function CheckInstall() +{ + SendData({cmd:"CheckInstall"}); +}; + +function SendTransaction(Body,TR,SumPow,F) +{ + SetError("Cannt SEND TR: " + JSON.stringify(TR)); +}; + +function CurrencyName(Num) +{ + var Name = MapCurrency[Num]; + if(!Name) + { + GetSmartList({StartNum:Num, CountNum:1, TokenGenerate:1}, function (Err,Arr) + { + if(Err || Arr.length === 0) + return ; + var Smart = Arr[0]; + Name = GetTokenName(Smart.Num, Smart.ShortName); + MapCurrency[Smart.Num] = Name; + }); + Name = GetTokenName(Num, ""); + } + return Name; +}; +var SendCountUpdate = 0; + +function FindAllCurrency() +{ + SendCountUpdate++; + GetSmartList({StartNum:8, CountNum:100, TokenGenerate:1}, function (Err,Arr) + { + SendCountUpdate--; + if(Err) + return ; + for(var i = 0; i < Arr.length; i++) + { + var Smart = Arr[i]; + if(!MapCurrency[Smart.Num]) + { + var Name = GetTokenName(Smart.Num, Smart.ShortName); + MapCurrency[Smart.Num] = Name; + } + } + }); +}; + +function GetFilePath(Path) +{ + if(window.PROTOCOL_SERVER_PATH && Path.indexOf("file/")) + { + if(Path.substr(0, 1) !== "/") + Path = "/" + Path; + Path = window.PROTOCOL_SERVER_PATH + Path; + } + return Path; +}; + +function GetParamsFromPath(Name) +{ + if(!OPEN_PATH) + return undefined; + var arr = OPEN_PATH.split("&"); + for(var i = 0; i < arr.length; i++) + { + if(arr[i].indexOf(Name + "=") === 0) + { + return arr[i].split("=")[1]; + } + } +}; + +function GetState(AccNum,F,FErr) +{ + SendCountUpdate++; + GetAccountList({StartNum:AccNum, CountNum:1}, function (Err,Arr) + { + SendCountUpdate--; + if(!Err && Arr.length) + { + var Item = Arr[0].SmartState; + if(Item) + { + F(Item); + return ; + } + } + if(FErr) + { + FErr(); + return ; + } + }); +}; +var glMapF = {}; +var glKeyF = 0; + +function SendData(Data,F) +{ + if(!window.parent) + return ; + if(F) + { + glKeyF++; + Data.CallID = glKeyF; + glMapF[glKeyF] = F; + } + window.parent.postMessage(Data, "*"); +}; + +function OnMessage(event) +{ + var Data = event.data; + if(!Data || typeof Data !== "object") + return ; + var CallID = Data.CallID; + var cmd = Data.cmd; + if(CallID) + { + var F = glMapF[CallID]; + if(F) + { + delete Data.CallID; + delete Data.cmd; + switch(cmd) + { + case "getstorage": + case "getcommon": + F(Data.Key, Data.Value); + break; + case "DappCall": + F(Data.Err, Data.RetValue); + break; + case "DappInfo": + F(Data.Err, Data); + break; + case "DappWalletList": + case "DappAccountList": + case "DappSmartList": + case "DappBlockList": + case "DappTransactionList": + F(Data.Err, Data.arr); + break; + case "DappBlockFile": + case "DappSmartHTMLFile": + F(Data.Err, Data.Body); + break; + case "ComputeSecret": + F(Data.Result); + break; + default: + console.log("Error cmd: " + cmd); + } + delete glMapF[CallID]; + } + } + else + { + switch(cmd) + { + case "History": + var eventEvent = new CustomEvent("History", {detail:Data}); + window.dispatchEvent(eventEvent); + break; + case "OnEvent": + if(window.OnEvent) + { + window.OnEvent(Data); + } + var eventEvent = new CustomEvent("Event", {detail:Data}); + window.dispatchEvent(eventEvent); + } + } +}; + +function OpenRefFile(Str) +{ + var Param = ParseFileName(Str); + if(Param.BlockNum) + DappBlockFile(Param.BlockNum, Param.TrNum, function (Err,Body) + { + document.write(Body); + }); + else + { + OpenLink(Str); + } +}; + +function SaveToStorageByArr(Arr) +{ + SetStorage("VerSave", "1"); + for(var i = 0; i < Arr.length; i++) + { + var name = Arr[i]; + var Item = $(name); + if(Item) + { + if(Item.type === "checkbox") + SetStorage(name, 0 + Item.checked); + else + SetStorage(name, Item.value); + } + } +}; + +function LoadFromStorageByArr(Arr,F,bAll) +{ + GetStorage("VerSave", function (Key,Value) + { + if(Value === "1") + { + for(var i = 0; i < Arr.length; i++) + { + if(i === Arr.length - 1) + LoadFromStorageById(Arr[i], F); + else + LoadFromStorageById(Arr[i]); + } + } + if(bAll && F) + F(0); + }); +}; + +function LoadFromStorageById(Name,F) +{ + GetStorage(Name, function (Key,Value) + { + var Item = document.getElementById(Name); + if(Item) + { + if(Item.type === "checkbox") + Item.checked = parseInt(Value); + else + Item.value = Value; + } + if(F) + F(Key, Value); + }); +}; +var SendCountDappParams = 0; + +function GetDappParams(BNum,TrNum,F,bAll) +{ + if(!BNum) + { + if(bAll) + F(); + return ; + } + SendCountDappParams++; + DappBlockFile(BNum, TrNum, function (Err,Data) + { + SendCountDappParams--; + if(!Err && Data.Type === 135) + { + try + { + var Params = JSON.parse(Data.Params); + } + catch(e) + { + } + if(Params) + { + F(Params, Data.MethodName, Data.FromNum); + return ; + } + } + if(bAll) + F(); + }); +}; +document.addEventListener("DOMContentLoaded", function () +{ + var refs = document.getElementsByTagName("A"); + for(var i = 0, L = refs.length; i < L; i++) + { + if(refs[i].href.indexOf("/file/") >= 0) + { + refs[i].onclick = function () + { + OpenRefFile(this.href); + }; + } + } +}); +if(window.addEventListener) +{ + window.addEventListener("message", OnMessage); +} +else +{ + window.attachEvent("onmessage", OnMessage); +} +var SMART = {}, BASE_ACCOUNT = {}, INFO = {}, USER_ACCOUNT = [], USER_ACCOUNT_MAP = {}, OPEN_PATH = "", ACCOUNT_OPEN_NUM = 0; +var ALL_ACCOUNTS = 0; +var WasStartInit = 0, WasStartInit2 = 0; +var eventInfo = new Event("UpdateInfo"); + +function UpdateDappInfo() +{ + GetInfo(function (Err,Data) + { + if(Err) + { + return ; + } + INFO = Data; + SMART = Data.Smart; + BASE_ACCOUNT = Data.Account; + OPEN_PATH = Data.OPEN_PATH; + ACCOUNT_OPEN_NUM = ParseNum(OPEN_PATH); + SetBlockChainConstant(Data); + USER_ACCOUNT = Data.ArrWallet; + USER_ACCOUNT_MAP = {}; + for(var i = 0; i < USER_ACCOUNT.length; i++) + USER_ACCOUNT_MAP[USER_ACCOUNT[i].Num] = USER_ACCOUNT[i]; + if(window.OnInit && !WasStartInit) + { + WasStartInit = 1; + window.OnInit(1); + } + else + if(window.OnUpdateInfo) + { + window.OnUpdateInfo(); + } + if(!WasStartInit2) + { + WasStartInit2 = 1; + var eventInit = new Event("Init"); + window.dispatchEvent(eventInit); + } + window.dispatchEvent(eventInfo); + if(Data.ArrEvent) + for(var i = 0; i < Data.ArrEvent.length; i++) + { + var Item = Data.ArrEvent[i]; + Item.cmd = "OnEvent"; + OnMessage({data:Item}); + } + }, 1); +}; +window.addEventListener('load', function () +{ + if(!window.sha3) + LoadLib("./JS/sha3.js"); + UpdateDappInfo(); + setInterval(UpdateDappInfo, 1000); +}); diff --git a/Source/HTML/JS/diagram.js b/Source/HTML/JS/diagram.js new file mode 100644 index 0000000..cd2178e --- /dev/null +++ b/Source/HTML/JS/diagram.js @@ -0,0 +1,455 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var DiagramMap = {}; +var DiagramMapId = {}; +var LMouseOn = false; +if(!window.toStaticHTML) + toStaticHTML = function (Str) + { + return Str; + }; + +function Rigth(Str,Count) +{ + if(Str.length < Count) + return Str; + else + return Str.substr(Str.length - Count); +}; + +function SetHTMLDiagramItem(Item,width) +{ + Item.mouseX = width - 50; + if(Item.Extern || Item.Delete) + return ; + var MinHeight = 80; + if(!Item.id) + Item.id = "DgrmId" + Item.num; + DiagramMap[Item.name] = Item; + DiagramMapId[Item.id] = Item; + if(Item.isLine) + { + if(Item.text) + Str = "
" + Item.text + ''; + else + Str = "
"; + } + else + { + Str = '
' + Item.text + '
\ +
'; + } + var ElBlock = document.getElementById("B" + Item.id); + if(ElBlock) + ElBlock.innerHTML = toStaticHTML(Str); + else + { + var diargams = document.getElementById("diargams"); + diargams.innerHTML = toStaticHTML(diargams.innerHTML + "
" + Str + "
"); + } +}; + +function SetDiagramMouseX(event,mode) +{ + if(event.srcElement && event.srcElement.className && event.srcElement.className.indexOf && event.srcElement.className.indexOf("DIAGRAM") >= 0) + { + if(mode === "down") + LMouseOn = true; + else + if(mode === "up") + LMouseOn = false; + event.preventDefault(); + if(LMouseOn === true) + { + var obj = event.srcElement; + var mouse = getMouse(obj, event); + if(event.ctrlKey === true) + { + for(var key in DiagramMapId) + { + var Item = DiagramMapId[key]; + Item.mouseX = mouse.x; + DrawDiagram(Item); + } + } + else + { + var Item = DiagramMapId[obj.id]; + if(Item) + { + Item.mouseX = mouse.x; + DrawDiagram(Item); + } + } + } + } +}; + +function DrawDiagram(Item) +{ + if(Item.Delete) + return ; + var arr = Item.arr; + if(!arr) + arr = Item.ArrList; + var arrX = Item.arrX; + var GreenValue = Item.value; + var StepTime = Item.steptime; + var StartNumber = Item.startnumber; + var StartServer = Item.starttime; + var mouseX = Item.mouseX; + var KPrecision = Item.KPrecision; + if(!KPrecision) + KPrecision = 1; + if(!arr) + return ; + var obj = document.getElementById(Item.id); + var ctx = obj.getContext('2d'); + var Left = 50; + var Top = 11; + var Button = 15; + var Right = 50; + if(Item.fillStyle) + ctx.fillStyle = Item.fillStyle; + else + ctx.fillStyle = "#FFF"; + ctx.fillRect(0, 0, obj.width, obj.height); + if(arr.length <= 0) + return ; + var Pow2 = 0; + if(Item.name.substr(Item.name.length - 2) === "**") + Pow2 = 1; + var MaxValue = arr[0]; + var MinValue = arr[0]; + var AvgValue = 0; + for(var i = 0; i < arr.length; i++) + { + if(arr[i] > MaxValue) + MaxValue = arr[i]; + if(arr[i] < MinValue) + MinValue = arr[i]; + if(arr[i]) + AvgValue += arr[i]; + } + if(Item.name.substr(0, 4) !== "MAX:" || !Item.AvgValue) + AvgValue = AvgValue / arr.length; + else + AvgValue = Item.AvgValue; + if(Pow2 && AvgValue) + AvgValue = Math.pow(2, AvgValue) / 1000000; + if(AvgValue < 50) + AvgValue = AvgValue.toFixed(2); + else + AvgValue = Math.floor(AvgValue); + if(Item.MaxValue !== undefined) + MaxValue = Item.MaxValue; + if(Pow2 && MaxValue) + MaxValue = Math.pow(2, MaxValue) / 1000000; + var HValue = MaxValue; + if(HValue <= 0) + HValue = 1; + var KX = (obj.width - Left - Right) / arr.length; + var KY = (obj.height - Top - Button) / HValue; + var DeltaY = 0; + var bLine = Item.line; + if(Item.zero) + { + bLine = 1; + DeltaY -= Item.zero * KY; + MaxValue -= Item.zero; + AvgValue -= Item.zero; + } + MaxValue = Math.floor(MaxValue + 0.5); + if(bLine) + ctx.lineWidth = 3; + else + if(KX > 1) + ctx.lineWidth = KX; + else + ctx.lineWidth = 1; + var StartX = Left; + var StartY = obj.height - Button; + var mouseValueX = 0; + var mouseValue = undefined; + var mouseColor = undefined; + +function DrawLines(arr,mode,color) + { + var WasMove0 = 0; + ctx.beginPath(); + ctx.moveTo(Left, obj.height - Button); + ctx.strokeStyle = color; + var PrevX = undefined; + for(var i = 0; i < arr.length; i++) + { + var Value = arr[i]; + if(!Value) + Value = 0; + if(Value) + { + if(Pow2) + Value = Math.pow(2, Value) / 1000000; + } + if(mode === "green") + { + if(Value > GreenValue) + continue; + } + else + if(mode === "red") + { + if(Value <= GreenValue) + continue; + } + var Value1 = Value; + if(Value1 > GreenValue) + Value1 = GreenValue; + var VX1 = Math.floor(Value1 * KY); + var VX2 = Math.floor(Value * KY); + if(VX1 === VX2) + VX1 -= 2; + var x = StartX + ctx.lineWidth / 2 + (i) * KX; + if(bLine) + { + if(!WasMove0) + { + WasMove0 = 1; + ctx.moveTo(x, StartY - VX2); + } + else + { + ctx.lineTo(x, StartY - VX2); + } + } + else + { + ctx.moveTo(x, StartY - VX1); + ctx.lineTo(x, StartY - VX2); + } + if(mouseX) + { + var deltaCur = Math.abs(x - mouseX); + var deltaWas = Math.abs(mouseValueX - mouseX); + if(deltaCur < deltaWas) + { + mouseValueX = x; + mouseValue = Value; + if(Item.zero) + mouseValue -= Item.zero; + mouseColor = color; + } + } + } + ctx.stroke(); + }; + if(!Item.red) + Item.red = "#A00"; + if(bLine) + { + DrawLines(arr, "line", Item.red); + } + else + { + DrawLines(arr, "red", Item.red); + if(GreenValue > 0) + DrawLines(arr, "green", "#0A0"); + } + var MaxValueText = GetValueByItemProperty(MaxValue, Item); + var AvgValueText = GetValueByItemProperty(AvgValue, Item); + ctx.lineWidth = 0.5; + ctx.beginPath(); + ctx.strokeStyle = "#000"; + Left--; + StartX--; + StartY += 2; + ctx.moveTo(Left, Top); + ctx.lineTo(StartX, StartY); + ctx.moveTo(StartX, StartY + DeltaY); + ctx.lineTo(obj.width - 10, StartY + DeltaY); + ctx.stroke(); + if(mouseX !== undefined) + { + ctx.beginPath(); + ctx.lineWidth = 0.5; + ctx.strokeStyle = "#00F"; + ctx.moveTo(mouseX, Top); + ctx.lineTo(mouseX, StartY); + ctx.stroke(); + if(mouseValue !== undefined) + { + ctx.fillStyle = mouseColor; + var Val = GetValueByItemProperty(mouseValue, Item); + var mouseTextX = mouseX; + if(Item.MouseText) + mouseTextX -= 3 * Item.MouseText.length; + else + Item.MouseText = ""; + ctx.fillText("" + Val + Item.MouseText, mouseTextX - 3, Top - 2); + } + } + ctx.fillStyle = "#000"; + if(!Item.NoTextMax) + ctx.fillText(Rigth("          " + MaxValueText, 8), 0, Top - 3); + if(MaxValue > 0 && AvgValue > 0) + { + var heigh = StartY - Top; + var KKY = AvgValue / MaxValue; + var y = (heigh - Math.floor(KKY * heigh)); + var yT = y; + if(yT < 10) + { + yT = 10; + } + ctx.beginPath(); + ctx.moveTo(Left - 2, y + Top); + ctx.lineTo(Left + 2, y + Top); + ctx.stroke(); + ctx.strokeStyle = "#00F"; + ctx.fillText(Rigth("          " + AvgValueText, 8), 0, yT + Top); + } + var CountNameX = 10; + if(arr.length < CountNameX) + CountNameX = arr.length; + var KX3 = (obj.width - Left - Right) / CountNameX; + var KDelitel = 1; + var Step = arr.length / CountNameX; + var StartTime, bNumber; + if(arrX) + { + } + else + if(StartNumber !== undefined) + { + bNumber = 1; + StartTime = StartNumber; + } + else + if(StartServer) + { + bNumber = 1; + StartTime = Math.floor(((Date.now() - StartServer) - StepTime * arr.length * 1000) / 1000); + if(StartTime < 0) + StartTime = 0; + var KDelitel = Math.floor(Step / 10) * 10; + if(KDelitel == 0) + KDelitel = 1; + } + else + { + bNumber = 0; + StartTime = Date.now() - StepTime * arr.length * 1000; + StartX = StartX - 16; + } + for(i = 0; i <= CountNameX; i++) + { + var Val; + if(i === CountNameX) + { + Val = arr.length * StepTime; + KDelitel = 1; + } + else + if(i === 0) + Val = 0; + else + Val = i * Step * StepTime; + var Str; + if(arrX) + { + Val = Math.floor(Val); + Str = arrX[Val]; + if(Str === undefined) + Str = ""; + } + else + if(bNumber) + { + Val = Math.floor((StartTime + Val) / KDelitel) * KDelitel; + Str = Val; + } + else + { + var Time = new Date(StartTime + Val * 1000); + Str = "" + Time.getHours(); + Str += ":" + Rigth("0" + Time.getMinutes(), 2); + Str += ":" + Rigth("0" + Time.getSeconds(), 2); + } + ctx.fillText(Str, StartX + i * KX3, StartY + 10); + } +}; + +function GetValueByItemProperty(Value,Item) +{ + if(Item.MathPow && Item.MathDiv) + { + Value = Math.pow(Item.MathPow, Value) / Item.MathDiv; + } + var KPrecision = Item.KPrecision; + if(!Item.KPrecision) + KPrecision = 1; + Value = Math.floor(Value * KPrecision + 0.5) / KPrecision; + return Value; +}; + +function InitDiagramByArr(Arr,width) +{ + for(var i = 0; i < Arr.length; i++) + { + Arr[i].num = i + 1; + SetHTMLDiagramItem(Arr[i], width); + } + window.addEventListener('mousedown', function (event) + { + SetDiagramMouseX(event, "down"); + }, false); + window.addEventListener('mouseup', function (event) + { + SetDiagramMouseX(event, "up"); + }, false); + window.addEventListener('onmousemove', function (event) + { + SetDiagramMouseX(event, "move"); + }, false); +}; + +function getMouse(canvas,e) +{ + var x = e.clientX - getTrueOffsetLeft(canvas); + if(window.pageXOffset) + x = x + window.pageXOffset; + var y = e.clientY - getTrueOffsetTop(canvas); + if(window.pageYOffset) + y = y + window.pageYOffset; + var coord = {x:x, y:y}; + return coord; +}; + +function getTrueOffsetLeft(ele) +{ + var n = 0; + while(ele) + { + n += ele.offsetLeft || 0; + ele = ele.offsetParent; + } + return n; +}; + +function getTrueOffsetTop(ele) +{ + var n = 0; + while(ele) + { + n += ele.offsetTop || 0; + ele = ele.offsetParent; + } + return n; +}; diff --git a/Source/HTML/JS/highlight-html.js b/Source/HTML/JS/highlight-html.js new file mode 100644 index 0000000..6e34bdf --- /dev/null +++ b/Source/HTML/JS/highlight-html.js @@ -0,0 +1,109 @@ +hljs.registerLanguage('html', FLanguage); +function FLanguage(hljs) +{ + var XML_IDENT_RE = '[A-Za-z0-9\\._:-]+'; + var TAG_INTERNALS = { + endsWithParent: true, + illegal: /`]+/} + ] + } + ] + } + ] + }; + return { + aliases: ['html', 'xhtml', 'rss', 'atom', 'xjb', 'xsd', 'xsl', 'plist'], + case_insensitive: true, + contains: [ + { + className: 'meta', + begin: '', + relevance: 10, + contains: [{begin: '\\[', end: '\\]'}] + }, + hljs.COMMENT( + '', + { + relevance: 10 + } + ), + { + begin: '<\\!\\[CDATA\\[', end: '\\]\\]>', + relevance: 10 + }, + { + className: 'meta', + begin: /<\?xml/, end: /\?>/, relevance: 10 + }, + { + begin: /<\?(php)?/, end: /\?>/, + subLanguage: 'php', + contains: [ + // We don't want the php closing tag ?> to close the PHP block when + // inside any of the following blocks: + {begin: '/\\*', end: '\\*/', skip: true}, + {begin: 'b"', end: '"', skip: true}, + {begin: 'b\'', end: '\'', skip: true}, + hljs.inherit(hljs.APOS_STRING_MODE, {illegal: null, className: null, contains: null, skip: true}), + hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null, className: null, contains: null, skip: true}) + ] + }, + { + className: 'tag', + /* + The lookahead pattern (?=...) ensures that 'begin' only matches + '|$)', end: '>', + keywords: {name: 'style'}, + contains: [TAG_INTERNALS], + starts: { + end: '', returnEnd: true, + subLanguage: ['css', 'xml'] + } + }, + { + className: 'tag', + // See the comment in the + +
+ + + + +
+
+
+
+
+ + +
+
+ + + +Use sound alert
+
+ + + + + + + + + + + + + + + + diff --git a/Source/HTML/console.html b/Source/HTML/console.html new file mode 100644 index 0000000..ffc0fcb --- /dev/null +++ b/Source/HTML/console.html @@ -0,0 +1,523 @@ + + + + + Console + + + + + + + + + + + + + + + + + + + + + + +
+ CONSOLE + +

+
...

+ + + Mode: + + + POW: Delta BlockNum: + +
+ +
+
+ +
+ +
+
+
+ + Name: + +
+ +
+ + + + + + + sec + TX process: + WEB process: + + +
+ +
+ + +
+
+ + +
+ + diff --git a/Source/HTML/dapp-edit.html b/Source/HTML/dapp-edit.html new file mode 100644 index 0000000..2dc9f0f --- /dev/null +++ b/Source/HTML/dapp-edit.html @@ -0,0 +1,1048 @@ + + + + + Dapps IDE (simple) + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + +
+
-----------------------
+ +
+ + + +
+
+ Log from node: +

+
+
+
+ + + + + diff --git a/Source/HTML/dapp-frame.html b/Source/HTML/dapp-frame.html new file mode 100644 index 0000000..953c1c6 --- /dev/null +++ b/Source/HTML/dapp-frame.html @@ -0,0 +1,594 @@ + + + + + + + + DAPP Loading... + + + + + + + + + + + + + + +
+ + + + diff --git a/Source/HTML/history.html b/Source/HTML/history.html new file mode 100644 index 0000000..9e0485f --- /dev/null +++ b/Source/HTML/history.html @@ -0,0 +1,308 @@ + + + + + History account + + + + + + + + + + + + + + +
+
+ +
+ +
+
+
+
+
+
+
+ + History of +
+
+
Balance: 000
+
+
+
+ + + + +
+ +
+ + + + + + + + + + + + + +
...FromToDateAmountCurDescriptionConfirmBlockTx
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/Source/HTML/mobile-wallet.html b/Source/HTML/mobile-wallet.html new file mode 100644 index 0000000..891c4ed --- /dev/null +++ b/Source/HTML/mobile-wallet.html @@ -0,0 +1,716 @@ + + + + + TERA Mobile + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + +
+ +
+
+ +
+
+
+ + + + +
+

Welcome to TERA Wallet

+

ATTENTION: Before using the wallet, save the private key.

+ + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + +
+ +
+ +
+
+
 
+ + + + + +
+
+ + + + + +
+
+ + +
+ +
+ + + + + + + + diff --git a/Source/HTML/monitor.html b/Source/HTML/monitor.html new file mode 100644 index 0000000..c9b0ecd --- /dev/null +++ b/Source/HTML/monitor.html @@ -0,0 +1,832 @@ + + + + Monitor + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+
+ + +
+
+
+
Blockchain Power
+ Adviser: + Miner: + <= + + Nonce: + + Block start: length: + + + + + +
+ + + + + +
+
+
+
+ +
+
+ New diagram + + + + +
+
+
+ + +
+ + + + + + diff --git a/Source/HTML/network.html b/Source/HTML/network.html new file mode 100644 index 0000000..1fdca61 --- /dev/null +++ b/Source/HTML/network.html @@ -0,0 +1,530 @@ + + + + + Network + + + + + + + + + + + + + + + + + + + + +
+ NETWORK
+ Max nodes on level: + + +

+ +
+ + + + + + + + + + + + + + +
LNode1Node2Node3Node4Node5Node6Node7ConnectedNot connected
+ + +
+
+
+ + +
+
+ + + \ No newline at end of file diff --git a/Source/HTML/password.html b/Source/HTML/password.html new file mode 100644 index 0000000..d31c236 --- /dev/null +++ b/Source/HTML/password.html @@ -0,0 +1,339 @@ + + + + + Password + + + + + + + + +
+ ENTER PASSWORD +
+ +
+
+
+ + diff --git a/Source/HTML/stat.html b/Source/HTML/stat.html new file mode 100644 index 0000000..55f955e --- /dev/null +++ b/Source/HTML/stat.html @@ -0,0 +1,382 @@ + + + + Counters + + + + + + + + + + + + + + + + + + + +
+ +
+ +

Performance counters

+ +
+ Filter: +
+ +
+ + + + + + + + + + + + + + + +
Counters10 sec10 mintotalDiagr
Total
+ +
+ + + + + + + + + + + + + + + +
Errors10 sec10 mintotalDiagr
Total
+ + + +
+ + +
+ + + +
+
+ + + + + diff --git a/Source/HTML/tera.ico b/Source/HTML/tera.ico new file mode 100644 index 0000000000000000000000000000000000000000..bffcf5650b9d2d46a1c51ff8c87a56f8421077e7 GIT binary patch literal 9662 zcmeI1YiL|W6vyvAnpfT3n4mSKb~j3}r4pjptRGA_!9)`UjUR$vT8L1p^@S(|MRZfC z7}P*p^@E^L5w%8JDuOQzrV@pseo*n#v)(%7qh*SMcB1*jRzW92C~T z|9lPf_4O5Hv)Oo0PfsEjw3VyAx3{;Hv=5Q@A?nKfbfrH@99a~!PspZh%bu(6xKFna z+Tx1UV=vUz)wQCfrDa1Vli8e$#>U1i)Ne|s)9YigSY>N#YjJyfds|aeQ?FN_y3Im; z8X6iNtgEYQBR;RsX=^?nk5|;!*Iz3i>St_tZ8tYJ-=kPHe`X9Cdn%PmhQr|Y@9{6%3kdizWtA2||@MqfxKlPUD4*`mAv zu7tfHqkbHrtshoE0`7y~!B1cHe@yC?yW^gWJw)3%@Cy6`Haq)hzX}$3?M&LLpluV3 zfX$BLy&GbnV{#CJeD6W06~b=clsQqI+u>KR`BY2~L8ZID`!j8tiwoc#&^-IuQ`@I- zF%-i(I0%0Fs{b3Fhb6`*pTn>UK7=5D@1c{1Y3s(=GLm-1b2Iz^LGe6;P8BSMCqXgV z`pD)DP%MA^od#PW==)Z`TcO0nX_M$Jh3zm3ws^jyeJzxLz6*UPHhs7viu1g4Xe$=sW-y!s}pLH}d@zTmdS8rg#s)YY?<=X{^0)%)aG~*ITBKa`}9C8-n(h&(XOQ zw2nGK@!7_#F&qxikcB4K3D>J{CFIKa@jUMwfzV;f%V+> z#_TQAUokxmLGz_CUJs?P3O<0K82#Gi>sdGx{?pu>anL^km%ta`=TG%o6N8|=ZuG><7~xBxPIYL#@|;zGZ9{!CKInWs5`ey#tgtI$s|F<+rVm zX*-DCb&yxi`i&L4XTTE>v?lhTqnt7Ko1k-9@yq5@u-MOkzV+BvE;qr)VCKv#M`?Qr zszGPSN_ZcaK=EG&Ly&8|m^$TbKP;P#7;Qd?L3_ONS#l}p>@_)I;`Pb{=&XhlazC@hH`6v^ zQO@27KY=%1cUgPvqp)bEwr4|6@qo_%pqy2%Tm;kJ1IFP*v<`RUKcdg4yPbFdv! z^D#zSO#Dt2uCA_LSyNM!?&#>4llz%1zL~V~J6lA|M7N+bJI`)~c`nTJJX-?_NAE4H za=>vi13X=Iv#$^Eq=HOzBdJg5scTs8J9*=YERy7+8P+d2xr3Lj$LwCKmJiR5=RM@ag=mR9A#8C5=U8;k70oK^Qxbc?3XGJ^BbdE b9^!qn%0`YPC(5Il>GDW+qCCc*^zHIrO3mUB literal 0 HcmV?d00001 diff --git a/Source/HTML/wallet.html b/Source/HTML/wallet.html new file mode 100644 index 0000000..a397df8 --- /dev/null +++ b/Source/HTML/wallet.html @@ -0,0 +1,2062 @@ + + + + + + + TERA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
CONFIG
SEND
DApps
EXPLORER
+ + + +
 
+


+ +
+
+
+
+ + +
+
+ + +
+ +
+ + +

+
+ Block: + DB delta: + TX delta: + Time delta: + Wallet ver: + Max: tps +
+ + + + + + +
+ + + + + +
+ + +
+ + + + + + + + + + + + +
IDAmountCurNameOPAdvSmart
+ +
+ + + + + + + +
+ + + + + +
+ + + Style: + + + + +
+ + + + + + + + + + + + + +
+
+
+ Log from node: +

+
+ + + + + + + + + + + + + + +
+ +
+ + + +
+ Web + ANN + Twitter + Telegram + Discord + QQ + +
+ diff --git a/Source/HTML/web-wallet.html b/Source/HTML/web-wallet.html new file mode 100644 index 0000000..105056b --- /dev/null +++ b/Source/HTML/web-wallet.html @@ -0,0 +1,669 @@ + + + + + TERA WALLET + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + +
+ +
+
+ +
+
+
+ + + + +
+

Welcome to TERA Wallet

+

ATTENTION: Before using the wallet, save the private key.

+ + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + +
+
+
 
+ + + + + +
+
+ + + + + +
+
+ +
+
+ + + + + + + + + + + diff --git a/Source/core/base.js b/Source/core/base.js new file mode 100644 index 0000000..6bdd3e2 --- /dev/null +++ b/Source/core/base.js @@ -0,0 +1,440 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require("./library.js"); +require("./crypto-library"); +require("./terahashmining"); +const crypto = require('crypto'); +const os = require('os'); +global.glStopNode = false; +const MAX_TIME_NETWORK_TRANSPORT = 1 * 1000; +var GlSumUser; +var GlSumSys; +var GlSumIdle; +global.CountAllNode = 0; +module.exports = class CCommon +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + global.SERVER = this + this.VirtualMode = bVirtual + this.KeyPair = SetKeyPair + var PubKey = SetKeyPair.getPublicKey('', 'compressed'); + this.PubKeyType = PubKey[0] + this.addrArr = PubKey.slice(1) + this.addrStr = GetHexFromArr(this.addrArr) + this.HashDBArr = shaarr2(this.KeyPair.getPrivateKey(), [0, 0, 0, 0, 0, 0, 0, 1]) + this.ServerSign = [] + } + AddStatOnTimer() + { + var CountAll = 0; + var CurTime = GetCurrentTime() - 0; + for(var i = 0; i < this.NodesArr.length; i++) + { + var Item = this.NodesArr[i]; + if(Item.LastTime && (CurTime - Item.LastTime) < NODES_DELTA_CALC_HOUR * 3600 * 1000) + CountAll++ + else + if(Item.LastTimeGetNode && (CurTime - Item.LastTimeGetNode) < NODES_DELTA_CALC_HOUR * 3600 * 1000) + CountAll++ + } + global.CountAllNode = CountAll + if(!global.STAT_MODE) + return ; + var StateTX = DApps.Accounts.DBStateTX.Read(0); + if(StateTX) + { + var Delta = this.CurrentBlockNum - StateTX.BlockNum; + ADD_TO_STAT("MAX:DELTA_TX", Delta) + } + var bHasCP = 0; + if(CHECK_POINT.BlockNum) + { + var Block = this.ReadBlockHeaderDB(CHECK_POINT.BlockNum); + if(Block && CompareArr(CHECK_POINT.Hash, Block.Hash) === 0) + bHasCP = 1 + } + var MinVer = global.MIN_VER_STAT; + if(MinVer === 0) + MinVer = global.UPDATE_CODE_VERSION_NUM + var BufMap = {}, BufMap2 = {}; + var arr = this.GetActualNodes(); + var Count = 0, CountHot = 0, CountHotOK = 0, CountActualOK = 0, SumDeltaHot = 0, SumDeltaActual = 0, CountCP = 0, CountLH = 0, + CountHash = 0, CountVer = 0, CountStop = 0; + var CountAutoCorrectTime = 0; + var SumAvgDeltaTime = 0; + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + if(!Node || Node.IsAddrList) + continue; + var INFO = Node.INFO; + if(!INFO) + INFO = {} + if(bHasCP && CHECK_POINT.BlockNum && INFO.CheckPointHashDB && CompareArr(CHECK_POINT.Hash, INFO.CheckPointHashDB) === 0) + { + CountCP++ + } + if(INFO.LoadHistoryMode) + CountLH++ + if(Node.StopGetBlock) + CountStop++ + Count++ + SumAvgDeltaTime += Node.DeltaGlobTime + if(Node.VersionNum >= MinVer) + CountVer++ + if(INFO && INFO.BlockNumDB && INFO.BlockNumDB <= this.BlockNumDB) + { + var HashDB = ReadHashFromBufDB(BufMap2, INFO.BlockNumDB); + if(HashDB && CompareArr(HashDB, INFO.HashDB) === 0) + CountHash++ + } + var StrChk = GetCheckAccHash(BufMap, INFO.AccountBlockNum, INFO.AccountsHash); + var Chck = 0; + if(StrChk.indexOf("=OK=") >= 0) + { + Chck = 1 + } + var DeltaTime = Node.DeltaTime; + if(!DeltaTime) + DeltaTime = 0 + CountActualOK += Chck + SumDeltaActual += DeltaTime + if(Node.Hot) + { + CountHot++ + CountHotOK += Chck + SumDeltaHot += DeltaTime + } + if(INFO.AutoCorrectTime) + CountAutoCorrectTime++ + } + ADD_TO_STAT("MAX:ALL_NODES", CountAll) + ADD_TO_STAT("MAX:CONNECTED_NODES", Count) + ADD_TO_STAT("MAX:HOT_NODES", CountHot) + ADD_TO_STAT("MAX:HOT_OK", CountHotOK) + ADD_TO_STAT("MAX:ACTUAL_OK", CountActualOK) + ADD_TO_STAT("MAX:CHECK_POINT_OK", CountCP) + ADD_TO_STAT("MAX:COUNTLH", CountLH) + ADD_TO_STAT("MAX:HASH_OK", CountHash) + ADD_TO_STAT("MAX:MIN_VERSION", CountVer) + ADD_TO_STAT("MAX:STOP_GET", CountStop) + ADD_TO_STAT("MAX:AUTOCORRECT", CountAutoCorrectTime) + ADD_TO_STAT("MAX:TIME_DELTA", DELTA_CURRENT_TIME) + if(!Count) + Count = 1 + if(!CountHot) + CountHot = 1 + if(Count >= 20) + { + var SumDeltaAvg = 0; + var AvgGlobTime = SumAvgDeltaTime / Count; + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + if(!Node || Node.IsAddrList) + continue; + var Delta = AvgGlobTime - Node.DeltaGlobTime; + SumDeltaAvg += Delta * Delta + } + SumDeltaAvg = Math.sqrt(SumDeltaAvg / Count) + ADD_TO_STAT("MAX:DELTA_GLOB_TIME", 100 + AvgGlobTime) + ADD_TO_STAT("MAX:DISP_DELTA_GLOB_TIME", SumDeltaAvg) + arr.sort(function (a,b) + { + return a.DeltaGlobTime - b.DeltaGlobTime; + }) + var SumDeltaAvgM = 0; + var AvgGlobTimeM = arr[Math.trunc(arr.length / 2)].DeltaGlobTime; + var Length = arr.length; + var Start = Math.trunc(Length * 0.05); + var End = Math.trunc(Length * 0.95); + var NodesCount = 0; + for(var i = Start; i < End; i++) + { + var Node = arr[i]; + if(!Node || Node.IsAddrList) + continue; + NodesCount++ + var Delta = AvgGlobTimeM - Node.DeltaGlobTime; + SumDeltaAvgM += Delta * Delta + } + if(!NodesCount) + NodesCount = 1 + SumDeltaAvgM = Math.sqrt(SumDeltaAvgM / NodesCount) + ADD_TO_STAT("MAX:MEDIAN_GLOB_TIME", 100 + AvgGlobTimeM) + ADD_TO_STAT("MAX:DISP_MEDIAN_GLOB_TIME", SumDeltaAvgM) + } + ADD_TO_STAT("MAX:DELTA_TIME_HOT", SumDeltaHot / CountHot) + ADD_TO_STAT("MAX:DELTA_TIME_ACTUAL", SumDeltaActual / Count) + ADD_TO_STAT("MAX:MEMORY_USAGE", process.memoryUsage().heapTotal / 1024 / 1024) + ADD_TO_STAT("MAX:MEMORY_FREE", os.freemem() / 1024 / 1024) + var SumUser = 0; + var SumSys = 0; + var SumIdle = 0; + var cpus = os.cpus(); + for(var i = 0; i < cpus.length; i++) + { + var cpu = cpus[i]; + SumUser += cpu.times.user + SumSys += cpu.times.sys + cpu.times.irq + SumIdle += cpu.times.idle + } + if(GlSumUser !== undefined) + { + var maxsum = cpus.length * 1000; + ADD_TO_STAT("MAX:CPU_USER_MODE", Math.min(maxsum, SumUser - GlSumUser)) + ADD_TO_STAT("MAX:CPU_SYS_MODE", Math.min(maxsum, SumSys - GlSumSys)) + ADD_TO_STAT("MAX:CPU_IDLE_MODE", Math.min(maxsum, SumIdle - GlSumIdle)) + ADD_TO_STAT("MAX:CPU", Math.min(maxsum, SumUser + SumSys - GlSumUser - GlSumSys)) + } + GlSumUser = SumUser + GlSumSys = SumSys + GlSumIdle = SumIdle + } + GetNewMeta() + { + return crypto.randomBytes(32); + } +}; +class SMemBuffer +{ + constructor(MaxTime, CheckName) + { + this.MetaMap1 = {} + this.MetaMap2 = {} + this.CheckName = CheckName + setInterval(this.ShiftMapDirect.bind(this), MaxTime) + } + GetStrKey(Arr) + { + if(typeof Arr === "number" || typeof Arr === "string") + { + return Arr; + } + else + { + return GetHexFromAddres(Arr); + } + throw "NOT RET!"; + } + LoadValue(Arr, bStay) + { + if(!Arr) + return undefined; + var Key = this.GetStrKey(Arr); + var Value = this.MetaMap1[Key]; + if(Value !== undefined) + { + if(!bStay) + delete this.MetaMap1[Key] + return Value; + } + Value = this.MetaMap2[Key] + if(Value !== undefined) + { + if(!bStay) + delete this.MetaMap2[Key] + } + return Value; + } + SaveValue(Arr, Value) + { + var Key = this.GetStrKey(Arr); + if(Value !== undefined) + this.MetaMap1[Key] = Value + } + ShiftMapDirect() + { + if(glStopNode) + return ; + if(this.CheckName) + { + var Count = 0; + for(var key in this.MetaMap2) + { + Count++ + } + if(Count) + { + ADD_TO_STAT(this.CheckName, 1, 1) + } + } + this.MetaMap2 = this.MetaMap1 + this.MetaMap1 = {} + } + Clear() + { + this.MetaMap2 = {} + this.MetaMap1 = {} + } +}; +class STreeBuffer +{ + constructor(MaxTime, CompareFunction, KeyType, CheckName) + { + this.KeyType = KeyType + this.MetaTree1 = new RBTree(CompareFunction) + this.MetaTree2 = new RBTree(CompareFunction) + this.CheckName = CheckName + setInterval(this.ShiftMapDirect.bind(this), MaxTime) + } + LoadValue(Hash, bStay) + { + if(!Hash) + return undefined; + if(typeof Hash !== this.KeyType) + throw "MUST ONLY HASH ARRAY: " + Hash; + var element = this.MetaTree1.find({hash:Hash}); + if(element) + { + if(!bStay) + this.MetaTree1.remove(element) + return element.value; + } + element = this.MetaTree2.find({hash:Hash}) + if(element) + { + if(!bStay) + this.MetaTree2.remove(element) + return element.value; + } + return undefined; + } + SaveValue(Hash, Value) + { + if(typeof Hash !== this.KeyType) + throw "MUST ONLY TYPE=" + this.KeyType + " in " + Hash; + if(Value !== undefined) + { + var element = this.MetaTree1.find({hash:Hash}); + if(element) + element.value = Value + else + this.MetaTree1.insert({hash:Hash, value:Value}) + } + } + ShiftMapDirect() + { + if(this.CheckName && this.MetaTree2.size) + { + ADD_TO_STAT(this.CheckName, this.MetaTree2.size, 1) + var it = this.MetaTree2.iterator(), Item; + while((Item = it.next()) !== null) + { + var Name = Item.value.Name; + ADD_TO_STAT(this.CheckName + ":" + Name, 1, 1) + } + } + this.MetaTree2.clear() + var empty_tree = this.MetaTree2; + this.MetaTree2 = this.MetaTree1 + this.MetaTree1 = empty_tree + } + Clear() + { + this.MetaTree1.clear() + this.MetaTree2.clear() + } +}; + +function ReadHashFromBufDB(Map,BlockNum) +{ + var MyHash = Map[BlockNum]; + if(!MyHash) + { + var Block = SERVER.ReadBlockHeaderDB(BlockNum); + if(Block) + MyHash = Block.Hash; + else + MyHash = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Map[BlockNum] = MyHash; + } + return MyHash; +}; + +function GetCheckAccHash(Map,BlockNum,Hash) +{ + var MyHash = Map[BlockNum]; + if(!MyHash) + { + MyHash = DApps.Accounts.GetHashOrUndefined(BlockNum); + Map[BlockNum] = MyHash; + } + if(MyHash) + { + if(!Hash) + return "=ERR:NO="; + if(CompareArr(Hash, MyHash) !== 0) + return "=ERR:BAD="; + else + return "=OK="; + } + else + { + if(!Hash) + return "=OK=:NO"; + else + return "=MY:NO="; + } +}; +global.GetCheckAccHash = GetCheckAccHash; +global.ReadHashFromBufDB = ReadHashFromBufDB; +global.STreeBuffer = STreeBuffer; +global.TestCreateTr = TestCreateTr; + +function TestCreateTr() +{ + const FORMAT_CREATE = "{\ + Type:byte,\ + Currency:uint,\ + PubKey:arr33,\ + Description:str40,\ + Adviser:uint,\ + Reserve:arr7,\ + POWCreate:arr12,\ + }"; + var TR = {Type:100, Currency:0, PubKey:[2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0], Description:"Description", Adviser:10, }; + var Body = BufLib.GetBufferFromObject(TR, FORMAT_CREATE, 1000, {}); + var startTime = process.hrtime(); + var StartData = Date.now(); + var nonce = CreateHashBodyPOWInnerMinPower(Body, 1000, 17); + var Time = process.hrtime(startTime); + var power = GetPowPower(shaarr(Body)); + var deltaTime = (Time[0] * 1000 + Time[1] / 1e6) / 1000; + var DeltaData = (new Date() - StartData) / 1000; + ToLog("power=" + power + " nonce=" + nonce + " TIME=" + deltaTime + " sec" + " DeltaData=" + DeltaData + " sec"); + return {time1:deltaTime, time2:DeltaData}; +}; + +function CreateHashBody(body,Num,Nonce) +{ + body.writeUIntLE(Num, body.length - 12, 6); + body.writeUIntLE(Nonce, body.length - 6, 6); + return shaarr(body); +}; + +function CreateHashBodyPOWInnerMinPower(arr,BlockNum,MinPow) +{ + var nonce = 0; + while(1) + { + var arrhash = CreateHashBody(arr, BlockNum, nonce); + var power = GetPowPower(arrhash); + if(power >= MinPow) + { + return nonce; + } + nonce++; + } +}; diff --git a/Source/core/block-exchange.js b/Source/core/block-exchange.js new file mode 100644 index 0000000..08a1387 --- /dev/null +++ b/Source/core/block-exchange.js @@ -0,0 +1,1644 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require('./library'); +require('./crypto-library'); +const TX_PROCESS_TIME = 100; +const TX_DELTA_PROCESS_TIME = 300; +global.CAN_START = false; +global.StrWarn = ""; +global.SUM_LIST_LENGTH = 2 * BLOCK_PROCESSING_LENGTH; +global.CONSENSUS_TIK_TIME = CONSENSUS_PERIOD_TIME / 10; +global.CONSENSUS_CHECK_TIME = CONSENSUS_PERIOD_TIME / 20; +const PERIOD_FOR_NEXT_SEND = CONSENSUS_TIK_TIME * 3; +global.BLOCK_DELTA_ACTIVATE = 0; +global.TIME_END_EXCHANGE = - 3; +global.TIME_START_POW = - 4; +global.TIME_START_SAVE = - 4; +global.TIME_START_LOAD = global.TIME_START_SAVE - 4; +var FORMAT_DATA_TRANSFER = "{\ + Version:uint16,\ + BlockNum:uint,\ + Reserv1:uint32,\ + MaxPOW:[{BlockNum:uint,AddrHash:hash,SeqHash:hash}],\ + Reserv2:uint32,\ + BaseBlockNum:uint,\ + MaxSumID:[{BlockNum:uint,SumHash:hash,SumListID:[uint16]}],\ + BlockList:[{ID:uint16, AddrHash:hash,SeqHash:hash}],\ + TicketArray:[{HashTicket:arr10}],\ + TxArray:[{body:tr}],\ + NoSendTx:uint,\ + }"; +const WorkStructSend = {}; +module.exports = class CConsensus extends require("./block-loader") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.CurrentBlockNum = 0 + this.SendBlockID = 0 + this.RelayMode = false + this.TreeSendPacket = new RBTree(CompareItemHash) + if(!global.ADDRLIST_MODE && !this.VirtualMode) + { + this.idBlockChainTimer = setInterval(this.StartBlockChain.bind(this), CONSENSUS_PERIOD_TIME - 5) + setInterval(this.DoTransfer.bind(this), CONSENSUS_CHECK_TIME) + } + } + StartBlockChain() + { + this.OnStartSecond() + var CurTimeNum = GetCurrentTime() - CONSENSUS_PERIOD_TIME / 2; + var StartTimeNum = Math.floor((CurTimeNum + CONSENSUS_PERIOD_TIME) / CONSENSUS_PERIOD_TIME) * CONSENSUS_PERIOD_TIME; + var DeltaForStart = StartTimeNum - CurTimeNum; + if(DeltaForStart < (CONSENSUS_PERIOD_TIME - 5)) + { + var self = this; + if(self.idBlockChainTimer) + clearInterval(self.idBlockChainTimer) + self.idBlockChainTimer = 0 + setTimeout(function () + { + self.idBlockChainTimer = setInterval(self.StartBlockChain.bind(self), CONSENSUS_PERIOD_TIME) + self.OnStartSecond() + }, DeltaForStart) + } + } + OnStartSecond() + { + PrepareStatEverySecond() + this.AddStatOnTimer() + this.DoBlockChain() + } + CreateBlockContext() + { + var Context = {}; + Context.AddInfo = AddInfoBlock.bind(Context) + Context.Active = false + Context.TransferFromAddr = {} + Context.LevelsTransfer = [] + Context.ErrRun = "" + Context.PowTxTree = new RBTree(CompareItemTimePow) + Context.PowTicketTree = new RBTree(CompareItemTimePow) + Context.bSave = false + Context.PrevHash = undefined + Context.TreeHash = undefined + Context.MaxPOW = {} + Context.MaxSum = {} + Context.SumPow = 0 + Context.Power = 0 + Context.TrCount = 0 + Context.TrDataPos = 0 + Context.TrDataLen = 0 + Context.Info = "Create at:" + GetStrOnlyTimeUTC() + var Transfer; + var TransferM2; + var LocalLevel = 0; + var Levels = this.LevelNodes; + for(let L = 0; L < Levels.length; L++) + { + var arr = Levels[L]; + if(arr && arr.length > 0) + { + Transfer = {LocalLevel:LocalLevel, TreeLevel:L, SendCount:0, GetCount:0, TransferNodes:{}, WasGet:false, WasSend:false, MustDeltaTime:CONSENSUS_TIK_TIME * (2 + MAX_LEVEL_SPECIALIZATION - L), + } + LocalLevel++ + Context.LevelsTransfer.push(Transfer) + Context.StartLevel = Context.LevelsTransfer.length - 1 + for(let j = 0; j < arr.length; j++) + { + var Node = arr[j]; + var Addr = Node.addrStr; + if(!Transfer.TransferNodes[Addr]) + { + let Item = {Node:Node, SendCount:0, GetCount:0, addrStr:Addr, TreeLevel:L, GetTiming:3 * CONSENSUS_PERIOD_TIME, }; + Transfer.TransferNodes[Addr] = Item + } + Context.TransferFromAddr[Addr] = Transfer + } + } + } + Context.MLevelSend = Context.StartLevel + return Context; + } + StartConsensus() + { + if(!CAN_START) + return ; + var StartBlockNum = GetCurrentBlockNumByTime(); + if(StartBlockNum < BLOCK_PROCESSING_LENGTH2) + return ; + this.CurrentBlockNum = StartBlockNum + if(!this.WasReloadSenderMapFromDB) + this.ReloadSenderMapFromDB() + var Block0 = this.GetBlockContext(StartBlockNum - BLOCK_DELTA_ACTIVATE); + if(!Block0.Active) + { + AddInfoBlock(Block0, "Activate") + this.StartBlock(Block0) + } + else + { + AddInfoBlock(Block0, "Was Active") + } + } + TrToInfo(Block, Array, StrInfo) + { + var Str = ""; + for(var i = 0; i < Array.length; i++) + { + var Item = Array[i]; + this.CheckCreateTransactionObject(Item) + Str += this.GetStrFromHashShort(shaarr(Item.body)) + "(" + Item.body.length + ")," + } + AddInfoBlock(Block, "" + StrInfo + ": Arr=[" + Str + "]") + } + TRANSFER(Info, CurTime) + { + var startTime = process.hrtime(); + var Data = this.DataFromF(Info); + var Node = Info.Node; + Node.TransferBlockNum = Data.BlockNum + Node.CurBlockNum = Data.BaseBlockNum + Data.BlockNum + if(Data.Version !== 5) + return ; + var Block = this.GetBlockContext(Data.BlockNum); + if(!Block || Block.StartLevel === undefined) + { + ADD_TO_STAT("TRANSFER_ERR_STARTLEVEL") + this.AddCheckErrCount(Node, 1, "Err GetBlockContext") + return ; + } + if(!Block.Active) + this.StartBlock(Block) + var Key = Node.addrStr; + var Transfer = Block.TransferFromAddr[Key]; + if(!Transfer) + { + ADD_TO_STAT("NO_TRANSFER") + this.AddCheckErrCount(Node, 1, "Err Transfer") + return ; + } + Transfer.WasGet = true + if(global.DoTxLog && Data.TicketArray.length) + ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TicketArray=" + Data.TicketArray.length + " from " + NodeName(Node)) + if(global.DoTxLog && Data.TxArray.length) + ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TxArray=" + Data.TxArray.length + " from " + NodeName(Node)) + this.ToMaxPOWList(Data.MaxPOW) + this.ToMaxSumList(this.GetMaxSumListFromID(Node, Data.MaxSumID, Data.BlockList)) + var WasNewAdd = 0; + if(Data.TxArray.length) + { + for(var i = 0; i < Data.TxArray.length; i++) + { + var Tr = Data.TxArray[i]; + var Res = this.AddTrToBlockQuote(Block, Tr); + if(Res === 1) + WasNewAdd = 1 + if(global.USE_CHECK_SENDING && Res > 0) + { + var Tt = Block.PowTxTree.find(Tr); + if(Tt) + { + if(!Tt.NodesList) + Tt.NodesList = [] + Tt.NodesList.push(Node) + Tt.TreeLevel = Transfer.TreeLevel + } + } + } + } + else + { + for(var i = 0; i < Data.TicketArray.length; i++) + { + var Tr = this.AddTicketToBlockQuote(Block, Data.TicketArray[i]); + if(Tr) + { + if(!Tr.NodesList) + Tr.NodesList = [] + Tr.NodesList.push(Node) + } + } + } + ADD_TO_STAT_TIME("TRANSFER_MS", startTime) + var Delta = Date.now() - this.StartLoadBlockTime; + if(Delta > 10 * 1000 && Node.TransferCount > 10) + { + Node.BlockProcessCount++ + Node.NextHotDelta = 10 * 1000 + } + Node.TransferCount++ + Node.LastTimeTransfer = GetCurrentTime() - 0 + var Item = Transfer.TransferNodes[Key]; + Item.GetTiming = GetCurrentTime(Block.DELTA_CURRENT_TIME) - Block.StartTimeNum + if(!Block.TransferNodesCount) + Block.TransferNodesCount = 0 + Block.TransferNodesCount++ + } + DoTransfer() + { + if(glStopNode) + return ; + if(!CAN_START) + return ; + var MaxPOWList; + var MaxSumList; + var start = this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH; + var finish = this.GetLastCorrectBlockNum(); + for(var b = start; b <= finish; b++) + { + var Block = this.GetBlock(b); + if(!Block) + continue; + if(Block.StartLevel === undefined || Block.MLevelSend === undefined) + continue; + if(!Block.Active) + continue; + if(global.USE_TICKET) + this.DoJobListTX(Block) + if(Block.MLevelSend < 0) + { + this.CheckEndExchange(Block) + continue; + } + if(Block.EndExchange) + continue; + var Transfer = Block.LevelsTransfer[Block.MLevelSend]; + if(!Transfer.WasSend) + { + if(!MaxPOWList) + { + MaxPOWList = this.GetMaxPOWList() + MaxSumList = this.GetMaxSumList() + } + var ArrT; + if(global.USE_TICKET) + ArrT = this.GetArrayFromTicketTree(Block) + else + ArrT = this.GetArrayFromTxTree(Block) + this.SendDataTransfer(Transfer, ArrT, MaxPOWList, MaxSumList, Block) + } + Transfer.WasSend = true + var bNext = Transfer.WasGet; + if(!bNext) + { + var CurTimeNum = GetCurrentTime(Block.DELTA_CURRENT_TIME) - 0; + var DeltaTime = CurTimeNum - Block.StartTimeNum; + if(DeltaTime > Transfer.MustDeltaTime) + { + bNext = true + Block.ErrRun = "" + Transfer.LocalLevel + " " + Block.ErrRun + for(var Addr in Transfer.TransferNodes) + { + var Item = Transfer.TransferNodes[Addr]; + ADD_TO_STAT("TRANSFER_TIME_OUT") + this.AddCheckErrCount(Item.Node, 1, "TRANSFER_TIME_OUT") + } + ADD_TO_STAT("TimeOutLevel") + } + } + if(bNext) + { + if(Block.MLevelSend === 0) + { + Block.EndExchangeTime = Date.now() + if(!global.USE_TICKET) + this.CheckEndExchange(Block) + } + Block.MLevelSend-- + } + } + } + CheckEndExchange(Block) + { + if(Block.EndExchange) + return ; + if(!global.USE_TICKET) + { + this.CreateTreeHash(Block) + return ; + } + if(!Block.JobListTX || !Block.EndExchangeTime) + return ; + var CurTime = Date.now(); + var Delta = CurTime - Block.EndExchangeTime; + if(Delta >= TX_DELTA_PROCESS_TIME * 2) + { + if(global.DoTxLog) + ToLog("END:" + Block.BlockNum + " -> CreateTreeHash") + this.CreateTreeHash(Block) + } + } + SendDataTransfer(Transfer, ArrT, MaxPOWList, MaxSumList, Block) + { + for(var Addr in Transfer.TransferNodes) + { + var Item = Transfer.TransferNodes[Addr]; + Transfer.SendCount++ + var arrPow = []; + for(var i = 0; i < MaxPOWList.length; i++) + { + var elem = MaxPOWList[i]; + var Str = "POW:" + Item.Node.id + elem.BlockNum + "-" + GetHexFromArr(elem.AddrHash); + var bWasSend = global.TreeBlockBuf.LoadValue(Str, 1); + if(!bWasSend) + { + global.TreeBlockBuf.SaveValue(Str, true) + arrPow.push(elem) + } + } + var arrSum = []; + for(var i = 0; i < MaxSumList.length; i++) + { + var elem = MaxSumList[i]; + var Str = "MAX:" + Item.Node.id + elem.BlockNum + "-" + GetHexFromArr(elem.SumHash); + var bWasSend = global.TreeBlockBuf.LoadValue(Str, 1); + if(!bWasSend) + { + global.TreeBlockBuf.SaveValue(Str, true) + arrSum.push(elem) + } + } + var Arr; + if(global.USE_CHECK_SENDING) + Arr = this.FilterArrForSendNode(Block, Item.Node, ArrT, global.USE_TICKET) + else + Arr = ArrT + if(global.USE_LEVEL_WAY) + { + var Arr2 = []; + for(var t = 0; t < Arr.length; t++) + { + var Tr = Arr[t]; + if(Tr.TreeLevel !== Transfer.TreeLevel) + Arr2.push(Tr) + } + Arr = Arr2 + } + if(global.DoTxLog) + ToLog("SEND TRANSFER BlockNum:" + Block.BlockNum + " Arr=" + Arr.length + " to " + NodeName(Item.Node)) + var BufData = this.CreateTransferBuffer(Arr, arrPow, arrSum, Block, Item.Node); + this.Send(Item.Node, {"Method":"TRANSFER", "Context":{}, "Data":BufData}, 1) + if(!Block.JobListTX) + Block.JobListTX = [] + Block.JobListTX.push({Node:Item.Node, TreeLevel:Item.TreeLevel, Time:Date.now()}) + } + } + TRANSFERTX(Info, CurTime) + { + var Data = this.DataFromF(Info); + var Node = Info.Node; + var Block = this.GetBlockContext(Data.BlockNum); + if(!Block) + { + return ; + } + if(global.DoTxLog) + ToLog("TRANSFERTX BlockNum:" + Block.BlockNum + " Array=" + Data.Array.length + " from " + NodeName(Node)) + for(var i = 0; i < Data.Array.length; i++) + { + this.AddTrToBlockQuote(Block, Data.Array[i], 1) + } + } + static + TRANSFERTX_F() + { + return "{BlockNum:uint, Array:[{body:tr}]}"; + } + static + GETTRANSFERTX_F() + { + return "{BlockNum:uint, TicketArray:[{HashTicket:arr10}]}"; + } + GETTRANSFERTX(Info, CurTime) + { + return ; + var Data = this.DataFromF(Info); + var Node = Info.Node; + var Block = this.GetBlockContext(Data.BlockNum); + if(!Block) + { + return ; + } + this.SendTrByTickets(Info, Node, Block, Data.TicketArray, 10) + } + CanSendTest() + { + return 1; + } + SendTrByTickets(Info, Node, Block, ArrTT, CountTrySend) + { + if(!Block.PowTxTree) + return ; + var Arr = []; + var bFindTT = 0; + var BufLength = 0; + for(var i = 0; i < ArrTT.length; i++) + { + var Tr = ArrTT[i]; + this.CheckCreateTicketObject(Tr, Block.BlockNum) + var Tr0 = Block.PowTxTree.find(Tr); + if(Tr0) + { + if(Tr0.IsTx) + { + if(this.CanSendTest()) + Arr.push(Tr0) + BufLength += Tr0.body.length + if(BufLength > MAX_BLOCK_SIZE) + break; + } + else + { + bFindTT = 1 + } + } + } + if(bFindTT && CountTrySend) + { + let SELF = this; + setTimeout(function () + { + SELF.SendTrByTickets(Info, Node, Block, ArrTT, CountTrySend - 1) + }, 100) + return ; + } + if(!Arr.length) + return ; + var SendData = {"Method":"RETTRANSFERTX", "Context":Info.Context, "Data":{BlockNum:Block.BlockNum, Array:Arr, }}; + this.SendF(Node, SendData, global.MAX_BLOCK_SIZE + 1000) + } + static + RETTRANSFERTX_F() + { + return "{BlockNum:uint,Array:[{body:tr}]}"; + } + RETTRANSFERTX(Info, CurTime) + { + return ; + var Data = this.DataFromF(Info); + var Node = Info.Node; + Node.TransferBlockNum = Data.BlockNum + var Block = this.GetBlockContext(Data.BlockNum); + if(!Block || !Block.PowTxTree) + { + return ; + } + if(global.DoTxLog) + ToLog("RETTRANSFERTX BlockNum:" + Block.BlockNum + " Array=" + Data.Array.length + " from " + NodeName(Node)) + for(var i = 0; i < Data.Array.length; i++) + { + var Tr = Data.Array[i]; + this.CheckCreateTransactionObject(Tr) + var Tr0 = Block.PowTxTree.find(Tr); + if(Tr0) + { + if(!Tr0.IsTx) + { + Tr0.IsTx = 1 + Tr0.body = Tr.body + Tr0.HASH = Tr.HASH + } + } + } + } + DoJobListTX(Block) + { + if(Block.EndExchange || !Block.JobListTX || !Block.PowTicketTree) + return ; + var ArrTx; + var CurTime = Date.now(); + for(var i = 0; i < Block.JobListTX.length; i++) + { + var JobItem = Block.JobListTX[i]; + var Delta = CurTime - JobItem.Time; + if(!JobItem.WasSend && Delta >= TX_DELTA_PROCESS_TIME) + { + JobItem.WasSend = 1 + if(!ArrTx) + ArrTx = this.GetArrayFromTxTree(Block) + var Arr = this.FilterArrForSendNode(Block, JobItem.Node, ArrTx); + if(!Arr.length) + return ; + if(global.DoTxLog) + ToLog("DoJobListTX BlockNum:" + Block.BlockNum + " Arr=" + Arr.length + " to " + NodeName(JobItem.Node)) + var SendData = {"Method":"TRANSFERTX", "Context":{}, "Data":{BlockNum:Block.BlockNum, Array:Arr, }}; + this.SendF(JobItem.Node, SendData, global.MAX_BLOCK_SIZE + 1000) + } + } + } + FindNodeTicket(Block, Tr, Node, bTt) + { + var Ticket; + if(bTt) + Ticket = Tr + else + Ticket = Block.PowTicketTree.find(Tr) + if(Ticket && Ticket.NodesList) + { + for(var n = 0; n < Ticket.NodesList.length; n++) + { + var NodeItem = Ticket.NodesList[n]; + if(NodeItem === Node) + { + return 1; + } + } + } + return 0; + } + FilterArrForSendNode(Block, Node, ArrTx, bTt) + { + var Arr = []; + for(var t = 0; t < ArrTx.length; t++) + { + var Tr = ArrTx[t]; + if(this.FindNodeTicket(Block, Tr, Node, bTt)) + continue; + Arr.push(Tr) + } + return Arr; + } + CheckEndExchange00(Block) + { + if(Block.EndExchange) + return ; + var CurTime = Date.now(); + var CanEnd = 1; + var bSend = 0; + var it = Block.PowTxTree.iterator(), Tr; + while((Tr = it.next()) !== null) + { + if(!Tr.IsTx) + { + if(!Tr.LastProcessTime) + throw "!Tr.LastProcessTime"; + var Delta = CurTime - Tr.LastProcessTime; + if(Delta < TX_PROCESS_TIME * 10) + { + bSend = 1 + CanEnd = 0 + break; + } + else + { + var Stop = 1; + } + } + } + if(CanEnd) + { + if(global.DoTxLog) + ToLog("END:" + Block.BlockNum + " -> CreateTreeHash") + this.CreateTreeHash(Block) + } + else + if(bSend) + { + this.CheckTxExchange(Block, bSend) + } + } + CheckTxExchange(Block) + { + return ; + if(Block.EndExchange) + return ; + if(!Block.PowTxTree) + return ; + var CurTime = Date.now(); + var ArrNodesArr = []; + var it = Block.PowTxTree.iterator(), Tr; + while((Tr = it.next()) !== null) + { + if(!Tr.IsTx) + { + if(!Tr.LastProcessTime) + throw "!Tr.LastProcessTime"; + var Delta = CurTime - Tr.LastProcessTime; + if(Delta >= TX_PROCESS_TIME) + { + for(var i = 0; i < Tr.Nodes.length; i++) + { + var TrNode = Tr.Nodes[i]; + var LocDelta = CurTime - TrNode.Time; + if(!TrNode.WasSend && LocDelta >= TX_PROCESS_TIME) + { + var FindArr = undefined; + for(var n = 0; n < ArrNodesArr.length; n++) + { + var ElArr = ArrNodesArr[n]; + if(ElArr.Node === TrNode.Node) + { + FindArr = ElArr + break; + } + } + if(!FindArr) + { + FindArr = {Node:TrNode.Node, Arr:[]} + ArrNodesArr.push(FindArr) + } + Tr.LastProcessTime = CurTime + TrNode.WasSend = 1 + FindArr.Arr.push(Tr) + break; + } + } + } + } + } + for(var n = 0; n < ArrNodesArr.length; n++) + { + var ElArr = ArrNodesArr[n]; + if(global.DoTxLog) + ToLog("CheckTxExchange BlockNum:" + Block.BlockNum + " Array=" + ElArr.Arr.length + " to " + NodeName(ElArr.Node)) + var SendData = {"Method":"GETTRANSFERTX", "Context":{}, "Data":{BlockNum:Block.BlockNum, TicketArray:ElArr.Arr, }}; + this.SendF(ElArr.Node, SendData, ElArr.Arr.length * global.TR_TICKET_HASH_LENGTH + 1000) + } + } + GetMaxSumListFromID(Node, MaxSumID, BlockList) + { + var Str0 = "GETBL:" + Node.id; + for(var i = 0; i < BlockList.length; i++) + { + var elemBlockList = BlockList[i]; + global.TreeBlockBuf.SaveValue(Str0 + elemBlockList.ID, elemBlockList) + } + var MaxSum = []; + MaxSum: + for(var i = 0; i < MaxSumID.length; i++) + { + var elem = MaxSumID[i]; + var Arr = []; + for(var n = 0; n < elem.SumListID.length; n++) + { + var elemBlockList = global.TreeBlockBuf.LoadValue(Str0 + elem.SumListID[n], 1); + if(elemBlockList === undefined) + { + continue MaxSum; + } + Arr.push(elemBlockList) + } + elem.SumList = Arr + MaxSum.push(elem) + } + return MaxSum; + } + CreateTransferBuffer(ArrT, MaxPOWList, MaxSumList, Block, Node) + { + var Data; + var MaxSumID = []; + var BlockList = []; + for(var i = 0; i < MaxSumList.length; i++) + { + var elem0 = MaxSumList[i]; + var ArrID = []; + for(var n = 0; n < elem0.SumList.length; n++) + { + var elemBlockList = elem0.SumList[n]; + var Str = "BL:" + Node.id + GetHexFromArr(elemBlockList.Hash3); + var ID = global.TreeBlockBuf.LoadValue(Str, 1); + if(ID === undefined) + { + this.SendBlockID++ + ID = this.SendBlockID % 65535 + global.TreeBlockBuf.SaveValue(Str, ID) + elemBlockList.ID = ID + BlockList.push(elemBlockList) + } + ArrID.push(ID) + } + MaxSumID.push({BlockNum:elem0.BlockNum, SumHash:elem0.SumHash, SumListID:ArrID}) + } + var ArrTt, ArrTx; + if(global.USE_TICKET) + { + ArrTt = ArrT + ArrTx = [] + } + else + { + ArrTt = [] + ArrTx = ArrT + } + Data = {"Version":5, "BlockNum":Block.BlockNum, "Reserv1":0, "MaxPOW":MaxPOWList, "Reserv2":0, "BaseBlockNum":this.CurrentBlockNum - Block.BlockNum, + "MaxSumID":MaxSumID, "BlockList":BlockList, "TicketArray":ArrTt, "TxArray":ArrTx, "NoSendTx":Node.NoSendTx, } + var BufWrite = BufLib.GetBufferFromObject(Data, FORMAT_DATA_TRANSFER, MAX_BLOCK_SIZE + 30000, WorkStructSend); + return BufWrite; + } + static + TRANSFER_F() + { + return FORMAT_DATA_TRANSFER; + } + CheckingMaxPowOther(Block) + { + var POW = Block.MaxPOW; + if(POW && POW.Hash && CompareArr(POW.PowHash, Block.PowHash) < 0) + { + var LoadBlockNum = Block.BlockNum; + var LoadHash = POW.Hash; + var StrKey = this.GetStrFromHashShort(LoadHash); + var StrHashWas = this.GetStrFromHashShort(Block.Hash); + this.StartLoadBlockHeader(LoadHash, LoadBlockNum, "START OTHER:" + StrKey + " WAS:" + StrHashWas, false) + AddInfoBlock(Block, "REQ H: " + StrKey) + } + Block.CheckMaxPow = true + } + AddToMaxPOW(Block, item, Node) + { + if(Block && item) + { + if(!Block.MaxPOW) + Block.MaxPOW = {} + var POW = Block.MaxPOW; + if(!Block.PrevHash) + return ; + item.BlockNum = Block.BlockNum + item.PrevHash = Block.PrevHash + CalcHashBlockFromSeqAddr(item, Block.PrevHash, global.MINING_VERSION_NUM) + if(POW.SeqHash === undefined || CompareArr(item.PowHash, POW.PowHash) < 0) + { + POW.AddrHash = item.AddrHash + POW.Hash = item.Hash + POW.PowHash = item.PowHash + POW.PrevHash = item.PrevHash + POW.TreeHash = item.TreeHash + POW.SeqHash = item.SeqHash + } + if(Block.SeqHash && CompareArr(item.SeqHash, Block.SeqHash) === 0) + { + if(POW.LocalSeqHash === undefined || CompareArr(POW.LocalSeqHash, Block.SeqHash) !== 0 || CompareArr(item.PowHash, POW.PowLocalHash) < 0) + { + POW.LocalAddrHash = item.AddrHash + POW.PowLocalHash = item.PowHash + POW.LocalSeqHash = Block.SeqHash + } + } + var wasLider; + if(POW.MaxTree) + wasLider = POW.MaxTree.min() + this.AddPOWToMaxTree(POW, item) + if(wasLider) + { + var newLider = POW.MaxTree.min(); + if(newLider !== wasLider) + { + var Power = GetPowPower(newLider.PowHash); + AddInfoBlock(Block, "MaxPOW: " + Power) + } + } + } + } + AddPOWToMaxTree(POW, item) + { + if(!POW.MaxTree) + { + POW.MaxTree = new RBTree(function (a,b) + { + return CompareArr(a.PowHash, b.PowHash); + }) + } + if(!POW.MaxTree.find(item)) + { + POW.MaxTree.insert(item) + if(POW.MaxTree.size > 12) + { + var maxitem = POW.MaxTree.max(); + POW.MaxTree.remove(maxitem) + } + } + } + GetMaxPOWList() + { + var arr = []; + var start, finish; + start = this.CurrentBlockNum + TIME_START_SAVE - 2 + finish = this.CurrentBlockNum + for(var b = start; b < finish; b++) + { + var Block = this.GetBlock(b); + if(Block && Block.Prepared && Block.MaxPOW) + { + if(Block.MaxPOW && Block.MaxPOW.MaxTree) + { + this.RecreateMaxPOW(Block) + var it = Block.MaxPOW.MaxTree.iterator(), Item; + while((Item = it.next()) !== null) + { + arr.push(Item) + } + } + } + } + return arr; + } + ToMaxPOWList(Arr) + { + for(var i = 0; i < Arr.length; i++) + { + var item = Arr[i]; + if(item && item.BlockNum >= this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH && item.BlockNum < this.CurrentBlockNum) + { + var Block = this.GetBlock(item.BlockNum); + this.AddToMaxPOW(Block, item) + } + } + } + RecreateMaxPOW(Block) + { + if(Block.MaxPOW && Block.MaxPOW.MaxTree) + { + var Tree = Block.MaxPOW.MaxTree; + var it = Tree.iterator(), Item; + while((Item = it.next()) !== null) + { + if(!Item.PrevHash) + ToLog("NO Item.PrevHash in " + Block.BlockNum) + if(Item.PrevHash && CompareArr(Item.PrevHash, Block.PrevHash) !== 0) + { + Tree.remove(Item) + it = Tree.iterator() + } + } + } + Block.CheckMaxSum = false + } + CheckMaxSum(Block) + { + var POW = Block.MaxSum; + var List = this.GetBlockList(Block.BlockNum); + var SumPow = this.GetSumFromList(List, Block.BlockNum); + if(POW && POW.SumHash && POW.SumPow > SumPow) + { + var LoadBlockNum = Block.BlockNum; + var LoadHash = POW.SumHash; + var StrKey = this.GetStrFromHashShort(LoadHash); + if(this.StartLoadBlockHeader(LoadHash, LoadBlockNum, "START POW:" + POW.SumPow + ">" + SumPow + " SH:" + StrKey, true)) + AddInfoBlock(Block, "REQ SH: " + StrKey) + } + Block.CheckMaxSum = true + } + AddToMaxSum(Block, item) + { + if(Block && item) + { + if(!Block.MaxSum) + Block.MaxSum = {} + var POW = Block.MaxSum; + var SumPow = this.GetSumFromList(item.SumList, Block.BlockNum); + if(POW.SumHash === undefined || SumPow > POW.SumPow) + { + POW.SumPow = SumPow + POW.SumHash = item.SumHash + POW.SumList = item.SumList + AddInfoBlock(Block, "SumPow:" + POW.SumPow) + Block.CheckMaxSum = false + } + return SumPow; + } + return 0; + } + GetMaxSumList() + { + var Arr = []; + var start, finish; + start = this.CurrentBlockNum + TIME_START_LOAD - 2 + finish = this.CurrentBlockNum + for(var b = start; b <= finish; b++) + { + var Block = this.GetBlock(b); + if(Block && Block.bSave && Block.MaxSum && Block.MaxSum.SumHash) + { + var POW = Block.MaxSum; + var item = {BlockNum:Block.BlockNum, SumHash:POW.SumHash, SumList:POW.SumList, }; + Arr.push(item) + } + } + return Arr; + } + ToMaxSumList(Arr) + { + var start, finish; + start = this.CurrentBlockNum + TIME_START_LOAD - 2 + finish = this.CurrentBlockNum + for(var i = 0; i < Arr.length; i++) + { + var item = Arr[i]; + if(item && item.BlockNum >= start && item.BlockNum <= finish) + { + var Block = this.GetBlock(item.BlockNum); + if(Block) + { + this.AddToMaxSum(Block, item) + this.CheckMaxSum(Block) + } + } + } + } + GetBlockList(CurBlockNum) + { + var arr = []; + for(var b = CurBlockNum - SUM_LIST_LENGTH + 1; b <= CurBlockNum; b++) + { + var Block = this.GetBlock(b); + if(Block && Block.bSave) + { + var item = {AddrHash:Block.AddrHash, SeqHash:Block.SeqHash, }; + arr.push(item) + } + else + { + return []; + } + } + return arr; + } + GetSumFromList(arr, CurBlockNum) + { + var SumPow = 0; + if(arr.length !== SUM_LIST_LENGTH) + return SumPow; + var CountLoad = 0; + var BlockNumStart = CurBlockNum - arr.length + 1; + for(var i = 0; i < arr.length; i++) + { + var Item = arr[i]; + if(Item) + { + Item.BlockNum = BlockNumStart + i + var Value = GetHashFromSeqAddr(Item.SeqHash, Item.AddrHash, Item.BlockNum, undefined, global.MINING_VERSION_NUM); + SumPow += GetPowPower(Value.PowHash) + Item.Hash3 = Value.Hash + } + else + { + break; + } + } + return SumPow; + } + GetArrayFromTxTree(Block) + { + if(!Block.PowTxTree) + return []; + var BufLength = 0; + var arr = []; + var it = Block.PowTxTree.iterator(), Item; + while((Item = it.next()) !== null) + { + arr.push(Item) + BufLength += Item.body.length + if(BufLength > MAX_BLOCK_SIZE) + break; + } + return arr; + } + GetArrayFromTicketTree(Block) + { + if(!Block.PowTicketTree) + return []; + var arr = []; + var it = Block.PowTicketTree.iterator(), Item; + while((Item = it.next()) !== null) + { + arr.push(Item) + } + return arr; + } + CheckPrioritetTx(Tr) + { + if(Tr.Prioritet === undefined) + { + var Body = Tr.body; + Tr.Prioritet = MAX_LENGTH_SENDER_MAP + var App = DAppByType[Body[0]]; + if(App) + { + Tr.SenderNum = App.GetSenderNum(Tr.num, Body) + if(Tr.SenderNum && Tr.SenderNum > 0) + { + Tr.Prioritet = this.GetSenderPrioritet(Tr.num, Tr.SenderNum) + Tr.TimePow = Tr.Prioritet + Tr.power + } + } + } + } + AddToQuote(Tree, Tr) + { + this.CheckPrioritetTx(Tr) + var Tr0 = Tree.find(Tr); + if(Tr0) + { + return 3; + } + else + { + Tree.insert(Tr) + if(Tree.size > MAX_TRANSACTION_LIMIT) + { + var maxitem = Tree.max(); + Tree.remove(maxitem) + if(CompareArr(maxitem.HashPow, Tr.HashPow) === 0) + return 0; + } + return 1; + } + } + AddTicketToBlockQuote(Block, Tr) + { + if(Block.PowTicketTree) + { + var Res = this.IsValidTicket(Tr, Block.BlockNum); + if(Res >= 1) + { + Res = this.AddToQuote(Block.PowTicketTree, Tr) + if(Res) + return Block.PowTicketTree.find(Tr); + } + return null; + } + } + AddTrToBlockQuote(Block, Tr, bTTAdd) + { + if(Block.PowTxTree) + { + var Res = this.IsValidTransaction(Tr, Block.BlockNum); + if(Res >= 1) + { + if(bTTAdd) + { + Res = this.AddToQuote(Block.PowTicketTree, Tr) + if(Res <= 0) + return Res; + } + Res = this.AddToQuote(Block.PowTxTree, Tr) + } + return Res; + } + } + GetBlockContext(BlockNum) + { + if(BlockNum === undefined || !this.IsCorrectBlockNum(BlockNum)) + return undefined; + var Context = this.GetBlock(BlockNum); + if(!Context || !Context.StartTimeNum) + { + Context = this.CreateBlockContext() + Context.BlockNum = BlockNum + Context.DELTA_CURRENT_TIME = GetDeltaCurrentTime() + Context.StartTimeNum = (BlockNum - 1 + BLOCK_DELTA_ACTIVATE) * CONSENSUS_PERIOD_TIME + START_NETWORK_DATE + this.BlockChain[BlockNum] = Context + } + if(!Context.TransferFromAddr) + { + Context.TransferFromAddr = {} + Context.LevelsTransfer = [] + } + return Context; + } + StartBlock(Block) + { + Block.Active = true + } + IsCorrectBlockNum(BlockNum) + { + var start = this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH; + var finish = this.GetLastCorrectBlockNum(); + if(BlockNum < start || BlockNum > finish) + { + return false; + } + return true; + } + GetLastCorrectBlockNum() + { + return this.CurrentBlockNum + 4; + } + GetStrSendCount(Block) + { + if(!Block) + return ""; + var Str = ""; + var Count = 0; + for(var L = 0; L < Block.LevelsTransfer.length; L++) + { + var Transfer = Block.LevelsTransfer[L]; + Str = Str + "," + Transfer.SendCount + if(typeof Transfer.SendCount === "number") + Count = Count + Transfer.SendCount + } + return "" + Count + ":[" + Str.substr(1) + "]"; + } + GetStrGetCount(Block) + { + if(!Block) + return ""; + var Str = ""; + var Count = 0; + for(var L = 0; L < Block.LevelsTransfer.length; L++) + { + var Transfer = Block.LevelsTransfer[L]; + Str = Str + "," + Transfer.GetCount + Count = Count + Transfer.GetCount + } + return "" + Count + ":[" + Str.substr(1) + "]"; + } + ToStrBlocks(DopStr) + { + var num = Math.floor(this.CurrentBlockNum / 3) * 3; + var start = num - BLOCK_PROCESSING_LENGTH2 + 2; + var finish = this.CurrentBlockNum; + if(!DopStr) + DopStr = "" + var Str = ""; + for(var b = start; b <= finish; b++) + { + var hashStr = ""; + var Block = this.GetBlock(b); + if(Block && Block.ErrRun) + { + if(Block.ErrRun) + hashStr = Block.ErrRun.substr(0, 5) + else + if(Block && Block.TreeHash) + hashStr = "-" + GetHexFromAddres(Block.TreeHash).substr(0, 3) + "-" + } + else + if(Block && Block.TreeHash) + { + hashStr = GetHexFromAddres(Block.TreeHash).substr(0, 5) + } + Str = Str + "|" + (hashStr + " ").substr(0, 5) + } + Str = Str.substr(1) + ToInfo("" + finish + " -> " + Str + " " + DopStr) + } + PreparePOWHash(Block) + { + if(!Block.TreeHash) + Block.TreeHash = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + var PrevHash = this.GetPrevHash(Block); + if(!PrevHash) + { + AddInfoBlock(Block, "-err prev hash-") + return false; + } + Block.PrevHash = PrevHash + Block.SeqHash = this.GetSeqHash(Block.BlockNum, Block.PrevHash, Block.TreeHash) + this.CreatePOWNew(Block) + Block.Prepared = true + if(global.USE_MINING && !Block.StartMining) + { + Block.StartMining = true + AddInfoBlock(Block, "-send mining-") + global.SetCalcPOW(Block, "FastCalcBlock") + } + return true; + } + CalcTreeHashFromArrTr(BlockNum, arrTr) + { + var arrHASH = []; + for(var i = 0; i < arrTr.length; i++) + { + var Tr = arrTr[i]; + arrHASH.push(Tr.HASH) + } + var Tree = CalcMerklFromArray(BlockNum, arrHASH); + return Tree.Root; + } + CreateTreeHash(Block) + { + if(Block.EndExchange) + return ; + Block.EndExchange = true + if(Block.bSave) + return ; + var PrevBlock = this.GetBlock(Block.BlockNum - 1); + if(PrevBlock && !PrevBlock.EndExchange && !PrevBlock.bSave) + { + AddInfoBlock(Block, "Prev Not End Exchange") + return ; + } + AddInfoBlock(Block, "End Exchange,N=" + Block.TransferNodesCount) + var arrContent = []; + var arrHASH = []; + var arrTr = this.GetArrayFromTxTree(Block); + this.AddDAppTransactions(Block.BlockNum, arrTr) + for(var i = 0; i < arrTr.length; i++) + { + var Tr = arrTr[i]; + arrContent.push(Tr.body) + arrHASH.push(Tr.HASH) + } + var Tree = CalcMerklFromArray(Block.BlockNum, arrHASH); + Block.TreeHash = Tree.Root + Block.arrContent = arrContent + Block.TrCount = Block.arrContent.length + } + WatchdogSaved(BlockNum) + { + var Block = this.GetBlock(BlockNum); + if(!Block) + { + ToLog("#1 WatchdogSaved: no BlockNum=" + BlockNum) + return ; + } + if(Block.bSave) + { + var BlockDB = this.ReadBlockDB(BlockNum); + if(!BlockDB) + { + Block.bSave = false + return ; + } + if(CompareArr(BlockDB.Hash, Block.Hash) !== 0) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#3 WatchdogSaved: Error Hash on Num=" + BlockNum) + return ; + } + if(CompareArr(BlockDB.SumHash, Block.SumHash) !== 0) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#4 WatchdogSaved: Error SumHash on Num=" + BlockNum) + return ; + } + if(CompareArr(BlockDB.SeqHash, Block.SeqHash) !== 0) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#5 WatchdogSaved: Error SeqHash on Num=" + BlockNum) + return ; + } + var PrevHash = this.GetPrevHash(Block); + if(!PrevHash) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#6 WatchdogSaved: Error PrevHash on Num=" + BlockNum) + return ; + } + var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash); + if(CompareArr(SeqHash, Block.SeqHash) !== 0) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#7 WatchdogSaved: Error SeqHash on Num=" + BlockNum) + return ; + } + PrevHash = this.GetPrevHashDB(BlockDB) + SeqHash = this.GetSeqHash(BlockDB.BlockNum, PrevHash, BlockDB.TreeHash) + if(CompareArr(SeqHash, BlockDB.SeqHash) !== 0) + { + AddInfoBlock(Block, "=ERR:WATCHDOG=") + ToLog("#8 WatchdogSaved: Error SeqHash on Num=" + BlockNum) + return ; + } + } + } + DoBlockChain() + { + if(glStopNode) + return ; + if(!CAN_START) + return ; + this.StartConsensus() + var CURRENTBLOCKNUM = this.CurrentBlockNum; + if(GrayConnect()) + { + if(!this.LoadHistoryMode) + this.StartSyncBlockchain(undefined, 1) + return ; + } + if(this.LoadHistoryMode) + return ; + var bWasSave = false; + var LoadBlockNum; + var LoadHash; + var start_save = CURRENTBLOCKNUM + TIME_START_SAVE; + for(var BlockNum = CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2; BlockNum > BLOCK_PROCESSING_LENGTH2 && BlockNum < CURRENTBLOCKNUM; BlockNum++) + { + var Block = this.GetBlock(BlockNum); + if(!Block) + { + Block = this.GetBlockContext(BlockNum) + if(!Block) + { + continue; + } + } + if(Block.bSave) + { + var BlockDB = this.ReadBlockDB(BlockNum); + if(!BlockDB) + { + Block.bSave = false + } + } + if(global.WATCHDOG_DEV) + this.WatchdogSaved(Block.BlockNum) + if(Block.bSave) + { + bWasSave = true + if(Block.MaxSum && !Block.CheckMaxSum) + { + AddInfoBlock(Block, "CheckMaxSum") + this.CheckMaxSum(Block) + } + if(BlockNum <= CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH * 4) + { + Block.TransferFromAddr = undefined + Block.LevelsTransfer = undefined + Block.mapData = undefined + Block.MaxPOW = undefined + Block.MaxSum = undefined + Block.arrContent = undefined + if(Block.PowTxTree) + { + Block.PowTxTree.clear() + Block.PowTxTree = undefined + } + } + continue; + } + var PrevBlock = this.GetBlock(BlockNum - 1); + if(!PrevBlock) + { + Block.HasErr = 1 + AddInfoBlock(Block, "!PrevBlock") + continue; + } + if(BlockNum >= CURRENTBLOCKNUM + TIME_END_EXCHANGE) + { + if(!Block.Active) + { + AddInfoBlock(Block, "WAIT ACTIVATE") + continue; + } + else + if(!Block.EndExchange) + { + AddInfoBlock(Block, "WAIT EXCHANGE") + continue; + } + } + if(BlockNum === CURRENTBLOCKNUM + TIME_START_POW || Block.EndExchange) + if(!Block.Prepared) + { + if(!Block.EndExchange) + this.CreateTreeHash(Block) + AddInfoBlock(Block, "Start POW") + this.PreparePOWHash(Block) + if(!Block.Prepared) + AddInfoBlock(Block, "!!Prepared") + continue; + } + if(!Block.EndExchange) + { + AddInfoBlock(Block, "Not EndExchange") + Block.HasErr = 1 + Block.Prepared = 0 + this.CreateTreeHash(Block) + } + if(!Block.Prepared) + { + Block.HasErr = 1 + AddInfoBlock(Block, "Not was Prepared") + this.PreparePOWHash(Block) + if(!Block.Prepared) + continue; + } + { + var PrevHash = this.GetPrevHash(Block); + if(!PrevHash) + { + Block.HasErr = 1 + continue; + } + var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash); + if(CompareArr(SeqHash, Block.SeqHash) !== 0) + { + Block.HasErr = 1 + AddInfoBlock(Block, "New fast pow") + this.PreparePOWHash(Block) + } + if(Block.MaxPOW && Block.MaxPOW.SeqHash && Block.MaxPOW.AddrHash && Block.MaxPOW.LocalSeqHash && CompareArr(Block.SeqHash, + Block.MaxPOW.LocalSeqHash) === 0) + { + if(CompareArr(Block.SeqHash, Block.MaxPOW.LocalSeqHash) === 0 && CompareArr(Block.MaxPOW.PowLocalHash, Block.PowHash) < 0) + { + Block.AddrHash = Block.MaxPOW.LocalAddrHash + CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM) + AddInfoBlock(Block, "->Local lider:" + GetPowPower(Block.PowHash)) + } + if(CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) === 0 && CompareArr(Block.MaxPOW.AddrHash, Block.AddrHash) !== 0 && CompareArr(Block.MaxPOW.PowHash, + Block.PowHash) < 0) + { + Block.AddrHash = Block.MaxPOW.AddrHash + CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM) + AddInfoBlock(Block, "->Max lider") + } + } + else + { + Block.HasErr = 1 + AddInfoBlock(Block, "ERROR MaxPOW") + } + if(Block.MaxPOW && Block.MaxPOW.SeqHash && !Block.CheckMaxPow && !Block.CheckMaxSum && CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) !== 0) + { + AddInfoBlock(Block, "CheckMaxPow") + this.CheckingMaxPowOther(Block) + } + if(BlockNum > start_save) + continue; + if(PrevBlock.bSave && this.BlockNumDB + 1 >= Block.BlockNum) + { + this.AddToStatBlockConfirmation(Block) + var Power = GetPowPower(Block.PowHash); + if(this.WriteBlockDB(Block)) + { + if(Block.arrContent && Block.arrContent.length) + ADD_TO_STAT("MAX:TRANSACTION_COUNT", Block.arrContent.length) + AddInfoBlock(Block, "SAVE:" + Power + " TH:" + this.GetStrFromHashShort(Block.TreeHash).substr(0, 4)) + } + else + { + Block.HasErr = 1 + AddInfoBlock(Block, "ERROR WRITE DB") + } + this.AddToMaxSum(Block, {SumHash:Block.SumHash, SumList:this.GetBlockList(Block.BlockNum), }) + if(typeof global.RESYNC_CONDITION === "object") + { + if(!this.OwnBlockCount) + this.OwnBlockCount = 0 + var Miner = ReadUintFromArr(Block.AddrHash, 0); + var MultK = RESYNC_CONDITION.K_POW; + var MaxBlocks = RESYNC_CONDITION.OWN_BLOCKS; + if(Miner === GENERATE_BLOCK_ACCOUNT) + { + this.OwnBlockCount++ + if(this.OwnBlockCount >= MaxBlocks) + { + var PrevSumPow = this.GetAvgPowBlock(Block.BlockNum - 2 * MaxBlocks, MaxBlocks); + var CurrentPow = this.GetAvgPowBlock(Block.BlockNum - MaxBlocks, MaxBlocks); + if(CurrentPow === 0 || PrevSumPow - CurrentPow >= MultK) + { + ToLog("START RESYNC CONDITION") + this.OwnBlockCount = 0 + this.StartSyncBlockchain() + return ; + } + } + } + else + { + this.OwnBlockCount = 0 + } + } + } + else + { + Block.HasErr = 1 + if(!PrevBlock.bSave) + AddInfoBlock(Block, "Prev block not saved") + else + AddInfoBlock(Block, "Low BlockNumDB") + } + } + } + var MaxNumBlockDB = this.GetMaxNumBlockDB(); + if(CURRENTBLOCKNUM + BLOCK_PROCESSING_LENGTH2 > MaxNumBlockDB && CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2 < MaxNumBlockDB) + for(var BlockNum = CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2; BlockNum > BLOCK_PROCESSING_LENGTH2 && BlockNum < start_save; BlockNum++) + { + var Block = this.GetBlock(BlockNum); + if(Block && !Block.bSave && Block.TrCount && Block.TreeHash && !IsZeroArr(Block.TreeHash) && !Block.WasSaveDataTree) + { + this.PreSaveDataTreeToDB(Block) + Block.WasSaveDataTree = 1 + AddInfoBlock(Block, "*PRESAVE DATA TREE*") + ToLog("PRESAVE DATA: " + Block.BlockNum, 2) + } + } + this.RelayMode = !bWasSave + this.FREE_MEM_BLOCKS(CURRENTBLOCKNUM - BLOCK_COUNT_IN_MEMORY) + } + GetAvgPowBlock(StartNum, CountNum) + { + var Count = 0; + var SumPow = 0; + for(var Num = StartNum; Num < StartNum + CountNum; Num++) + { + var Block = this.GetBlock(Num); + if(Block && Block.bSave) + { + var Power = GetPowPower(Block.PowHash); + SumPow += Power + Count++ + } + } + if(!Count) + return 0; + else + return SumPow / Count; + } + CreatePOWNew(Block) + { + CreateHashMinimal(Block, GENERATE_BLOCK_ACCOUNT) + this.AddToMaxPOW(Block, {SeqHash:Block.SeqHash, AddrHash:Block.AddrHash, PrevHash:Block.PrevHash, TreeHash:Block.TreeHash, + }) + } + SetNoPOW(BlockNumFrom, bReload, RefBlockNum) + { + var CurNum = BlockNumFrom; + var finish = this.GetLastCorrectBlockNum(); + while(true) + { + var BlockMem = this.BlockChain[CurNum]; + if(BlockMem) + { + if(BlockMem.Prepared) + { + AddInfoBlock(BlockMem, "-reset POW:" + RefBlockNum + "/" + bReload) + BlockMem.bSave = false + BlockMem.Prepared = false + BlockMem.StartMining = false + this.PreparePOWHash(BlockMem) + } + this.RecreateMaxPOW(BlockMem) + } + if(!BlockMem && CurNum > finish) + break; + CurNum++ + } + } + MiningProcess(msg) + { + var BlockMining = this.GetBlock(msg.BlockNum); + if(!BlockMining) + { + return ; + } + if(!BlockMining.StartMining || BlockMining.bSave) + return ; + if(BlockMining && BlockMining.Hash && BlockMining.SeqHash && CompareArr(BlockMining.SeqHash, msg.SeqHash) === 0) + { + var ValueOld = GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum); + var ValueMsg = GetHashFromSeqAddr(msg.SeqHash, msg.AddrHash, BlockMining.BlockNum); + var bWas = 0; + if(CompareArr(ValueOld.Hash1, ValueMsg.Hash1) > 0) + { + var Nonce1 = ReadUintFromArr(msg.AddrHash, 12); + var DeltaNum1 = ReadUint16FromArr(msg.AddrHash, 24); + WriteUintToArrOnPos(BlockMining.AddrHash, Nonce1, 12) + WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum1, 24) + bWas += 1 + } + if(CompareArr(ValueOld.Hash2, ValueMsg.Hash2) > 0) + { + var Nonce0 = ReadUintFromArr(msg.AddrHash, 6); + var Nonce2 = ReadUintFromArr(msg.AddrHash, 18); + var DeltaNum2 = ReadUint16FromArr(msg.AddrHash, 26); + WriteUintToArrOnPos(BlockMining.AddrHash, Nonce0, 6) + WriteUintToArrOnPos(BlockMining.AddrHash, Nonce2, 18) + WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum2, 26) + bWas += 2 + } + if(!bWas) + return ; + var ValueNew = GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum); + BlockMining.Hash = ValueNew.Hash + BlockMining.PowHash = ValueNew.PowHash + BlockMining.Power = GetPowPower(BlockMining.PowHash) + ADD_TO_STAT("MAX:POWER", BlockMining.Power) + var Power = GetPowPower(BlockMining.PowHash); + var HashCount = Math.pow(2, Power); + ADD_HASH_RATE(HashCount) + AddInfoBlock(BlockMining, "Set POW: " + Power) + this.SetNoPOW(BlockMining.BlockNum + 8, 0, BlockMining.BlockNum) + this.AddToMaxPOW(BlockMining, {SeqHash:BlockMining.SeqHash, AddrHash:BlockMining.AddrHash, PrevHash:BlockMining.PrevHash, TreeHash:BlockMining.TreeHash, + }) + } + } +}; +global.TreeBlockBuf = new STreeBuffer(50 * 1000, CompareItemHashSimple, "string"); +var PrevTimeIdle = 0; +OnTimeIdle(); + +function OnTimeIdle() +{ + var CurTime = Date.now(); + var Delta = CurTime - PrevTimeIdle; + if(Delta <= 51) + { + ADD_TO_STAT("TIME_IDLE", 5); + } + setTimeout(OnTimeIdle, 49); + PrevTimeIdle = CurTime; +}; diff --git a/Source/core/block-loader-const.js b/Source/core/block-loader-const.js new file mode 100644 index 0000000..61ef3ad --- /dev/null +++ b/Source/core/block-loader-const.js @@ -0,0 +1,16 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PERIOD_GET_BLOCK = 300, global.COUNT_HISTORY_BLOCKS_FOR_LOAD = 600, global.COUNT_BLOCKS_FOR_CHECK_POW = 50, global.MAX_DELTA_COUNT_SUM_FOR_LOAD = 10, +global.MAX_COUNT_CHAIN_LOAD = 120, global.PACKET_ALIVE_PERIOD = 4 * CONSENSUS_PERIOD_TIME, global.PACKET_ALIVE_PERIOD_NEXT_NODE = PACKET_ALIVE_PERIOD / 2, +global.MAX_BLOCK_SEND = 8, global.COUNT_TASK_FOR_NODE = 10, global.FORMAT_BLOCK_TRANSFER = "{ BlockNum:uint, TreeHash:hash, arrContent:[tr], }", +global.WRK_BLOCK_TRANSFER = {}, global.MAX_ACCOUNTS_TRANSFER = 1024, global.MAX_SMARTS_TRANSFER = 10, global.TEST_NETWORK && (global.MAX_ACCOUNTS_TRANSFER = 128, +global.MAX_SMARTS_TRANSFER = 10), global.FORMAT_REST_TRANSFER = "{ Result:uint, Version:uint, Arr:[arr200], ProofHash:hash, ProofArrL:, ProofArrR:, }", +global.FORMAT_SMART_TRANSFER = "{ Result:uint, Arr:[tr], }"; diff --git a/Source/core/block-loader.js b/Source/core/block-loader.js new file mode 100644 index 0000000..92eea8f --- /dev/null +++ b/Source/core/block-loader.js @@ -0,0 +1,1503 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require("fs"); +const crypto = require('crypto'); +require('./block-loader-const'); +const STAT_BLOCK_LOAD_PERIOD = CONSENSUS_PERIOD_TIME / 5; +module.exports = class CBlock extends require("./rest-loader.js") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.MapMapLoaded = {} + this.BlockChain = {} + this.ChainID = 0 + this.BlockID = 0 + this.TaskNodeIndex = 0 + this.LoadedChainList = [] + this.LastChainLoad = undefined + this.StartLoadBlockTime = 0 + this.LoadHistoryMode = false + this.MapBlockBodyLoad = {} + if(!global.ADDRLIST_MODE && !this.VirtualMode) + { + let Self = this; + setTimeout(function () + { + Self.CheckStartedBlocks() + setInterval(Self.LoopChainLoad.bind(Self), 100) + setInterval(Self.LoopBlockLoad.bind(Self), 10) + setInterval(Self.LoopBlockBodyLoad.bind(Self), 1 * 1000) + }, 1000) + } + } + StopNode() + { + global.glStopNode = true + } + GenesisBlockHeaderDB(Num) + { + if(Num < 0) + return undefined; + var Block = {BlockNum:Num, TreeHash:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0], AddrHash:DEVELOP_PUB_KEY0, Hash:this.GetHashGenesis(Num), PowHash:this.GetHashGenesis(Num), PrevHash:[0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], SeqHash:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], SumHash:[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], Comment1:"GENESIS", Comment2:"", TrCount:0, TrDataPos:0, TrDataLen:0, }; + Block.SeqHash = this.GetSeqHash(Block.BlockNum, Block.PrevHash, Block.TreeHash) + Block.SumPow = 0 + Block.bSave = true + return Block; + } + CheckStartedBlocks() + { + this.FindStartBlockNum() + if(this.UseTruncateBlockDB) + this.TruncateBlockDB(this.UseTruncateBlockDB) + var CurNum = GetCurrentBlockNumByTime(); + if(CurNum <= this.BlockNumDB) + { + this.TruncateBlockDB(CurNum) + } + CurNum = this.GetMaxNumBlockDB() + if(CurNum <= this.BlockNumDB) + { + this.TruncateBlockDB(CurNum) + this.BlockNumDB = CurNum + } + if(this.BlockNumDB < BLOCK_PROCESSING_LENGTH2) + this.CreateGenesisBlocks() + if(fs.existsSync(GetCodePath("EXPERIMENTAL/_run.js"))) + { + require(GetCodePath("EXPERIMENTAL/_run.js")).Run() + } + this.LoadMemBlocksOnStart() + } + CreateGenesisBlocks() + { + ToLog("====CreateGenesisBlock====") + var PrevArr = []; + for(var n = 0; n < BLOCK_PROCESSING_LENGTH2; n++) + { + var Block = this.GenesisBlockHeaderDB(n); + PrevArr[n] = Block + this.WriteBlockDB(Block) + } + return PrevArr; + } + GetPrevHash(Block) + { + var startPrev = Block.BlockNum - BLOCK_PROCESSING_LENGTH2; + var Sum = 0; + var arr = []; + for(var i = 0; i < BLOCK_PROCESSING_LENGTH; i++) + { + var PrevBlock = this.GetBlock(startPrev + i); + if(PrevBlock && PrevBlock.bSave) + { + Sum = Sum + PrevBlock.SumPow + arr.push(PrevBlock.Hash) + } + else + { + return undefined; + } + } + var PrevHash = CalcHashFromArray(arr, true); + return PrevHash; + } + GetPrevHashDB(Block) + { + var startPrev = Block.BlockNum - BLOCK_PROCESSING_LENGTH2; + var arr = []; + for(var i = 0; i < BLOCK_PROCESSING_LENGTH; i++) + { + var num = startPrev + i; + var PrevBlock = this.ReadBlockHeaderDB(num); + if(!PrevBlock || !PrevBlock.bSave) + { + ToError(" ERROR CALC BLOCK: " + Block.BlockNum + " - prev block not found: " + num) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + arr.push(PrevBlock.Hash) + } + var PrevHash = CalcHashFromArray(arr, true); + return PrevHash; + } + StartSyncBlockchain(Node, bSilent, bCheckPoint) + { + this.FREE_ALL_MEM_CHAINS() + if(global.NO_HISTORY_MODE) + { + this.LoadHistoryMode = 0 + return ; + } + if(global.CREATE_ON_START && !LOCAL_RUN) + return ; + if(!GrayConnect()) + this.RelayMode = true + else + this.RelayMode = false + if(!bSilent) + this.RelayMode = true + this.LoadHistoryMode = true + this.LoadHistoryMessage = !bSilent + this.LoadHistoryContext = {PrevBlockNum: - 1, Node:Node, BlockNum:this.BlockNumDB, MapSend:{}, Foward:1, Pause:0, DeltaBlockNum:10, + StartTimeHistory:Date.now(), MaxTimeOut:30 * 1000} + if(!bSilent && !bCheckPoint && REST_START_COUNT) + { + this.CheckSyncRest() + } + if(!this.ActualNodes.size) + { + ToLog("There is no connections to other nodes") + } + else + { + if(this.LoadHistoryMessage) + { + ToLog("Start synchronization") + } + } + } + LoopSyncBlockchain() + { + if(!this.ActualNodes.size) + return ; + var Context; + if(this.LoadRestContext && this.LoadRestContext.Mode < 200) + Context = this.LoadRestContext + else + Context = this.LoadHistoryContext + if(Context.PrevBlockNum === Context.BlockNum) + { + var DeltaTime = Date.now() - Context.StartTimeHistory; + if(DeltaTime > Context.MaxTimeOut) + { + ToLog("DETECT TIMEOUT LOAD") + this.StartSyncBlockchain() + return ; + } + } + else + { + Context.PrevBlockNum = Context.BlockNum + Context.StartTimeHistory = Date.now() + } + if(Context.LoopSyncRest) + { + this.LoopSyncRest() + return ; + } + if(Context.Pause) + { + if(this.LoadedChainList.length) + { + return ; + } + Context.Pause = 0 + Context.BlockNum = this.BlockNumDB + } + var BlockDB = this.ReadBlockHeaderDB(Context.BlockNum); + if(!BlockDB || this.BlockNumDB >= GetCurrentBlockNumByTime() - BLOCK_PROCESSING_LENGTH - 2) + { + this.LoadHistoryMode = false + if(this.LoadHistoryMessage) + ToLog("Finish synchronization") + if(!BlockDB) + return ; + } + var Ret = this.GetNextNode(Context, Context.BlockNum, 1); + if(Ret.Result) + { + var Node = Ret.Node; + this.SendF(Node, {"Method":"GETBLOCKHEADER", "Context":Context, "Data":{Foward:1, BlockNum:Context.BlockNum, Hash:BlockDB.SumHash}}) + } + } + StartLoadBlockHeader(LoadHash, Num, StrInfo, bIsSum) + { + if(this.LoadHistoryMode) + return ; + if(global.NO_HISTORY_MODE) + return ; + this.StartLoadBlockTime = Date.now() + if(Num > this.CurrentBlockNum + TIME_START_SAVE) + { + return ; + } + bIsSum = bIsSum || false + var Tree = this.GetHistoryTree("StartLoadBlockHeader"); + if(Tree.find({hash:LoadHash})) + return false; + Tree.insert({hash:LoadHash}) + var chain = {id:0, Count:16, BlockNum:Num, IsSum:bIsSum, Hash:LoadHash, time:undefined, FindBlockDB:false, LoadDB:false, LoadCountDB:0, + LoadSumDB:0, LoadSum:0, ParentChain:undefined, RootChain:undefined, BlockNumStart:Num, HashStart:LoadHash, IsSumStart:bIsSum, + BlockHead:undefined, MapSend:{}, Comment2:"", StopSend:false, Info:"", Error:false, }; + this.ChainBindMethods(chain) + chain.AddInfo(StrInfo) + this.SetChainNum(chain) + var max = 3; + while(max > 0) + { + max-- + if(!this.SendChainNext(chain, false)) + break; + } + return true; + } + SetChainNum(chain) + { + if(!chain.id) + { + this.ChainID++ + chain.id = this.ChainID + } + this.LoadedChainList.push(chain) + } + LoopChainLoad() + { + if(glStopNode) + return ; + if(this.UseTruncateBlockDB) + this.TruncateBlockDB(this.UseTruncateBlockDB) + if(this.LoadHistoryMode) + { + this.LoopSyncBlockchain() + return ; + } + if(this.BlockNumDB < this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH2) + { + this.StartSyncBlockchain() + return ; + } + if(global.LOAD_TO_BEGIN && this.BlockNumDBMin) + { + this.SendLoadToBegin() + } + var CountStopSend = 0; + var min_num = this.CurrentBlockNum - MAX_COUNT_CHAIN_LOAD; + var min_num_load = this.CurrentBlockNum; + for(var i = 0; i < this.LoadedChainList.length; i++) + { + var chain = this.LoadedChainList[i]; + if(!chain) + { + this.LoadedChainList.splice(i, 1) + continue; + } + var RootChain = chain.GetRootChain(); + if(chain.RootChain) + chain.RootChain = RootChain + if(RootChain.BlockNum < min_num_load) + min_num_load = RootChain.BlockNum + if(!chain.StopSend) + { + if(chain.BlockHead) + { + if(chain.BlockNum < this.CurrentBlockNum - COUNT_HISTORY_BLOCKS_FOR_LOAD) + { + if(global.WATCHDOG_DEV) + ToLog("Very long length of blocks to load history, stop chain with id=" + chain.id + " (" + chain.BlockNum + "-" + chain.BlockNumMax + ")") + chain.StopSend = true + chain.AddInfo("Stop load #1") + this.FREE_ALL_MEM_CHAINS() + return ; + } + else + if(chain.BlockNumMax < min_num) + { + if(global.WATCHDOG_DEV) + ToLog("Timeout - stop load chain with id=" + chain.id + " (" + chain.BlockNum + "-" + chain.BlockNumMax + ")") + chain.StopSend = true + chain.AddInfo("Stop load #2") + this.ClearChains(chain, 0) + } + } + } + if(chain && !chain.IsSum && !chain.StopSend) + { + var StrKey = "H:" + GetHexFromArr(chain.Hash); + var Map = this.GetMapLoadedFromChain(chain); + var WasBlock = Map[StrKey]; + if(WasBlock && WasBlock.chain !== chain && CompareArr(WasBlock.Hash, chain.Hash) === 0 && !WasBlock.chain.Deleted) + { + if(global.WATCHDOG_DEV) + ToLog("Was hash in chain " + WasBlock.chain.id + " - stop load chain with id=" + chain.id + " (" + chain.BlockNum + ")") + chain.Comment2 = "was hash" + chain.StopSend = true + } + } + if(chain && chain.StopSend) + CountStopSend++ + chain = this.LoadedChainList[i] + if(chain && !chain.GetFindDB() && !chain.StopSend) + { + this.SendChainNext(chain, true) + } + } + ADD_TO_STAT("MAX:LOADEDCHAINLIST", this.LoadedChainList.length) + this.FREE_MEM_CHAINS(min_num_load) + this.LastLoadedBlockNum = 0 + if(this.LoadedChainList.length > COUNT_HISTORY_BLOCKS_FOR_LOAD) + { + if(global.WATCHDOG_DEV) + ToLog("LoadedChainList>COUNT_HISTORY_BLOCKS_FOR_LOAD -> FREE_ALL_MEM_CHAINS") + this.FREE_ALL_MEM_CHAINS() + } + } + GetNextNode(task, keyid, checktime, BlockNum) + { + var CurTime = GetCurrentTime(0) - 0; + if(checktime && task.time) + { + var Delta = CurTime - task.time; + if(Delta < PACKET_ALIVE_PERIOD_NEXT_NODE) + return {Result:false, timewait:true}; + } + task.time = undefined + var StartI = 0; + if(task.Node) + StartI = - 1 + var timewait = false; + var arr = this.GetActualNodes(); + arr.sort(function (a,b) + { + return b.BlockProcessCount - a.BlockProcessCount; + }) + if(arr.length > 40) + arr.length = 40 + for(var i = StartI; i < arr.length; i++) + { + var Node; + if(i === - 1) + { + Node = task.Node + task.Node = undefined + } + else + { + this.TaskNodeIndex++ + Node = arr[this.TaskNodeIndex % arr.length] + } + if(Node.Active) + { + if(!Node.INFO || !Node.INFO.WasPing || Node.StopGetBlock || (Node.INFO.CheckPointHashDB && CHECK_POINT.BlockNum && CompareArr(CHECK_POINT.Hash, + Node.INFO.CheckPointHashDB) !== 0)) + { + timewait = true + continue; + } + if(BlockNum !== undefined && Node.INFO && BlockNum > Node.INFO.BlockNumDB) + { + timewait = true + continue; + } + if(Node.TaskLastSend) + { + var Delta = CurTime - Node.TaskLastSend; + if(Delta < global.PERIOD_GET_BLOCK) + { + timewait = true + continue; + } + } + var keysend = "" + Node.addrStr + ":" + keyid; + if(task.MapSend[keysend]) + continue; + Node.TaskLastSend = CurTime + task.time = CurTime + return {Result:true, Node:Node, timewait:timewait}; + } + } + if(!task.RestartGetNextNode) + task.RestartGetNextNode = 0 + if(!timewait && task.RestartGetNextNode < 3) + { + if(!task.LastTimeRestartGetNextNode) + task.LastTimeRestartGetNextNode = 0 + var Delta = Date.now() - task.LastTimeRestartGetNextNode; + if(Delta > 3000) + { + task.RestartGetNextNode++ + task.LastTimeRestartGetNextNode = Date.now() + task.MapSend = {} + return {Result:false, timewait:true}; + } + } + return {Result:false, timewait:timewait}; + } + SendChainNext(chain, checktime) + { + var Ret = this.GetNextNode(chain, chain.BlockNum, checktime); + if(Ret.Result) + { + if(!chain.Context) + chain.Context = {Chain:chain} + var Node = Ret.Node; + this.SendF(Node, {"Method":"GETBLOCKHEADER", "Context":chain.Context, "Data":{Foward:0, BlockNum:chain.BlockNum, Hash:chain.Hash, + IsSum:chain.IsSum, Count:chain.Count}}) + var DopStr = ""; + if(chain.IsSum) + DopStr = "SUM:" + chain.AddInfo(chain.BlockNum + "/" + DopStr + this.GetStrFromHashShort(chain.Hash) + "->" + GetNodeStrPort(Node)) + return true; + } + return false; + } + static + GETBLOCKHEADER_F() + { + return "{\ + Foward:byte,\ + BlockNum:uint,\ + Hash:hash,\ + Count:uint,\ + IsSum:byte\ + }"; + } + static + GETBLOCKHEADER100_F() + { + return "{\ + BlockNum:uint,\ + Hash:hash,\ + Count:uint\ + }"; + } + GetBlockArrFromBuffer_Load(BufRead, Info) + { + var BlockArr = GetBlockArrFromBuffer(BufRead, Info); + if(BlockArr.length > 0 && BlockArr[0].BlockNum === BLOCK_PROCESSING_LENGTH2) + BlockArr.unshift(this.ReadBlockHeaderDB(BLOCK_PROCESSING_LENGTH2 - 1)) + return BlockArr; + } + RETBLOCKHEADER_FOWARD(Info, CurTime) + { + if(!Info.Context.Foward) + return ; + var Context = this.LoadHistoryContext; + Context.time = undefined + var BufRead = BufLib.GetReadBuffer(Info.Data); + var arr = this.GetBlockArrFromBuffer_Load(BufRead, Info); + var arr2 = []; + var bFindDB = 0; + if(arr.length > 1) + for(var i = 0; i < arr.length; i++) + { + var Block = arr[i]; + if(!Block) + return ; + if(Block.BlockNum === CHECK_POINT.BlockNum && !IsZeroArr(CHECK_POINT.Hash)) + { + if(CompareArr(CHECK_POINT.Hash, Block.Hash) !== 0) + { + break; + } + Context.FindCheckPoint = true + } + if(Block.BlockNum < this.BlockNumDB) + { + break; + } + if(!bFindDB) + { + var BlockDB = this.ReadBlockHeaderDB(Block.BlockNum); + if(BlockDB && CompareArr(BlockDB.SumHash, Block.SumHash) === 0) + { + bFindDB = 1 + arr2.push(Block) + } + else + if(BlockDB && IsZeroArr(BlockDB.SumHash)) + { + bFindDB = 1 + arr2.push(Block) + } + } + else + if(bFindDB) + { + arr2.push(Block) + } + } + if(arr2.length > 1) + { + Context.WasLoadNum = 1 + var chain = {id:0, StopSend:1, WriteToDBAfterLoad:1}; + this.ChainBindMethods(chain) + this.SetChainNum(chain) + this.PrepareTransactionsForLoad(chain, arr2) + Context.BlockNum = Block.BlockNum + Context.Pause = 1 + } + else + { + if(!Context.WasLoadNum) + { + Context.BlockNum = Math.floor(Context.BlockNum - Context.DeltaBlockNum) + Context.DeltaBlockNum = Context.DeltaBlockNum * 1.2 + if(Context.BlockNum < BLOCK_PROCESSING_LENGTH2) + Context.BlockNum = BLOCK_PROCESSING_LENGTH2 - 1 + this.BlockNumDB = Context.BlockNum + this.SetTruncateBlockDB(Context.BlockNum) + } + else + { + var keysend = "" + Info.Node.addrStr + ":" + Context.BlockNum; + Context.MapSend[keysend] = 1 + } + } + } + RETBLOCKHEADER(Info, CurTime) + { + Info.Node.NextPing = MIN_PERIOD_PING + if(Info.Context.Foward) + return this.RETBLOCKHEADER_FOWARD(Info, CurTime); + var chain = Info.Context.Chain; + if(chain && !chain.StopSend && !chain.Deleted) + { + var BufRead = BufLib.GetReadBuffer(Info.Data); + chain.time = undefined + var arr = this.GetBlockArrFromBuffer_Load(BufRead, Info); + if(arr.length <= 1) + { + var keysend = "" + Info.Node.addrStr + ":" + chain.BlockNum; + chain.MapSend[keysend] = 1 + chain.AddInfo("NO:" + GetNodeStrPort(Info.Node)) + return ; + } + chain.AddInfo("L=" + arr.length + " from:" + GetNodeStrPort(Info.Node)) + var NextLoadBlock; + var PrevBlock; + for(var i = arr.length - 1; i >= 0; i--) + { + var Block = arr[i]; + var StrKey = GetHexFromArr(Block.SumHash); + var MapBlockLoaded = this.GetMapLoaded(Block.BlockNum); + var BlockFind = MapBlockLoaded[StrKey]; + if(BlockFind && BlockFind.chain !== chain && BlockFind.chain.Deleted) + { + delete MapBlockLoaded[StrKey] + BlockFind = undefined + } + if(!chain.BlockHead) + chain.BlockHead = Block + if(!chain.BlockNumMax) + chain.BlockNumMax = Block.BlockNum + var PrevBlock0 = PrevBlock; + if(BlockFind) + { + if(PrevBlock) + { + PrevBlock.BlockDown = BlockFind + PrevBlock.Send = undefined + } + PrevBlock = BlockFind + } + else + { + if(PrevBlock) + { + PrevBlock.BlockDown = Block + PrevBlock.Send = undefined + } + PrevBlock = Block + } + if(BlockFind && BlockFind.chain !== chain) + { + chain.ParentChain = BlockFind.chain + chain.RootChain = BlockFind.chain.GetRootChain() + if(chain.RootChain) + chain.RootChain.BlockNumMax = chain.BlockHead.BlockNum + chain.StopSend = true + chain.AddInfo("StopSend - Find load Block") + break; + } + else + if(!BlockFind) + { + Block.chain = chain + Block.Node = Info.Node + var StrSumHash = GetHexFromArr(Block.SumHash); + MapBlockLoaded[StrSumHash] = Block + var StrHash = GetHexFromArr(Block.Hash); + MapBlockLoaded["H:" + StrHash] = Block + var StrTreeHash = GetHexFromArr(Block.TreeHash); + MapBlockLoaded["TH:" + StrTreeHash] = Block + var BlockDB = this.ReadBlockHeaderDB(Block.BlockNum); + if(BlockDB) + { + Block.Power = GetPowPower(Block.PowHash) + chain.LoadCountDB++ + chain.LoadSumDB += BlockDB.Power + chain.LoadSum += Block.Power + if(CompareArr(BlockDB.SumHash, Block.SumHash) === 0) + { + Block.FindBlockDB = true + Block.SumPow = BlockDB.SumPow + chain.FindBlockDB = true + chain.StopSend = true + chain.AddInfo("BlockFind - Find Block in DB") + NextLoadBlock = undefined + break; + } + } + NextLoadBlock = Block + } + } + if(NextLoadBlock && !NextLoadBlock.chain.StopSend) + { + if(arr.length >= chain.Count) + { + chain.Count = chain.Count * 2 + if(chain.Count > COUNT_BLOCKS_FOR_LOAD) + chain.Count = COUNT_BLOCKS_FOR_LOAD + } + if(chain.LoadCountDB >= COUNT_BLOCKS_FOR_CHECK_POW) + { + if(chain.LoadSumDB - chain.LoadSum > MAX_DELTA_COUNT_SUM_FOR_LOAD) + { + var Str = "ERR LOADED SUM POW chains: SumDB > Sum loaded from: " + NodeInfo(Info.Node); + chain.StopSend = true + chain.AddInfo(Str) + } + } + if(!chain.StopSend) + this.BlockChainLoad(NextLoadBlock) + } + if(chain.GetFindDB()) + this.CheckToStartLoadBlockData(chain) + } + } + BlockChainLoad(Block) + { + var chain = Block.chain; + Block.Send = undefined + chain.BlockNum = Block.BlockNum + chain.Hash = Block.SumHash + chain.IsSum = true + chain.StopSend = false + chain.FindBlockDB = false + chain.RootChain = undefined + chain.ParentChain = undefined + chain.AddInfo("SetChainSend:" + chain.BlockNum) + } + CheckToStartLoadBlockData(chain) + { + if(chain.Deleted) + return ; + var arr = this.GetArrFromChain(chain); + if(arr.length < 2) + return ; + var BlockMax = arr[arr.length - 1]; + var BlockMin = arr[0]; + var PrevBlock = BlockMin; + for(var i = 1; i < arr.length; i++) + { + var Block = arr[i]; + Block.Power = GetPowPower(Block.PowHash) + Block.SumPow = PrevBlock.SumPow + Block.Power + PrevBlock = Block + } + var BlockNow = this.ReadBlockHeaderDB(BlockMax.BlockNum); + if(BlockNow && (BlockMax.SumPow < BlockNow.SumPow || BlockMax.SumPow === BlockNow.SumPow && CompareArr(BlockMax.PowHash, BlockNow.PowHash) < 0)) + { + var Str = "Low SumPow"; + chain.AddInfo(Str) + return ; + } + var Str = "Start Load blocks: " + (BlockMin.BlockNum + 1) + " - " + BlockMax.BlockNum; + chain.AddInfo(Str) + this.PrepareTransactionsForLoad(chain, arr) + } + GetArrFromChain(chain) + { + var arr = []; + var Block = chain.BlockHead; + while(Block) + { + arr.unshift(Block) + if(Block.AddToLoad || Block.FindBlockDB || Block.LoadDBFinaly) + { + break; + } + Block = Block.BlockDown + } + return arr; + } + PrepareTransactionsForLoad(chain, arr, bNoSlice) + { + if(!bNoSlice) + arr = arr.slice(1) + chain.arr = arr + if(arr.length > 0) + { + for(var i = 0; i < arr.length; i++) + arr[i].AddToLoad = 1 + chain.CurNumArrLoad = 0 + } + } + LoopBlockLoad() + { + if(glStopNode) + return ; + var CountSend = 0; + for(var num = 0; num < this.LoadedChainList.length; num++) + { + var chain = this.LoadedChainList[num]; + if(chain && chain.arr && chain.arr.length && chain.StopSend) + { + var Count = 0; + for(var i = chain.CurNumArrLoad; i < chain.arr.length; i++) + { + Count++ + var Block = chain.arr[i]; + if(!IsZeroArr(Block.TreeHash) && !Block.TreeEq && !Block.LoadDBFinaly) + { + if(!Block.MapSend) + { + if(!Block.BodyLoad) + { + var BlockDB = this.ReadBlockHeaderDB(Block.BlockNum); + if(BlockDB) + { + if(CompareArr(BlockDB.TreeHash, Block.TreeHash) == 0) + { + Block.TreeEq = true + Block.Reserv500 = BlockDB.Reserv500 + Block.TrDataPos = BlockDB.TrDataPos + Block.TrDataLen = BlockDB.TrDataLen + continue; + } + } + } + Block.MapSend = {} + } + if(this.SendBlockNext(Block)) + { + CountSend++ + if(CountSend >= MAX_BLOCK_SEND) + return ; + } + } + else + if(i === chain.CurNumArrLoad) + { + chain.CurNumArrLoad++ + Block.LoadDBFinaly = true + Count = 0 + } + } + this.CheckAndWriteLoadedChain(chain) + } + } + } + CheckAndWriteLoadedChain(chain) + { + if(chain.CurNumArrLoad >= chain.arr.length) + { + var Block = chain.arr[chain.arr.length - 1]; + if(chain.WriteToDBAfterLoad || Block.BlockNum >= this.CurrentBlockNum + TIME_START_SAVE - 2) + { + var bAllLoaded = true; + if(!chain.WriteToDBAfterLoad) + { + var cur_parent = chain.ParentChain; + while(cur_parent) + { + if(cur_parent.arr && cur_parent.CurNumArrLoad < cur_parent.arr.length) + { + bAllLoaded = false + break; + } + cur_parent = cur_parent.ParentChain + } + } + if(bAllLoaded) + { + var arr = []; + var cur_chain = chain; + while(cur_chain) + { + if(cur_chain.arr) + for(var i = cur_chain.arr.length - 1; i >= 0; i--) + { + var Block = cur_chain.arr[i]; + arr.unshift(Block) + } + cur_chain = cur_chain.ParentChain + } + this.WriteLoadedBlockArr(arr) + } + } + } + } + WriteLoadedBlockArr(arr) + { + if(!arr.length) + return ; + var startTime = process.hrtime(); + if(this.LoadHistoryMessage) + ToLog("WRITE DATA Count:" + arr.length + " " + arr[0].BlockNum + "-" + arr[arr.length - 1].BlockNum, 2) + var CurrentBlockNum = GetCurrentBlockNumByTime(); + var Block, FirstBlock; + for(var i = 0; i < arr.length; i++) + { + Block = arr[i] + if(Block.BlockNum > this.BlockNumDB + 1) + break; + if(!FirstBlock) + FirstBlock = Block + Block.BlockDown = undefined + if(Block.BlockNum > this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2) + { + var PrevHash = this.GetPrevHashDB(Block); + if(CompareArr(PrevHash, Block.PrevHash) !== 0) + { + if(global.WATCHDOG_DEV) + ToError("******** ERROR LOADED DATA Count:" + arr.length + " AT BLOCK NUM:" + Block.BlockNum + " (" + arr[0].BlockNum + "-" + arr[arr.length - 1].BlockNum + ")") + this.FREE_ALL_MEM_CHAINS() + return ; + } + } + var Res = 0; + if(Block.TreeEq) + { + this.ReadBlockBodyDB(Block) + Res = this.WriteBlockDBFinaly(Block) + } + else + { + if(IsZeroArr(Block.TreeHash)) + { + Res = this.WriteBlockDB(Block) + } + else + { + ToLogTrace("IsZeroArr(Block.TreeHash)") + throw "IsZeroArr(Block.TreeHash)"; + } + } + if(!Res) + { + ToLog("ERROR WRITE DB, NUM=" + Block.BlockNum) + this.FREE_ALL_MEM_CHAINS() + return ; + } + Block.LoadDB = true + if(Block.BlockNum >= CurrentBlockNum - BLOCK_COUNT_IN_MEMORY) + { + this.CopyBlockToMem(Block) + } + else + { + if(Block.arrContent) + Block.arrContent.length = 0 + Block.arrContent = undefined + } + var BlockMem = this.BlockChain[Block.BlockNum]; + if(BlockMem) + { + AddInfoBlock(BlockMem, "LOAD:" + GetPowPower(Block.PowHash) + " TH:" + this.GetStrFromHashShort(Block.TreeHash).substr(0, 4)) + } + } + if(Block && FirstBlock) + { + var CurNumStart = Math.max(FirstBlock.BlockNum + 8, Block.BlockNum + 1); + this.SetNoPOW(CurNumStart, 1, FirstBlock.BlockNum) + } + this.FREE_ALL_MEM_CHAINS() + ADD_TO_STAT_TIME("WRITECHAIN_TO_DB_TIME", startTime) + } + CopyBlock(Block, BlockDst) + { + BlockDst.BlockNum = Block.BlockNum + BlockDst.TreeHash = Block.TreeHash + BlockDst.AddrHash = Block.AddrHash + BlockDst.PrevHash = Block.PrevHash + BlockDst.SumHash = Block.SumHash + BlockDst.SumPow = Block.SumPow + BlockDst.TrDataPos = Block.TrDataPos + BlockDst.TrDataLen = Block.TrDataLen + BlockDst.SeqHash = Block.SeqHash + BlockDst.Hash = Block.Hash + BlockDst.PowHash = Block.PowHash + BlockDst.TrCount = Block.TrCount + BlockDst.arrContent = Block.arrContent + BlockDst.bSave = Block.bSave + } + CopyBlockToMem(Block) + { + var BlockMem = this.BlockChain[Block.BlockNum]; + if(BlockMem) + { + this.CopyBlock(Block, BlockMem) + BlockMem.Prepared = true + BlockMem.MinTrPow = undefined + this.RecreateMaxPOW(BlockMem) + } + this.AddToStatBlockConfirmation(Block) + } + ClearMaxInBlock(Block) + { + Block.MaxPOW = {} + var POW = Block.MaxPOW; + POW.SeqHash = Block.SeqHash + POW.AddrHash = Block.AddrHash + POW.PrevHash = Block.PrevHash + POW.TreeHash = Block.TreeHash + POW.Hash = Block.Hash + POW.PowHash = Block.PowHash + POW.SumPow = Block.SumPow + Block.MaxSum = {} + POW = Block.MaxSum + POW.SeqHash = Block.SeqHash + POW.AddrHash = Block.AddrHash + POW.PrevHash = Block.PrevHash + POW.TreeHash = Block.TreeHash + POW.Hash = Block.Hash + POW.PowHash = Block.PowHash + POW.SumHash = Block.SumHash + POW.SumPow = Block.SumPow + } + AddToStatBlockConfirmation(Block) + { + if(Block.BlockNum > START_BLOCK_RUN + BLOCK_PROCESSING_LENGTH2) + { + var TimeDelta = this.CurrentBlockNum - Block.BlockNum; + ADD_TO_STAT("MAX:BlockConfirmation", TimeDelta) + } + else + { + ADD_TO_STAT("MAX:BlockConfirmation", BLOCK_PROCESSING_LENGTH) + } + } + SendBlockNext(Block) + { + var SendResult = 0; + var Key = GetHexFromArr(Block.TreeHash); + while(true) + { + var Ret = this.GetNextNode(Block, Key, true, Block.BlockNum); + if(Ret.Result) + { + var Node = Ret.Node; + if(!Block.Context) + Block.Context = {Block:Block} + this.SendF(Node, {"Method":"GETBLOCK", "Context":Block.Context, "Data":{BlockNum:Block.BlockNum, TreeHash:Block.TreeHash}}) + Node.SendBlockCount++ + SendResult = 1 + AddInfoBlock(Block, "SendNext") + if(Block.chain) + Block.chain.AddInfo("QUERY BL:" + Block.BlockNum + "/" + this.GetStrFromHashShort(Block.TreeHash) + " TO:" + GetNodeStrPort(Node)) + } + else + { + if(!Ret.timewait) + { + this.ClearChains(Block.chain, true) + } + } + break; + } + return SendResult; + } + ClearChains(DeleteChain, bShow) + { + if(!DeleteChain) + { + this.FREE_ALL_MEM_CHAINS() + return this.LoadedChainList.length; + } + var allsum = this.LoadedChainList.length; + var Sum = 0; + for(var i = 0; i < this.LoadedChainList.length; i++) + { + var chain = this.LoadedChainList[i]; + if(chain) + { + if(chain === DeleteChain) + { + chain.Deleted = true + this.LoadedChainList[i] = undefined + Sum++ + } + if(chain.ParentChain === DeleteChain) + { + Sum += this.ClearChains(chain) + } + } + } + if(bShow) + { + ToLog("===========ClearChains================= " + DeleteChain.id + " count=" + Sum + "/" + allsum) + } + return Sum; + } + RecalcLoadBlockStatictic() + { + return ; + var TimeNum = Math.floor(Date.now() / STAT_BLOCK_LOAD_PERIOD); + if(this.LoadBlockStatNum === TimeNum) + return ; + this.LoadBlockStatNum = TimeNum + const PeriodSec = 5; + const Period = CONSENSUS_PERIOD_TIME / STAT_BLOCK_LOAD_PERIOD; + const PeriodCount = PeriodSec * Period; + var FreeGet = 64; + var it = this.ActualNodes.iterator(), Node; + while((Node = it.next()) !== null) + { + var arr = Node.SendBlockArr; + arr.push(Node.SendBlockCount) + if(arr.length > PeriodCount) + { + arr.shift() + } + arr = Node.LoadBlockArr + arr.push(Node.LoadBlockCount) + if(arr.length > PeriodCount) + { + arr.shift() + } + var SendPackets = 0; + var LoadPackets = 0; + for(var i = 0; i < Node.SendBlockArr.length; i++) + SendPackets += Node.SendBlockArr[i] + for(var i = 0; i < Node.LoadBlockArr.length; i++) + LoadPackets += Node.LoadBlockArr[i] + Node.SendBlockCountAll = SendPackets + Node.LoadBlockCountAll = LoadPackets + var Nuts = Math.floor(LoadPackets / PeriodSec); + var RestPackets = SendPackets - LoadPackets; + var CountGet = 1 + Math.floor(Math.max(0, (Nuts - RestPackets / Period))); + Node.CanGetBlocks = Math.min(FreeGet, CountGet) + FreeGet -= Node.CanGetBlocks + Node.SendBlockCount = 0 + Node.LoadBlockCount = 0 + ADD_TO_STAT("NODE_CAN_GET:" + NodeName(Node), Node.CanGetBlocks, 1) + } + } + static + GETBLOCK_F() + { + return "{\ + BlockNum:uint,\ + TreeHash:hash,\ + }"; + } + RETGETBLOCK(Info, CurTime) + { + Info.Node.NextPing = MIN_PERIOD_PING + var Block = Info.Context.Block; + if(Block && !Block.TreeEq) + { + var Data = BufLib.GetObjectFromBuffer(Info.Data, FORMAT_BLOCK_TRANSFER, WRK_BLOCK_TRANSFER); + Info.Data = undefined + if(Data.BlockNum !== Block.BlockNum || CompareArr(Data.TreeHash, Block.TreeHash) !== 0) + { + this.SetBlockNOSendToNode(Block, Info.Node, "NO") + return ; + } + if(Block.chain) + { + Block.chain.AddInfo("Load TR:" + Data.BlockNum + "/" + this.GetStrFromHashShort(Data.TreeHash) + " from:" + GetNodeStrPort(Info.Node)) + AddInfoBlock(Block, "LOAD TR OK") + } + var arrContent = Data.arrContent; + var TreeHash = CalcTreeHashFromArrBody(Block.BlockNum, arrContent); + if(CompareArr(Block.TreeHash, TreeHash) !== 0) + { + ToLog("2. BAD CMP TreeHash block=" + Block.BlockNum + " from:" + NodeName(Info.Node) + " TreeHash=" + GetHexFromArr(TreeHash) + " BlockTreeHash=" + GetHexFromArr(Block.TreeHash)) + this.SetBlockNOSendToNode(Block, Info.Node, "BAD CMP TreeHash") + return ; + } + if(arrContent.length > 0 && Data.BlockNum % PERIOD_ACCOUNT_HASH === 0) + { + var TR = arrContent[0]; + if(TR[0] === TYPE_TRANSACTION_ACC_HASH) + { + if(!DApps.Accounts.TRCheckAccountHash(TR, Data.BlockNum)) + { + if(!this.BADHashCount) + this.BADHashCount = 0 + this.BADHashCount++ + ToLog("**** BAD ACCOUNT Hash in block=" + Block.BlockNum + " from:" + NodeName(Info.Node) + " ****") + ToLog("May be need to Rewrite transactions from: " + (Block.BlockNum - 2 * DELTA_BLOCK_ACCOUNT_HASH)) + this.SetBlockNOSendToNode(Block, Info.Node, "BAD CMP ACC HASH") + if(global.WATCHDOG_BADACCOUNT && this.BADHashCount > 60) + { + ToLog("Run WATCHDOG!") + this.BADHashCount = 0 + this.FREE_ALL_MEM_CHAINS() + this.SetTruncateBlockDB(Block.BlockNum - 5 * DELTA_BLOCK_ACCOUNT_HASH) + } + else + { + } + return ; + } + } + } + Block.arrContent = arrContent + var Ret = this.WriteBodyDB(Block); + Block.TrCount = 0 + Block.arrContent.length = 0 + Block.arrContent = undefined + if(!Ret) + { + this.SetBlockNOSendToNode(Block, Info.Node, "Error write") + return ; + } + Block.TreeEq = true + Block.Send = undefined + ADD_TO_STAT("BLOCK_LOADED", 1) + Info.Node.LoadBlockCount++ + if(GrayConnect()) + Info.Node.BlockProcessCount++ + if(this.LoadHistoryMode) + { + var Context = this.LoadHistoryContext; + Context.PrevBlockNum = Context.BlockNum + Context.StartTimeHistory = Date.now() + } + } + } + SendCanBlock(Node, Block) + { + Node.SendBlockCount++ + if(!Node.INFO.BlockNumDB) + return ; + if(Node.INFO.BlockNumDB >= Block.BlockNum) + { + this.SendF(Node, {"Method":"CANBLOCK", "Data":{BlockNum:Block.BlockNum}}) + } + } + static + CANBLOCK_F() + { + return "{BlockNum:uint}"; + } + CANBLOCK(Info, CurTime) + { + var Data = this.DataFromF(Info); + this.SendF(Info.Node, {"Method":"RETCANBLOCK", "Data":{Result:1}}) + } + static + RETCANBLOCK_F() + { + return "{Result:byte}"; + } + RETCANBLOCK(Info, CurTime) + { + var Data = this.DataFromF(Info); + if(Data.Result === 1) + { + Info.Node.LoadBlockCount++ + } + } + SetBlockNOSendToNode(Block, Node, Str) + { + var Str = GetHexFromArr(Block.TreeHash); + var Str2 = this.GetStrFromHashShort(Block.TreeHash); + var keysend = "" + Node.addrStr + ":" + Str; + Block.MapSend[keysend] = 1 + if(Block.chain) + Block.chain.AddInfo("" + Block.BlockNum + " " + Str2 + "<-" + GetNodeStrPort(Node)) + } + FindBlockInLoadedChain(BlockNum, TreeHash) + { + var StrTreeHash = GetHexFromArr(TreeHash); + var MapBlockLoaded = this.GetMapLoaded(BlockNum); + var BlockFind = MapBlockLoaded["TH:" + StrTreeHash]; + if(BlockFind && BlockFind.TreeEq) + return BlockFind; + else + return undefined; + } + CheckSeqHashDB(Block, StrError) + { + if(Block.BlockNum < BLOCK_PROCESSING_LENGTH2) + return true; + var TreeHashTest = CalcTreeHashFromArrBody(Block.BlockNum, Block.arrContent); + if(CompareArr(TreeHashTest, Block.TreeHash) !== 0) + { + var StrHex = GetHexFromArr(TreeHashTest); + var StrHex0 = GetHexFromArr(Block.TreeHash); + var Str = StrError + " #3 ERROR TREEHASH: " + Block.BlockNum + " Hex:" + StrHex0.substr(0, 12) + " != " + StrHex.substr(0, + 12); + if(global.WATCHDOG_DEV) + ToErrorTrace(Str) + else + ToError(Str) + return false; + } + var PrevHash = this.GetPrevHashDB(Block); + var testSeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash); + var TestValue = GetHashFromSeqAddr(testSeqHash, Block.AddrHash, Block.BlockNum, PrevHash); + if(CompareArr(TestValue.Hash, Block.Hash) !== 0) + { + var Str = StrError + " #2 ERROR HASH - block num: " + Block.BlockNum; + if(global.WATCHDOG_DEV) + ToErrorTrace(Str) + else + ToError(Str) + return false; + } + return true; + } + ToLogBlock(Block, StrInfo, arr) + { + ToLog("-------------" + StrInfo) + ToLog("BlockNum=" + (Block.BlockNum)) + ToLog("Hash=" + GetHexFromArr(Block.Hash)) + ToLog("SeqHash=" + GetHexFromArr(Block.SeqHash)) + ToLog("PrevHash=" + GetHexFromArr(Block.PrevHash)) + ToLog("TreeHash=" + GetHexFromArr(Block.TreeHash)) + ToLog("AddrHash=" + GetHexFromArr(Block.AddrHash)) + ToLog("SumHash=" + GetHexFromArr(Block.SumHash)) + ToLog("SumPow=" + Block.SumPow) + for(var i = 0; i < arr.length; i++) + { + ToLog("arr[" + i + "]=" + GetHexFromArr(arr[i])) + } + } + GetBlock(num, bToMem, bReadBody) + { + if(bToMem === undefined) + bToMem = true + if(num < this.CurrentBlockNum - BLOCK_COUNT_IN_MEMORY) + bToMem = false + var Block = this.BlockChain[num]; + if(!Block) + { + if(bReadBody) + Block = this.ReadBlockDB(num) + else + Block = this.ReadBlockHeaderDB(num) + if(bToMem) + { + this.BlockChain[num] = Block + } + } + return Block; + } + GetMapLoaded(num) + { + if(num < 0) + num = 0 + var index = Math.floor(num / BLOCK_COUNT_IN_MEMORY); + var map = this.MapMapLoaded[index]; + if(!map) + { + map = {} + this.MapMapLoaded[index] = map + } + return map; + } + GetMapLoadedFromChain(chain) + { + return this.GetMapLoaded(chain.BlockNumStart); + } + FREE_MEM_BLOCKS(NumMax) + { + for(var key in this.BlockChain) + { + var Block = this.BlockChain[key]; + if(!Block || Block.BlockNum < NumMax) + { + delete this.BlockChain[key] + } + } + } + FREE_MEM_CHAINS(NumMax) + { + this.FREE_MEM_BLOCKS(NumMax - BLOCK_COUNT_IN_MEMORY) + var maxArrMap = Math.floor(NumMax / BLOCK_COUNT_IN_MEMORY) - 1; + if(maxArrMap >= 0) + { + var nWasCount = 0; + for(var key in this.MapMapLoaded) + if(key < maxArrMap) + { + nWasCount++ + delete this.MapMapLoaded[key] + } + } + } + FREE_ALL_MEM_CHAINS() + { + this.FREE_MEM_BLOCKS(this.BlockNumDB - BLOCK_COUNT_IN_MEMORY) + for(var i = 0; i < this.LoadedChainList.length; i++) + { + var chain = this.LoadedChainList[i]; + if(chain) + { + chain.Deleted = true + chain.ChainMax = undefined + } + } + if(!this.LoadHistoryMode) + { + this.AddValueToHistory("LoadedChainList", this.LoadedChainList) + this.AddValueToHistory("MapMapLoaded", this.MapMapLoaded) + } + this.LoadedChainList = [] + this.MapMapLoaded = {} + if(typeof gc === "function") + gc() + } + AddValueToHistory(typedata, val) + { + var Arr = global.HistoryBlockBuf.LoadValue(typedata, 1); + if(!Arr) + { + Arr = [] + global.HistoryBlockBuf.SaveValue(typedata, Arr) + } + Arr.push(val) + } + GetHistoryTree(typedata) + { + var Tree = global.HistoryBlockBuf.LoadValue(typedata, 1); + if(!Tree) + { + Tree = new RBTree(CompareItemHash) + global.HistoryBlockBuf.SaveValue(typedata, Tree) + } + return Tree; + } + ChainBindMethods(chain) + { + +function GetRootChain() + { + var Count = 0; + var root_chain = this; + while(root_chain.RootChain) + { + Count++ + root_chain = root_chain.RootChain + if(Count > MAX_COUNT_CHAIN_LOAD) + { + TO_ERROR_LOG("BLOCK", 10, "Error COUNT GetRootChain") + SERVER.FREE_ALL_MEM_CHAINS() + return undefined; + } + } + return root_chain; + }; + +function GetFindDB() + { + var Root = this.GetRootChain(); + if(Root) + return Root.FindBlockDB; + else + return false; + }; + chain.GetRootChain = GetRootChain.bind(chain) + chain.GetFindDB = GetFindDB.bind(chain) + chain.AddInfo = AddInfoChain.bind(chain) + } + GetMemoryStamp(Str) + { + return Str + ":##:" + Math.floor(this.CurrentBlockNum / BLOCK_COUNT_IN_MEMORY); + } + GetStrFromHashShort(Hash) + { + var Str = GetHexFromArr(Hash); + if(typeof Str === "string") + return Str.substr(0, 6); + else + return ""; + } + ToLogTime(startTime, Str) + { + const Time = process.hrtime(startTime); + var deltaTime = (Time[0] * 1000 + Time[1] / 1e6); + ToLog(Str + " : " + deltaTime + "ms") + } + AddBlockToLoadBody(Block) + { + if(!this.MapBlockBodyLoad[Block.BlockNum]) + { + this.MapBlockBodyLoad[Block.BlockNum] = Block + } + } + LoopBlockBodyLoad() + { + var arr = []; + for(var key in this.MapBlockBodyLoad) + { + var Block = this.MapBlockBodyLoad[key]; + if(!Block.BodyLoad) + { + Block.BodyLoad = 1 + arr.push(Block) + } + } + this.MapBlockBodyLoad = {} + if(!arr.length) + return ; + var chain = {StopSend:1, WriteToDBAfterLoad:1, BodyLoad:1}; + this.ChainBindMethods(chain) + this.SetChainNum(chain) + this.PrepareTransactionsForLoad(chain, arr, 1) + } +}; +global.LoadBlockFromNetwork = function (Params,F) +{ + var BlockNum = Params.BlockNum; + if(BlockNum >= SERVER.BlockNumDBMin) + { + ToLog("Cant LoadBlockFromNetwork:" + BlockNum, 2); + F(1); + return ; + } + ToLog("Start DownloadBlockFromNetwork:" + BlockNum, 2); + var TaskLoadBlockFromNetwork = {MapSend:{}}; + var Ret = SERVER.GetNextNode(TaskLoadBlockFromNetwork, BlockNum, 1); + if(Ret.Result) + { + let Node = Ret.Node; + SERVER.SendF(Node, {"Method":"GETBLOCK", "Data":{BlockNum:BlockNum, TreeHash:[]}, "Context":{F:function (Info) + { + var Block = BufLib.GetObjectFromBuffer(Info.Data, FORMAT_BLOCK_TRANSFER, WRK_BLOCK_TRANSFER); + Info.Data = undefined; + if(!Block.BlockNum || Block.BlockNum !== Params.BlockNum) + { + ToLog("Error get BlockNum:" + Params.BlockNum + " from " + NodeName(Info.Node), 2); + F(1); + return ; + } + ToLog("Got BlockFromNetwork:" + Params.BlockNum + " from " + NodeName(Info.Node), 2); + var ResError; + if(!Block.arrContent || Block.arrContent.length === 0) + { + ResError = 1; + } + else + { + ResError = 0; + SERVER.WriteBlockDB(Block); + } + F(ResError, Block); + }}, }); + } + else + { + ToLog("Not find node for download block", 2); + F(1); + } +}; +global.HistoryBlockBuf = new STreeBuffer(HISTORY_BLOCK_COUNT * 1000, CompareItemHashSimple, "string"); diff --git a/Source/core/buffer.js b/Source/core/buffer.js new file mode 100644 index 0000000..52a1bb0 --- /dev/null +++ b/Source/core/buffer.js @@ -0,0 +1,364 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + + +function Write(e,r,t,n,l) +{ + if(!(e.len >= e.length)) + { + if("number" == typeof t) + throw ToLogTrace("ERRR StringFormat "), "ERR!!"; + var a = t; + if("buffer" === a.substr(0, 6) && 6 < a.length) + n = parseInt(a.substr(6)), a = "buffer"; + else + if("arr" === a.substr(0, 3) && 3 < a.length) + n = parseInt(a.substr(3)), a = "arr"; + else + if("str" === a.substr(0, 3) && 3 < a.length) + { + var i = parseInt(a.substr(3)); + return r && e.write(r, e.len, i), void (e.len += i); + } + switch(a) + { + case "str": + var f = toUTF8Array(r); + 65535 < (i = f.length) && (i = 0), e[e.len] = 255 & i, e[e.len + 1] = i >>> 8 & 255, e.len += 2; + for(var s = 0; s < i; s++) + e[e.len + s] = f[s]; + e.len += i; + break; + case "byte": + r < 0 && (r = 0), e[e.len] = r, e.len += 1; + break; + case "double": + e.writeDoubleLE(r, e.len, 8), e.len += 8; + break; + case "uint": + r < 0 && (r = 0), 0xffffffffffff <= r && (r = 0), e.writeUIntLE(r, e.len, 6), e.len += 6; + break; + case "uint16": + r < 0 && (r = 0), e[e.len] = 255 & r, e[e.len + 1] = r >>> 8 & 255, e.len += 2; + break; + case "uint32": + r < 0 && (r = 0), e.writeUInt32LE(r, e.len, 4), e.len += 4; + break; + case "time": + var u = r.valueOf(); + e.writeUIntLE(u, e.len, 6), e.len += 6; + break; + case "addres": + case "hash": + i = r ? Math.min(32, r.length) : 0; + for(s = 0; s < i; s++) + e[e.len + s] = r[s]; + e.len += 32; + break; + case "buffer": + i = void 0 === n ? r.length : Math.min(n, r.length); + for(s = 0; s < i; s++) + e[e.len + s] = r[s]; + e.len += n; + break; + case "arr": + i = r ? Math.min(n, r.length) : 0; + for(s = 0; s < i; s++) + e[e.len + s] = r[s]; + e.len += n; + break; + case "tr": + i = r.length; + MAX_TRANSACTION_SIZE > MAX_TRANSACTION_SIZE && (i = MAX_TRANSACTION_SIZE), e[e.len] = 255 & i, e[e.len + 1] = i >>> 8 & 255, + e.len += 2; + for(s = 0; s < i; s++) + e[e.len + s] = r[s]; + e.len += i; + break; + case "data": + i = r.length; + e.writeUInt32LE(i, e.len, 4), e.len += 4; + for(s = 0; s < i; s++) + e[e.len + s] = r[s]; + e.len += i; + break; + case "hashSTR": + var o = GetHexFromAddres(r); + e.write(o, e.len, 64), e.len += 64; + break; + case "uintSTR": + o = r.toString(); + e.write(o, e.len, 10), e.len += 10; + break; + default: + l = l || {}; + var d = t.substr(0, 1); + if("[" === d) + { + r && (i = r.length); + var b = GetMiddleString(a); + Write(e, i, "uint32"); + for(s = 0; s < i; s++) + Write(e, r[s], b, void 0, l); + } + else + if("<" === d) + { + r && (i = r.length); + b = GetMiddleString(a); + var h = 0, c = e.len; + e.len += 4; + for(s = 0; s < i; s++) + r[s] && (h++, Write(e, s, "uint32"), Write(e, r[s], b, void 0, l)); + e.writeUInt32LE(h, c, 4); + } + else + { + if("{" !== d) + throw "Bad write type params: " + a; + var g = l[a]; + g || (g = GetAttributes(GetMiddleString(a)), l[a] = g); + for(s = 0; s < g.length; s++) + { + var v = g[s]; + Write(e, r[v.Key], v.Value, void 0, l); + } + } + } + } +}; + +function Read(e,r,t,n,l) +{ + var a; + if("number" == typeof r) + throw ToLogTrace("ERR StringFormat"), "ERRR!"; + var i = r; + if("buffer" === i.substr(0, 6)) + 6 < i.length ? (t = parseInt(i.substr(6)), i = "buffer") : t = 0; + else + if("arr" === i.substr(0, 3)) + 3 < i.length ? (t = parseInt(i.substr(3)), i = "arr") : t = 0; + else + if("str" === i.substr(0, 3)) + { + if(3 < i.length) + { + var f = parseInt(i.substr(3)); + a = e.toString("utf8", e.len, e.len + f), e.len += f; + for(var s = - 1, u = a.length - 1; 0 <= u; u--) + if(0 !== a.charCodeAt(u)) + { + s = u; + break; + } + return a = 0 <= s ? a.substr(0, u + 1) : ""; + } + t = 0; + } + switch(i) + { + case "str": + f = e.len + 2 <= e.length ? e[e.len] + 256 * e[e.len + 1] : 0, e.len += 2; + var o = e.slice(e.len, e.len + f); + a = Utf8ArrayToStr(o), e.len += f; + break; + case "byte": + a = e.len + 1 <= e.length ? e[e.len] : 0, e.len += 1; + break; + case "double": + a = e.len + 8 <= e.length ? e.readDoubleLE(e.len, 8) : 0, e.len += 8; + break; + case "uint": + a = e.len + 6 <= e.length ? e.readUIntLE(e.len, 6) : 0, e.len += 6; + break; + case "uint16": + a = e.len + 2 <= e.length ? e[e.len] + 256 * e[e.len + 1] : 0, e.len += 2; + break; + case "uint32": + a = e.len + 4 <= e.length ? e.readUInt32LE(e.len, 4) : 0, e.len += 4; + break; + case "time": + if(l) + throw "Bad read type params: time - DisableTime ON"; + a = e.len + 6 <= e.length ? e.readUIntLE(e.len, 6) : 0, a = new Date(a), e.len += 6; + break; + case "addres": + case "hash": + a = []; + for(u = 0; u < 32; u++) + e.len + u <= e.length ? a[u] = e[e.len + u] : a[u] = 0; + e.len += 32; + break; + case "buffer": + case "arr": + a = e.len + t <= e.length ? e.slice(e.len, e.len + t) : Buffer.alloc(t), e.len += t; + break; + case "tr": + if(e.len + 1 >= e.length) + { + a = void 0; + break; + } + f = e[e.len] + 256 * e[e.len + 1]; + e.len += 2, a = e.slice(e.len, e.len + f), e.len += f; + break; + case "data": + (f = e.len + 4 <= e.length ? e.readUInt32LE(e.len, 4) : 0) > e.length - e.len - 4 && (f = 0), e.len += 4, a = e.slice(e.len, + e.len + f), e.len += f; + break; + case "hashSTR": + var d = e.toString("utf8", e.len, e.len + 64); + a = GetAddresFromHex(d), e.len += 64; + break; + case "uintSTR": + d = e.toString("utf8", e.len, e.len + 10); + a = parseInt(d), e.len += 10; + break; + default: + n = n || {}; + var b = i.substr(0, 1); + if("[" === b || "<" === b) + { + var h = "<" === b; + a = []; + var c = GetMiddleString(i); + for(f = Read(e, "uint32"), u = 0; u < f && e.len <= e.length; u++) + { + h ? a[Read(e, "uint32")] = Read(e, c, void 0, n, l) : a[u] = Read(e, c, void 0, n, l); + } + } + else + { + if("{" !== b) + throw "Bad read type params: " + i; + var g = n[i]; + g || (g = GetAttributes(GetMiddleString(i)), n[i] = g), a = {}; + for(u = 0; u < g.length; u++) + { + var v = g[u]; + a[v.Key] = Read(e, v.Value, void 0, n, l); + } + } + } + return a; +}; + +function BufWriteByte(e) +{ + this[this.len] = e, this.len += 1; +}; + +function BufWrite(e,r,t) +{ + Write(this, e, r, t); +}; + +function BufRead(e,r) +{ + return Read(this, e, r); +}; + +function GetNewBuffer(e) +{ + var r = Buffer.alloc(e); + return r.Read = BufRead.bind(r), r.Write = BufWrite.bind(r), r.len = 0, r; +}; + +function GetReadBuffer(e) +{ + var r = Buffer.from(e); + return r.Read = BufRead.bind(r), r.Write = BufWrite.bind(r), r.len = 0, r; +}; + +function GetObjectFromBuffer(e,r,t,n) +{ + var l = Buffer.from(e); + return l.len = 0, Read(l, r, void 0, t, n); +}; + +function GetBufferFromObject(e,r,t,n,l) +{ + var a = Buffer.alloc(t); + return a.len = 0, Write(a, e, r, void 0, n), l || (a = a.slice(0, a.len)), a; +}; + +function GetMiddleString(e) +{ + return e.substr(1, e.length - 2); +}; + +function GetMiddleString2(e,r,t) +{ + for(var n = 0, l = "", a = 0; a < e.length; a++) + { + var i = e.substr(a, 1); + if(" " !== i && "\n" !== i && (i !== r || 1 != ++n)) + { + if(i === t && 0 === --n) + break; + n && (l += i); + } + } + return l; +}; + +function GetAttributeStrings(e) +{ + for(var r = 0, t = [], n = "", l = 0; l < e.length; l++) + { + var a = e.substr(l, 1); + if("{" === a) + r++; + else + if("}" === a) + r--; + else + { + if("," === a && 0 === r) + { + 0 < n.length && t.push(n), n = ""; + continue; + } + if(" " === a || "\n" === a) + continue; + } + n += a; + } + return 0 < n.length && t.push(n), t; +}; + +function GetKeyValueStrings(e) +{ + for(var r = "", t = 0; t < e.length; t++) + { + var n = e.substr(t, 1); + if(" " !== n && "\n" !== n) + { + if(":" === n) + return {Key:r, Value:e.substr(t + 1)}; + r += n; + } + } + throw "Error format Key:Value = " + e; +}; + +function GetAttributes(e) +{ + for(var r = [], t = GetAttributeStrings(e), n = 0; n < t.length; n++) + { + var l = GetKeyValueStrings(t[n]); + r.push(l); + } + return r; +}; +module.exports.GetNewBuffer = GetNewBuffer, module.exports.GetReadBuffer = GetReadBuffer, module.exports.alloc = GetNewBuffer, +module.exports.from = GetReadBuffer, module.exports.Write = Write, module.exports.Read = Read, module.exports.GetObjectFromBuffer = GetObjectFromBuffer, +module.exports.GetBufferFromObject = GetBufferFromObject; diff --git a/Source/core/code.js b/Source/core/code.js new file mode 100644 index 0000000..4c05dd5 --- /dev/null +++ b/Source/core/code.js @@ -0,0 +1,245 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +var fs = require("fs"); +const FORMAT_EVAL_SEND = "{MaxBlockNum:uint,Code:str,Sign:arr64}"; +module.exports = class CCode extends require("./base") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + if(!global.ADDRLIST_MODE && !this.VirtualMode) + { + setInterval(this.CheckLoadCodeTime.bind(this), 10 * 1000) + } + this.LastEvalCodeNum = 0 + CheckCreateDir(GetDataPath("Update")) + } + CheckLoadCodeTime() + { + if(START_LOAD_CODE.StartLoadNode && START_LOAD_CODE.StartLoadVersionNum) + { + var Delta = new Date() - START_LOAD_CODE.StartLoadVersionNumTime; + if(Delta > 20 * 1000) + { + ToError("Cannot load code version:" + START_LOAD_CODE.StartLoadVersionNum + " from node: " + START_LOAD_CODE.StartLoadNode.ip + ":" + START_LOAD_CODE.StartLoadNode.port) + this.ClearLoadCode() + } + } + } + ClearLoadCode() + { + START_LOAD_CODE.StartLoad = undefined + START_LOAD_CODE.StartLoadVersionNum = 0 + START_LOAD_CODE.StartLoadVersionNumTime = 0 + } + StartLoadCode(Node, CodeVersion) + { + var VersionNum = CodeVersion.VersionNum; + START_LOAD_CODE.StartLoad = CodeVersion + START_LOAD_CODE.StartLoadNode = Node + START_LOAD_CODE.StartLoadVersionNum = VersionNum + START_LOAD_CODE.StartLoadVersionNumTime = new Date() + var fname = GetDataPath("Update/wallet-" + VersionNum + ".zip"); + if(fs.existsSync(fname)) + { + this.UseCode(VersionNum, false) + return ; + } + var Context = {"VersionNum":VersionNum}; + this.SendF(Node, {"Method":"GETCODE", "Context":Context, "Data":VersionNum}) + } + static + GETCODE_F() + { + return "uint"; + } + RETCODE(Info) + { + var VersionNum = Info.Context.VersionNum; + if(!VersionNum || !START_LOAD_CODE.StartLoad) + return ; + var fname = GetDataPath("Update/wallet-" + VersionNum + ".zip"); + if(!fs.existsSync(fname)) + { + var Hash = shaarr(Info.Data); + if(CompareArr(Hash, START_LOAD_CODE.StartLoad.Hash) === 0) + { + var file_handle = fs.openSync(fname, "w"); + fs.writeSync(file_handle, Info.Data, 0, Info.Data.length) + fs.closeSync(file_handle) + this.UseCode(VersionNum, global.USE_AUTO_UPDATE) + } + else + { + ToError("Error check hash of version code :" + START_LOAD_CODE.StartLoadVersionNum + " from node: " + Info.Node.ip + ":" + Info.Node.port) + this.ClearLoadCode() + this.AddCheckErrCount(Info.Node, 1, "Error check hash of version code") + } + } + } + UseCode(VersionNum, bUpdate) + { + if(bUpdate) + { + UpdateCodeFiles(VersionNum) + } + if(global.START_LOAD_CODE.StartLoad) + { + global.CODE_VERSION = START_LOAD_CODE.StartLoad + this.ClearLoadCode() + } + } + SetNewCodeVersion(Data, PrivateKey) + { + var fname = GetDataPath("ToUpdate/wallet.zip"); + if(fs.existsSync(fname)) + { + var fname2 = GetDataPath("Update/wallet-" + Data.VersionNum + ".zip"); + if(fs.existsSync(fname2)) + { + fs.unlinkSync(fname2) + } + var data = fs.readFileSync(fname); + var Hash = shaarr(data); + var file_handle = fs.openSync(fname2, "w"); + fs.writeSync(file_handle, data, 0, data.length) + fs.closeSync(file_handle) + var SignArr = arr2(Hash, GetArrFromValue(Data.VersionNum)); + var Sign = secp256k1.sign(SHA3BUF(SignArr), PrivateKey).signature; + global.CODE_VERSION = Data + global.CODE_VERSION.Hash = Hash + global.CODE_VERSION.Sign = Sign + return "OK Set new code version=" + Data.VersionNum; + } + else + { + return "File not exist: " + fname; + } + } +}; + +function UpdateCodeFiles(StartNum) +{ + var fname = GetDataPath("Update"); + if(!fs.existsSync(fname)) + return 0; + var arr = fs.readdirSync(fname); + var arr2 = []; + for(var i = 0; i < arr.length; i++) + { + if(arr[i].substr(0, 7) === "wallet-") + { + arr2.push(parseInt(arr[i].substr(7))); + } + } + arr2.sort(function (a,b) + { + return a - b; + }); + for(var i = 0; i < arr2.length; i++) + { + var Num = arr2[i]; + var Name = "wallet-" + Num + ".zip"; + var Path = fname + "/" + Name; + ToLog("Check file:" + Name); + if(fs.existsSync(Path)) + { + if(StartNum === Num) + { + ToLog("UnpackCodeFile:" + Name); + UnpackCodeFile(Path); + if(StartNum % 2 === 0) + { + global.RestartNode(1); + } + else + { + } + return 1; + } + else + { + ToLog("Delete old file update:" + Name); + fs.unlinkSync(Path); + } + } + } + return 0; +}; +global.UnpackCodeFile = UnpackCodeFile; + +function UnpackCodeFile(fname) +{ + var data = fs.readFileSync(fname); + var reader = ZIP.Reader(data); + reader.forEach(function (entry) + { + var Name = entry.getName(); + var Path = GetCodePath(Name); + if(entry.isFile()) + { + var buf = entry.getData(); + CheckCreateDir(Path, true, true); + var file_handle = fs.openSync(Path, "w"); + fs.writeSync(file_handle, buf, 0, buf.length); + fs.closeSync(file_handle); + } + else + { + } + }); + reader.close(); +}; +global.RestartNode = function RestartNode(bForce) +{ + global.NeedRestart = 1; + setTimeout(DoExit, 5000); + if(global.nw || global.NWMODE) + { + } + else + { + StopChildProcess(); + ToLog("********************************** FORCE RESTART!!!"); + return ; + } + if(this.ActualNodes) + { + var it = this.ActualNodes.iterator(), Node; + while((Node = it.next()) !== null) + { + if(Node.Socket) + CloseSocket(Node.Socket, "Restart"); + } + } + SERVER.StopServer(); + SERVER.StopNode(); + StopChildProcess(); + ToLog("****************************************** RESTART!!!"); + ToLog("EXIT 1"); +}; + +function DoExit() +{ + ToLog("EXIT 2"); + if(global.nw || global.NWMODE) + { + ToLog("RESTART NW"); + var StrRun = '"' + process.argv[0] + '" .\n'; + StrRun += '"' + process.argv[0] + '" .\n'; + SaveToFile("run-next.bat", StrRun); + const child_process = require('child_process'); + child_process.exec("run-next.bat", {shell:true}); + } + ToLog("EXIT 3"); + process.exit(0); +}; diff --git a/Source/core/connect.js b/Source/core/connect.js new file mode 100644 index 0000000..5fce1ad --- /dev/null +++ b/Source/core/connect.js @@ -0,0 +1,1595 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const crypto = require('crypto'); +const CNode = require("./node"); +global.PERIOD_FOR_RECONNECT = 3600 * 1000; +global.CHECK_DELTA_TIME = {Num:0, bUse:0, StartBlockNum:0, EndBlockNum:0, bAddTime:0, DeltaTime:0, Sign:[]}; +global.CHECK_POINT = {BlockNum:0, Hash:[], Sign:[]}; +global.CODE_VERSION = {BlockNum:0, addrArr:[], LevelUpdate:0, BlockPeriod:0, VersionNum:UPDATE_CODE_VERSION_NUM, Hash:[], Sign:[], + StartLoadVersionNum:0}; +global.NET_CONSTANT = {Num:0, BlockNum:0, MaxTrasactionLimit:MAX_TRANSACTION_LIMIT, Reserv1:0, Reserv2:0, Reserv3:0, Reserv4:0, + Reserv5:0, Hash:[], Sign:[]}; +global.START_LOAD_CODE = {}; +const MAX_PERIOD_GETNODES = 120 * 1000; +global.MIN_PERIOD_PING = 4 * 1000; +const MAX_PERIOD_PING = 120 * 1000; +global.MAX_PING_FOR_CONNECT = 400; +var MAX_TIME_CORRECT = 3 * 3600 * 1000; +global.MAX_WAIT_PERIOD_FOR_HOT = 4 * CONSENSUS_PERIOD_TIME; +const PERIOD_FOR_START_CHECK_TIME = 300; +module.exports = class CConnect extends require("./transfer-msg") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.StartTime = Date.now() + this.WasNodesSort = false + this.LevelNodes = [] + this.NodesArr = [] + this.NodesArrUnSort = [] + this.NodesMap = {} + this.NodesIPMap = {} + this.WasNodesSort = true + this.PerioadAfterCanStart = 0 + this.КодДляРазработчикаХекс = GetHexFromArr(this.KeyPair.computeSecret(DEVELOP_PUB_KEY, null)) + this.DO_CONSTANT() + if(!global.ADDRLIST_MODE && !this.VirtualMode) + { + setInterval(this.StartPingPong.bind(this), 1000) + setInterval(this.DeleteBadConnectingByTimer.bind(this), MAX_WAIT_PERIOD_FOR_STATUS / 2) + setInterval(this.StartCheckTransferTree.bind(this), 1000) + } + setInterval(this.NodesArrSort.bind(this), 30000) + } + DO_CONSTANT() + { + this.CommonKey = GetHexFromArr(WALLET.HashProtect(global.COMMON_KEY)) + this.KeyToNode = shaarr(global.COMMON_KEY) + this.NameToNode = this.ValueToXOR("Name", global.NODES_NAME) + } + СтатДанныеОтладкиИзБлока() + { + var Массив = []; + if(this.СтатБлок && this.СтатБлок.SeqHash) + { + WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:BlockNum", this.СтатБлок.BlockNum, "uint"), 6) + WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:SeqHash", this.СтатБлок.SeqHash, "hash"), 32) + WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:AddrHash", this.СтатБлок.AddrHash, "hash"), 32) + } + return Массив; + } + ДоступенКлючРазработчика(Node) + { + if(Node.PubKey && WALLET.WalletOpen !== false && IsDeveloperAccount(WALLET.PubKeyArr)) + { + return 1; + } + return 0; + } + БлокИзДанных(Node, Arr) + { + var Block = {}; + if(this.ДоступенКлючРазработчика(Node) && !IsZeroArr(Arr)) + { + var Data = BufLib.GetObjectFromBuffer(Arr, "{BlockNum:arr6,SeqHash:arr32,AddrHash:arr32}", {}); + Block.BlockNum = this.ValueFromXORDevelop(Node, "Stat:BlockNum", Data.BlockNum, "uint") + Block.SeqHash = this.ValueFromXORDevelop(Node, "Stat:SeqHash", Data.SeqHash, "hash") + Block.AddrHash = this.ValueFromXORDevelop(Node, "Stat:AddrHash", Data.AddrHash, "hash") + } + return Block; + } + StartConnectTry(Node) + { + var Delta = Date.now() - Node.StartTimeConnect; + if(Delta >= Node.NextConnectDelta && this.IsCanConnect(Node)) + { + if(!GetSocketStatus(Node.Socket)) + { + Node.StartTimeConnect = Date.now() + if(Delta < 60 * 1000) + Node.NextConnectDelta = Node.NextConnectDelta * 2 + else + Node.NextConnectDelta = Math.trunc(Node.NextConnectDelta * 1.2) + Node.CreateConnect() + } + } + } + FindRunNodeContext(addrArr, ip, port, bUpdate) + { + var Node, addrStr; + addrStr = GetHexFromAddres(addrArr) + Node = this.NodesMap[addrStr] + if(!Node) + { + var key = "" + ip + ":" + port; + Node = this.NodesIPMap[key] + if(!Node) + { + Node = this.GetNewNode(addrStr, ip, port) + } + } + if(Node.addrStr !== addrStr) + { + delete this.NodesMap[Node.addrStr] + this.NodesMap[addrStr] = Node + Node.addrStrTemp = undefined + } + Node.addrArr = addrArr + Node.addrStr = addrStr + Node.ip = ip.trim() + Node.port = port + return Node; + } + CheckNodeMap(Node) + { + if(Node.addrStrTemp && Node.addrStrTemp !== Node.addrStr) + { + delete this.NodesMap[Node.addrStrTemp] + var Node2 = this.NodesMap[Node.addrStr]; + if(Node2 && Node2 !== Node) + { + Node2.Delete = 1 + AddNodeInfo(Node2, "FIND DOUBLE!!") + delete this.NodesMap[Node.addrStr] + } + this.NodesMap[Node.addrStr] = Node + Node.addrStrTemp = undefined + } + } + StartHandshake(Node) + { + return this.StartConnectTry(Node); + } + StartPingPong() + { + if(glStopNode) + return ; + if(global.CAN_START) + this.PerioadAfterCanStart++ + this.TimeDevCorrect() + var arr = SERVER.GetActualNodes(); + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + if(this.IsCanConnect(Node) && !Node.IsAddrList) + { + if(Node.Hot) + Node.NextPing = MIN_PERIOD_PING + if(Node.NextPing < MIN_PERIOD_PING) + Node.NextPing = MIN_PERIOD_PING + var Delta = Date.now() - Node.PingStart; + if(Delta >= Node.NextPing) + { + Node.PingStart = Date.now() + Node.NextPing = Node.NextPing * 1.5 + if(Node.NextPing > MAX_PERIOD_PING) + Node.NextPing = MAX_PERIOD_PING + if(!Node.PingNumber) + Node.PingNumber = 0 + Node.PingNumber++ + var Context = {"StartTime":GetCurrentTime(0), PingNumber:Node.PingNumber}; + this.SendF(Node, {"Method":"PING", "Context":Context, "Data":this.GetPingData(Node)}) + } + } + } + } + GetPingData(Node) + { + var GrayAddres = 0; + if(GrayConnect()) + GrayAddres = 1 + var BlockNumHash = Math.trunc((GetCurrentBlockNumByTime() - BLOCK_PROCESSING_LENGTH2) / PERIOD_ACCOUNT_HASH) * PERIOD_ACCOUNT_HASH; + var AccountsHash = DApps.Accounts.GetHashOrUndefined(BlockNumHash); + var CheckPointHashDB = []; + if(CHECK_POINT.BlockNum && CHECK_POINT.BlockNum <= this.BlockNumDB) + { + var Block = this.ReadBlockHeaderFromMapDB(CHECK_POINT.BlockNum); + if(Block) + { + CheckPointHashDB = Block.Hash + } + } + var HashDB = []; + if(this.BlockNumDB > 0) + { + var Block = this.ReadBlockHeaderFromMapDB(this.BlockNumDB); + if(Block) + HashDB = Block.Hash + } + var LevelCount = this.GetLevelEnum(Node); + var StopGetBlock = global.STOPGETBLOCK; + if(!StopGetBlock && this.BusyLevel) + { + if(Node.BlockProcessCount <= this.BusyLevel) + StopGetBlock = 1 + } + var СтатДанные = []; + var DirectMAccount = 0; + var Ret = {VERSIONMAX:DEF_VERSION, FIRST_TIME_BLOCK:0, PingVersion:3, GrayConnect:GrayAddres, Reserve2:0, AutoCorrectTime:AUTO_CORRECT_TIME, + LevelCount:LevelCount, Time:(GetCurrentTime() - 0), BlockNumDB:this.BlockNumDB, LoadHistoryMode:this.LoadHistoryMode, CanStart:global.CAN_START, + CheckPoint:CHECK_POINT, Reserv3:[], Key:this.KeyToNode, Name:this.NameToNode, TrafficFree:this.SendTrafficFree, AccountBlockNum:BlockNumHash, + AccountsHash:AccountsHash, MemoryUsage:Math.trunc(process.memoryUsage().heapTotal / 1024 / 1024), CheckDeltaTime:CHECK_DELTA_TIME, + CodeVersion:CODE_VERSION, IsAddrList:global.ADDRLIST_MODE, CheckPointHashDB:CheckPointHashDB, PortWeb:HTTP_HOSTING_PORT, HashDB:HashDB, + StopGetBlock:StopGetBlock, NetConstant:NET_CONSTANT, }; + return Ret; + } + static + PING_F(bSend) + { + return "{\ + VERSIONMAX:str15,\ + PingVersion:byte,\ + GrayConnect:byte,\ + Reserve2:byte,\ + AutoCorrectTime:byte,\ + LevelCount:uint16,\ + Time:uint,\ + BlockNumDB:uint,\ + LoadHistoryMode:byte,\ + CanStart:byte,\ + CheckPoint:{BlockNum:uint,Hash:hash,Sign:arr64},\ + Reserv3:arr38,\ + Key:arr32,\ + Name:arr32,\ + TrafficFree:uint,\ + AccountBlockNum:uint,\ + AccountsHash:hash,\ + MemoryUsage:uint,\ + CheckDeltaTime:{Num:uint,bUse:byte,StartBlockNum:uint,EndBlockNum:uint,bAddTime:byte,DeltaTime:uint,Sign:arr64},\ + CodeVersion:{BlockNum:uint,addrArr:arr32,LevelUpdate:byte,BlockPeriod:uint,VersionNum:uint,Hash:hash,Sign:arr64},\ + IsAddrList:byte,\ + CheckPointHashDB:hash,\ + PortWeb:uint16,\ + HashDB:hash,\ + StopGetBlock:uint,\ + NetConstant:{Num:uint,BlockNum:uint,MaxTrasactionLimit:uint,Reserv1:uint,Reserv2:uint,Reserv3:uint,Reserv4:uint,Reserv5:uint,Sign:arr64},\ + }"; + } + static + PONG_F(bSend) + { + return CConnect.PING_F(bSend); + } + PING(Info, CurTime) + { + this.DoPingData(Info, 1) + this.SendF(Info.Node, {"Method":"PONG", "Context":Info.Context, "Data":this.GetPingData(Info.Node)}) + } + DoPingData(Info, bCheckPoint) + { + var Node = Info.Node; + var Data = this.DataFromF(Info); + Info.Node.VERSIONMAX = Data.VERSIONMAX + if(Data.PingVersion >= 3 && global.COMMON_KEY && CompareArr(Data.Key, this.KeyToNode) === 0) + { + Node.Name = this.ValueFromXOR(Node, "Name", Data.Name) + if(Node.BlockProcessCount < 5000000 + global.TRUST_PROCESS_COUNT) + Node.BlockProcessCount = 5000000 + global.TRUST_PROCESS_COUNT + } + else + { + Node.Name = "" + } + Node.INFO = Data + Node.INFO.WasPing = 1 + Node.LevelCount = Data.LevelCount + Node.LoadHistoryMode = Data.LoadHistoryMode + Node.LastTime = GetCurrentTime() - 0 + Node.NextConnectDelta = 1000 + Node.StopGetBlock = Data.StopGetBlock + Node.portweb = Data.PortWeb + if(bCheckPoint) + { + this.CheckCheckPoint(Data, Info.Node) + this.CheckCodeVersion(Data, Info.Node) + this.CheckDeltaTime(Data, Info.Node) + } + } + PONG(Info, CurTime) + { + var Data = this.DataFromF(Info); + var Node = Info.Node; + if(!Info.Context) + return ; + if(!Info.Context.StartTime) + return ; + if(Info.Context.PingNumber !== Node.PingNumber) + return ; + this.DoPingData(Info, 0) + var DeltaTime = GetCurrentTime(0) - Info.Context.StartTime; + Node.DeltaTimeM = DeltaTime + Node.SumDeltaTime += DeltaTime + Node.CountDeltaTime++ + Node.DeltaTime = Math.trunc(Node.SumDeltaTime / Node.CountDeltaTime) + if(!Node.DeltaTime) + Node.DeltaTime = 1000 + if(DeltaTime) + { + Node.DeltaGlobTime = GetCurrentTime() - (Data.Time + DeltaTime / 2) + } + this.CheckCheckPoint(Data, Info.Node) + if(!START_LOAD_CODE.StartLoadVersionNum) + START_LOAD_CODE.StartLoadVersionNum = 0 + this.CheckNetConstant(Data, Info.Node) + this.CheckCodeVersion(Data, Info.Node) + if(!global.CAN_START) + { + if(DeltaTime > MAX_PING_FOR_CONNECT) + ToLog("DeltaTime=" + DeltaTime + ">" + MAX_PING_FOR_CONNECT + " ms - " + NodeInfo(Node), 2) + } + var Times; + if(DeltaTime <= MAX_PING_FOR_CONNECT) + { + Times = Node.Times + if(!Times || Times.Count >= 10) + { + Times = {SumDelta:0, Count:0, AvgDelta:0, Arr:[]} + Node.Times = Times + } + var Time1 = Data.Time; + var Time2 = GetCurrentTime(); + var Delta2 = - (Time2 - Time1 - DeltaTime / 2); + Times.Arr.push(Delta2) + Times.SumDelta += Delta2 + Times.Count++ + Times.AvgDelta = Times.SumDelta / Times.Count + if(Times.Count >= 2) + { + Times.Arr.sort(function (a,b) + { + return Math.abs(a) - Math.abs(b); + }) + Node.AvgDelta = Times.Arr[0] + } + if(global.AUTO_CORRECT_TIME) + { + this.CorrectTime() + } + } + else + { + } + ADD_TO_STAT("MAX:PING_TIME", DeltaTime) + if(!global.CAN_START) + if(Times && Times.Count >= 1 && Times.AvgDelta <= 200) + { + ToLog("****************************************** CAN_START") + global.CAN_START = true + } + this.CheckDeltaTime(Data, Info.Node) + } + CheckCheckPoint(Data, Node) + { + if(CREATE_ON_START) + return ; + if(Data.CheckPoint.BlockNum && Data.CheckPoint.BlockNum > CHECK_POINT.BlockNum) + { + var SignArr = arr2(Data.CheckPoint.Hash, GetArrFromValue(Data.CheckPoint.BlockNum)); + if(CheckDevelopSign(SignArr, Data.CheckPoint.Sign)) + { + global.CHECK_POINT = Data.CheckPoint + this.ResetNextPingAllNode() + if(Data.CheckPoint.BlockNum < this.BlockNumDBMin) + return ; + var Block = this.ReadBlockHeaderDB(CHECK_POINT.BlockNum); + if(Block && CompareArr(Block.Hash, CHECK_POINT.Hash) !== 0) + { + this.BlockNumDB = CHECK_POINT.BlockNum - 1 + this.TruncateBlockDB(this.BlockNumDB) + this.StartSyncBlockchain(Node, 0, 1) + } + } + else + { + Node.NextConnectDelta = 60 * 1000 + ToLog("Error Sign CheckPoint=" + Data.CheckPoint.BlockNum + " from " + NodeInfo(Node)) + this.AddCheckErrCount(Node, 10, "Error Sign CheckPoint") + } + } + } + CheckDeltaTime(Data, Node) + { + if(global.AUTO_CORRECT_TIME) + if(global.CAN_START && !CREATE_ON_START) + { + if(Data.CheckDeltaTime.Num > CHECK_DELTA_TIME.Num) + { + var SignArr = this.GetSignCheckDeltaTime(Data.CheckDeltaTime); + if(CheckDevelopSign(SignArr, Data.CheckDeltaTime.Sign)) + { + global.CHECK_DELTA_TIME = Data.CheckDeltaTime + } + else + { + Node.NextConnectDelta = 60 * 1000 + ToLog("Error Sign CheckDeltaTime Num=" + Data.CheckDeltaTime.Num + " from " + NodeInfo(Node)) + this.AddCheckErrCount(Node, 10, "Error Sign CheckDeltaTime") + } + } + } + } + CheckNetConstant(Data, Node) + { + if(Data.NetConstant.Num > NET_CONSTANT.Num) + { + var SignArr = this.GetSignCheckNetConstant(Data.NetConstant); + if(CheckDevelopSign(SignArr, Data.NetConstant.Sign)) + { + global.NET_CONSTANT = Data.NetConstant + var CurBlockNum = GetCurrentBlockNumByTime(); + var Delta = Data.NetConstant.BlockNum - CurBlockNum; + if(Delta < 1) + Delta = 1 + this.ResetNextPingAllNode() + ToLog("Get new NetConstant (wait " + Delta + " s) Num=" + Data.NetConstant.Num) + if(this.idTimerSetConst) + clearTimeout(this.idTimerSetConst) + let SELF = this; + this.idTimerSetConst = setTimeout(function () + { + SELF.DoNetConst() + this.idTimerSetConst = 0 + }, Delta * 1000) + } + else + { + Node.NextConnectDelta = 60 * 1000 + ToLog("Error Sign CheckNetConstant Num=" + Data.NetConstant.Num + " from " + NodeInfo(Node)) + this.AddCheckErrCount(Node, 10, "Error Sign CheckNetConstant") + } + } + } + DoNetConst() + { + global.MAX_TRANSACTION_LIMIT = NET_CONSTANT.MaxTrasactionLimit + } + CheckCodeVersion(Data, Node) + { + var CodeVersion = Data.CodeVersion; + Node.VersionNum = CodeVersion.VersionNum + if(CodeVersion.VersionNum >= MIN_CODE_VERSION_NUM) + { + Node.VersionOK = true + } + else + { + Node.VersionOK = false + } + if(Node.VersionOK) + { + Node.CanHot = true + if(CHECK_POINT.BlockNum && Data.CheckPoint.BlockNum) + if(CHECK_POINT.BlockNum !== Data.CheckPoint.BlockNum || CompareArr(CHECK_POINT.Hash, Data.CheckPoint.Hash) !== 0) + { + Node.CanHot = false + Node.NextConnectDelta = 60 * 1000 + } + } + else + { + Node.CanHot = false + if(!Node.VersionOK) + { + Node.NextConnectDelta = 60 * 1000 + } + } + var bLoadVer = 0; + if(CodeVersion.BlockNum && (CodeVersion.BlockNum <= GetCurrentBlockNumByTime() || CodeVersion.BlockPeriod === 0) && CodeVersion.BlockNum > CODE_VERSION.BlockNum && !IsZeroArr(CodeVersion.Hash) && (CodeVersion.VersionNum > CODE_VERSION.VersionNum && CodeVersion.VersionNum > START_LOAD_CODE.StartLoadVersionNum || CodeVersion.VersionNum === CODE_VERSION.VersionNum && IsZeroArr(CODE_VERSION.Hash))) + { + bLoadVer = 1 + } + if(bLoadVer) + { + var Level = AddrLevelArrFromBegin(this.addrArr, CodeVersion.addrArr); + if(CodeVersion.BlockPeriod) + { + var Delta = GetCurrentBlockNumByTime() - CodeVersion.BlockNum; + Level += Delta / CodeVersion.BlockPeriod + } + if(Level >= CodeVersion.LevelUpdate) + { + var SignArr = arr2(CodeVersion.Hash, GetArrFromValue(CodeVersion.VersionNum)); + if(CheckDevelopSign(SignArr, CodeVersion.Sign)) + { + ToLog("Get new CodeVersion = " + CodeVersion.VersionNum + " HASH:" + GetHexFromArr(CodeVersion.Hash).substr(0, 20)) + if(CodeVersion.VersionNum > CODE_VERSION.VersionNum && CodeVersion.VersionNum > START_LOAD_CODE.StartLoadVersionNum) + { + this.StartLoadCode(Node, CodeVersion) + } + else + { + CODE_VERSION = CodeVersion + } + } + else + { + ToLog("Error Sign CodeVersion=" + CodeVersion.VersionNum + " from " + NodeInfo(Node) + " HASH:" + GetHexFromArr(CodeVersion.Hash).substr(0, + 20)) + ToLog(JSON.stringify(CodeVersion)) + this.AddCheckErrCount(Node, 10, "Error Sign CodeVersion") + Node.NextConnectDelta = 60 * 1000 + } + } + } + } + GetSignCheckNetConstant(Data) + { + var Buf = BufLib.GetBufferFromObject(Data, "{Num:uint,BlockNum:uint,MaxTrasactionLimit:uint,Reserv1:uint,Reserv2:uint,Reserv3:uint,Reserv4:uint,Reserv5:uint}", + 1000, {}); + return shaarr(Buf); + } + GetSignCheckDeltaTime(Data) + { + var Buf = BufLib.GetBufferFromObject(Data, "{Num:uint,bUse:byte,StartBlockNum:uint,EndBlockNum:uint,bAddTime:byte,DeltaTime:uint}", + 1000, {}); + return shaarr(Buf); + } + ResetNextPingAllNode() + { + var arr = SERVER.GetActualNodes(); + for(var i = 0; i < arr.length; i++) + { + var Node2 = arr[i]; + if(Node2 && Node2.NextPing > 5 * 1000) + Node2.NextPing = 5 * 1000 + } + } + StartDisconnectHot(Node, StrError, bDeleteHot) + { + AddNodeInfo(Node, "DisconnectHot:" + StrError) + if(Node.Active && Node.Hot) + { + AddNodeInfo(Node, "SEND DISCONNECTHOT") + this.Send(Node, {"Method":"DISCONNECTHOT", "Context":{}, "Data":StrError}, STR_TYPE) + } + this.DeleteNodeFromHot(Node) + } + DISCONNECTHOT(Info, CurTime) + { + this.DeleteNodeFromHot(Info.Node) + ADD_TO_STAT("DISCONNECTHOT") + AddNodeInfo(Info.Node, "GET DISCONNECTHOT:" + Info.Data) + } + StartGetNodes(Node) + { + if(glStopNode) + return ; + var Delta = Date.now() - Node.StartTimeGetNodes; + if(Delta >= Node.NextGetNodesDelta) + { + Node.StartTimeGetNodes = Date.now() + Node.NextGetNodesDelta = Math.min(Node.NextGetNodesDelta * 2, MAX_PERIOD_GETNODES) + if(global.ADDRLIST_MODE) + Node.NextGetNodesDelta = MAX_PERIOD_GETNODES + this.Send(Node, {"Method":"GETNODES", "Context":{}, "Data":undefined}) + } + } + GETNODES(Info, CurTime) + { + this.SendF(Info.Node, {"Method":"RETGETNODES", "Context":Info.Context, "Data":{arr:this.GetDirectNodesArray(false, 0, 1), IsAddrList:global.ADDRLIST_MODE, + }}, MAX_NODES_RETURN * 250 + 300) + } + static + RETGETNODES_F() + { + return "{arr:[\ + {\ + addrArr:arr32,\ + ip:str20,\ + port:uint16,\ + portweb:uint16,\ + LastTime:uint,\ + DeltaTime:uint,\ + Reserv:arr8\ + }\ + ],\ + IsAddrList:byte}"; + } + RETGETNODES(Info, CurTime) + { + var Data = this.DataFromF(Info); + var arr = Data.arr; + if(arr && arr.length > 0) + { + for(var i = 0; i < arr.length; i++) + { + arr[i].addrStr = GetHexFromArr(arr[i].addrArr) + var Item = this.AddToArrNodes(arr[i], true); + if(Item) + Item.LastTimeGetNode = CurTime - 0 + } + } + Info.Node.IsAddrList = Data.IsAddrList + AddNodeInfo(Info.Node, "RETGETNODES length=" + arr.length) + } + static + RETGETNODES2_F() + { + return "{arr:[\ + {\ + addrStr:str64,\ + ip:str30,\ + port:uint16,\ + portweb:uint16,\ + LastTime:uint,\ + DeltaTime:uint,\ + StatData:arr70\ + }\ + ],\ + IsAddrList:byte}"; + } + RETGETNODES2(Info, CurTime) + { + var Data = this.DataFromF(Info); + var arr = Data.arr; + if(arr && arr.length > 0) + { + for(var i = 0; i < arr.length; i++) + { + var Item = this.AddToArrNodes(arr[i], true); + if(Item) + Item.LastTimeGetNode = CurTime - 0 + } + } + Info.Node.IsAddrList = Data.IsAddrList + AddNodeInfo(Info.Node, "RETGETNODES2 length=" + arr.length) + } + GetNewNode(addrStr, ip, port) + { + var Node = new CNode(addrStr, ip, port); + this.AddToArrNodes(Node, false) + return Node; + } + IsCanConnect(Node) + { + if(Node.addrStr === this.addrStr || this.NodeInBan(Node) || Node.Delete || Node.Self || Node.DoubleConnection) + return false; + if(Node.ip === this.ip && Node.port === this.port) + return false; + if(this.addrStr === Node.addrStr) + return false; + return true; + } + GetDirectNodesArray(bAll, bWebPort, bGetAddrArr) + { + var ret = []; + var Value = {addrStr:this.addrStr, ip:this.ip, port:this.port, LastTime:0, DeltaTime:0, Hot:true, BlockProcessCount:0, portweb:HTTP_HOSTING_PORT, + }; + if(bGetAddrArr) + Value.addrArr = GetArrFromHex(Value.addrStr) + ret.push(Value) + var len = this.NodesArr.length; + var UseRandom = 0; + var MaxDeltaTime = 24 * 3600 * 1000; + if(len > MAX_NODES_RETURN && !bAll) + { + UseRandom = 1 + MaxDeltaTime = NODES_DELTA_CALC_HOUR * 3600 * 1000 + len = MAX_NODES_RETURN + } + var mapWasAdd = {}; + var CurTime = GetCurrentTime(); + for(var i = 0; i < len; i++) + { + var Item; + if(UseRandom) + { + Item = this.NodesArr[random(this.NodesArr.length)] + if(mapWasAdd[Item.addrStr]) + { + continue; + } + mapWasAdd[Item.addrStr] = 1 + } + else + { + Item = this.NodesArr[i] + } + if(bWebPort && !Item.portweb) + continue; + if(!this.IsCanConnect(Item)) + continue; + if(Item.GrayConnect) + continue; + if(Item.BlockProcessCount < 0) + continue; + if(!GrayConnect() && Item.LastTime - 0 < CurTime - MaxDeltaTime) + continue; + var Value = {addrStr:Item.addrStr, ip:Item.ip, port:Item.port, FirstTime:Item.FirstTime, FirstTimeStr:Item.FirstTimeStr, LastTime:Item.LastTime - 0, + DeltaTime:Item.DeltaTime, Hot:Item.Hot, BlockProcessCount:Item.BlockProcessCount, Name:Item.Name, portweb:Item.portweb, }; + if(bGetAddrArr) + Value.addrArr = GetArrFromHex(Value.addrStr) + ret.push(Value) + } + return ret; + } + AddToArrNodes(Item) + { + if(Item.addrStr === "" || Item.addrStr === this.addrStr) + return ; + var Node; + var key = Item.ip + ":" + Item.port; + Node = this.NodesMap[Item.addrStr] + if(!Node) + { + Node = this.NodesIPMap[key] + } + if(!Node) + { + if(Item instanceof CNode) + Node = Item + else + Node = new CNode(Item.addrStr, Item.ip, Item.port) + Node.id = this.NodesArr.length + Node.addrArr = GetAddresFromHex(Node.addrStr) + this.NodesMap[Node.addrStr] = Node + this.NodesArr.push(Node) + this.NodesArrUnSort.push(Node) + ADD_TO_STAT("AddToNodes") + } + this.NodesMap[Node.addrStr] = Node + this.NodesIPMap[key] = Node + if(Node.addrArr && CompareArr(Node.addrArr, this.addrArr) === 0) + { + Node.Self = true + } + if(Item.BlockProcessCount) + Node.BlockProcessCount = Item.BlockProcessCount + if(Item.FirstTime) + { + Node.FirstTime = Item.FirstTime + Node.FirstTimeStr = Item.FirstTimeStr + } + if(Item.Name) + Node.Name = Item.Name + if(Item.portweb) + Node.portweb = Item.portweb + return Node; + } + NodesArrSort() + { + this.NodesArr.sort(SortNodeBlockProcessCount) + if((GrayConnect() || !this.LoadHistoryMode) && Date.now() - this.StartTime > 120 * 1000) + { + var arr0 = this.GetDirectNodesArray(true); + var arr = arr0.slice(1, 2000); + SaveParams(GetDataPath("nodes.lst"), arr) + } + } + LoadNodesFromFile() + { + var arr = LoadParams(GetDataPath("nodes.lst"), []); + arr.sort(SortNodeBlockProcessCount) + for(var i = 0; i < arr.length; i++) + { + if(arr[i].LastTime) + { + if(typeof arr[i].LastTime === "string") + arr[i].LastTime = 0 + } + this.AddToArrNodes(arr[i], true) + } + } + GetLevelEnum(Node) + { + var Level = this.AddrLevelNode(Node); + var arr0 = this.LevelNodes[Level]; + if(!arr0) + { + Node.LevelEnum = 1 + return 1; + } + var arr = [].concat(arr0); + var bWas = 0; + for(var n = 0; n < arr.length; n++) + { + if(arr[n] === Node) + { + bWas = 1 + break; + } + } + if(!bWas) + arr.push(Node) + arr.sort(SortNodeBlockProcessCount) + for(var n = 0; n < arr.length; n++) + { + if(arr[n] === Node) + { + Node.LevelEnum = 1 + n + break; + } + } + return Node.LevelEnum; + } + StartAddLevelConnect(Node) + { + if(!global.CAN_START) + return ; + ADD_TO_STAT("NETCONFIGURATION") + if(Node.Active && Node.CanHot) + this.SendF(Node, {"Method":"ADDLEVELCONNECT", "Context":{}, "Data":this.GetLevelEnum(Node)}) + } + static + ADDLEVELCONNECT_F() + { + return "uint"; + } + ADDLEVELCONNECT(Info, CurTime) + { + Info.Node.LevelCount = this.DataFromF(Info) + var ret; + var Count; + if(!global.CAN_START) + return ; + if(Info.Node.GrayConnect || GrayConnect()) + return ; + var Count = this.GetLevelEnum(Info.Node); + var bAdd = this.AddLevelConnect(Info.Node); + if(bAdd) + { + ret = {result:1, Count:Count} + } + else + { + ret = {result:0, Count:Count} + } + AddNodeInfo(Info.Node, "GET ADDLEVELCONNECT, DO bAdd=" + bAdd) + this.SendF(Info.Node, {"Method":"RETADDLEVELCONNECT", "Context":Info.Context, "Data":ret}) + } + AddLevelConnect(Node) + { + if(!global.CAN_START) + return false; + var Level = this.AddrLevelNode(Node); + Node.Hot = true + var arr = this.LevelNodes[Level]; + if(!arr) + { + arr = [] + this.LevelNodes[Level] = arr + } + var bWas = 0; + for(var i = 0; i < arr.length; i++) + if(arr[i] === Node) + { + bWas = 1 + } + if(!bWas) + arr.push(Node) + Node.TransferCount = 0 + if(this.LoadHistoryMode) + Node.LastTimeTransfer = (GetCurrentTime() - 0) + 30 * 1000 + else + Node.LastTimeTransfer = (GetCurrentTime() - 0) + 10 * 1000 + Node.CanHot = true + this.CheckDisconnectHot(Level) + if(!Node.CanHot) + return false; + this.SendGetMessage(Node) + ADD_TO_STAT("NETCONFIGURATION") + ADD_TO_STAT("AddLevelConnect") + AddNodeInfo(Node, "Add Level connect") + return true; + } + static + RETADDLEVELCONNECT_F() + { + return "{result:byte,Count:uint}"; + } + RETADDLEVELCONNECT(Info, CurTime) + { + var Data = this.DataFromF(Info); + AddNodeInfo(Info.Node, "GET RETADDLEVELCONNECT: " + Data.result) + if(Data.result === 1) + { + this.AddLevelConnect(Info.Node) + } + else + { + this.AddCheckErrCount(Info.Node, 1) + } + Info.Node.LevelCount = Data.Count + } + DeleteBadConnectingByTimer() + { + if(glStopNode) + return ; + var CurTime = GetCurrentTime(); + var arr = SERVER.NodesArr; + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + var Status = GetSocketStatus(Node.Socket); + if(Node.Active && Status < 100) + { + var Delta = CurTime - Node.LastTime; + if(Delta > MAX_WAIT_PERIOD_FOR_STATUS) + { + AddNodeInfo(Node, "Close bad connecting by time") + this.DeleteNodeFromActive(Node) + } + } + } + } + CheckDisconnectHot(Level) + { + var CurTime = GetCurrentTime() - 0; + var MaxCountChilds; + if(Level < 3) + MaxCountChilds = 4 + else + MaxCountChilds = MAX_CONNECT_CHILD + var arr = this.LevelNodes[Level]; + if(arr) + { + for(var n = arr.length - 1; n >= 0; n--) + { + var Node = arr[n]; + if(Node) + { + var DeltaTime = CurTime - Node.LastTimeTransfer; + if(!Node.Hot || DeltaTime > MAX_WAIT_PERIOD_FOR_HOT) + { + this.StartDisconnectHot(Node, "TimeDisconnectHot") + } + } + } + arr.sort(SortNodeBlockProcessCount) + var ChildCount = arr.length; + for(var n = arr.length - 1; n >= MIN_CONNECT_CHILD; n--) + { + var Node = arr[n]; + if(Node) + { + if(ChildCount > MaxCountChilds) + { + ChildCount-- + Node.CanHot = false + this.StartDisconnectHot(Node, "MAX_CONNECT_CHILD") + ADD_TO_STAT("DisconnectChild") + continue; + } + if(ChildCount > (MIN_CONNECT_CHILD) && Node.LevelCount > MIN_CONNECT_CHILD) + { + ChildCount-- + Node.CanHot = false + this.AddCheckErrCount(Node, 1) + this.StartDisconnectHot(Node, "MIN_CONNECT_CHILD:" + Node.LevelCount + " LevelEnum:" + (n + 1)) + ADD_TO_STAT("DisconnectChild") + continue; + } + } + } + } + } + SetTime(NewTime) + { + ToLog("Set new time: " + NewTime) + if(NewTime) + { + global.DELTA_CURRENT_TIME = NewTime - (GetCurrentTime(0) - 0) + SAVE_CONST(true) + } + } + static + TIME_F() + { + return "{Time:uint, Sign:arr64}"; + } + SendTimeDev(Node) + { + if(!WALLET.WalletOpen) + { + ToLog("Error Wallet not open") + return 0; + } + if(!this.SignCurrentTimeDev) + { + var SignArr = GetArrFromHex(SERVER.addrStr); + this.SignCurrentTimeDev = secp256k1.sign(SHA3BUF(SignArr), WALLET.KeyPair.getPrivateKey('')).signature + } + var Time = GetCurrentTime() - 0; + ToLog("Send time: " + Time + " to " + NodeInfo(Node)) + this.SendF(Node, {"Method":"TIME", "Data":{Time:Time, Sign:this.SignCurrentTimeDev}}) + return 1; + } + SendTimeToAll() + { + var Count = 0; + for(var i = 0; i < this.NodesArr.length; i++) + { + var Node = this.NodesArr[i]; + if(Node.Active) + { + if(this.SendTimeDev(Node)) + Count++ + } + } + return Count; + } + TIME(Info, CurTime) + { + if(global.AUTO_CORRECT_TIME) + { + var Node = Info.Node; + var Data = this.DataFromF(Info); + var SignArr = GetArrFromHex(Node.addrStr); + if(CheckDevelopSign(SignArr, Data.Sign)) + { + this.SetTime(Data.Time) + } + else + { + Node.NextConnectDelta = 60 * 1000 + ToLog("Error Sign TIME from " + NodeInfo(Node)) + this.AddCheckErrCount(Node, 10, "Error Sign TIME") + } + } + } + ConnectToAll() + { + var Count = 0; + for(var i = 0; i < this.NodesArr.length; i++) + { + var Node = this.NodesArr[i]; + if(!Node.Active && this.IsCanConnect(Node) && !Node.WasAddToConnect) + { + AddNodeInfo(Node, "To connect all") + Node.NextConnectDelta = 1000 + Node.WasAddToConnect = 1 + ArrConnect.push(Node) + Count++ + } + } + return Count; + } + DisconnectAll() + { + var Count = 0; + for(var i = 0; i < this.NodesArr.length; i++) + { + var Node = this.NodesArr[i]; + if(Node.Active) + { + AddNodeInfo(Node, "Disconnect hot all") + Node.NextConnectDelta = 10000 + this.DeleteNodeFromActive(Node) + Count++ + } + } + return Count; + } + GetHotTimeNodes() + { + if(this.LoadHistoryMode || !global.CAN_START) + return this.GetActualNodes(); + else + return this.GetHotNodes(); + } + CorrectTime() + { + var MaxCorrect = MAX_TIME_CORRECT; + var PerioadAfterCanStart = this.PerioadAfterCanStart; + var ArrNodes = this.GetHotTimeNodes(); + var CountNodes = ArrNodes.length; + var DeltaArr = []; + var NodesSet = new Set(); + for(var i = 0; i < ArrNodes.length; i++) + { + var Node = ArrNodes[i]; + if(!Node.Times) + continue; + if(Node.Times.Count < 2) + continue; + if(PerioadAfterCanStart >= PERIOD_FOR_START_CHECK_TIME) + if(Node.Times.Count < 5) + continue; + NodesSet.add(Node) + } + for(var Node of NodesSet) + { + DeltaArr.push(Node.Times.AvgDelta) + } + if(DeltaArr.length < 1) + return ; + if(this.LoadHistoryMode && CountNodes > 10) + { + PerioadAfterCanStart = 0 + CountNodes = 10 + } + if(DeltaArr.length < CountNodes / 2) + return ; + if(PerioadAfterCanStart >= PERIOD_FOR_START_CHECK_TIME) + { + if(DeltaArr.length < 3 * CountNodes / 4) + return ; + } + DeltaArr.sort(function (a,b) + { + return a - b; + }) + var start, finish; + if(Math.floor(DeltaArr.length / 2) === DeltaArr.length / 2) + { + start = DeltaArr.length / 2 - 1 + finish = start + 1 + } + else + { + start = Math.floor(DeltaArr.length / 2) + finish = start + } + var Sum = 0; + var Count = 0; + for(var i = start; i <= finish; i++) + { + Sum = Sum + DeltaArr[i] + Count++ + } + var AvgDelta = Sum / Count; + if(PerioadAfterCanStart < PERIOD_FOR_START_CHECK_TIME) + { + var KT = (PERIOD_FOR_START_CHECK_TIME - PerioadAfterCanStart) / PERIOD_FOR_START_CHECK_TIME; + AvgDelta = AvgDelta * KT + } + else + { + MaxCorrect = 25 + } + if(AvgDelta < ( - MaxCorrect)) + AvgDelta = - MaxCorrect + else + if(AvgDelta > MaxCorrect) + AvgDelta = MaxCorrect + AvgDelta = Math.trunc(AvgDelta) + if(Math.abs(AvgDelta) < 15) + { + return ; + } + if(AvgDelta > 0) + ADD_TO_STAT("CORRECT_TIME_UP", AvgDelta) + else + ADD_TO_STAT("CORRECT_TIME_DOWN", - AvgDelta) + global.DELTA_CURRENT_TIME = Math.trunc(global.DELTA_CURRENT_TIME + AvgDelta) + this.ClearTimeStat() + SAVE_CONST() + } + ClearTimeStat() + { + var ArrNodes = this.GetHotTimeNodes(); + for(var Node of ArrNodes) + { + Node.Times = undefined + } + } + TimeDevCorrect() + { + if(CHECK_DELTA_TIME.bUse) + { + var BlockNum = GetCurrentBlockNumByTime(); + if(CHECK_DELTA_TIME.StartBlockNum <= BlockNum && CHECK_DELTA_TIME.EndBlockNum > BlockNum) + { + if(!global.DELTA_CURRENT_TIME) + global.DELTA_CURRENT_TIME = 0 + var CorrectTime = 0; + if(CHECK_DELTA_TIME.bAddTime) + CorrectTime = CHECK_DELTA_TIME.DeltaTime + else + CorrectTime = - CHECK_DELTA_TIME.DeltaTime + global.DELTA_CURRENT_TIME += CorrectTime + this.ClearTimeStat() + SAVE_CONST(true) + } + } + } + SetNodePrioritet(Node, Prioritet) + { + if(Node.Prioritet === Prioritet) + return ; + if(Node.addrArr) + { + var Item = this.ActualNodes.find(Node); + if(Item) + { + this.ActualNodes.remove(Node) + Node.Prioritet = Prioritet + this.ActualNodes.insert(Node) + } + } + Node.Prioritet = Prioritet + } + AddNodeToActive(Node) + { + if(Node.addrArr) + { + if(CompareArr(Node.addrArr, this.addrArr) === 0) + { + return ; + } + this.CheckNodeMap(Node) + this.ActualNodes.insert(Node) + } + Node.ResetNode() + Node.Active = true + Node.NextConnectDelta = 1000 + if(!Node.FirstTime) + { + Node.FirstTime = GetCurrentTime() - 0 + Node.FirstTimeStr = "" + GetStrTimeUTC() + } + ADD_TO_STAT("AddToActive") + } + DeleteNodeFromActive(Node) + { + Node.Active = false + if(Node.Hot) + this.StartDisconnectHot(Node, "NotActive", 1) + Node.Hot = false + this.ActualNodes.remove(Node) + CloseSocket(Node.Socket, "DeleteNodeFromActive") + CloseSocket(Node.Socket2, "DeleteNodeFromActive") + Node.ResetNode() + Node.Socket = undefined + Node.Socket2 = undefined + } + StartReconnect() + { + return ; + var arr = this.GetActualNodes(); + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + if(Node.Socket && Node.Socket.ConnectToServer) + { + if(!Node.SocketStart) + Node.SocketStart = Date.now() + var DeltaTime = Date.now() - Node.SocketStart; + if(DeltaTime >= PERIOD_FOR_RECONNECT) + { + if(random(100) >= 90) + Node.CreateReconnection() + } + } + } + } + IsLocalIP(addr) + { + if(addr.substr(0, 7) === "192.168" || addr.substr(0, 3) === "10.") + return 1; + else + return 0; + } + GetActualsServerIP(bFlag) + { + var arr = this.GetActualNodes(); + var Str = ""; + arr.sort(function (a,b) + { + if(a.ip > b.ip) + return - 1; + else + if(a.ip < b.ip) + return 1; + else + return 0; + }) + if(bFlag) + return arr; + for(var i = 0; i < arr.length; i++) + { + Str += arr[i].ip + ", " + } + return Str.substr(0, Str.length - 2); + } + AddrLevelNode(Node) + { + if(Node.GrayConnect) + return MAX_LEVEL_SPECIALIZATION - 1; + return AddrLevelArr(this.addrArr, Node.addrArr); + } + GetNodesLevelCount() + { + var Count = 0; + for(var i = 0; i < this.LevelNodes.length; i++) + { + var arr = this.LevelNodes[i]; + for(var n = 0; arr && n < arr.length; n++) + if(arr[n].Hot) + { + Count++ + break; + } + } + return Count; + } + GetHotNodes() + { + var ArrNodes = []; + for(var L = 0; L < this.LevelNodes.length; L++) + { + var arr = this.LevelNodes[L]; + for(let j = 0; arr && j < arr.length; j++) + { + ArrNodes.push(arr[j]) + } + } + return ArrNodes; + } + DeleteNodeFromHot(Node) + { + if(Node.Hot) + { + Node.Hot = false + } + Node.CanHot = false + for(var i = 0; i < this.LevelNodes.length; i++) + { + var arr = this.LevelNodes[i]; + for(var n = 0; arr && n < arr.length; n++) + if(arr[n] === Node) + { + arr.splice(n, 1) + ADD_TO_STAT("DeleteLevelConnect") + ADD_TO_STAT("NETCONFIGURATION") + break; + } + } + } + DeleteAllNodesFromHot(Str) + { + for(var i = 0; i < this.LevelNodes.length; i++) + { + var arr = this.LevelNodes[i]; + for(var n = 0; arr && n < arr.length; n++) + { + var Node = arr[n]; + if(Node.Hot) + { + ADD_TO_STAT("DeleteAllNodesFromHot") + this.StartDisconnectHot(Node, Str, 1) + } + } + } + } + GetTransferTree() + { + var HotArr = []; + for(var Level = 0; Level < this.LevelNodes.length; Level++) + { + var arr = this.LevelNodes[Level]; + HotArr[Level] = [] + for(var n = 0; arr && n < arr.length; n++) + { + var Node = arr[n]; + if(Node) + { + Node.Hot = 1 + Node.Level = Level + HotArr[Level].push(Node) + } + } + } + var arr = this.NodesArr; + for(var n = 0; arr && n < arr.length; n++) + { + var Node = arr[n]; + if(!Node) + continue; + if(Node.Hot) + continue; + if(!this.IsCanConnect(Node)) + continue; + Node.Level = this.AddrLevelNode(Node) + if(!HotArr[Node.Level]) + HotArr[Node.Level] = [] + HotArr[Node.Level].push(Node) + } + return HotArr; + } + DetectGrayMode() + { + if(global.NET_WORK_MODE) + return ; + var CurTime = Date.now(); + var CountNodes = this.ActualNodes.size; + if(CountNodes || this.StopDetectGrayMode) + { + this.SetDirectMode() + this.StopDetectGrayMode = 1 + return ; + } + if(!this.LastNotZeroNodesTime) + this.LastNotZeroNodesTime = CurTime + var DeltaTime = CurTime - this.LastNotZeroNodesTime; + if(DeltaTime > 10 * 1000) + { + ToLog("DETECT GRAY MODE") + if(!global.NET_WORK_MODE) + { + global.NET_WORK_MODE = {ip:"", port:""} + } + global.NET_WORK_MODE.UseDirectIP = 0 + SAVE_CONST() + } + } + SetDirectMode() + { + var CountNodes = this.ActualNodes.size; + if(CountNodes && !global.NET_WORK_MODE) + { + global.NET_WORK_MODE = {ip:"", port:"30000"} + global.NET_WORK_MODE.UseDirectIP = 1 + SAVE_CONST() + } + } + StartCheckTransferTree() + { + var ArrTree = this.GetTransferTree(); + this.TransferTree = ArrTree + var CurTime = Date.now(); + if(GrayConnect()) + { + var MustCount = GetGrayServerConnections(); + if(this.ActualNodes.size < MustCount) + { + this.NodesArr.sort(SortNodeBlockProcessCountGray) + var WasDoConnect = 0; + var arr = this.NodesArr; + for(var n = 0; arr && n < arr.length; n++) + { + var Node = arr[n]; + if(!Node) + continue; + if(!this.IsCanConnect(Node)) + continue; + var DeltaTime = CurTime - Node.StartTimeConnect; + if(!Node.Active && WasDoConnect < 5 && !Node.WasAddToConnect && DeltaTime >= Node.NextConnectDelta) + { + AddNodeInfo(Node, "To connect") + Node.WasAddToConnect = 1 + ArrConnect.push(Node) + WasDoConnect++ + } + } + } + while(this.ActualNodes.size > MustCount) + { + var Node = this.ActualNodes.max(); + AddNodeInfo(Node, "DeleteFromActive") + this.DeleteNodeFromActive(Node) + } + } + else + { + this.DetectGrayMode() + for(var Level = 0; Level < ArrTree.length; Level++) + { + var arr = ArrTree[Level]; + if(!arr) + continue; + arr.sort(SortNodeBlockProcessCount) + var WasDoConnect = 0; + var WasDoHot = 0; + var length = Math.min(arr.length, 10); + for(var n = 0; n < length; n++) + { + var Node = arr[n]; + var DeltaTime = CurTime - Node.StartTimeConnect; + if(!Node.Active && WasDoConnect < 5 && !Node.WasAddToConnect && DeltaTime >= Node.NextConnectDelta) + { + AddNodeInfo(Node, "To connect") + Node.WasAddToConnect = 1 + ArrConnect.push(Node) + WasDoConnect++ + } + DeltaTime = CurTime - Node.StartTimeHot + if(Node.Active && !Node.Hot && WasDoHot < MIN_CONNECT_CHILD && DeltaTime > Node.NextHotDelta && !Node.GrayConnect) + { + AddNodeInfo(Node, "To hot level") + this.StartAddLevelConnect(Node) + Node.StartTimeHot = CurTime + Node.NextHotDelta = Node.NextHotDelta * 2 + WasDoHot++ + } + if(Node.Hot) + WasDoHot++ + } + this.CheckDisconnectHot(Level) + } + } + } + ValueToXOR(StrType, Str) + { + var Arr1 = toUTF8Array(Str); + var Arr2 = shaarr(this.CommonKey + ":" + this.addrStr + ":" + StrType); + return WALLET.XORHash(Arr1, Arr2, 32); + } + ValueFromXOR(Node, StrType, Arr1) + { + var Arr2 = shaarr(this.CommonKey + ":" + Node.addrStr + ":" + StrType); + var Arr = WALLET.XORHash(Arr1, Arr2, 32); + var Str = Utf8ArrayToStr(Arr); + return Str; + } + ValueToXORDevelop(StrName, Data, Type) + { + var Arr1; + if(Type === "uint") + { + Arr1 = [] + WriteUintToArr(Arr1, Data) + } + else + if(Type === "hash") + { + Arr1 = Data + } + else + if(Type === "str") + { + Arr1 = toUTF8Array(Data) + } + var Arr2 = shaarr(this.КодДляРазработчикаХекс + ":" + StrName); + return WALLET.XORHash(Arr1, Arr2, Arr1.length); + } + ValueFromXORDevelop(Node, StrName, Arr1, Type) + { + if(!Node.КодДляРазработчикаХекс) + { + Node.КодДляРазработчикаХекс = GetHexFromArr(WALLET.KeyPair.computeSecret(Node.PubKey, null)) + } + var Arr2 = shaarr(Node.КодДляРазработчикаХекс + ":" + StrName); + var Arr = WALLET.XORHash(Arr1, Arr2, Arr1.length); + if(Type === "uint") + { + return ReadUintFromArr(Arr, 0); + } + else + if(Type === "hash") + { + return Arr; + } + var Str = Utf8ArrayToStr(Arr); + return Str; + } +}; + +function SortNodeBlockProcessCount(a,b) +{ + if(b.BlockProcessCount !== a.BlockProcessCount) + return b.BlockProcessCount - a.BlockProcessCount; + if(a.DeltaTime !== b.DeltaTime) + return a.DeltaTime - b.DeltaTime; + return a.id - b.id; +}; + +function SortNodeBlockProcessCountGray(a,b) +{ + if(a.StartFindList !== b.StartFindList) + return a.StartFindList - b.StartFindList; + if(b.BlockProcessCount !== a.BlockProcessCount) + return b.BlockProcessCount - a.BlockProcessCount; + if(a.DeltaTime !== b.DeltaTime) + return a.DeltaTime - b.DeltaTime; + return a.id - b.id; +}; + +function GetGrayServerConnections() +{ + var Count = MAX_GRAY_CONNECTIONS_TO_SERVER; + if(SERVER.LoadHistoryMode && SERVER.LoadHistoryMessage) + Count = Count * 10; + return Count; +}; +global.GetGrayServerConnections = GetGrayServerConnections; +global.SortNodeBlockProcessCount = SortNodeBlockProcessCount; diff --git a/Source/core/constant.js b/Source/core/constant.js new file mode 100644 index 0000000..d453cb5 --- /dev/null +++ b/Source/core/constant.js @@ -0,0 +1,330 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.UPDATE_CODE_VERSION_NUM = 1131; +global.MIN_CODE_VERSION_NUM = 1114; +global.MINING_VERSION_NUM = 0; +global.InitParamsArg = InitParamsArg; +global.CONST_NAME_ARR = ["AUTO_CORRECT_TIME", "DELTA_CURRENT_TIME", "COMMON_KEY", "NODES_NAME", "SERVER_PRIVATE_KEY_HEX", "USE_NET_FOR_SERVER_ADDRES", +"NET_WORK_MODE", "STAT_MODE", "MAX_STAT_PERIOD", "LISTEN_IP", "HTTP_PORT_NUMBER", "HTTP_PORT_PASSWORD", "HTTP_IP_CONNECT", +"WALLET_NAME", "WALLET_DESCRIPTION", "USE_API_WALLET", "USE_API_V1", "USE_HARD_API_V2", "COUNT_VIEW_ROWS", "USE_HINT", "ALL_VIEW_ROWS", +"ALL_LOG_TO_CLIENT", "LOG_LEVEL", "USE_MINING", "MINING_START_TIME", "MINING_PERIOD_TIME", "POW_MAX_PERCENT", "COUNT_MINING_CPU", +"SIZE_MINING_MEMORY", "POW_RUN_COUNT", "USE_AUTO_UPDATE", "RESTART_PERIOD_SEC", "MAX_GRAY_CONNECTIONS_TO_SERVER", "TRANSACTION_PROOF_COUNT", +"UPDATE_NUM_COMPLETE", "LIMIT_SEND_TRAFIC", "WATCHDOG_DEV", "ADDRLIST_MODE", "CheckPointDelta", "MIN_VER_STAT", "DEBUG_WALLET", +"HTTP_HOSTING_PORT", "HTTPS_HOSTING_DOMAIN", "HTTP_MAX_COUNT_ROWS", "HTTP_ADMIN_PASSORD", "WATCHDOG_BADACCOUNT", "RESYNC_CONDITION", +"MAX_CONNECTIONS_COUNT", "TRUST_PROCESS_COUNT", "REST_START_COUNT", "LOAD_TO_BEGIN", ]; +global.MAX_LENGTH_SENDER_MAP = 3000; +global.DELTA_START_SENDER_MAP = 24; +global.NODES_DELTA_CALC_HOUR = 4; +global.USE_API_WALLET = 1; +global.USE_API_V1 = 1; +global.USE_HARD_API_V2 = 0; +global.USE_TICKET = 0; +global.USE_CHECK_SENDING = 1; +global.USE_LEVEL_WAY = 0; +global.TR_TICKET_HASH_LENGTH = 10; +global.BLOCKNUM_TICKET_ALGO = 16070000; +global.WATCHDOG_BADACCOUNT = 1; +global.WATCHDOG_DEV = 0; +global.RESYNC_CONDITION = {"OWN_BLOCKS":20, "K_POW":5}; +global.REST_BLOCK_SCALE = 1000; +global.REST_START_COUNT = 1000; +global.LOAD_TO_BEGIN = 2; +global.DEBUG_WALLET = 0; +global.CHECK_GLOBAL_TIME = 1; +global.AUTO_CORRECT_TIME = 1; +global.DELTA_CURRENT_TIME = 0; +global.NODES_NAME = ""; +global.COMMON_KEY = ""; +global.SERVER_PRIVATE_KEY_HEX = undefined; +global.USE_NET_FOR_SERVER_ADDRES = 1; +global.NET_WORK_MODE = undefined; +global.STAT_MODE = 0; +global.MAX_STAT_PERIOD = 1 * 3600; +global.UPDATE_NUM_COMPLETE = 0; +global.WALLET_NAME = "TERA"; +global.WALLET_DESCRIPTION = ""; +global.USE_MINING = 0; +global.POW_MAX_PERCENT = 50; +global.POW_RUN_COUNT = 5000; +global.POWRunPeriod = 1; +global.CheckPointDelta = 20; +global.ALL_LOG_TO_CLIENT = 1; +global.LOG_LEVEL = 1; +global.LIMIT_SEND_TRAFIC = 0; +global.COUNT_VIEW_ROWS = 20; +global.MIN_VER_STAT = 0; +global.STOPGETBLOCK = 0; +global.RESTART_PERIOD_SEC = 0; +global.HARD_PACKET_PERIOD120 = 160; +global.MINING_START_TIME = ""; +global.MINING_PERIOD_TIME = ""; +global.CHECK_RUN_MINING = 21 * 1000; +global.CHECK_STOP_CHILD_PROCESS = 10 * 1000; +global.COUNT_MINING_CPU = 0; +global.SIZE_MINING_MEMORY = 0; +global.HTTP_HOSTING_PORT = 0; +global.HTTPS_HOSTING_DOMAIN = ""; +global.HTTP_MAX_COUNT_ROWS = 20; +global.HTTP_ADMIN_PASSORD = ""; +require("./startlib.js"); +global.MIN_POWER_POW_HANDSHAKE = 12; +global.USE_HINT = 0; +global.ALL_VIEW_ROWS = 0; +global.COUNT_BLOCK_PROOF = 300; +global.COUNT_NODE_PROOF = 10; +global.MIN_POWER_POW_MSG = 2; +global.MEM_POOL_MSG_COUNT = 1000; +global.MAX_LEVEL_SPECIALIZATION = 24; +global.MIN_CONNECT_CHILD = 2; +global.MAX_CONNECT_CHILD = 7; +global.MAX_CONNECTIONS_COUNT = 1000; +global.TRUST_PROCESS_COUNT = 80000; +global.MAX_NODES_RETURN = 100; +global.MAX_WAIT_PERIOD_FOR_STATUS = 10 * 1000; +global.MAX_GRAY_CONNECTIONS_TO_SERVER = 10; +global.MAX_PACKET_LENGTH = 450 * 1024; +global.COUNT_BLOCKS_FOR_LOAD = 600; +global.TR_LEN = 100; +global.BLOCK_PROCESSING_LENGTH = 8; +global.BLOCK_PROCESSING_LENGTH2 = BLOCK_PROCESSING_LENGTH * 2; +global.CONSENSUS_PERIOD_TIME = 1000; +global.MAX_BLOCK_SIZE = 130 * 1024; +global.MAX_TRANSACTION_SIZE = 65535; +global.MIN_TRANSACTION_SIZE = 32; +global.MAX_TRANSACTION_COUNT = 2000; +global.MAX_TRANSACTION_LIMIT = 250; +global.MIN_POWER_POW_TR = 10; +if(global.MIN_POWER_POW_BL === undefined) + global.MIN_POWER_POW_BL = 5; +global.GENERATE_BLOCK_ACCOUNT = 0; +global.TOTAL_SUPPLY_TERA = 1e9; +global.TRANSACTION_PROOF_COUNT = 1000 * 1000; +global.MIN_POWER_POW_ACC_CREATE = 16; +global.START_MINING = 2 * 1000 * 1000; +global.REF_PERIOD_MINING = 1 * 1000 * 1000; +global.DELTA_BLOCK_ACCOUNT_HASH = 1000; +global.PERIOD_ACCOUNT_HASH = 50; +global.START_BLOCK_ACCOUNT_HASH = 14500000; +global.START_BLOCK_ACCOUNT_HASH3 = 24015000; +global.NEW_ACCOUNT_INCREMENT = 22305000; +global.NEW_BLOCK_REWARD1 = 22500000; +global.NEW_FORMULA_START = 32000000; +global.NEW_FORMULA_KTERA = 3; +global.NEW_FORMULA_TARGET1 = 43000000; +global.NEW_FORMULA_TARGET2 = 45000000; +global.BLOCK_COUNT_IN_MEMORY = 40; +global.HISTORY_BLOCK_COUNT = 40; +global.MAX_SIZE_LOG = 200 * 1024 * 1024; +global.READ_ONLY_DB = 0; +global.USE_CHECK_SAVE_DB = 1; +global.START_NETWORK_DATE = 1530446400000; +global.NETWORK = "TERA-MAIN"; +global.DEF_MAJOR_VERSION = "0001"; +global.SMART_BLOCKNUM_START = 10000000; +global.PRICE_DAO = function (BlockNum) +{ + return {NewAccount:10, NewSmart:100, NewTokenSmart:10000}; +}; +if(global.LOCAL_RUN) +{ + var Num = Date.now() - 300 * 1000; + global.START_NETWORK_DATE = Math.trunc(Num / 1000) * 1000; +} +global.NEW_SIGN_TIME = 25500000; +InitParamsArg(); +if(global.LOCAL_RUN) +{ + global.REST_BLOCK_SCALE = 100; + global.DELTA_BLOCK_ACCOUNT_HASH = 30; + global.PERIOD_ACCOUNT_HASH = 10; + global.START_BLOCK_ACCOUNT_HASH = 1; + global.START_BLOCK_ACCOUNT_HASH3 = 1; + global.BLOCKNUM_TICKET_ALGO = 1; + global.SMART_BLOCKNUM_START = 0; + global.START_MINING = 60; + global.REF_PERIOD_MINING = 10; + global.TEST_TRANSACTION_GENERATE = 0; + global.MIN_POWER_POW_TR = 8; + global.MIN_POWER_POW_ACC_CREATE = 8; + global.NEW_ACCOUNT_INCREMENT = 1; + global.NEW_BLOCK_REWARD1 = 1; + global.NEW_FORMULA_START = 1; + global.NEW_FORMULA_KTERA = 3; + global.NEW_FORMULA_TARGET1 = 0; + global.NEW_FORMULA_TARGET2 = 1; + NETWORK = "LOCAL"; + global.ALL_VIEW_ROWS = 1; + global.COUNT_NODE_PROOF = 1; + global.NEW_SIGN_TIME = 0; +} +else + if(global.TEST_NETWORK) + { + global.REST_BLOCK_SCALE = 100; + var Num = Date.now() - 50 * 1000; + console.log("CURRENT NUM: " + (Math.trunc(Num / 1000) * 1000)); + global.SMART_BLOCKNUM_START = 0; + global.START_NETWORK_DATE = 1550843168000 + 1000 * 1000; + global.START_MINING = 1000; + global.REF_PERIOD_MINING = 1000; + global.MIN_POWER_POW_ACC_CREATE = 8; + global.TRANSACTION_PROOF_COUNT = 200 * 1000; + global.MAX_SIZE_LOG = 20 * 1024 * 1024; + global.START_BLOCK_ACCOUNT_HASH = 1000; + global.START_BLOCK_ACCOUNT_HASH3 = 2356000; + global.BLOCKNUM_TICKET_ALGO = 1; + global.WALLET_NAME = "TEST"; + NETWORK = "TERA-TEST2"; + if(global.START_PORT_NUMBER === undefined) + global.START_PORT_NUMBER = 40000; + global.ALL_VIEW_ROWS = 1; + global.NEW_ACCOUNT_INCREMENT = 1903000; + global.NEW_BLOCK_REWARD1 = 1905000; + global.NEW_FORMULA_START = 11402000; + global.NEW_FORMULA_KTERA = 3; + global.NEW_FORMULA_TARGET1 = 11403000; + global.NEW_FORMULA_TARGET2 = 11405000; + global.COUNT_NODE_PROOF = 8; + global.REST_START_COUNT = 10000; + global.NEW_SIGN_TIME = 4859000; + global.MAX_LENGTH_SENDER_MAP = 100; + global.DELTA_START_SENDER_MAP = 12; + } +if(global.LOCAL_RUN) +{ + global.BLOCKNUM_TICKET_ALGO = 0; + global.MIN_POWER_POW_TR = 0; + global.AUTO_CORRECT_TIME = 0; + global.CHECK_GLOBAL_TIME = 0; +} +global.GetNetworkName = function () +{ + return NETWORK + "-" + DEF_MAJOR_VERSION; +}; +global.DEF_VERSION = DEF_MAJOR_VERSION + "." + UPDATE_CODE_VERSION_NUM; +global.DEF_CLIENT = "TERA-CORE"; +global.FIRST_TIME_BLOCK = START_NETWORK_DATE; +global.START_BLOCK_RUN = 0; +if(global.START_IP === undefined) + global.START_IP = ""; +if(global.LISTEN_IP === undefined) + global.LISTEN_IP = "0.0.0.0"; +if(global.START_PORT_NUMBER === undefined) + global.START_PORT_NUMBER = 30000; +if(global.HTTP_PORT_PASSWORD === undefined) + global.HTTP_PORT_PASSWORD = ""; +if(global.HTTP_IP_CONNECT === undefined) + global.HTTP_IP_CONNECT = ""; +if(global.USE_AUTO_UPDATE === undefined) + global.USE_AUTO_UPDATE = 1; +if(global.USE_PARAM_JS === undefined) + global.USE_PARAM_JS = 1; +if(global.DATA_PATH === undefined) + global.DATA_PATH = ""; +if(global.CREATE_ON_START === undefined) + global.CREATE_ON_START = false; +if(global.LOCAL_RUN === undefined) + global.LOCAL_RUN = 0; +if(global.CODE_PATH === undefined) + global.CODE_PATH = process.cwd(); +if(global.DEBUG_MODE === undefined) + global.DEBUG_MODE = 0; +global.DEBUG_MODE = 0; +if(typeof window === 'object') +{ + window.RUN_CLIENT = 0; + window.RUN_SERVER = 1; +} +global.RUN_CLIENT = 0; +global.RUN_SERVER = 1; + +function InitParamsArg() +{ + for(var i = 1; i < process.argv.length; i++) + { + var str0 = process.argv[i]; + var str = str0.toUpperCase(); + if(str.substr(0, 9) == "HTTPPORT:") + { + global.HTTP_PORT_NUMBER = parseInt(str.substr(9)); + } + else + if(str.substr(0, 9) == "PASSWORD:") + { + global.HTTP_PORT_PASSWORD = str0.substr(9); + } + else + if(str.substr(0, 5) == "PATH:") + global.DATA_PATH = str0.substr(5); + else + if(str.substr(0, 5) == "PORT:") + global.START_PORT_NUMBER = parseInt(str.substr(5)); + else + if(str.substr(0, 3) == "IP:") + global.START_IP = str.substr(3); + else + if(str.substr(0, 7) == "LISTEN:") + global.LISTEN_IP = str.substr(7); + else + if(str.substr(0, 8) == "HOSTING:") + { + global.HTTP_HOSTING_PORT = parseInt(str.substr(8)); + } + else + if(str.substr(0, 13) == "STARTNETWORK:") + { + global.START_NETWORK_DATE = parseInt(str.substr(13)); + console.log("START_NETWORK_DATE: " + START_NETWORK_DATE); + } + else + { + switch(str) + { + case "CHILDPOW": + global.CHILD_POW = true; + break; + case "ADDRLIST": + global.ADDRLIST_MODE = true; + break; + case "CREATEONSTART": + global.CREATE_ON_START = true; + break; + case "LOCALRUN": + global.LOCAL_RUN = 1; + break; + case "TESTRUN": + global.TEST_NETWORK = 1; + break; + case "NOLOCALRUN": + global.LOCAL_RUN = 0; + break; + case "NOAUTOUPDATE": + global.USE_AUTO_UPDATE = 0; + break; + case "NOPARAMJS": + global.USE_PARAM_JS = 0; + break; + case "READONLYDB": + global.READ_ONLY_DB = 1; + break; + case "NWMODE": + global.NWMODE = 1; + break; + case "NOALIVE": + global.NOALIVE = 1; + break; + case "DEV_MODE": + global.DEV_MODE = 1; + break; + } + } + } +}; diff --git a/Source/core/crypto-library.js b/Source/core/crypto-library.js new file mode 100644 index 0000000..30b2dbb --- /dev/null +++ b/Source/core/crypto-library.js @@ -0,0 +1,1106 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +require("./library.js"); +const crypto = require('crypto'); +global.MAX_SUPER_VALUE_POW = (1 << 30) * 2; +var BuferForStr = Buffer.alloc(32); +global.GetHexFromAddres = function (arr) +{ + if(!arr) + return ""; + if(arr.data !== undefined) + arr = arr.data; + for(var i = 0; i < 32; i++) + BuferForStr[i] = arr[i]; + return BuferForStr.toString('hex').toUpperCase(); +}; +global.GetArr32FromHex = function (Str) +{ + var array = new Uint8Array(32); + for(var i = 0; i < array.length; i++) + { + array[i] = parseInt(Str.substr(i * 2, 2), 16); + } + return array; +}; +global.GetAddresFromHex = GetArr32FromHex; +global.GetHexAddresFromPublicKey = function (arr) +{ + return Buffer.from(arr.slice(1)).toString('hex').toUpperCase(); +}; +global.GetHexFromArr = function (arr) +{ + if(!arr) + return ""; + else + return Buffer.from(arr).toString('hex').toUpperCase(); +}; + +function GetArrFromHex(Str) +{ + var array = []; + for(var i = 0; i < Str.length / 2; i++) + { + array[i] = parseInt(Str.substr(i * 2, 2), 16); + } + return array; +}; +global.GetArrFromHex = GetArrFromHex; +global.GetHexFromArrBlock = function (Arr,LenBlock) +{ + var Str = ""; + var Arr2 = []; + for(var i = 0; i < Arr.length; i++) + { + Arr2[i % LenBlock] = Arr[i]; + if(Arr2.length >= LenBlock) + { + Str += GetHexFromArr(Arr2) + "\n"; + Arr2 = []; + } + } + if(Arr2.length) + { + Str += GetHexFromArr(Arr2); + } + return Str; +}; +global.GetPublicKeyFromAddres = function (Arr) +{ + var RetArr = new Uint8Array(33); + RetArr[0] = 2; + for(var i = 1; i < 33; i++) + RetArr[i] = Arr[i - 1]; + return RetArr; +}; +global.CheckDevelopSign = function (SignArr,Sign) +{ + var hash = SHA3BUF(SignArr); + for(var i = 0; i < DEVELOP_PUB_KEY_ARR.length; i++) + { + var Result = secp256k1.verify(hash, Buffer.from(Sign), DEVELOP_PUB_KEY_ARR[i]); + if(Result) + return 1; + } + return 0; +}; +global.CheckContextSecret = function (Context,ContextAddrTo) +{ + if(ContextAddrTo.Secret === undefined) + { + if(ContextAddrTo.publickey === undefined) + { + ContextAddrTo.publickey = GetPublicKeyFromAddres(ContextAddrTo.addrArr); + } + ContextAddrTo.Secret = Context.KeyPair.computeSecret(ContextAddrTo.publickey, null); + } +}; +global.GetSignHash = function (Context,ContextAddrTo,Msg) +{ + CheckContextSecret(Context, ContextAddrTo); + if(typeof Msg === "string") + Msg = Buffer.from(Msg); + var Buf = Buffer.concat([Msg, ContextAddrTo.Secret], Msg.length + ContextAddrTo.Secret.length); + var Arr = shaarr(Buf); + return Arr; +}; +global.GetVerifyHash = function (Context,ContextAddr,Msg,Sign1) +{ + try + { + var Sign2 = GetSignHash(Context, ContextAddr, Msg); + for(var i = 0; i < Sign1.length; i++) + if(Sign1[i] !== Sign2[i]) + return false; + return true; + } + catch(e) + { + return false; + } +}; +global.GetKeyPair = function (password,secret,startnonce1,startnonce2) +{ + secret = secret || "low"; + startnonce1 = startnonce1 || 0; + startnonce2 = startnonce2 || 0; + var KeyPair = crypto.createECDH('secp256k1'); + var private1 = shaarr(password); + var private2 = private1; + var nonce1 = 0; + if(secret === "high") + for(nonce1 = startnonce1; nonce1 < 2000000000; nonce1++) + { + private1[31] = nonce1 & 0xFF; + private1[30] = (nonce1 >>> 8) & 0xFF; + private1[29] = (nonce1 >>> 16) & 0xFF; + private1[28] = (nonce1 >>> 24) & 0xFF; + private2 = shaarr(private1); + if(private2[0] === 0 && private2[1] === 0 && private2[2] === 0) + { + break; + } + nonce1++; + } + var nonce2; + for(nonce2 = startnonce2; nonce2 < 2000000000; nonce2++) + { + private2[31] = nonce2 & 0xFF; + private2[30] = (nonce2 >>> 8) & 0xFF; + private2[29] = (nonce2 >>> 16) & 0xFF; + private2[28] = (nonce2 >>> 24) & 0xFF; + KeyPair.setPrivateKey(Buffer.from(private2)); + var Data = KeyPair.getPublicKey('', 'compressed'); + if(Data[0] === 2 && Data[31] === 0 && Data[32] === 0) + { + KeyPair.nonce1 = nonce1; + KeyPair.nonce2 = nonce2; + KeyPair.PubKeyArr = KeyPair.getPublicKey('', 'compressed'); + KeyPair.addrArr = KeyPair.PubKeyArr.slice(1); + KeyPair.addrStr = GetHexFromArr(KeyPair.addrArr); + KeyPair.addr = KeyPair.addrArr; + return KeyPair; + } + nonce2++; + } + throw "ERROR. Key pair not found. Try another password!"; +}; +global.GetKeyPairTest = function (password,Power) +{ + var KeyPair = crypto.createECDH('secp256k1'); + var private2 = shaarr(password); + for(var nonce2 = 0; nonce2 < 1000000000; nonce2++) + { + private2[31] = nonce2 & 0xFF; + private2[30] = (nonce2 >> 8) & 0xFF; + private2[29] = (nonce2 >> 16) & 0xFF; + private2[28] = (nonce2 >> 24) & 0xFF; + KeyPair.setPrivateKey(Buffer.from(private2)); + var Data = KeyPair.getPublicKey('', 'compressed'); + if(Data[0] === 2) + { + if(Power) + { + var nBits = GetPowPower(Data.slice(1)); + if(nBits < Power) + continue; + } + KeyPair.PubKeyArr = Data; + KeyPair.addrArr = KeyPair.PubKeyArr.slice(1); + KeyPair.addrStr = GetHexFromArr(KeyPair.addrArr); + KeyPair.addr = KeyPair.addrArr; + return KeyPair; + } + } + throw "ERROR. Key pair not found. Try another password!"; +}; + +function GetArrFromValue(Num) +{ + var arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + arr[0] = Num & 0xFF; + arr[1] = (Num >>> 8) & 0xFF; + arr[2] = (Num >>> 16) & 0xFF; + arr[3] = (Num >>> 24) & 0xFF; + var NumH = Math.floor(Num / 4294967296); + arr[4] = NumH & 0xFF; + arr[5] = (NumH >>> 8) & 0xFF; + return arr; +}; + +function GetHashWithNonce(hash0,nonce) +{ + return shaarr2(hash0, GetArrFromValue(nonce)); +}; +global.GetHashWithValues = GetHashWithValues; + +function GetHashWithValues(hash0,value1,value2,bNotCopy) +{ + var hash; + if(bNotCopy) + hash = hash0; + else + hash = hash0.slice(); + hash[0] = value1 & 0xFF; + hash[1] = (value1 >>> 8) & 0xFF; + hash[2] = (value1 >>> 16) & 0xFF; + hash[3] = (value1 >>> 24) & 0xFF; + hash[4] = value2 & 0xFF; + hash[5] = (value2 >>> 8) & 0xFF; + hash[6] = (value2 >>> 16) & 0xFF; + hash[7] = (value2 >>> 24) & 0xFF; + var arrhash = shaarr(hash); + return arrhash; +}; + +function GetPowPower(arrhash) +{ + var SumBit = 0; + if(arrhash) + for(var i = 0; i < arrhash.length; i++) + { + var CurSum = Math.clz32(arrhash[i]) - 24; + SumBit += CurSum; + if(CurSum !== 8) + break; + } + return SumBit; +}; + +function GetPowValue(arrhash) +{ + var value = (arrhash[0] << 23) * 2 + (arrhash[1] << 16) + (arrhash[2] << 8) + arrhash[3]; + value = value * 256 + arrhash[4]; + value = value * 256 + arrhash[5]; + return value; +}; +global.CreateNoncePOWExtern = CreateNoncePOWExtern; + +function CreateNoncePOWExtern(arr0,BlockNum,count,startnone) +{ + var arr = []; + for(var i = 0; i < arr0.length; i++) + arr[i] = arr0[i]; + if(!startnone) + startnone = 0; + var maxnonce = 0; + var supervalue = MAX_SUPER_VALUE_POW; + for(var nonce = startnone; nonce <= startnone + count; nonce++) + { + var arrhash = GetHashWithValues(arr, nonce, BlockNum, true); + var value = GetPowValue(arrhash); + if(value < supervalue) + { + maxnonce = nonce; + supervalue = value; + } + } + return maxnonce; +}; +global.CreateNoncePOWExternMinPower = CreateNoncePOWExternMinPower; + +function CreateNoncePOWExternMinPower(arr0,BlockNum,MinPow) +{ + var arr = []; + for(var i = 0; i < arr0.length; i++) + arr[i] = arr0[i]; + var nonce = 0; + while(1) + { + var arrhash = GetHashWithValues(arr, nonce, BlockNum, true); + var power = GetPowPower(arrhash); + if(power >= MinPow) + { + return nonce; + } + nonce++; + } +}; +global.CreateNoncePOWInner = function (arr0,count) +{ + var Hash; + var arr = arr0.slice(); + var maxnonce = 0; + var supervalue = MAX_SUPER_VALUE_POW; + for(var nonce = 0; nonce < count; nonce++) + { + var hashTest = GetHashWithNonce(arr, nonce); + var value = GetPowValue(hashTest); + if(value < supervalue) + { + maxnonce = nonce; + supervalue = value; + Hash = hashTest; + } + } + return {nonce:maxnonce, Hash:Hash}; +}; +global.CreateAddrPOW = function (SeqHash,AddrArr,MaxHash,Start,CountNonce,BlockNum) +{ + var MaxNonce = 0; + var bFind = 0; + for(var nonce = Start; nonce < Start + CountNonce; nonce++) + { + AddrArr[6] = nonce & 0xFF; + AddrArr[7] = (nonce >>> 8) & 0xFF; + AddrArr[8] = (nonce >>> 16) & 0xFF; + AddrArr[9] = (nonce >>> 24) & 0xFF; + var HashTest = shaarrblock2(SeqHash, AddrArr, BlockNum); + if(CompareArr(MaxHash, HashTest) >= 0) + { + MaxHash = HashTest; + MaxNonce = nonce; + bFind = 1; + } + } + if(bFind) + { + AddrArr[6] = MaxNonce & 0xFF; + AddrArr[7] = (MaxNonce >>> 8) & 0xFF; + AddrArr[8] = (MaxNonce >>> 16) & 0xFF; + AddrArr[9] = (MaxNonce >>> 24) & 0xFF; + } + return {MaxHash:MaxHash, LastNonce:nonce, MaxNonce:MaxNonce, bFind:bFind}; +}; + +function IsZeroArr(arr) +{ + if(arr) + for(var i = 0; i < arr.length; i++) + { + if(arr[i]) + return false; + } + return true; +}; + +function CalcHashFromArray(ArrHashes,bOriginalSeq) +{ + if(bOriginalSeq === undefined) + ArrHashes.sort(CompareArr); + var Buf = []; + for(var i = 0; i < ArrHashes.length; i++) + { + var Value = ArrHashes[i]; + for(var n = 0; n < Value.length; n++) + Buf.push(Value[n]); + } + if(Buf.length === 0) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + else + if(Buf.length === 32) + return Buf; + var Hash = shaarr(Buf); + return Hash; +}; + +function CalcHash3FromArray(ArrHashes,bOriginalSeq) +{ + if(bOriginalSeq === undefined) + ArrHashes.sort(CompareArr); + var Buf = []; + for(var i = 0; i < ArrHashes.length; i++) + { + var Value = ArrHashes[i]; + for(var n = 0; n < Value.length; n++) + Buf.push(Value[n]); + } + if(Buf.length === 0) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + else + if(Buf.length === 32) + return Buf; + var Hash = sha3(Buf); + return Hash; +}; + +function CalcMerkl3FromArray(Arr,Tree0) +{ + var Tree, bSort; + if(Tree0) + { + bSort = 0; + Tree = Tree0; + } + else + { + bSort = 1; + Tree = {Levels:[], Full:true}; + } + Tree.Levels.push(Arr); + if(Arr.length < 2) + { + if(Arr.length === 0) + Tree.Root = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + else + { + if(Arr[0].length === 32) + Tree.Root = Arr[0]; + else + Tree.Root = sha3(Arr[0]); + } + return Tree; + } + if(bSort) + { + Arr.sort(CompareArr); + } + var Arr2 = []; + var len = Math.floor(Arr.length / 2); + for(var i = 0; i < len; i++) + { + var Hash = sha3arr2(Arr[i * 2], Arr[i * 2 + 1]); + Arr2.push(Hash); + } + if(len * 2 !== Arr.length) + { + Arr2.push(Arr[Arr.length - 1]); + } + return CalcMerkl3FromArray(Arr2, Tree); +}; + +function CalcMerkl0FromArray(Arr,Tree0) +{ + var Tree, bSort; + if(Tree0) + { + bSort = 0; + Tree = Tree0; + } + else + { + bSort = 1; + Tree = {Levels:[], Full:true}; + } + Tree.Levels.push(Arr); + if(Arr.length < 2) + { + if(Arr.length === 0) + Tree.Root = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + else + { + if(Arr[0].length === 32) + Tree.Root = Arr[0]; + else + Tree.Root = shaarr(Arr[0]); + } + return Tree; + } + if(bSort) + { + Arr.sort(CompareArr); + } + var Arr2 = []; + var len = Math.floor(Arr.length / 2); + for(var i = 0; i < len; i++) + { + var Hash = shaarr2(Arr[i * 2], Arr[i * 2 + 1]); + Arr2.push(Hash); + } + if(len * 2 !== Arr.length) + { + Arr2.push(Arr[Arr.length - 1]); + } + return CalcMerkl0FromArray(Arr2, Tree); +}; + +function CalcMerklFromArray(BlockNum,Arr) +{ + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + { + return CalcMerkl3FromArray(Arr); + } + else + { + return CalcMerkl0FromArray(Arr); + } +}; + +function CalcTreeHashFromArrBody(BlockNum,arrContent) +{ + if(arrContent) + { + var arrHASH = []; + for(var i = 0; i < arrContent.length; i++) + { + var HASH; + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + HASH = sha3(arrContent[i]); + else + HASH = shaarr(arrContent[i]); + arrHASH.push(HASH); + } + var Tree = CalcMerklFromArray(BlockNum, arrHASH); + return Tree.Root; + } + else + { + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } +}; + +function TestMerklTree() +{ + var h1 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + var h2 = sha3("2"); + var h3 = sha3("3"); + var h4 = sha3("4"); + var h5 = sha3("5"); + var Tree = {RecalcCount:0}; + var CalcMap = {}; + Tree.LevelsHash = [[h1, h2]]; + Tree.RecalcCount = 0; + CalcMap[0] = 1; + CalcMap[1] = 1; + UpdateMerklTree(Tree, CalcMap, 0); + CalcMap = {}; + Tree.LevelsHash[0] = [h1, h2, h3, h4]; + Tree.RecalcCount = 0; + CalcMap[2] = 1; + CalcMap[3] = 1; + UpdateMerklTree(Tree, CalcMap, 0); + CalcMap = {}; + Tree.LevelsHash[0] = [h1, h2, h3]; + Tree.RecalcCount = 0; + CalcMap[Tree.LevelsHash[0].length - 1] = 1; + UpdateMerklTree(Tree, CalcMap, 0); + ToLog("Root=" + GetHexFromArr(Tree.Root)); + ToLog("RecalcCount=" + Tree.RecalcCount); + return Tree; +}; +if(0) + setTimeout(function () + { + TestMerkleProof(100); + ToLog("=========END========="); + process.exit(0); + }); + +function TestMerkleProof(CountTest) +{ + for(var R = 0; R < CountTest; R++) + { + var CountItems = Math.floor(Math.random() * CountTest); + var Tree = {RecalcCount:0}; + var CalcMap = {}; + Tree.LevelsHash = []; + Tree.LevelsHash[0] = []; + for(var i = 0; i < CountItems; i++) + { + CalcMap[i] = 1; + Tree.LevelsHash[0][i] = sha3("" + i + "-" + R); + } + UpdateMerklTree(Tree, CalcMap, 0); + ToLog("Hash1=" + GetHexFromArr(Tree.Root) + " CountItems:" + CountItems); + var FirstNum = Math.floor(Math.random() * CountItems / 2); + var LastNum = Math.floor(CountItems / 2 + Math.random() * CountItems / 2); + var Ret = GetMerkleProof(Tree.LevelsHash, FirstNum, LastNum); + var ArrM = Tree.LevelsHash[0].slice(FirstNum, LastNum + 1); + var Hash2 = CheckMerkleProof(Ret.ArrL, ArrM, Ret.ArrR); + ToLog("Hash2=" + GetHexFromArr(Hash2) + " FirstNum=" + FirstNum + "/" + LastNum); + if(CompareArr(Tree.Root, Hash2) !== 0) + throw ("=========ERROR HASHTEST=============="); + else + ToLog("=========OK=============="); + } +}; + +function GetMerkleProof(LevelsHash,FirstNum,LastNum) +{ + var ArrL = []; + var ArrR = []; + var CurL = FirstNum; + var CurR = LastNum; + for(var L = 0; L < LevelsHash.length; L++) + { + var LevelArr = LevelsHash[L]; + if(CurL % 2 === 1) + ArrL[L] = LevelArr[CurL - 1]; + if(CurR % 2 === 0 && CurR + 1 < LevelArr.length) + ArrR[L] = LevelArr[CurR + 1]; + CurL = Math.floor(CurL / 2); + CurR = Math.floor(CurR / 2); + } + return {ArrL:ArrL, ArrR:ArrR}; +}; + +function CheckMerkleProof(ArrL,ArrM,ArrR) +{ + var L = 0; + var Arr2 = ArrM; + while(true) + { + var Arr = [].concat(Arr2); + if(ArrL[L]) + Arr.unshift(ArrL[L]); + if(ArrR[L]) + Arr.push(ArrR[L]); + if(Arr.length <= 1 && L >= ArrL.length && L >= ArrR.length) + { + if(!Arr.length) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + return Arr[0]; + } + var length2 = Math.floor(Arr.length / 2); + Arr2 = []; + for(i = 0; i < length2; i++) + { + var Hash = sha3(arr2(Arr[i * 2], Arr[i * 2 + 1])); + Arr2.push(Hash); + } + if(Arr.length / 2 > length2) + Arr2.push(Arr[Arr.length - 1]); + L++; + } +}; + +function UpdateMerklTree(Tree,CalcMap,NumLevel) +{ + var HashArr = Tree.LevelsHash[NumLevel]; + if(!HashArr || !HashArr.length) + { + Tree.LevelsHash.length = NumLevel + 1; + Tree.MaxLevel = NumLevel; + Tree.Root = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + } + else + if(HashArr.length === 1) + { + Tree.LevelsHash.length = NumLevel + 1; + Tree.MaxLevel = NumLevel; + Tree.Root = HashArr[0]; + } + else + { + var CalcMap2 = {}; + var HashArr2 = Tree.LevelsHash[NumLevel + 1]; + if(!HashArr2) + { + HashArr2 = []; + Tree.LevelsHash[NumLevel + 1] = HashArr2; + } + var len2 = Math.floor(HashArr.length / 2); + HashArr2.length = Math.floor(0.5 + HashArr.length / 2); + var Count = 0; + var LastIndex = HashArr.length - 1; + for(var key in CalcMap) + { + var i2 = Math.floor(key / 2); + if(i2 < len2) + { + Count++; + CalcMap2[i2] = 1; + HashArr2[i2] = sha3(arr2(HashArr[i2 * 2], HashArr[i2 * 2 + 1])); + } + else + { + if(key > LastIndex) + { + CalcMap2[i2] = 1; + } + else + if(i2 === len2) + { + Count++; + CalcMap2[i2] = 1; + HashArr2[i2] = HashArr[key]; + } + } + } + if(Count) + { + Tree.RecalcCount += Count; + UpdateMerklTree(Tree, CalcMap2, NumLevel + 1); + } + } +}; +var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649, 0, 2147516545, 2147483648, 32777, +2147483648, 138, 0, 136, 0, 2147516425, 0, 2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771, 2147483648, +32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648, 2147516545, 2147483648, 32896, 2147483648, 2147483649, +0, 2147516424, 2147483648]; + +function Mesh(s,Count) +{ + var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, + b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33, b34, b35, b36, b37, b38, b39, b40, + b41, b42, b43, b44, b45, b46, b47, b48, b49; + for(n = 0; n < Count; n += 2) + { + c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40]; + c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41]; + c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42]; + c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43]; + c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44]; + c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45]; + c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46]; + c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47]; + c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48]; + c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49]; + h = c8 ^ ((c2 << 1) | (c3 >>> 31)); + l = c9 ^ ((c3 << 1) | (c2 >>> 31)); + s[0] ^= h; + s[1] ^= l; + s[10] ^= h; + s[11] ^= l; + s[20] ^= h; + s[21] ^= l; + s[30] ^= h; + s[31] ^= l; + s[40] ^= h; + s[41] ^= l; + h = c0 ^ ((c4 << 1) | (c5 >>> 31)); + l = c1 ^ ((c5 << 1) | (c4 >>> 31)); + s[2] ^= h; + s[3] ^= l; + s[12] ^= h; + s[13] ^= l; + s[22] ^= h; + s[23] ^= l; + s[32] ^= h; + s[33] ^= l; + s[42] ^= h; + s[43] ^= l; + h = c2 ^ ((c6 << 1) | (c7 >>> 31)); + l = c3 ^ ((c7 << 1) | (c6 >>> 31)); + s[4] ^= h; + s[5] ^= l; + s[14] ^= h; + s[15] ^= l; + s[24] ^= h; + s[25] ^= l; + s[34] ^= h; + s[35] ^= l; + s[44] ^= h; + s[45] ^= l; + h = c4 ^ ((c8 << 1) | (c9 >>> 31)); + l = c5 ^ ((c9 << 1) | (c8 >>> 31)); + s[6] ^= h; + s[7] ^= l; + s[16] ^= h; + s[17] ^= l; + s[26] ^= h; + s[27] ^= l; + s[36] ^= h; + s[37] ^= l; + s[46] ^= h; + s[47] ^= l; + h = c6 ^ ((c0 << 1) | (c1 >>> 31)); + l = c7 ^ ((c1 << 1) | (c0 >>> 31)); + s[8] ^= h; + s[9] ^= l; + s[18] ^= h; + s[19] ^= l; + s[28] ^= h; + s[29] ^= l; + s[38] ^= h; + s[39] ^= l; + s[48] ^= h; + s[49] ^= l; + b0 = s[0]; + b1 = s[1]; + b32 = (s[11] << 4) | (s[10] >>> 28); + b33 = (s[10] << 4) | (s[11] >>> 28); + b14 = (s[20] << 3) | (s[21] >>> 29); + b15 = (s[21] << 3) | (s[20] >>> 29); + b46 = (s[31] << 9) | (s[30] >>> 23); + b47 = (s[30] << 9) | (s[31] >>> 23); + b28 = (s[40] << 18) | (s[41] >>> 14); + b29 = (s[41] << 18) | (s[40] >>> 14); + b20 = (s[2] << 1) | (s[3] >>> 31); + b21 = (s[3] << 1) | (s[2] >>> 31); + b2 = (s[13] << 12) | (s[12] >>> 20); + b3 = (s[12] << 12) | (s[13] >>> 20); + b34 = (s[22] << 10) | (s[23] >>> 22); + b35 = (s[23] << 10) | (s[22] >>> 22); + b16 = (s[33] << 13) | (s[32] >>> 19); + b17 = (s[32] << 13) | (s[33] >>> 19); + b48 = (s[42] << 2) | (s[43] >>> 30); + b49 = (s[43] << 2) | (s[42] >>> 30); + b40 = (s[5] << 30) | (s[4] >>> 2); + b41 = (s[4] << 30) | (s[5] >>> 2); + b22 = (s[14] << 6) | (s[15] >>> 26); + b23 = (s[15] << 6) | (s[14] >>> 26); + b4 = (s[25] << 11) | (s[24] >>> 21); + b5 = (s[24] << 11) | (s[25] >>> 21); + b36 = (s[34] << 15) | (s[35] >>> 17); + b37 = (s[35] << 15) | (s[34] >>> 17); + b18 = (s[45] << 29) | (s[44] >>> 3); + b19 = (s[44] << 29) | (s[45] >>> 3); + b10 = (s[6] << 28) | (s[7] >>> 4); + b11 = (s[7] << 28) | (s[6] >>> 4); + b42 = (s[17] << 23) | (s[16] >>> 9); + b43 = (s[16] << 23) | (s[17] >>> 9); + b24 = (s[26] << 25) | (s[27] >>> 7); + b25 = (s[27] << 25) | (s[26] >>> 7); + b6 = (s[36] << 21) | (s[37] >>> 11); + b7 = (s[37] << 21) | (s[36] >>> 11); + b38 = (s[47] << 24) | (s[46] >>> 8); + b39 = (s[46] << 24) | (s[47] >>> 8); + b30 = (s[8] << 27) | (s[9] >>> 5); + b31 = (s[9] << 27) | (s[8] >>> 5); + b12 = (s[18] << 20) | (s[19] >>> 12); + b13 = (s[19] << 20) | (s[18] >>> 12); + b44 = (s[29] << 7) | (s[28] >>> 25); + b45 = (s[28] << 7) | (s[29] >>> 25); + b26 = (s[38] << 8) | (s[39] >>> 24); + b27 = (s[39] << 8) | (s[38] >>> 24); + b8 = (s[48] << 14) | (s[49] >>> 18); + b9 = (s[49] << 14) | (s[48] >>> 18); + s[0] = b0 ^ (~b2 & b4); + s[1] = b1 ^ (~b3 & b5); + s[10] = b10 ^ (~b12 & b14); + s[11] = b11 ^ (~b13 & b15); + s[20] = b20 ^ (~b22 & b24); + s[21] = b21 ^ (~b23 & b25); + s[30] = b30 ^ (~b32 & b34); + s[31] = b31 ^ (~b33 & b35); + s[40] = b40 ^ (~b42 & b44); + s[41] = b41 ^ (~b43 & b45); + s[2] = b2 ^ (~b4 & b6); + s[3] = b3 ^ (~b5 & b7); + s[12] = b12 ^ (~b14 & b16); + s[13] = b13 ^ (~b15 & b17); + s[22] = b22 ^ (~b24 & b26); + s[23] = b23 ^ (~b25 & b27); + s[32] = b32 ^ (~b34 & b36); + s[33] = b33 ^ (~b35 & b37); + s[42] = b42 ^ (~b44 & b46); + s[43] = b43 ^ (~b45 & b47); + s[4] = b4 ^ (~b6 & b8); + s[5] = b5 ^ (~b7 & b9); + s[14] = b14 ^ (~b16 & b18); + s[15] = b15 ^ (~b17 & b19); + s[24] = b24 ^ (~b26 & b28); + s[25] = b25 ^ (~b27 & b29); + s[34] = b34 ^ (~b36 & b38); + s[35] = b35 ^ (~b37 & b39); + s[44] = b44 ^ (~b46 & b48); + s[45] = b45 ^ (~b47 & b49); + s[6] = b6 ^ (~b8 & b0); + s[7] = b7 ^ (~b9 & b1); + s[16] = b16 ^ (~b18 & b10); + s[17] = b17 ^ (~b19 & b11); + s[26] = b26 ^ (~b28 & b20); + s[27] = b27 ^ (~b29 & b21); + s[36] = b36 ^ (~b38 & b30); + s[37] = b37 ^ (~b39 & b31); + s[46] = b46 ^ (~b48 & b40); + s[47] = b47 ^ (~b49 & b41); + s[8] = b8 ^ (~b0 & b2); + s[9] = b9 ^ (~b1 & b3); + s[18] = b18 ^ (~b10 & b12); + s[19] = b19 ^ (~b11 & b13); + s[28] = b28 ^ (~b20 & b22); + s[29] = b29 ^ (~b21 & b23); + s[38] = b38 ^ (~b30 & b32); + s[39] = b39 ^ (~b31 & b33); + s[48] = b48 ^ (~b40 & b42); + s[49] = b49 ^ (~b41 & b43); + s[0] ^= RC[n]; + s[1] ^= RC[n + 1]; + } +}; + +function WriteNumToArr0(body,Num) +{ + body[0] ^= Num & 0xFF; + body[1] ^= (Num >>> 8) & 0xFF; + body[2] ^= (Num >>> 16) & 0xFF; + body[3] ^= (Num >>> 24) & 0xFF; +}; + +function ClientHex(Str,nonce) +{ + var arr = [0, 0, 0, 0]; + for(var i = 0; i < Str.length; i++) + arr[4 + i] = Str.charCodeAt(i); + WriteNumToArr0(arr, nonce); + Mesh(arr, 60); + return GetHexFromArr(arr) + "-" + nonce; +}; +global.ClientHex = ClientHex; +var GlobalCryptID = 0; +var DeltaTimeCryptID = new Date(2018, 1, 1) - 0; + +function Encrypt(Arr,Arr2,ArrSecret) +{ + const StartLen = 9; + var arrRnd = Buffer.alloc(StartLen); + GlobalCryptID++; + arrRnd.writeUInt32LE(GlobalCryptID, 1, 4); + var Time = Math.floor((Date.now() - DeltaTimeCryptID) / 1000); + arrRnd.writeUInt32LE(Time, 5, 4); + Arr2[0] = Arr[0]; + for(var i = 1; i < StartLen; i++) + Arr2[i] = arrRnd[i]; + var SecretBuf = Buffer.concat([Arr2.slice(0, StartLen), ArrSecret]); + DoSecret(Arr, Arr2, SecretBuf, 9); +}; + +function Decrypt(Arr,Arr2,ArrSecret) +{ + const StartLen = 9; + var SecretBuf = Buffer.concat([Arr.slice(0, StartLen), ArrSecret]); + DoSecret(Arr, Arr2, SecretBuf, StartLen); +}; + +function DoSecret(Arr,Arr2,SecretBuf,StartLen) +{ + var CryptID = SecretBuf.readUInt32LE(1, 4); + var Pos = StartLen; + while(Pos < Arr.length) + { + var CurBuf = shaarr(SecretBuf); + for(var i = 0; i < 32 && Pos < Arr.length; i++, Pos++) + { + Arr2[Pos] = Arr[Pos] ^ CurBuf[i]; + } + CryptID++; + SecretBuf.writeUInt32LE(CryptID, 5, 4); + } +}; + +function TestEncryptDecrypt() +{ + var ArrSecret = Buffer.from([1, 1, 1, 1, 1, 1]); + var Arr = GetArrFromStr(" Secret message", 64); + var Arr2 = Buffer.from(new Uint8Array(Arr.length)); + var Arr3 = Buffer.from(new Uint8Array(Arr.length)); + console.log("Message:"); + console.log(Arr); + console.log("-------------------"); + Encrypt(Arr, Arr2, ArrSecret); + console.log("Encrypt:"); + console.log(Arr2); + console.log("-------------------"); + Decrypt(Arr2, Arr3, ArrSecret); + console.log("Decrypt:"); + console.log(Utf8ArrayToStr(Arr3.slice(9))); +}; + +function toUTF8Array(str) +{ + var utf8 = []; + for(var i = 0; str && i < str.length; i++) + { + var charcode = str.charCodeAt(i); + if(charcode < 0x80) + utf8.push(charcode); + else + if(charcode < 0x800) + { + utf8.push(0xc0 | (charcode >> 6), 0x80 | (charcode & 0x3f)); + } + else + if(charcode < 0xd800 || charcode >= 0xe000) + { + utf8.push(0xe0 | (charcode >> 12), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f)); + } + else + { + i++; + charcode = 0x10000 + (((charcode & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff)); + utf8.push(0xf0 | (charcode >> 18), 0x80 | ((charcode >> 12) & 0x3f), 0x80 | ((charcode >> 6) & 0x3f), 0x80 | (charcode & 0x3f)); + } + } + return utf8; +}; + +function Utf8ArrayToStr(array) +{ + var out, i, len, c; + var char2, char3; + out = ""; + len = array.length; + i = 0; + while(i < len) + { + c = array[i++]; + switch(c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + out += String.fromCharCode(c); + break; + case 12: + case 13: + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); + break; + case 14: + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); + break; + } + } + for(var i = 0; i < out.length; i++) + { + if(out.charCodeAt(i) === 0) + { + out = out.substr(0, i); + break; + } + } + return out; +}; + +function GetArr32FromStr(Str) +{ + return GetArrFromStr(Str, 32); +}; + +function GetArrFromStr(Str,Len) +{ + var arr = toUTF8Array(Str); + for(var i = arr.length; i < Len; i++) + { + arr[i] = 0; + } + return arr.slice(0, Len); +}; +global.CalcHash3FromArray = CalcHash3FromArray; +global.CalcHashFromArray = CalcHashFromArray; +global.CalcMerklFromArray = CalcMerklFromArray; +global.CalcTreeHashFromArrBody = CalcTreeHashFromArrBody; +global.UpdateMerklTree = UpdateMerklTree; +global.GetMerkleProof = GetMerkleProof; +global.CheckMerkleProof = CheckMerkleProof; +global.IsZeroArr = IsZeroArr; +global.GetHashWithNonce = GetHashWithNonce; +global.GetPowPower = GetPowPower; +global.GetArrFromValue = GetArrFromValue; +global.GetPowValue = GetPowValue; +global.Mesh = Mesh; +global.Encrypt = Encrypt; +global.Decrypt = Decrypt; +global.toUTF8Array = toUTF8Array; +global.Utf8ArrayToStr = Utf8ArrayToStr; +global.GetArrFromStr = GetArrFromStr; +global.IsDeveloperAccount = function (Key) +{ + for(var i = 0; i < global.DEVELOP_PUB_KEY_ARR.length; i++) + if(CompareArr(Key, global.DEVELOP_PUB_KEY_ARR[i]) === 0) + { + return 1; + } + return 0; +}; +global.DEVELOP_PUB_KEY_ARR = ["0222263942b11f7a78e11c43903094f870a2bf728bc8be085023b4eaf4e6228cd9", "02250c8c8f7f7e1468cdc5e9b9203841ba14250e2f480f77ebcd502f3e691506d8", +"027c0caec4e6f8f08d220f6dab24bb6f53d3f1d7b95231216805d9fac85d34a95f", "025defd5b884cc02f6948cd0d62b03d7b7445bf942206ab935158b6be8f0f7a2ce", +"024a97d69cd81c965f162b4b8b7b506263bc8aec8dbcea9eec59f73b5b470a0be1", ]; +for(var i = 0; i < DEVELOP_PUB_KEY_ARR.length; i++) + DEVELOP_PUB_KEY_ARR[i] = Buffer.from(GetArrFromHex(DEVELOP_PUB_KEY_ARR[i])); +global.DEVELOP_PUB_KEY = DEVELOP_PUB_KEY_ARR[0]; +global.DEVELOP_PUB_KEY0 = Buffer.from(GetArrFromHex("022e80aa78bc07c72781fac12488096f0bfa7b4f48fbab0f2a92e208d1ee3654df")); +if(LOCAL_RUN) +{ + global.DEVELOP_PUB_KEY0 = Buffer.from(GetArrFromHex("026A04AB98D9E4774AD806E302DDDEB63BEA16B5CB5F223EE77478E861BB583EB3")); + global.DEVELOP_PUB_KEY = global.DEVELOP_PUB_KEY0; +} +global.ARR_PUB_KEY = ["027ae0dce92d8be1f893525b226695ddf0fe6ad756349a76777ff51f3b59067d70", "02769165a6f9950d023a415ee668b80bb96b5c9ae2035d97bdfb44f356175a44ff", +"021566c6feb5495594fc4bbea27795e1db5a663b3fe81ea9846268d5c394e24c23", "0215accbc993e67216c9b7f3912b29b91671864e861e61ab73589913c946839efa", +"0270e0c5acb8eefe7faddac45503da4885e02fb554975d12907f6c33ac6c6bdba5", "0202f2aad628f46d433faf70ba6bf12ab9ed96a46923b592a72508dc43af36cb80", +"0254f373fc44ac4a3e80ec8cb8cc3693a856caa82e0493067bdead78ce8ec354b8", "027617f9511b0b0cdbda8f3e17907732731296321846f3fd6fd19460f7227d9482", +]; +if(global.TEST_NETWORK || LOCAL_RUN) +{ + for(var i = 0; i < 100; i++) + global.ARR_PUB_KEY[i] = GetHexFromArr(DEVELOP_PUB_KEY0); + global.DEVELOP_PUB_KEY_ARR = [DEVELOP_PUB_KEY0]; + global.DEVELOP_PUB_KEY = DEVELOP_PUB_KEY_ARR[0]; +} diff --git a/Source/core/db/block-db.js b/Source/core/db/block-db.js new file mode 100644 index 0000000..3165832 --- /dev/null +++ b/Source/core/db/block-db.js @@ -0,0 +1,1193 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +const DBLib = require("./db"); +const DBRow = require("./db-row"); +global.BlockDB = new DBLib(); +global.BLOCK_HEADER_SIZE = 150; +const FILE_NAME_HEADER = "block-header"; +const FILE_NAME_BODY = "block-body"; +const FORMAT_STREAM_HEADER = "{\ + VersionDB:byte,\ + TreeHash:hash,\ + AddrHash:hash,\ + PrevHash:hash,\ + SumHash:hash,\ + SumPow:uint,\ + TrDataPos:uint,\ + TrDataLen:uint32,\ + Reserv500:uint\ + }"; +const WorkStructStreamHeader = {}; +global.BLOCK_HEADER_SIZE2 = 6; +const FORMAT_HEADER_VERSION2 = "{FilePos:uint}"; +const FILE_NAME_HEADER2 = "block-header2"; +const WorkStructHeader2 = {}; +const DEFAULT_DB_VERSION = 2; +module.exports = class CDB extends require("../code") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + var bWriteMode = (global.PROCESS_NAME === "MAIN"); + global.DB_VERSION = DEFAULT_DB_VERSION + var FileItem1 = BlockDB.OpenDBFile(FILE_NAME_HEADER, bWriteMode); + var FileItem2 = BlockDB.OpenDBFile(FILE_NAME_HEADER2, bWriteMode); + if(FileItem2.size) + global.DB_VERSION = 2 + else + if(FileItem1.size) + global.DB_VERSION = 1 + BlockDB.OpenDBFile(FILE_NAME_BODY, bWriteMode) + this.DBHeader100 = new DBRow("block-header100", 32 + 32, "{Hash100:hash,Hash:hash}", !bWriteMode) + this.BlockNumDB = 0 + this.BlockNumDBMin = 0 + this.ClearBufMap() + setTimeout(function () + { + SERVER.ReadStateTX() + }, 10) + } + ReadStateTX() + { + var StateTX = DApps.Accounts.DBStateTX.Read(0); + if(StateTX) + { + this.BlockNumDBMin = StateTX.BlockNumMin + } + } + LoadMemBlocksOnStart() + { + this.CurrentBlockNum = GetCurrentBlockNumByTime() + for(var i = this.BlockNumDB - BLOCK_COUNT_IN_MEMORY; i <= this.BlockNumDB; i++) + if(i >= 0) + { + if(i >= this.BlockNumDB - BLOCK_PROCESSING_LENGTH * 5) + this.GetBlock(i, true, true) + else + this.GetBlock(i, true, false) + } + } + GetMaxNumBlockDB() + { + var FileItem, BlockNum; + if(global.DB_VERSION === 2) + { + FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER2) + BlockNum = (FileItem.size / BLOCK_HEADER_SIZE2) - 1 + } + else + { + FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER) + BlockNum = (FileItem.size / BLOCK_HEADER_SIZE) - 1 + } + return BlockNum; + } + FindStartBlockNum() + { + this.ReadStateTX() + var BlockNum = this.GetMaxNumBlockDB(); + if(global.NO_CHECK_BLOCKNUM_ONSTART) + { + this.BlockNumDB = this.CheckBlocksOnStartFoward(BlockNum - 2, 0) + ToLog("START_BLOCK_NUM:" + this.BlockNumDB, 2) + return ; + } + BlockNum = this.CheckBlocksOnStartReverse(BlockNum) + this.BlockNumDB = this.CheckBlocksOnStartFoward(BlockNum - 2000, 0) + this.BlockNumDB = this.CheckBlocksOnStartFoward(this.BlockNumDB - 100, 1) + if(this.BlockNumDB >= BLOCK_PROCESSING_LENGTH2) + { + this.TruncateBlockDB(this.BlockNumDB) + } + ToLog("START_BLOCK_NUM:" + this.BlockNumDB, 2) + this.CheckOnStartComplete = 1 + } + CheckBlocksOnStartReverse(StartNum) + { + var delta = 1; + var Count = 0; + var PrevBlock; + for(var num = StartNum; num >= this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2; num -= delta) + { + var Block = this.ReadBlockHeaderDB(num); + if(!Block || IsZeroArr(Block.SumHash)) + { + delta++ + Count = 0 + continue; + } + var PrevBlock = this.ReadBlockHeaderDB(num - 1); + if(!PrevBlock || IsZeroArr(PrevBlock.SumHash)) + { + Count = 0 + continue; + } + var SumHash = shaarr2(PrevBlock.SumHash, Block.Hash); + if(CompareArr(SumHash, Block.SumHash) === 0) + { + delta = 1 + Count++ + if(Count > COUNT_BLOCKS_FOR_LOAD / 10) + return num; + } + else + { + delta++ + Count = 0 + } + } + return 0; + } + CheckBlocksOnStartFoward(StartNum, bCheckBody) + { + var PrevBlock; + if(StartNum < this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2) + StartNum = this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2 + var MaxNum = DApps.Accounts.GetHashedMaxBlockNum(); + var BlockNumTime = GetCurrentBlockNumByTime(); + if(BlockNumTime < MaxNum) + MaxNum = BlockNumTime + var arr = []; + for(var num = StartNum; num <= MaxNum; num++) + { + var Block; + if(bCheckBody) + Block = this.ReadBlockDB(num) + else + Block = this.ReadBlockHeaderDB(num) + if(!Block) + return num > 0 ? num - 1 : 0; + if(num % 100000 === 0) + ToLog("CheckBlocksOnStartFoward: " + num) + if(bCheckBody) + { + var TreeHash = CalcTreeHashFromArrBody(Block.BlockNum, Block.arrContent); + if(CompareArr(Block.TreeHash, TreeHash) !== 0) + { + ToLog("BAD TreeHash block=" + Block.BlockNum) + return num > 0 ? num - 1 : 0; + } + } + if(PrevBlock) + { + if(arr.length !== BLOCK_PROCESSING_LENGTH) + { + var start = num - BLOCK_PROCESSING_LENGTH2; + for(var n = 0; n < BLOCK_PROCESSING_LENGTH; n++) + { + var Prev = this.ReadBlockHeaderDB(start + n); + arr.push(Prev.Hash) + } + } + else + { + arr.shift() + var Prev = this.ReadBlockHeaderDB(num - BLOCK_PROCESSING_LENGTH - 1); + arr.push(Prev.Hash) + } + var PrevHash = CalcHashFromArray(arr, true); + var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash); + var Value = GetHashFromSeqAddr(SeqHash, Block.AddrHash, Block.BlockNum, PrevHash); + if(CompareArr(Value.Hash, Block.Hash) !== 0) + { + ToLog("=================== FIND ERR Hash in " + Block.BlockNum + " bCheckBody=" + bCheckBody) + return num > 0 ? num - 1 : 0; + } + var SumHash = shaarr2(PrevBlock.SumHash, Block.Hash); + if(CompareArr(SumHash, Block.SumHash) !== 0) + { + ToLog("=================== FIND ERR SumHash in " + Block.BlockNum) + return num > 0 ? num - 1 : 0; + } + } + PrevBlock = Block + } + return num > 0 ? num - 1 : 0; + } + WriteBlockDB(Block) + { + var startTime = process.hrtime(); + if(Block.TrCount === 0 && !IsZeroArr(Block.TreeHash)) + { + ToLogTrace("ERROR WRITE TrCount BLOCK:" + Block.BlockNum) + throw "ERROR WRITE"; + } + var Ret = this.WriteBodyDB(Block); + if(Ret) + { + Ret = this.WriteBlockDBFinaly(Block) + } + ADD_TO_STAT_TIME("MAX:WriteBlockDB", startTime) + ADD_TO_STAT_TIME("WriteBlockDB", startTime) + return Ret; + } + WriteBlockDBFinaly(Block) + { + var Ret = this.WriteBlockHeaderDB(Block); + if(Ret) + { + if(Block.TrDataLen === 0 && !IsZeroArr(Block.TreeHash)) + { + ToLogTrace("ERROR WRITE FINAL TrDataLen BLOCK") + throw "ERROR WRITE"; + } + this.OnWriteBlock(Block) + if(Block.BlockNum > this.BlockNumDBMin) + this.BlockNumDB = Block.BlockNum + Block.bSave = true + } + return Ret; + } + PreSaveDataTreeToDB(Block) + { + var Ret = this.WriteBodyDB(Block); + if(Ret) + { + Ret = this.WriteBlockHeaderDB(Block, 1) + } + return Ret; + } + WriteBodyResultDB(Block) + { + var arrTr = Block.arrContentResult; + if(Block.TrDataPos && Block.TrDataLen && Block.VersionBody && arrTr && arrTr.length) + { + var FileItem = BlockDB.OpenDBFile(FILE_NAME_BODY, 1); + var FD = FileItem.fd; + var Size = arrTr.length * 6; + var Position = Block.TrDataPos + Block.TrDataLen - Size; + if(FileItem.size < Position + Size) + { + TO_ERROR_LOG("DB", 241, "Error Position in WriteBodyResultDB on block: " + Block.BlockNum) + return false; + } + var BufWrite = BufLib.GetNewBuffer(Size); + for(var i = 0; i < arrTr.length; i++) + { + BufWrite.Write(arrTr[i], "uint") + } + var written = fs.writeSync(FD, BufWrite, 0, BufWrite.length, Position); + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB", 242, "Error write to file block-chain : " + written + " <> " + BufWrite.length) + return false; + } + } + return true; + } + WriteBodyDB(Block) + { + var FileItem = BlockDB.OpenDBFile(FILE_NAME_BODY, 1); + var FD = FileItem.fd; + var Position = FileItem.size; + Block.TrDataPos = Position + var arrTr = Block.arrContent; + if(!arrTr || arrTr.length === 0) + { + Block.TrCount = 0 + Block.TrDataLen = 0 + return true; + } + var TrDataLen = 4; + var arrSize = []; + for(var i = 0; i < arrTr.length; i++) + { + var body = arrTr[i]; + arrSize[i] = 2 + body.length + TrDataLen += arrSize[i] + } + Block.VersionBody = 1 + TrDataLen += arrTr.length * 6 + var BufWrite = BufLib.GetNewBuffer(TrDataLen); + BufWrite.Write(arrTr.length, "uint16") + BufWrite.Write(Block.VersionBody, "uint16") + for(var i = 0; i < arrTr.length; i++) + { + var body = arrTr[i]; + BufWrite.Write(body, "tr") + } + var written = fs.writeSync(FD, BufWrite, 0, BufWrite.length, Position); + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB", 240, "Error write to file block-chain : " + written + " <> " + BufWrite.length) + return false; + } + FileItem.size += TrDataLen + Block.TrCount = arrTr.length + Block.TrDataLen = TrDataLen + return true; + } + WriteBlockHeaderDB(Block, bPreSave) + { + if(!bPreSave && Block.BlockNum > this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2) + { + if(USE_CHECK_SAVE_DB) + if(!this.CheckSeqHashDB(Block, "WriteBlockHeaderDB")) + return false; + this.WriteBlockHeader100(Block) + this.TruncateBlockDBInner(Block) + this.BlockNumDB = Block.BlockNum - 1 + var PrevBlock = this.ReadBlockHeaderDB(Block.BlockNum - 1); + if(!PrevBlock) + { + ToLogTrace("Cant write header block:" + Block.BlockNum + " prev block not found") + throw "ERR: PREV BLOCK NOT FOUND"; + return false; + } + Block.SumHash = shaarr2(PrevBlock.SumHash, Block.Hash) + Block.SumPow = PrevBlock.SumPow + GetPowPower(Block.PowHash) + } + if(global.DB_VERSION === 2) + { + return this.WriteBlockHeaderToFile2(Block); + } + var BufWrite = BufLib.GetNewBuffer(BLOCK_HEADER_SIZE); + this.BlockHeaderToBuf(BufWrite, Block) + var Res = this.WriteBufHeaderToFile1(BufWrite, Block.BlockNum); + return Res; + } + WriteBlockHeaderToFile2(Block) + { + var BufWrite, FileItem, written; + var BlockNum = Math.trunc(Block.BlockNum); + this.ClearBufMap() + Block.VersionDB = global.DB_VERSION + BufWrite = BufLib.GetBufferFromObject(Block, FORMAT_STREAM_HEADER, 200, WorkStructStreamHeader) + FileItem = BlockDB.OpenDBFile(FILE_NAME_BODY, 1) + if(!Block.FilePos) + { + if(!FileItem.size) + FileItem.size = 100 + Block.FilePos = FileItem.size + } + written = fs.writeSync(FileItem.fd, BufWrite, 0, BufWrite.length, Block.FilePos) + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB", 242, "Error write to file block-chain : " + written + " <> " + BufWrite.length) + return false; + } + if(Block.FilePos >= FileItem.size) + { + FileItem.size = Block.FilePos + BufWrite.length + } + FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER2, 1) + var Position = BlockNum * BLOCK_HEADER_SIZE2; + BufWrite = BufLib.GetBufferFromObject(Block, FORMAT_HEADER_VERSION2, BLOCK_HEADER_SIZE2, WorkStructHeader2) + written = fs.writeSync(FileItem.fd, BufWrite, 0, BufWrite.length, Position) + if(Position >= FileItem.size) + { + FileItem.size = Position + BufWrite.length + } + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB", 262, "Error write to file block-header :" + written + " <> " + BufWrite.length) + return false; + } + else + { + return true; + } + } + WriteBufHeaderToFile1(BufWrite, BlockNum) + { + BlockNum = Math.trunc(BlockNum) + this.ClearBufMap() + var FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER, 1); + var Position = BlockNum * BLOCK_HEADER_SIZE; + var written = fs.writeSync(FileItem.fd, BufWrite, 0, BufWrite.length, Position); + if(Position >= FileItem.size) + { + FileItem.size = Position + BufWrite.length + } + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB", 260, "Error write to file block-header :" + written + " <> " + BufWrite.length) + return false; + } + else + { + return true; + } + } + WriteBlockHeader100(Block) + { + return ; + if(Block.BlockNum % 100 !== 0) + return ; + var Hash100; + var Num = Block.BlockNum / 100; + if(Num <= 0) + { + Hash100 = [] + } + else + { + var PrevHash100 = []; + var PrevData = this.DBHeader100.Read(Num - 1); + if(PrevData) + { + PrevHash100 = PrevData.Hash100 + } + else + if(Num > 1) + { + ToLog("NOT FIND PEVHASH100 BlockNum=" + Block.BlockNum, 2) + return ; + } + Hash100 = sha3arr2(PrevHash100, Block.Hash) + } + this.DBHeader100.Write({Num:Num, Hash100:Hash100, Hash:Block.Hash}) + } + TruncateBlockDBInner100(LastBlock) + { + return ; + this.DBHeader100.Truncate(Math.trunc(LastBlock.BlockNum / 100)) + } + ReadBlockDB(Num) + { + if(!Num) + Num = 0 + Num = Math.trunc(Num) + var Block = this.ReadBlockHeaderDB(Num); + if(Block && Block.TrDataLen) + { + var Ret = this.ReadBlockBodyDB(Block); + if(!Ret) + return undefined; + } + else + { + if(Block && !IsZeroArr(Block.TreeHash)) + { + ToLogTrace("ERROR arrContent on BlockNum=" + Num) + return undefined; + } + } + return Block; + } + ReadBlockBodyDB(Block) + { + var FileItem = BlockDB.OpenDBFile(FILE_NAME_BODY); + if(Block.TrDataLen > MAX_BLOCK_SIZE * 2) + { + ToLogTrace("Error value TrDataLen, BlockNum=" + Block.BlockNum) + return false; + } + var Position = Block.TrDataPos; + var BufRead = BufLib.GetNewBuffer(Block.TrDataLen); + var bytesRead = fs.readSync(FileItem.fd, BufRead, 0, BufRead.length, Position); + if(bytesRead !== BufRead.length) + { + TO_ERROR_LOG("DB", 272, "Error read block-body file: " + FileItem.name + " from POS:" + Position + " bytesRead=" + bytesRead + " of " + BufRead.length + " BlockNum=" + Block.BlockNum) + return false; + } + Block.arrContent = [] + Block.arrContentResult = [] + var TrCount = BufRead.Read("uint16"); + Block.VersionBody = BufRead.Read("uint16") + if(TrCount <= MAX_TRANSACTION_COUNT) + { + for(var i = 0; i < TrCount; i++) + { + var body = BufRead.Read("tr"); + if(!body) + break; + Block.arrContent[i] = body + } + if(Block.VersionBody === 1) + { + var Size = TrCount * 6; + BufRead.len = Block.TrDataLen - Size + for(var i = 0; i < TrCount; i++) + { + Block.arrContentResult[i] = BufRead.Read("uint") + } + } + } + Block.TrCount = Block.arrContent.length + return true; + } + ReadBlockHeaderDB(BlockNum) + { + if(global.DB_VERSION === 2) + { + return this.ReadBlockHeaderDB2(BlockNum); + } + if(BlockNum < 0) + { + return undefined; + } + BlockNum = Math.trunc(BlockNum) + var Block, BufRead, FileItem, bytesRead, Position; + BufRead = BufLib.GetNewBuffer(BLOCK_HEADER_SIZE) + Position = BlockNum * BLOCK_HEADER_SIZE + var FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER); + bytesRead = fs.readSync(FileItem.fd, BufRead, 0, BufRead.length, Position) + if(bytesRead !== BufRead.length) + return undefined; + Block = this.BufToBlockHeader(BufRead, BlockNum) + if(Block) + { + Block.bSave = true + Block.Prepared = true + } + return Block; + } + ReadBlockHeaderDB2(BlockNum) + { + var Block, BufRead, FileItem, bytesRead, Position; + if(BlockNum < 0) + { + return undefined; + } + BlockNum = Math.trunc(BlockNum) + Position = BlockNum * BLOCK_HEADER_SIZE2 + FileItem = BlockDB.OpenDBFile(FILE_NAME_HEADER2) + BufRead = BufLib.GetNewBuffer(BLOCK_HEADER_SIZE2) + bytesRead = fs.readSync(FileItem.fd, BufRead, 0, BufRead.length, Position) + if(bytesRead !== BufRead.length) + return undefined; + Block = BufLib.GetObjectFromBuffer(BufRead, FORMAT_HEADER_VERSION2, WorkStructHeader2) + if(!Block.FilePos) + { + return undefined; + } + Position = Block.FilePos + FileItem = BlockDB.OpenDBFile(FILE_NAME_BODY) + BufRead = BufLib.GetNewBuffer(200) + bytesRead = fs.readSync(FileItem.fd, BufRead, 0, BufRead.length, Position) + Block = BufLib.GetObjectFromBuffer(BufRead, FORMAT_STREAM_HEADER, WorkStructStreamHeader) + if(Block.VersionDB !== global.DB_VERSION) + { + throw ("ERROR Block.VersionDB"); + return undefined; + } + Block.FilePos = Position + Block.VersionDB = global.DB_VERSION + Block.bSave = true + Block.Prepared = true + return this.PrepareBlockFields(Block, BlockNum); + } + ReadBlockHeaderFromMapDB(BlockNum) + { + var Block = this.MapHeader[BlockNum]; + if(!Block) + { + Block = this.ReadBlockHeaderDB(BlockNum) + this.MapHeader[BlockNum] = Block + } + return Block; + } + SetTruncateBlockDB(BlockNum) + { + BlockNum = Math.trunc(BlockNum) + if(BlockNum < BLOCK_PROCESSING_LENGTH2) + BlockNum = BLOCK_PROCESSING_LENGTH2 + if(this.UseTruncateBlockDB) + { + if(BlockNum < this.UseTruncateBlockDB) + this.UseTruncateBlockDB = BlockNum + } + else + { + this.UseTruncateBlockDB = BlockNum + } + } + TruncateBlockDB(LastBlockNum) + { + this.UseTruncateBlockDB = undefined + var Block = this.ReadBlockDB(LastBlockNum); + if(!Block) + { + ToLog("************ ERROR TruncateBlockDB - not found block=" + LastBlockNum, 2) + return ; + } + this.WriteBlockDB(Block) + } + TruncateBlockDBInner(LastBlock) + { + var FItem1, size; + if(global.DB_VERSION === 2) + { + FItem1 = BlockDB.OpenDBFile(FILE_NAME_HEADER2, 1) + size = (LastBlock.BlockNum + 1) * BLOCK_HEADER_SIZE2 + } + else + { + FItem1 = BlockDB.OpenDBFile(FILE_NAME_HEADER, 1) + size = (LastBlock.BlockNum + 1) * BLOCK_HEADER_SIZE + } + if(size < 0) + size = 0 + if(FItem1.size > size) + { + FItem1.size = size + fs.ftruncateSync(FItem1.fd, FItem1.size) + } + this.TruncateStat(LastBlock.BlockNum) + this.TruncateBlockDBInner100(LastBlock) + } + ClearDataBase() + { + if(global.TX_PROCESS && global.TX_PROCESS.RunRPC) + global.TX_PROCESS.RunRPC("ClearDataBase", {}) + var FItem1 = BlockDB.OpenDBFile(FILE_NAME_HEADER, 1); + FItem1.size = 0 + fs.ftruncateSync(FItem1.fd, FItem1.size) + var FItem12 = BlockDB.OpenDBFile(FILE_NAME_HEADER2, 1); + FItem12.size = 0 + fs.ftruncateSync(FItem12.fd, FItem12.size) + var FItem2 = BlockDB.OpenDBFile(FILE_NAME_BODY, 1); + FItem2.size = 0 + fs.ftruncateSync(FItem2.fd, FItem2.size) + this.DBHeader100.Truncate( - 1) + global.DB_VERSION = DEFAULT_DB_VERSION + this.BlockNumDB = 0 + this.BlockNumDBMin = 0 + this.ClearBufMap() + this.ClearStat() + this.CreateGenesisBlocks() + this.StartSyncBlockchain() + } + ClearBufMap() + { + this.MapHeader = {} + } + Close() + { + this.ClearBufMap() + this.ReadStateTX() + BlockDB.CloseDBFile(FILE_NAME_HEADER) + BlockDB.CloseDBFile(FILE_NAME_HEADER2) + BlockDB.CloseDBFile(FILE_NAME_BODY) + this.DBHeader100.Close() + } + RewriteAllTransactions() + { + if(TX_PROCESS.Worker) + { + if(global.TX_PROCESS && global.TX_PROCESS.RunRPC) + { + global.TX_PROCESS.RunRPC("RewriteAllTransactions", {}) + return 1; + } + } + return 0; + } + BlockHeaderToBuf(BufWrite, Block) + { + Block.Reserv500 = 0 + var len = BufWrite.len; + BufWrite.Write(Block.TreeHash, "hash") + BufWrite.Write(Block.AddrHash, "hash") + BufWrite.Write(Block.PrevHash, "hash") + BufWrite.Write(Block.SumHash, "hash") + BufWrite.Write(Block.SumPow, "uint") + BufWrite.Write(Block.Reserv500, "uint") + BufWrite.Write(Block.TrDataPos, "uint") + BufWrite.Write(Block.TrDataLen, "uint32") + BufWrite.len = len + BLOCK_HEADER_SIZE + } + BufToBlockHeader(BufRead, Num) + { + var Block = {}; + var len = BufRead.len; + Block.TreeHash = BufRead.Read("hash") + Block.AddrHash = BufRead.Read("hash") + Block.PrevHash = BufRead.Read("hash") + Block.SumHash = BufRead.Read("hash") + Block.SumPow = BufRead.Read("uint") + Block.Reserv500 = BufRead.Read("uint") + Block.TrDataPos = BufRead.Read("uint") + Block.TrDataLen = BufRead.Read("uint32") + Block.TrCount = 0 + BufRead.len = len + BLOCK_HEADER_SIZE + return this.PrepareBlockFields(Block, Num); + } + PrepareBlockFields(Block, Num) + { + Block.AddInfo = AddInfoBlock.bind(Block) + Block.Info = "" + Block.BlockNum = Num + Block.SeqHash = this.GetSeqHash(Block.BlockNum, Block.PrevHash, Block.TreeHash) + if(Block.BlockNum >= BLOCK_PROCESSING_LENGTH2) + { + CalcHashBlockFromSeqAddr(Block) + } + else + { + Block.Hash = this.GetHashGenesis(Block.BlockNum) + Block.PowHash = Block.Hash + } + Block.Power = GetPowPower(Block.PowHash) + if(IsZeroArr(Block.Hash)) + return undefined; + return Block; + } + GetRows(start, count, Filter, bMinerName) + { + if(Filter) + { + Filter = Filter.trim() + Filter = Filter.toUpperCase() + } + var MaxAccount = DApps.Accounts.GetMaxAccount(); + var WasError = 0; + var arr = []; + for(var num = start; true; num++) + { + var Block = this.ReadBlockHeaderDB(num); + if(!Block) + break; + Block.Num = Block.BlockNum + if(Block.AddrHash) + { + Block.Miner = ReadUintFromArr(Block.AddrHash, 0) + if(Block.BlockNum < 16 || Block.Miner > MaxAccount) + Block.Miner = 0 + if(bMinerName) + { + Block.MinerName = "" + if(Block.Miner) + { + var Item = DApps.Accounts.ReadState(Block.Miner); + if(Item && Item.Name) + Block.MinerName = Item.Name.substr(0, 8) + } + } + var Value = GetHashFromSeqAddr(Block.SeqHash, Block.AddrHash, Block.BlockNum, Block.PrevHash); + Block.Hash1 = Value.Hash1 + Block.Hash2 = Value.Hash2 + } + if(Filter) + { + var Num = Block.BlockNum; + var Bytes = Block.TrDataLen; + var Pow = Block.Power; + var Miner = Block.Miner; + var Date = DateFromBlock(Block.BlockNum); + try + { + if(!eval(Filter)) + continue; + } + catch(e) + { + if(!WasError) + ToLog(e) + WasError = 1 + } + } + arr.push(Block) + count-- + if(count < 1) + break; + } + return arr; + } + GetTrRows(BlockNum, start, count) + { + var arr = []; + var Block = this.ReadBlockDB(BlockNum); + if(Block && Block.arrContent) + for(var num = start; num < start + count; num++) + { + if(num < 0) + continue; + if(num >= Block.arrContent.length) + break; + var Tr = {body:Block.arrContent[num]}; + this.CheckCreateTransactionObject(Tr, 1) + Tr.Num = num + Tr.Type = Tr.body[0] + Tr.Length = Tr.body.length + Tr.Body = [] + for(var j = 0; j < Tr.body.length; j++) + Tr.Body[j] = Tr.body[j] + var App = DAppByType[Tr.Type]; + if(App) + { + Tr.Script = App.GetScriptTransaction(Tr.body) + if(BlockNum >= this.BlockNumDBMin) + Tr.Verify = App.GetVerifyTransaction(Block, BlockNum, Tr.Num, Tr.body) + else + Tr.Verify = 0 + if(Tr.Verify >= 1) + { + Tr.VerifyHTML = "" + if(Tr.Verify > 1) + { + Tr.VerifyHTML += "(" + Tr.Verify + ")" + } + } + else + if(Tr.Verify == - 1) + Tr.VerifyHTML = "" + else + Tr.VerifyHTML = "" + } + else + { + Tr.Script = "" + Tr.VerifyHTML = "" + } + arr.push(Tr) + } + return arr; + } + ClearStat() + { + var MAX_ARR_PERIOD = MAX_STAT_PERIOD * 2 + 10; + this.StatMap = {StartPos:0, StartBlockNum:0, Length:0, "ArrPower":new Float64Array(MAX_ARR_PERIOD), "ArrPowerMy":new Float64Array(MAX_ARR_PERIOD), + } + } + TruncateStat(LastBlockNum) + { + if(this.StatMap) + { + var LastNumStat = this.StatMap.StartBlockNum + this.StatMap.Length; + var Delta = LastNumStat - LastBlockNum; + if(Delta > 0) + { + this.StatMap.Length -= Delta + if(this.StatMap.Length < 0) + this.StatMap.Length = 0 + } + this.StatMap.CaclBlockNum = 0 + } + } + GetStatBlockchainPeriod(Param) + { + var StartNum = Param.BlockNum; + if(!Param.Count || Param.Count < 0) + Param.Count = 1000 + if(!Param.Miner) + Param.Miner = 0 + if(!Param.Adviser) + Param.Adviser = 0 + var Map = {}; + var ArrList = new Array(Param.Count); + var i = 0; + for(var num = StartNum; num < StartNum + Param.Count; num++) + { + var Power = 0, PowerMy = 0, Nonce = 0; + if(num <= this.BlockNumDB) + { + var Block = this.ReadBlockHeaderDB(num); + if(Block) + { + Power = GetPowPower(Block.PowHash) + var Miner = ReadUintFromArr(Block.AddrHash, 0); + var Nonce = ReadUintFromArr(Block.AddrHash, 6); + if(Param.Miner < 0) + { + if(Param.Adviser) + { + var Adviser = DApps.Accounts.GetAdviserByMiner(Map, Miner); + if(Adviser === Param.Adviser) + PowerMy = Power + } + else + { + PowerMy = Power + } + } + else + if(Miner === Param.Miner && !Param.bMinerLess) + { + PowerMy = Power + } + else + if(Miner <= Param.Miner && Param.bMinerLess) + { + PowerMy = Power + } + } + } + ArrList[i] = PowerMy + if(Param.bNonce && PowerMy) + ArrList[i] = Nonce + i++ + } + var AvgValue = 0; + for(var j = 0; j < ArrList.length; j++) + { + if(ArrList[j]) + AvgValue += ArrList[j] + } + if(ArrList.length > 0) + AvgValue = AvgValue / ArrList.length + const MaxSizeArr = 1000; + var StepTime = 1; + while(ArrList.length >= MaxSizeArr) + { + if(Param.bNonce) + ArrList = ResizeArrMax(ArrList) + else + ArrList = ResizeArrAvg(ArrList) + StepTime = StepTime * 2 + } + return {ArrList:ArrList, AvgValue:AvgValue, steptime:StepTime}; + } + GetStatBlockchain(name, MinLength) + { + if(!MinLength) + return []; + var MAX_ARR_PERIOD = MAX_STAT_PERIOD * 2 + 10; + if(!this.StatMap) + { + this.ClearStat() + } + var MaxNumBlockDB = this.GetMaxNumBlockDB(); + if(this.StatMap.CaclBlockNum !== MaxNumBlockDB || this.StatMap.CalcMinLength !== MinLength) + { + this.StatMap.CaclBlockNum = MaxNumBlockDB + this.StatMap.CalcMinLength = MinLength + var start = MaxNumBlockDB - MinLength + 1; + var finish = MaxNumBlockDB + 1; + var StartPos = this.StatMap.StartPos; + var ArrPower = this.StatMap.ArrPower; + var ArrPowerMy = this.StatMap.ArrPowerMy; + var StartNumStat = this.StatMap.StartBlockNum; + var FinishNumStat = this.StatMap.StartBlockNum + this.StatMap.Length - 1; + var CountReadDB = 0; + var arr = new Array(MinLength); + var arrmy = new Array(MinLength); + for(var num = start; num < finish; num++) + { + var i = num - start; + var i2 = (StartPos + i) % MAX_ARR_PERIOD; + if(num >= StartNumStat && num <= FinishNumStat && (num < finish - 10)) + { + arr[i] = ArrPower[i2] + arrmy[i] = ArrPowerMy[i2] + } + else + { + CountReadDB++ + var Power = 0, PowerMy = 0; + if(num <= MaxNumBlockDB) + { + var Block = this.ReadBlockHeaderDB(num); + if(Block) + { + Power = GetPowPower(Block.PowHash) + var Miner = ReadUintFromArr(Block.AddrHash, 0); + if(Miner === GENERATE_BLOCK_ACCOUNT) + { + PowerMy = Power + } + } + } + arr[i] = Power + arrmy[i] = PowerMy + ArrPower[i2] = arr[i] + ArrPowerMy[i2] = arrmy[i] + if(num > FinishNumStat) + { + this.StatMap.StartBlockNum = num - this.StatMap.Length + this.StatMap.Length++ + if(this.StatMap.Length > MAX_ARR_PERIOD) + { + this.StatMap.Length = MAX_ARR_PERIOD + this.StatMap.StartBlockNum++ + this.StatMap.StartPos++ + } + } + } + } + this.StatMap["POWER_BLOCKCHAIN"] = arr + this.StatMap["POWER_MY_WIN"] = arrmy + } + var arr = this.StatMap[name]; + if(!arr) + arr = [] + var arrT = this.StatMap["POWER_BLOCKCHAIN"]; + for(var i = 0; i < arrT.length; i++) + if(!arrT[i]) + { + this.StatMap = undefined + break; + } + return arr; + } + GetHashGenesis(Num) + { + return [Num + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Num + 1]; + } + GetSeqHash(BlockNum, PrevHash, TreeHash) + { + var arr = [GetArrFromValue(BlockNum), PrevHash, TreeHash]; + var SeqHash = CalcHashFromArray(arr, true); + return SeqHash; + } + CheckCreateTicketObject(Tr, BlockNum, SetTxID) + { + if(!Tr.HashPow && Tr.HashTicket) + { + Tr.num = BlockNum + var FullHashTicket = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for(var i = 0; i < global.TR_TICKET_HASH_LENGTH; i++) + FullHashTicket[i] = Tr.HashTicket[i] + WriteUintToArrOnPos(FullHashTicket, Tr.num, global.TR_TICKET_HASH_LENGTH) + Tr.HashPow = sha3(FullHashTicket) + Tr.power = GetPowPower(Tr.HashPow) + Tr.TimePow = Tr.power + if(SetTxID) + Tr.TxID = GetHexFromArr(FullHashTicket.slice(0, TR_TICKET_HASH_LENGTH + 6)) + } + } + CheckCreateTransactionObject(Tr, SetTxID) + { + if(!Tr.HashPow) + { + var Body = Tr.body; + Tr.IsTx = 1 + Tr.num = ReadUintFromArr(Body, Body.length - 12) + if(Tr.num >= global.BLOCKNUM_TICKET_ALGO) + Tr.HASH = sha3(Body) + else + Tr.HASH = shaarr(Body) + Tr.HashTicket = Tr.HASH.slice(0, global.TR_TICKET_HASH_LENGTH) + this.CheckCreateTicketObject(Tr, Tr.num, SetTxID) + } + } + BlockChainToBuf(WriteNum, StartNum, EndBlockNum) + { + if(StartNum === undefined) + return BufLib.GetNewBuffer(10); + var GetLength = EndBlockNum - StartNum + 1; + var arr = []; + var arr0 = this.PrevBlockChainArr; + if(arr0 && arr0.length) + { + var Block = arr0[arr0.length - 1]; + if(Block.BlockNum >= StartNum && Block.BlockNum <= EndBlockNum) + { + var BlockDB = this.ReadBlockHeaderDB(Block.BlockNum); + if(!BlockDB || CompareArr(Block.SumHash, BlockDB.SumHash) !== 0) + { + arr0 = undefined + } + else + { + } + } + } + var i0 = 0; + for(var num = StartNum; num <= EndBlockNum; num++) + { + var Block = undefined; + if(arr0) + { + for(var i = i0; i < arr0.length; i++) + { + i0 = i + var Block0 = arr0[i]; + if(Block0.BlockNum === num) + { + Block = Block0 + break; + } + } + } + if(!Block) + Block = this.ReadBlockHeaderDB(num) + if(!Block || !Block.Prepared || !Block.Hash) + break; + arr.push(Block) + } + this.PrevBlockChainArr = arr + return this.ArrHeaderToBuf(WriteNum, arr); + } + ArrHeaderToBuf(StartNum, arr) + { + var CountSend = arr.length - BLOCK_PROCESSING_LENGTH2; + var BufWrite; + if(CountSend <= 0) + { + BufWrite = BufLib.GetNewBuffer(10) + } + else + { + var BufSize = 6 + 4 + BLOCK_PROCESSING_LENGTH2 * 32 + 32 + 6 + CountSend * 64; + BufWrite = BufLib.GetNewBuffer(BufSize) + BufWrite.Write(StartNum, "uint") + BufWrite.Write(CountSend, "uint32") + for(var i = 0; i < arr.length; i++) + { + var Block = arr[i]; + if(i < BLOCK_PROCESSING_LENGTH2) + { + BufWrite.Write(Block.Hash, "hash") + } + else + { + if(i === BLOCK_PROCESSING_LENGTH2) + { + BufWrite.Write(Block.SumHash, "hash") + BufWrite.Write(Block.SumPow, "uint") + } + BufWrite.Write(Block.TreeHash, "hash") + BufWrite.Write(Block.AddrHash, "hash") + } + } + } + return BufWrite; + } +}; + +function AddInfo(Block,Str,BlockNumStart) +{ + if(!global.STAT_MODE) + return ; + if(!Block.Info) + Block.Info = Str; + else + if(Block.Info.length < 2000) + { + var timesend = "" + SERVER.CurrentBlockNum - BlockNumStart; + var now = GetCurrentTime(); + timesend += ".[" + now.getSeconds().toStringZ(2) + "." + now.getMilliseconds().toStringZ(3) + "]"; + Str = timesend + ": " + Str; + Block.Info += "\n" + Str; + } +}; +global.AddInfoChain = function (Str) +{ + if(!global.STAT_MODE) + return ; + if(this.BlockNumStart > GetCurrentBlockNumByTime() - HISTORY_BLOCK_COUNT) + AddInfo(this, Str, this.BlockNumStart); +}; +global.AddInfoBlock = function (Block,Str) +{ + if(!global.STAT_MODE) + return ; + if(Block && Block.BlockNum && Block.BlockNum > GetCurrentBlockNumByTime() - HISTORY_BLOCK_COUNT) + AddInfo(Block, Str, Block.BlockNum); +}; +global.GetNodeStrPort = function (Node) +{ + if(!Node) + return ""; + if(LOCAL_RUN) + return "" + Node.port; + else + { + if(!Node.ip) + return ""; + var arr = Node.ip.split("."); + return "" + arr[2] + "." + arr[3]; + } +}; diff --git a/Source/core/db/db-row.js b/Source/core/db/db-row.js new file mode 100644 index 0000000..56c01ab --- /dev/null +++ b/Source/core/db/db-row.js @@ -0,0 +1,256 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +module.exports = class CDBState extends require("./db") +{ + constructor(FileName, DataSize, Format, bReadOnly) + { + super() + this.FileName = FileName + this.DataSize = DataSize + this.Format = Format + this.WorkStruct = {} + var FI = this.OpenDBFile(this.FileName, !bReadOnly); + this.FileNameFull = FI.fname + this.LastHash = undefined + this.WasUpdate = 1 + this.BufMap = {} + this.BufMapCount = 0 + setInterval(this.CheckBufMap.bind(this), 1000) + } + GetMaxNum() + { + var FI = this.OpenDBFile(this.FileName); + var Num = Math.floor(FI.size / this.DataSize) - 1; + return Num; + } + CheckNewNum(Data) + { + if(Data.Num === undefined) + Data.Num = this.GetMaxNum() + 1 + } + Write(Data, RetBuf) + { + var startTime = process.hrtime(); + this.LastHash = undefined + this.WasUpdate = 1 + this.CheckNewNum(Data) + Data.Num = Math.trunc(Data.Num) + this.DeleteMap(Data.Num) + var BufWrite = BufLib.GetBufferFromObject(Data, this.Format, this.DataSize, this.WorkStruct, 1); + var Position = Data.Num * this.DataSize; + var FI = this.OpenDBFile(this.FileName, 1); + var written = fs.writeSync(FI.fd, BufWrite, 0, BufWrite.length, Position); + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB-ROW", 10, "Error write to file:" + written + " <> " + BufWrite.length) + return false; + } + if(RetBuf) + { + RetBuf.Buf = BufWrite + } + if(Position >= FI.size) + { + FI.size = Position + this.DataSize + } + ADD_TO_STAT_TIME("ROWS_WRITE_MS", startTime) + ADD_TO_STAT("ROWS_WRITE") + return true; + } + Read(Num, GetBufOnly) + { + Num = Math.trunc(Num) + var Data; + if(isNaN(Num) || Num < 0 || Num > this.GetMaxNum()) + { + return undefined; + } + var BufRead = this.GetMap(Num); + if(!BufRead) + { + BufRead = BufLib.GetNewBuffer(this.DataSize) + var Position = Num * this.DataSize; + var FI = this.OpenDBFile(this.FileName); + var bytesRead = fs.readSync(FI.fd, BufRead, 0, BufRead.length, Position); + if(bytesRead !== BufRead.length) + return undefined; + this.SetMap(Num, BufRead) + } + if(GetBufOnly) + { + return BufRead; + } + try + { + Data = BufLib.GetObjectFromBuffer(BufRead, this.Format, this.WorkStruct) + } + catch(e) + { + ToLog("DBROW:" + e) + return undefined; + } + Data.Num = Num + return Data; + } + GetRows(start, count) + { + var arr = []; + for(var num = start; num < start + count; num++) + { + var Data = this.Read(num); + if(!Data) + break; + arr.push(Data) + } + return arr; + } + Truncate(LastNum) + { + var startTime = process.hrtime(); + LastNum = Math.trunc(LastNum) + var Position = (LastNum + 1) * this.DataSize; + if(Position < 0) + Position = 0 + var FI = this.OpenDBFile(this.FileName, 1); + if(Position < FI.size) + { + this.LastHash = undefined + this.WasUpdate = 1 + if(LastNum < 0) + ToLog("Truncate " + this.FileName + " from 0", 2) + FI.size = Position + fs.ftruncateSync(FI.fd, FI.size) + this.BufMap = {} + this.BufMapCount = 0 + } + ADD_TO_STAT_TIME("ROWS_WRITE_MS", startTime) + } + DeleteHistory(BlockNumFrom) + { + var MaxNum = this.GetMaxNum(); + if(MaxNum === - 1) + return ; + for(var num = MaxNum; num >= 0; num--) + { + var ItemCheck = this.Read(num); + if(!ItemCheck) + break; + if(ItemCheck.BlockNum < BlockNumFrom) + { + if(num < MaxNum) + { + this.Truncate(num) + } + return ; + } + } + this.Truncate( - 1) + } + FastFindBlockNum(BlockNum) + { + var MaxNum = this.GetMaxNum(); + if(MaxNum === - 1) + return ; + var StartNum = 0; + var EndNum = MaxNum; + var CurNum = Math.trunc(MaxNum / 2); + while(true) + { + var Item = this.Read(CurNum); + if(Item) + { + if(Item.BlockNum > BlockNum) + { + EndNum = CurNum - 1 + var Delta = CurNum - StartNum; + if(Delta === 0) + return "NoHistory"; + Delta = Math.trunc((1 + Delta) / 2) + CurNum = CurNum - Delta + } + else + if(Item.BlockNum < BlockNum) + { + StartNum = CurNum + 1 + var Delta = EndNum - CurNum; + if(Delta === 0) + return "NoPresent"; + Delta = Math.trunc((1 + Delta) / 2) + CurNum = CurNum + Delta + } + else + if(Item.BlockNum === BlockNum) + break; + } + else + { + throw "Error read num"; + return ; + } + } + var num = CurNum; + while(true) + { + num-- + if(num < 0) + return CurNum; + var Item = this.Read(num); + if(Item) + { + if(Item.BlockNum === BlockNum) + CurNum = num + else + return CurNum; + } + else + { + throw "Error read num"; + return ; + } + } + } + SetMap(Num, Value) + { + this.BufMap[Num] = Value + this.BufMapCount++ + } + GetMap(Num) + { + return this.BufMap[Num]; + } + DeleteMap(Num) + { + if(this.BufMap[Num]) + { + delete this.BufMap[Num] + this.BufMapCount-- + } + } + CheckBufMap() + { + if(this.BufMapCount > 1000) + { + this.ClearBufMap() + } + } + ClearBufMap() + { + this.BufMap = {} + this.BufMapCount = 0 + } + Close() + { + this.ClearBufMap() + this.CloseDBFile(this.FileName) + } +}; diff --git a/Source/core/db/db.js b/Source/core/db/db.js new file mode 100644 index 0000000..e0b6ce0 --- /dev/null +++ b/Source/core/db/db.js @@ -0,0 +1,120 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +module.exports = class +{ + constructor() + { + this.DBMap = {} + } + CheckPathDB() + { + var Path = GetDataPath("DB"); + CheckCreateDir(Path) + } + CloseDBFile(name, bdelete) + { + this.LastHash = undefined + this.WasUpdate = 1 + var Item = this.DBMap[name]; + if(Item) + { + let bDelete = bdelete; + let Name = name; + fs.close(Item.fd, function (err) + { + if(!err) + { + if(bDelete) + { + var fname = GetDataPath("DB/" + Name); + fs.unlink(fname, function (err) + { + if(err) + ToLog(err) + }) + } + } + else + { + ToLog(err) + } + }) + delete this.DBMap[name] + } + } + OpenDBFile(name, bWrite, bExist) + { + if(bWrite && global.READ_ONLY_DB) + { + ToLogTrace("CANNOT WRITE - DB IN READ_ONLY MODE!!!") + process.exit() + } + if(bWrite) + CheckStartOneProcess(name + "-run") + this.LastHash = undefined + this.WasUpdate = 1 + var Item = this.DBMap[name]; + if(Item === undefined) + { + if(!this.WasCheckPathDB) + { + this.CheckPathDB() + this.WasCheckPathDB = true + } + var fname = GetDataPath("DB/" + name); + if(!fs.existsSync(fname)) + { + if(bExist) + { + this.DBMap[name] = null + return null; + } + var fd = fs.openSync(fname, "w+"); + fs.closeSync(fd) + } + var fd = fs.openSync(fname, "r+"); + var stat = fs.statSync(fname); + var size = stat.size; + Item = {name:name, fname:fname, fd:fd, size:size, FillRows:0, CountRows:0, } + this.DBMap[name] = Item + } + return Item; + } +}; +var MapCheckProcess = {}; +var BlockDB = new module.exports(); + +function CheckStartOneProcess(Name) +{ + if(global.UpdateMode) + return ; + if(global.READ_ONLY_DB || MapCheckProcess[Name]) + return ; + MapCheckProcess[Name] = 1; + var path = GetDataPath("DB/" + Name); + if(fs.existsSync(path)) + { + fs.unlinkSync(path); + } + try + { + BlockDB.OpenDBFile(Name); + } + catch(e) + { + ToLog("****** DETECT START ANOTHER PROCESS for: " + Name); + ToLogTrace("EXIT"); + process.exit(); + } +}; +global.CheckStartOneProcess = CheckStartOneProcess; diff --git a/Source/core/geo.js b/Source/core/geo.js new file mode 100644 index 0000000..8323263 --- /dev/null +++ b/Source/core/geo.js @@ -0,0 +1,87 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var BufIP, fs = require("fs"); +require("./library.js"); +var MapNames = {}, FileIp = "./SITE/DB/iplocation.db", FileNames = "./SITE/DB/locationnames.csv", Format = "{Value:uint32,Length:uint32, id:uint32, latitude:uint32, longitude:uint32}", +FormatStruct = {}; + +function SetGeoLocation(e) +{ + if(!e.ip || !BufIP || !BufIP.length) + return !1; + var t = IPToUint(e.ip), i = FindItem(BufIP, 20, t); + return i && (e.latitude = i.latitude, e.longitude = i.longitude, e.name = MapNames[i.id]), e.Geo = 1, !0; +}; + +function ReadItem(e,t) +{ + return BufIP.len = e * t, BufLib.Read(BufIP, Format, void 0, FormatStruct); +}; + +function FindItem(e,t,i) +{ + var n, r = Math.trunc(e.length / t), a = (ReadItem(0, t), ReadItem(r, t), 0), u = r, o = Math.trunc(i * r / 4294967296); + r <= o && (o = r - 1), o < a && (o = a); + for(var f = 40; 0 < f; ) + { + if(f--, !(n = ReadItem(o, t))) + return void ToLog("GEO FindItem - Error read num: " + o); + if(n.Value > i) + { + if(u = o - 1, 0 === (l = o - a)) + return ; + o -= l = Math.trunc((1 + l) / 2); + } + else + if(n.Value < i) + { + if(n.Value + n.Length >= i) + return n; + var l; + if(a = o + 1, 0 === (l = u - o)) + return ; + o += l = Math.trunc((1 + l) / 2); + } + else + if(n.Value === i) + return n; + } +}; + +function Init() +{ + if(fs.existsSync(FileIp) && fs.existsSync(FileNames)) + { + BufIP = fs.readFileSync(FileIp); + for(var e = fs.readFileSync(FileNames), t = 0; ; ) + { + var i = e.indexOf("\n", t); + if(i < 0) + break; + var n = e.toString("utf-8", t, i - 1); + t = i + 1; + var r = n.split(","), a = parseInt(r[0]); + if(a) + { + 0; + var u = r[10]; + u = (u = u || r[7]) || r[5], MapNames[a] = u; + } + } + } +}; + +function IPToUint(e) +{ + var t = e.split("."); + return 256 * (256 * (256 * + t[0] + + t[1]) + + t[2]) + + t[3]; +}; +global.SetGeoLocation = SetGeoLocation, Init(); diff --git a/Source/core/html-server.js b/Source/core/html-server.js new file mode 100644 index 0000000..7f64d13 --- /dev/null +++ b/Source/core/html-server.js @@ -0,0 +1,1987 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require("./crypto-library"); +require("./log.js"); +const crypto = require('crypto'); +const os = require('os'); +var BlockTree = new STreeBuffer(300 * 1000, CompareItemHashSimple, "number"); +const http = require('http'), net = require('net'), url = require('url'), fs = require('fs'), querystring = require('querystring'); +var ContenTypeMap = {}; +ContenTypeMap["js"] = "application/javascript"; +ContenTypeMap["css"] = "text/css"; +ContenTypeMap["wav"] = "audio/wav"; +ContenTypeMap["mp3"] = "audio/mpeg"; +ContenTypeMap["mp4"] = "video/mp4"; +ContenTypeMap["ico"] = "image/vnd.microsoft.icon"; +ContenTypeMap["png"] = "image/png"; +ContenTypeMap["gif"] = "image/gif"; +ContenTypeMap["jpg"] = "image/jpeg"; +ContenTypeMap["html"] = "text/html"; +ContenTypeMap["txt"] = "text/plain"; +ContenTypeMap["csv"] = "text/csv"; +ContenTypeMap["svg"] = "image/svg+xml"; +ContenTypeMap[""] = "application/octet-stream"; +ContenTypeMap["zip"] = "application/zip"; +ContenTypeMap["pdf"] = "application/pdf"; +ContenTypeMap["exe"] = "application/octet-stream"; +ContenTypeMap["msi"] = "application/octet-stream"; +ContenTypeMap["woff"] = "application/font-woff"; +ContenTypeMap[".js"] = "application/javascript"; +ContenTypeMap["html"] = "text/html"; +ContenTypeMap["psd"] = "application/octet-stream"; +global.HTTPCaller = {}; + +function DoCommand(response,Type,Path,params,remoteAddress) +{ + var F = HTTPCaller[params[0]]; + if(F) + { + if(Type !== "POST") + { + response.end(); + return ; + } + response.writeHead(200, {'Content-Type':'text/plain'}); + var Ret = F(params[1], response); + if(Ret === null) + return ; + try + { + var Str = JSON.stringify(Ret); + response.end(Str); + } + catch(e) + { + ToLog("ERR PATH:" + Path); + ToLog(e); + response.end(); + } + return ; + } + var method = params[0]; + method = method.toLowerCase(); + if(method === "dapp" && params.length === 2) + method = "DappTemplateFile"; + switch(method) + { + case "": + SendWebFile(response, "./HTML/wallet.html"); + break; + case "file": + SendBlockFile(response, params[1], params[2]); + break; + case "DappTemplateFile": + DappTemplateFile(response, params[1]); + break; + case "smart": + DappSmartCodeFile(response, params[1]); + break; + case "client": + DappClientCodeFile(response, params[1]); + break; + default: + { + if(Path.indexOf(".") === - 1) + ToError("Error path:" + Path + " remoteAddress=" + remoteAddress); + var path = params[params.length - 1]; + if(typeof path !== "string") + path = "ErrorPath"; + else + if(path.indexOf("..") >= 0 || path.indexOf("\\") >= 0 || path.indexOf("/") >= 0) + path = "ErrorFilePath"; + if(path.indexOf(".") < 0) + path += ".html"; + var type = Path.substr(Path.length - 3, 3); + switch(type) + { + case ".js": + path = "./HTML/JS/" + path; + break; + case "css": + path = "./HTML/CSS/" + path; + break; + case "wav": + case "mp3": + path = "./HTML/SOUND/" + path; + break; + case "svg": + case "png": + case "gif": + case "jpg": + case "ico": + path = "./HTML/PIC/" + path; + break; + case "pdf": + case "zip": + case "exe": + case "msi": + path = "./HTML/FILES/" + path; + break; + default: + path = "./HTML/" + path; + break; + } + SendWebFile(response, path, Path); + break; + } + } +}; +global.DappTemplateFile = DappTemplateFile; + +function DappTemplateFile(response,StrNum) +{ + var Num = parseInt(StrNum); + if(Num && Num <= DApps.Smart.GetMaxNum()) + { + var Data = DApps.Smart.ReadSmart(Num); + if(Data) + { + response.writeHead(200, {'Content-Type':'text/html', "X-Frame-Options":"sameorigin"}); + var Str = fs.readFileSync("HTML/dapp-frame.html", {encoding:"utf8"}); + Str = Str.replace(/#template-number#/g, StrNum); + Str = Str.replace(/DAPP Loading.../g, Data.Name); + Str = Str.replace(/.\/tera.ico/g, "/file/" + Data.IconBlockNum + "/" + Data.IconTrNum); + response.end(Str); + return ; + } + } + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(); +}; +global.DappSmartCodeFile = DappSmartCodeFile; + +function DappSmartCodeFile(response,StrNum) +{ + var Num = parseInt(StrNum); + if(Num && Num <= DApps.Smart.GetMaxNum()) + { + var Data = DApps.Smart.ReadSmart(Num); + if(Data) + { + response.writeHead(200, {'Content-Type':'application/javascript'}); + response.end(Data.Code); + return ; + } + } + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(); +}; +global.DappClientCodeFile = DappClientCodeFile; + +function DappClientCodeFile(response,StrNum) +{ + var Num = parseInt(StrNum); + if(Num && Num <= DApps.Smart.GetMaxNum()) + { + var Data = DApps.Smart.ReadSmart(Num); + if(Data) + { + response.writeHead(200, {'Content-Type':"text/plain"}); + response.end(Data.HTML); + return ; + } + } + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(); +}; +HTTPCaller.DappSmartHTMLFile = function (Params) +{ + var Data = DApps.Smart.ReadSmart(ParseNum(Params.Smart)); + if(Data) + { + if(global.DEV_MODE && Params.DebugPath) + { + ToLog("Load: " + Params.DebugPath); + Data.HTML = fs.readFileSync(Params.DebugPath, {encoding:"utf8"}); + } + return {result:1, Body:Data.HTML}; + } + return {result:0}; +}; +global.SendBlockFile = SendBlockFile; + +function SendBlockFile(response,BlockNum,TrNum) +{ + BlockNum = parseInt(BlockNum); + TrNum = parseInt(TrNum); + if(BlockNum && BlockNum <= SERVER.GetMaxNumBlockDB() && TrNum <= MAX_TRANSACTION_COUNT) + { + var Block = SERVER.ReadBlockDB(BlockNum); + if(Block && Block.arrContent) + { + SendToResponceFile(response, Block, TrNum); + return ; + } + else + if(!Block || !Block.TrDataPos) + { + LoadBlockFromNetwork({BlockNum:BlockNum}, function (Err,Block) + { + if(Err) + { + SendToResponce404(response); + } + else + { + SendToResponceFile(response, Block, TrNum); + } + }); + return ; + } + } + SendToResponce404(response); +}; + +function SendToResponceFile(response,Block,TrNum) +{ + var Body = Block.arrContent[TrNum]; + if(Body && Body.data) + Body = Body.data; + if(Body && Body[0] === global.TYPE_TRANSACTION_FILE) + { + var TR = DApps.File.GetObjectTransaction(Body); + if(TR.ContentType.toLowerCase().indexOf("html") >= 0) + response.writeHead(200, {'Content-Type':"text/plain"}); + else + response.writeHead(200, {'Content-Type':TR.ContentType}); + response.end(TR.Data); + } + else + { + SendToResponce404(response); + } +}; + +function SendToResponce404(response) +{ + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(); +}; +HTTPCaller.DappBlockFile = function (Params,response) +{ + Params.BlockNum = ParseNum(Params.BlockNum); + Params.TrNum = ParseNum(Params.TrNum); + if(!Params.TrNum) + Params.TrNum = 0; + if(Params.BlockNum && Params.BlockNum <= SERVER.GetMaxNumBlockDB() && Params.TrNum <= MAX_TRANSACTION_COUNT) + { + var Block = SERVER.ReadBlockDB(Params.BlockNum); + if(Block && Block.arrContent) + { + SendToResponceDappFile(response, Block, Params.TrNum); + return null; + } + else + if(!Block || !Block.TrDataPos) + { + LoadBlockFromNetwork(Params, function (Err,Block) + { + if(Err) + { + SendToResponceResult0(response); + } + else + { + SendToResponceDappFile(response, Block, Params.TrNum); + } + }); + return null; + } + } + return {result:0}; +}; + +function SendToResponceDappFile(response,Block,TrNum) +{ + var Result = {result:0}; + var Body = Block.arrContent[TrNum]; + if(Body) + { + var Type = Body[0]; + if(Type === global.TYPE_TRANSACTION_FILE) + { + var TR = DApps.File.GetObjectTransaction(Body); + Result = {result:1, Type:Type, ContentType:TR.ContentType, Name:TR.Name, Body:TR.Data.toString('utf8')}; + } + else + { + var App = DAppByType[Type]; + if(App) + { + Body = JSON.parse(App.GetScriptTransaction(Body)); + } + Result = {result:1, Type:Type, Body:Body}; + } + } + var Str = JSON.stringify(Result); + response.end(Str); +}; + +function SendToResponceResult0(response) +{ + if(response) + response.end("{\"result\":0}"); +}; +var glBlock0; +HTTPCaller.DappStaticCall = function (Params,response) +{ + DApps.Accounts.BeginBlock(); + DApps.Accounts.BeginTransaction(); + global.TickCounter = 100000; + var Account = DApps.Accounts.ReadStateTR(ParseNum(Params.Account)); + if(!Account) + { + return {result:0, RetValue:"Error account Num: " + Params.Account}; + } + if(!glBlock0) + glBlock0 = SERVER.ReadBlockHeaderDB(0); + var RetValue; + try + { + RetValue = RunSmartMethod(glBlock0, Account.Value.Smart, Account, 0, 0, undefined, Params.MethodName, Params.Params, 1); + } + catch(e) + { + return {result:0, RetValue:"" + e}; + } + var Str = JSON.stringify({result:1, RetValue:RetValue}); + if(Str.length > 16000) + { + return {result:0, RetValue:"Error result length (more 16000)"}; + } + response.end(Str); + return null; +}; +HTTPCaller.DappInfo = function (Params,responce,ObjectOnly) +{ + var SmartNum = ParseNum(Params.Smart); + if(global.TX_PROCESS && global.TX_PROCESS.Worker) + global.TX_PROCESS.Worker.send({cmd:"SetSmartEvent", Smart:SmartNum}); + var Account; + var Smart = DApps.Smart.ReadSimple(SmartNum); + if(Smart) + { + delete Smart.HTML; + delete Smart.Code; + Account = DApps.Accounts.ReadState(Smart.Account); + try + { + Account.SmartState = BufLib.GetObjectFromBuffer(Account.Value.Data, Smart.StateFormat, {}); + if(typeof Account.SmartState === "object") + Account.SmartState.Num = Account.Num; + } + catch(e) + { + if(!Account) + Account = {}; + Account.SmartState = {}; + } + } + DeleteOldEvents(SmartNum); + var Context = GetUserContext(Params); + var EArr = GetEventArray(SmartNum, Context); + var WLData = HTTPCaller.DappWalletList(Params); + var ArrLog = []; + for(var i = 0; i < ArrLogClient.length; i++) + { + var Item = ArrLogClient[i]; + if(!Item.final) + continue; + ArrLog.push(Item); + } + var Ret = {result:1, DELTA_CURRENT_TIME:DELTA_CURRENT_TIME, MIN_POWER_POW_TR:MIN_POWER_POW_TR, FIRST_TIME_BLOCK:FIRST_TIME_BLOCK, + CONSENSUS_PERIOD_TIME:CONSENSUS_PERIOD_TIME, PRICE_DAO:PRICE_DAO(SERVER.BlockNumDB), NEW_SIGN_TIME:NEW_SIGN_TIME, Smart:Smart, + Account:Account, ArrWallet:WLData.arr, ArrEvent:EArr, ArrLog:ArrLog, }; + if(global.WALLET) + { + Ret.WalletIsOpen = (WALLET.WalletOpen !== false); + Ret.WalletCanSign = (WALLET.WalletOpen !== false && WALLET.KeyPair.WasInit); + Ret.PubKey = WALLET.KeyPair.PubKeyStr; + } + if(!ObjectOnly) + { + Ret.CurTime = Date.now(); + Ret.CurBlockNum = GetCurrentBlockNumByTime(); + Ret.BlockNumDB = SERVER.BlockNumDB; + Ret.MaxAccID = DApps.Accounts.GetMaxAccount(); + Ret.MaxDappsID = DApps.Smart.GetMaxNum(); + } + return Ret; +}; +HTTPCaller.DappWalletList = function (Params) +{ + var arr0 = DApps.Accounts.GetWalletAccountsByMap(WALLET.AccountMap); + var arr = []; + for(var i = 0; i < arr0.length; i++) + { + if(Params.AllAccounts || arr0[i].Value.Smart === Params.Smart) + { + arr.push(arr0[i]); + } + } + var Ret = {result:1, arr:arr, }; + return Ret; +}; +HTTPCaller.DappAccountList = function (Params) +{ + var arr = DApps.Accounts.GetRowsAccounts(Params.StartNum, Params.CountNum, undefined, 1); + return {arr:arr, result:1}; +}; +HTTPCaller.DappSmartList = function (Params) +{ + var arr = DApps.Smart.GetRows(Params.StartNum, Params.CountNum, undefined, undefined, Params.GetAllData, Params.TokenGenerate); + return {arr:arr, result:1}; +}; +HTTPCaller.DappBlockList = function (Params,response) +{ + Params.Filter = undefined; + return HTTPCaller.GetBlockList(Params, response, 1); +}; +HTTPCaller.DappTransactionList = function (Params,response) +{ + Params.Filter = undefined; + Params.Param3 = Params.BlockNum; + return HTTPCaller.GetTransactionAll(Params, response); +}; +var sessionid = GetHexFromAddres(crypto.randomBytes(20)); +HTTPCaller.RestartNode = function (Params) +{ + global.RestartNode(); + return {result:1}; +}; +HTTPCaller.ToLogServer = function (Str) +{ + ToLogClient(Str); + return {result:1}; +}; +HTTPCaller.FindMyAccounts = function (Params) +{ + WALLET.FindMyAccounts(1); + return {result:1}; +}; +HTTPCaller.GetAccount = function (id) +{ + id = parseInt(id); + var arr = DApps.Accounts.GetRowsAccounts(id, 1); + return {Item:arr[0], result:1}; +}; +HTTPCaller.GetAccountList = function (Params) +{ + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Accounts.GetRowsAccounts(Params.StartNum, Params.CountNum, Params.Filter); + return {arr:arr, result:1}; +}; +HTTPCaller.GetDappList = function (Params) +{ + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Smart.GetRows(Params.StartNum, Params.CountNum, Params.Filter, Params.Filter2, 1); + return {arr:arr, result:1}; +}; +HTTPCaller.GetBlockList = function (Params,response,bOnlyNum) +{ + if(!Params.CountNum) + Params.CountNum = 1; + if(Params.StartNum < SERVER.BlockNumDBMin) + { + let CountWait = 0; + var WasWait = 0; + for(var BlockNum = Params.StartNum; BlockNum < Params.StartNum + Params.CountNum; BlockNum++) + { + var Block = SERVER.ReadBlockHeaderDB(BlockNum); + if(!Block) + { + CountWait++; + WasWait = 1; + LoadBlockFromNetwork({BlockNum:BlockNum}, function (Err,Block) + { + CountWait--; + if(CountWait === 0) + { + var arr = SERVER.GetRows(Params.StartNum, Params.CountNum, Params.Filter); + var Result = {arr:arr, result:1}; + response.end(JSON.stringify(Result)); + } + }); + } + } + if(WasWait) + return null; + } + var arr = SERVER.GetRows(Params.StartNum, Params.CountNum, Params.Filter, !bOnlyNum); + return {arr:arr, result:1}; +}; +HTTPCaller.GetTransactionAll = function (Params,response) +{ + if(!Params.CountNum) + Params.CountNum = 1; + var BlockNum = Params.Param3; + if(BlockNum < SERVER.BlockNumDBMin) + { + var Block = SERVER.ReadBlockHeaderDB(BlockNum); + if(!Block) + { + LoadBlockFromNetwork({BlockNum:BlockNum}, function (Err,Block) + { + var Result; + if(Err) + { + Result = {arr:[], result:0}; + } + else + { + var arr = SERVER.GetTrRows(Block.BlockNum, Params.StartNum, Params.CountNum, Params.Filter); + Result = {arr:arr, result:1}; + } + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; + } + } + var arr = SERVER.GetTrRows(BlockNum, Params.StartNum, Params.CountNum, Params.Filter); + return {arr:arr, result:1}; +}; +HTTPCaller.GetActList = function (Params) +{ + var arr = DApps.Accounts.GetActList(Params.StartNum, Params.CountNum, Params.Filter); + return {arr:arr, result:1}; +}; +HTTPCaller.GetHashList = function (Params) +{ + var arr = DApps.Accounts.DBAccountsHash.GetRows(Params.StartNum, Params.CountNum, Params.Filter); + for(var i = 0; i < arr.length; i++) + { + var item = arr[i]; + item.BlockNum = item.Num * PERIOD_ACCOUNT_HASH; + item.Hash100 = []; + if(item.BlockNum % 100 === 0) + { + var Data = SERVER.DBHeader100.Read(item.BlockNum / 100); + if(Data) + item.Hash100 = Data.Hash100; + } + } + return {arr:arr, result:1}; +}; +HTTPCaller.GetHistoryAct = function (Params) +{ + var arr = WALLET.GetHistory(Params.StartNum, Params.CountNum, Params.Filter); + return {arr:arr, result:1}; +}; +var LastTimeGetHashRate = 0; +var LastHashRate = 0; +var HashRateOneSec = 0; +HTTPCaller.GetWalletInfo = function (Params) +{ + var Constants = {}; + for(var i = 0; i < global.CONST_NAME_ARR.length; i++) + { + var key = global.CONST_NAME_ARR[i]; + Constants[key] = global[key]; + } + var MaxHistory = 0; + var Delta = Date.now() - LastTimeGetHashRate; + if(Delta >= 1000) + { + if(Delta < 1100) + HashRateOneSec = global.HASH_RATE - LastHashRate; + LastHashRate = global.HASH_RATE; + LastTimeGetHashRate = Date.now(); + } + var StateTX = DApps.Accounts.DBStateTX.Read(0); + var TXBlockNum = 0; + if(StateTX) + TXBlockNum = StateTX.BlockNum; + var Ret = {result:1, WalletOpen:WALLET.WalletOpen, WalletIsOpen:(WALLET.WalletOpen !== false), WalletCanSign:(WALLET.WalletOpen !== false && WALLET.KeyPair.WasInit), + CODE_VERSION:CODE_VERSION, MAX_TRANSACTION_LIMIT:MAX_TRANSACTION_LIMIT, VersionNum:global.UPDATE_CODE_VERSION_NUM, RelayMode:SERVER.RelayMode, + BlockNumDB:SERVER.BlockNumDB, CurBlockNum:GetCurrentBlockNumByTime(), CurTime:Date.now(), IsDevelopAccount:IsDeveloperAccount(WALLET.PubKeyArr), + AccountMap:WALLET.AccountMap, ArrLog:ArrLogClient, MaxAccID:DApps.Accounts.GetMaxAccount(), MaxActNum:DApps.Accounts.GetActsMaxNum(), + MaxDappsID:DApps.Smart.GetMaxNum(), NeedRestart:global.NeedRestart, ip:SERVER.ip, port:SERVER.port, NET_WORK_MODE:global.NET_WORK_MODE, + INTERNET_IP_FROM_STUN:global.INTERNET_IP_FROM_STUN, HistoryMaxNum:MaxHistory, DELTA_CURRENT_TIME:DELTA_CURRENT_TIME, FIRST_TIME_BLOCK:FIRST_TIME_BLOCK, + CONSENSUS_PERIOD_TIME:CONSENSUS_PERIOD_TIME, NEW_SIGN_TIME:NEW_SIGN_TIME, DATA_PATH:(DATA_PATH.substr(1, 1) === ":" ? DATA_PATH : GetNormalPathString(process.cwd() + "/" + DATA_PATH)), + NodeAddrStr:SERVER.addrStr, STAT_MODE:global.STAT_MODE, HTTPPort:global.HTTP_PORT_NUMBER, HTTPPassword:HTTP_PORT_PASSWORD, + CONSTANTS:Constants, CheckPointBlockNum:CHECK_POINT.BlockNum, MiningAccount:global.GENERATE_BLOCK_ACCOUNT, CountMiningCPU:GetCountMiningCPU(), + CountRunCPU:global.ArrMiningWrk.length, MiningPaused:global.MiningPaused, HashRate:HashRateOneSec, MIN_POWER_POW_TR:MIN_POWER_POW_TR, + PRICE_DAO:PRICE_DAO(SERVER.BlockNumDB), NWMODE:global.NWMODE, PERIOD_ACCOUNT_HASH:PERIOD_ACCOUNT_HASH, MAX_ACCOUNT_HASH:DApps.Accounts.DBAccountsHash.GetMaxNum(), + TXBlockNum:TXBlockNum, SpeedSignLib:global.SpeedSignLib, }; + if(Params.Account) + Ret.PrivateKey = GetHexFromArr(WALLET.GetPrivateKey(WALLET.AccountMap[Params.Account])); + else + Ret.PrivateKey = GetHexFromArr(WALLET.GetPrivateKey()); + Ret.PublicKey = WALLET.KeyPair.PubKeyStr; + return Ret; +}; +HTTPCaller.GetCurrentInfo = HTTPCaller.GetWalletInfo; +HTTPCaller.TestSignLib = function () +{ + global.TestSignLib(); +}; +HTTPCaller.GetWalletAccounts = function () +{ + var Ret = {result:1, arr:DApps.Accounts.GetWalletAccountsByMap(WALLET.AccountMap), }; + Ret.PrivateKey = WALLET.KeyPair.PrivKeyStr; + Ret.PublicKey = WALLET.KeyPair.PubKeyStr; + return Ret; +}; +HTTPCaller.SetWalletKey = function (PrivateKeyStr) +{ + WALLET.SetPrivateKey(PrivateKeyStr, true); + return {result:1}; +}; +HTTPCaller.SetWalletPasswordNew = function (Password) +{ + WALLET.SetPasswordNew(Password); + return {result:1}; +}; +HTTPCaller.OpenWallet = function (Password) +{ + var res = WALLET.OpenWallet(Password); + return {result:res}; +}; +HTTPCaller.CloseWallet = function () +{ + var res = WALLET.CloseWallet(); + return {result:res}; +}; +HTTPCaller.GetSignTransaction = function (TR) +{ + var Sign = WALLET.GetSignTransaction(TR); + return {Sign:Sign, result:1}; +}; +HTTPCaller.GetSignFromHEX = function (Params) +{ + var Arr = GetArrFromHex(Params.Hex); + var Sign; + if(Params.Account) + Sign = WALLET.GetSignFromArr(Arr, WALLET.AccountMap[Params.Account]); + else + Sign = WALLET.GetSignFromArr(Arr); + return {Sign:Sign, result:1}; +}; +HTTPCaller.SendTransactionHex = function (Params) +{ + var body = GetArrFromHex(Params.Hex); + var Result = {result:1}; + var Res = SERVER.AddTransactionOwn({body:body, ToAll:1}); + Result.sessionid = sessionid; + Result.text = AddTrMap[Res]; + var final = false; + if(Res <= 0 && Res !== - 3) + final = true; + ToLogClient("Send: " + Result.text, GetHexFromArr(sha3(body)), final); + return Result; +}; +HTTPCaller.SendDirectCode = function (Params,response) +{ + var Result; + if(Params.TX || Params.WEB) + { + var RunProcess; + if(Params.TX) + RunProcess = global.TX_PROCESS; + if(Params.WEB) + RunProcess = global.WEB_PROCESS; + if(RunProcess && RunProcess.RunRPC) + { + RunProcess.RunRPC("EvalCode", Params.Code, function (Err,Ret) + { + Result = {result:!Err, sessionid:sessionid, text:Ret, }; + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; + } + else + { + Result = "No process for send call"; + } + } + else + { + try + { + var ret = eval(Params.Code); + Result = JSON.stringify(ret, "", 4); + } + catch(e) + { + Result = "" + e; + } + } + var Struct = {result:1, sessionid:sessionid, text:Result}; + return Struct; +}; +HTTPCaller.SetMining = function (MiningAccount) +{ + WALLET.SetMiningAccount(parseInt(MiningAccount)); + return {result:1}; +}; + +function CheckCorrectDevKey() +{ + if(WALLET.WalletOpen === false) + { + var StrErr = "Not open wallet"; + ToLogClient(StrErr); + return {result:0, text:StrErr}; + } + if(!IsDeveloperAccount(WALLET.PubKeyArr)) + { + var StrErr = "Not developer key"; + ToLogClient(StrErr); + return {result:0, text:StrErr}; + } + return true; +}; +HTTPCaller.SendECode = function (Param) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(Param.All) + { + SERVER.ConnectToAll(); + var arr = SERVER.GetActualNodes(); + for(var i = 0; i < arr.length; i++) + { + var Node = arr[i]; + SERVER.SendECode(Param, Node); + } + return {result:1, text:"Sent to " + arr.length + " nodes"}; + } + var Node = FindNodeByAddr(Param.Addr, 1); + if(Node === undefined) + return {result:0, text:"Node not found"}; + if(Node === false) + return {result:0, text:"Node not active - reconnect"}; + SERVER.SendECode(Param, Node); + return {result:1, text:"Send"}; +}; +HTTPCaller.SetCheckPoint = function (BlockNum) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(!BlockNum) + BlockNum = SERVER.BlockNumDB; + else + BlockNum = parseInt(BlockNum); + if(SetCheckPointOnBlock(BlockNum)) + return {result:1, text:"Set check point on BlockNum=" + BlockNum}; + else + return {result:0, text:"Error on check point BlockNum=" + BlockNum}; +}; + +function SetCheckPointOnBlock(BlockNum) +{ + if(WALLET.WalletOpen === false) + return 0; + var Block = SERVER.ReadBlockHeaderDB(BlockNum); + if(!Block) + return 0; + var SignArr = arr2(Block.Hash, GetArrFromValue(Block.BlockNum)); + var Sign = secp256k1.sign(SHA3BUF(SignArr, Block.BlockNum), WALLET.KeyPair.getPrivateKey('')).signature; + global.CHECK_POINT = {BlockNum:BlockNum, Hash:Block.Hash, Sign:Sign}; + SERVER.ResetNextPingAllNode(); + return 1; +}; +global.SetCheckPointOnBlock = SetCheckPointOnBlock; +var idSetTimeSetCheckPoint; +HTTPCaller.SetAutoCheckPoint = function (Param) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(idSetTimeSetCheckPoint) + clearInterval(idSetTimeSetCheckPoint); + idSetTimeSetCheckPoint = undefined; + if(Param.Set) + idSetTimeSetCheckPoint = setInterval(RunSetCheckPoint, Param.Period * 1000); + return {result:1, text:"AutoCheck: " + Param.Set + " each " + Param.Period + " sec"}; +}; +var SumCheckPow = 0; +var CountCheckPow = 0; + +function RunSetCheckPoint() +{ + if(!SERVER.BlockNumDB) + return ; + if(SERVER.BlockNumDB < 2100000) + return ; + var Delta = GetCurrentBlockNumByTime() - SERVER.BlockNumDB; + if(Delta > 16) + return ; + var BlockNum = SERVER.BlockNumDB - global.CheckPointDelta; + var Block = SERVER.ReadBlockHeaderDB(BlockNum); + if(Block) + { + var Power = GetPowPower(Block.PowHash); + if(Power < 30) + { + ToLog("CANNOT SET CHECK POINT Power=" + Power + " BlockNum=" + BlockNum); + return ; + } + CountCheckPow++; + SumCheckPow += Power; + var AvgPow = SumCheckPow / CountCheckPow; + if(CountCheckPow > 10) + { + if(Power < AvgPow - 2) + { + ToLog("**************** CANNOT SET CHECK POINT Power=" + Power + "/" + AvgPow + " BlockNum=" + BlockNum); + return ; + } + } + SetCheckPointOnBlock(BlockNum); + ToLog("SET CHECK POINT Power=" + Power + "/" + AvgPow + " BlockNum=" + BlockNum); + } +}; +HTTPCaller.SetNewCodeVersion = function (Data) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + var Ret = SERVER.SetNewCodeVersion(Data, WALLET.KeyPair.getPrivateKey('')); + SERVER.ResetNextPingAllNode(); + return {result:1, text:Ret}; +}; +HTTPCaller.SetCheckNetConstant = function (Data) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(!Data) + { + ToLogClient("Data not set"); + return {result:0, text:"Data not set"}; + } + Data.Num = GetCurrentBlockNumByTime(); + Data.BlockNum = GetCurrentBlockNumByTime() + 10; + var SignArr = SERVER.GetSignCheckNetConstant(Data); + Data.Sign = secp256k1.sign(SHA3BUF(SignArr), WALLET.KeyPair.getPrivateKey('')).signature; + SERVER.CheckNetConstant({NetConstant:Data}, {addrStr:"local"}); + SERVER.ResetNextPingAllNode(); + return {result:1, text:"Set NET_CONSTANT BlockNum=" + Data.BlockNum}; +}; +HTTPCaller.SetCheckDeltaTime = function (Data) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(!Data || !Data.Num) + { + ToLogClient("Num not set"); + return {result:0}; + } + var SignArr = SERVER.GetSignCheckDeltaTime(Data); + Data.Sign = secp256k1.sign(SHA3BUF(SignArr), WALLET.KeyPair.getPrivateKey('')).signature; + global.CHECK_DELTA_TIME = Data; + SERVER.ResetNextPingAllNode(); + return {result:1, text:"Set check time Num=" + Data.Num}; +}; +var idAutoCorrTime; +HTTPCaller.SetAutoCorrTime = function (bSet) +{ + var Ret = CheckCorrectDevKey(); + if(Ret !== true) + return Ret; + if(idAutoCorrTime) + clearInterval(idAutoCorrTime); + idAutoCorrTime = undefined; + if(bSet) + idAutoCorrTime = setInterval(RunAutoCorrTime, 1000); + return {result:1, text:"Auto correct: " + bSet}; +}; +var StartCheckTimeNum = 0; + +function RunAutoCorrTime() +{ + if(WALLET.WalletOpen === false) + return ; + if(GetCurrentBlockNumByTime() > StartCheckTimeNum && Math.abs(global.DELTA_CURRENT_TIME) >= 120) + { + var AutoDelta = - Math.trunc(global.DELTA_CURRENT_TIME); + var Data = {Num:GetCurrentBlockNumByTime(), bUse:1, bAddTime:1}; + if(AutoDelta < 0) + { + AutoDelta = - AutoDelta; + Data.bAddTime = 0; + } + Data.DeltaTime = 40; + Data.StartBlockNum = Data.Num + 5; + Data.EndBlockNum = Data.StartBlockNum + Math.trunc(AutoDelta / Data.DeltaTime); + var SignArr = SERVER.GetSignCheckDeltaTime(Data); + Data.Sign = secp256k1.sign(SHA3BUF(SignArr), WALLET.KeyPair.getPrivateKey('')).signature; + global.CHECK_DELTA_TIME = Data; + SERVER.ResetNextPingAllNode(); + StartCheckTimeNum = Data.EndBlockNum + 1; + ToLog("Auto corr time Num:" + Data.Num + " AutoDelta=" + AutoDelta); + } +}; +HTTPCaller.SaveConstant = function (SetObj) +{ + var WasUpdate = global.USE_AUTO_UPDATE; + for(var key in SetObj) + { + global[key] = SetObj[key]; + } + SAVE_CONST(true); + SERVER.DO_CONSTANT(); + if(!WasUpdate && global.USE_AUTO_UPDATE && CODE_VERSION.VersionNum && global.UPDATE_CODE_VERSION_NUM < CODE_VERSION.VersionNum) + { + SERVER.UseCode(CODE_VERSION.VersionNum, true); + } + if(SetObj.DoRestartNode) + global.RestartNode(); + else + { + if(SetObj.DoMining) + global.RunStopPOWProcess(); + } + return {result:1}; +}; +HTTPCaller.SetHTTPParams = function (SetObj) +{ + global.HTTP_PORT_NUMBER = SetObj.HTTPPort; + global.HTTP_PORT_PASSWORD = SetObj.HTTPPassword; + SAVE_CONST(true); + if(SetObj.DoRestartNode) + global.RestartNode(); + return {result:1}; +}; +HTTPCaller.SetNetMode = function (SetObj) +{ + if(!global.NET_WORK_MODE) + global.NET_WORK_MODE = {}; + for(var key in SetObj) + { + global.NET_WORK_MODE[key] = SetObj[key]; + } + if(NET_WORK_MODE) + { + global.START_IP = NET_WORK_MODE.ip; + global.START_PORT_NUMBER = NET_WORK_MODE.port; + } + SAVE_CONST(true); + if(SetObj.DoRestartNode) + global.RestartNode(); + return {result:1}; +}; +HTTPCaller.GetAccountKey = function (Num) +{ + var Result = {}; + Result.result = 0; + var KeyPair = WALLET.GetAccountKey(Num); + if(KeyPair) + { + Result.result = 1; + Result.PubKeyStr = GetHexFromArr(KeyPair.getPublicKey('', 'compressed')); + } + return Result; +}; +HTTPCaller.GetHotArray = function (Param) +{ + var ArrTree = SERVER.GetTransferTree(); + if(!ArrTree) + return {result:0}; + for(var Level = 0; Level < ArrTree.length; Level++) + { + var arr = ArrTree[Level]; + if(arr) + for(var n = 0; n < arr.length; n++) + { + arr[n].GetTiming = 0; + } + } + var BlockCounts = 0; + if(Param && Param.CountBlock) + for(var i = SERVER.CurrentBlockNum - Param.CountBlock; i <= SERVER.CurrentBlockNum - Param.CountBlock; i++) + { + var Block = SERVER.GetBlock(i); + if(!Block || !Block.Active || !Block.LevelsTransfer) + { + continue; + } + BlockCounts++; + for(var n = 0; n < Block.LevelsTransfer.length; n++) + { + var Transfer = Block.LevelsTransfer[n]; + for(var Addr in Transfer.TransferNodes) + { + var Item = Transfer.TransferNodes[Addr]; + Item.Node.GetTiming += Item.GetTiming; + } + } + } + for(var Level = 0; Level < ArrTree.length; Level++) + { + var arr = ArrTree[Level]; + if(!arr) + continue; + arr.sort(SortNodeHot); + for(var n = 0; n < arr.length; n++) + { + arr[n] = GetCopyNode(arr[n], BlockCounts); + } + } + +function SortNodeHot(a,b) + { + if(b.Hot !== a.Hot) + return b.Hot - a.Hot; + if(b.BlockProcessCount !== a.BlockProcessCount) + return b.BlockProcessCount - a.BlockProcessCount; + if(a.DeltaTime !== b.DeltaTime) + return a.DeltaTime - b.DeltaTime; + return a.id - b.id; + }; + return {result:1, ArrTree:ArrTree}; +}; + +function GetCopyNode(Node,BlockCounts) +{ + if(!Node) + return ; + if(Node.Socket && Node.Socket.Info) + { + Node.Info += Node.Socket.Info + "\n"; + Node.Socket.Info = ""; + } + if(!Node.PrevInfo) + Node.PrevInfo = ""; + var GetTiming = 0; + if(BlockCounts !== 0) + GetTiming = Math.trunc(Node.GetTiming / BlockCounts) / 1000; + var Item = {VersionNum:Node.VersionNum, NoSendTx:Node.NoSendTx, GetNoSendTx:Node.GetNoSendTx, DirectMAccount:Node.DirectMAccount, + id:Node.id, ip:Node.ip, portweb:Node.portweb, port:Node.port, TransferCount:Node.TransferCount, GetTiming:GetTiming, ErrCountAll:Node.ErrCountAll, + LevelCount:Node.LevelCount, LevelEnum:Node.LevelEnum, TimeTransfer:GetStrOnlyTimeUTC(new Date(Node.LastTimeTransfer)), BlockProcessCount:Node.BlockProcessCount, + DeltaTime:Node.DeltaTime, DeltaTimeM:Node.DeltaTimeM, DeltaGlobTime:Node.DeltaGlobTime, PingNumber:Node.PingNumber, NextConnectDelta:Node.NextConnectDelta, + NextGetNodesDelta:Node.NextGetNodesDelta, NextHotDelta:Node.NextHotDelta, Name:Node.Name, addrStr:Node.addrStr, CanHot:Node.CanHot, + Active:Node.Active, Hot:Node.Hot, Info:Node.PrevInfo + Node.Info, InConnectArr:Node.WasAddToConnect, Level:Node.Level, TransferBlockNum:Node.TransferBlockNum, + TransferSize:Node.TransferSize, TransferBlockNumFix:Node.TransferBlockNumFix, CurBlockNum:Node.CurBlockNum, }; + return Item; +}; +HTTPCaller.GetBlockchainStat = function (Param) +{ + var Result = SERVER.GetStatBlockchainPeriod(Param); + Result.result = 1; + Result.sessionid = sessionid; + return Result; +}; +HTTPCaller.GetAllCounters = function (Params,response) +{ + let Result = GET_STATS(); + Result.result = 1; + Result.sessionid = sessionid; + Result.STAT_MODE = global.STAT_MODE; + if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) + return Result; + global.TX_PROCESS.RunRPC("GET_STATS", "", function (Err,Params) + { + Result.result = !Err; + if(Result.result) + { + AddStatData(Params, Result, "TX"); + } + if(global.WEB_PROCESS && global.WEB_PROCESS.RunRPC) + { + global.WEB_PROCESS.RunRPC("GET_STATS", "", function (Err,Params) + { + Result.result = !Err; + if(Result.result) + { + AddStatData(Params, Result, "WEB"); + } + response.end(JSON.stringify(Result)); + }); + } + else + { + response.end(JSON.stringify(Result)); + } + }); + return null; +}; + +function AddStatData(Params,Result,Prefix) +{ + for(var name in Params.stats) + { + for(var key in Params.stats[name]) + { + var Item = Params.stats[name][key]; + Result.stats[name][key + "-" + Prefix] = Item; + } + } +}; +HTTPCaller.SetStatMode = function (flag) +{ + if(flag) + StartCommonStat(); + global.STAT_MODE = flag; + SAVE_CONST(true); + global.TX_PROCESS.RunRPC("LOAD_CONST"); + return {result:1, sessionid:sessionid, STAT_MODE:global.STAT_MODE}; +}; +HTTPCaller.ClearStat = function () +{ + global.ClearCommonStat(); + if(global.TX_PROCESS && global.TX_PROCESS.RunRPC) + { + global.TX_PROCESS.RunRPC("ClearCommonStat", "", function (Err,Params) + { + }); + } + if(global.WEB_PROCESS && global.WEB_PROCESS.RunRPC) + { + global.WEB_PROCESS.RunRPC("ClearCommonStat", "", function (Err,Params) + { + }); + } + return {result:1, sessionid:sessionid, STAT_MODE:global.STAT_MODE}; +}; +HTTPCaller.RewriteAllTransactions = function (Param) +{ + RewriteAllTransactions(); + return {result:1, sessionid:sessionid}; +}; +HTTPCaller.RewriteTransactions = function (Param) +{ + var Ret = SERVER.ReWriteDAppTransactions(Param.BlockCount); + return {result:Ret, sessionid:sessionid}; +}; +HTTPCaller.TruncateBlockChain = function (Param) +{ + var StartNum = SERVER.BlockNumDB - Param.BlockCount; + var MinBlock = DApps.Accounts.GetMinBlockAct(); + if(MinBlock > StartNum) + { + ToLog("Cant Truncate BlockChain. Very long length. Max length=" + (SERVER.BlockNumDB - MinBlock), 0); + return {result:0, sessionid:sessionid}; + } + SERVER.TruncateBlockDB(StartNum); + return {result:1, sessionid:sessionid}; +}; +HTTPCaller.ClearDataBase = function (Param) +{ + BlockTree.Clear(); + SERVER.ClearDataBase(); + return {result:1, sessionid:sessionid}; +}; +HTTPCaller.CleanChain = function (Param) +{ + if(global.CleanChain) + { + var StartNum = SERVER.BlockNumDB - Param.BlockCount; + global.CleanChain(StartNum); + return {result:1, sessionid:sessionid}; + } + return {result:0, sessionid:sessionid}; +}; +HTTPCaller.GetArrStats = function (Keys,response) +{ + var arr = GET_STATDIAGRAMS(Keys); + let Result = {result:1, sessionid:sessionid, arr:arr, STAT_MODE:global.STAT_MODE}; + if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) + return Result; + var Keys2 = []; + for(var i = 0; i < Keys.length; i++) + { + var Str = Keys[i]; + if(Str.substr(Str.length - 3) == "-TX") + Keys2.push(Str.substr(0, Str.length - 3)); + } + global.TX_PROCESS.RunRPC("GET_STATDIAGRAMS", Keys2, function (Err,Arr) + { + Result.result = !Err; + if(Result.result) + { + for(var i = 0; i < Arr.length; i++) + { + var Item = Arr[i]; + Item.name = Item.name + "-TX"; + Result.arr.push(Item); + } + } + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; +}; +HTTPCaller.GetBlockChain = function (type) +{ + if(!global.SERVER || !SERVER.LoadedChainList) + { + return {result:0}; + } + var MainChains = {}; + for(var i = 0; i < SERVER.LoadedChainList.length; i++) + { + var chain = SERVER.LoadedChainList[i]; + if(chain && !chain.Deleted) + MainChains[chain.id] = true; + } + var arrBlocks = []; + var arrLoadedChainList = []; + var arrLoadedBlocks = []; + for(var key in SERVER.BlockChain) + { + var Block = SERVER.BlockChain[key]; + if(Block) + { + arrBlocks.push(CopyBlockDraw(Block, MainChains)); + } + } + AddChainList(arrLoadedChainList, SERVER.LoadedChainList, true); + AddMapList(arrLoadedBlocks, type, SERVER.MapMapLoaded, MainChains); + var ArrLoadedChainList = global.HistoryBlockBuf.LoadValue("LoadedChainList", 1); + if(ArrLoadedChainList) + for(var List of ArrLoadedChainList) + { + AddChainList(arrLoadedChainList, List); + } + var ArrMapMapLoaded = global.HistoryBlockBuf.LoadValue("MapMapLoaded", 1); + if(ArrMapMapLoaded) + for(var List of ArrMapMapLoaded) + { + AddMapList(arrLoadedBlocks, type, List); + } + var obj = {LastCurrentBlockNum:SERVER.GetLastCorrectBlockNum(), CurrentBlockNum:SERVER.CurrentBlockNum, LoadedChainList:arrLoadedChainList, + LoadedBlocks:arrLoadedBlocks, BlockChain:arrBlocks, port:SERVER.port, DELTA_CURRENT_TIME:DELTA_CURRENT_TIME, memoryUsage:process.memoryUsage(), + IsDevelopAccount:IsDeveloperAccount(WALLET.PubKeyArr), LoadedChainCount:SERVER.LoadedChainList.length, StartLoadBlockTime:SERVER.StartLoadBlockTime, + sessionid:sessionid, result:1}; + var obj2 = HTTPCaller.GetHotArray(); + obj.ArrTree = obj2.ArrTree; + arrBlocks = []; + arrLoadedChainList = []; + arrLoadedBlocks = []; + return obj; +}; +HTTPCaller.GetHistoryTransactions = function (Params) +{ + if(typeof Params === "object" && Params.AccountID) + { + var Account = DApps.Accounts.ReadState(Params.AccountID); + if(!Account) + return {result:0}; + if(!Params.Count) + Params.Count = 100; + var arr = DApps.Accounts.GetHistory(Params.AccountID, Params.Count, Params.NextPos); + if(Params.GetDescription) + { + for(var i = 0; i < arr.length; i++) + { + var Item = arr[i]; + var Block = SERVER.ReadBlockDB(Item.BlockNum); + if(!Block || (!Block.arrContent)) + continue; + var Body = Block.arrContent[Item.TrNum]; + if(!Body) + continue; + var TR = DApps.Accounts.GetObjectTransaction(Body); + if(TR) + Item.Description = TR.Description; + } + } + var Result = {Value:{SumCOIN:Account.Value.SumCOIN, SumCENT:Account.Value.SumCENT}, Name:Account.Name, Currency:Account.Currency, + MaxBlockNum:GetCurrentBlockNumByTime(), FIRST_TIME_BLOCK:FIRST_TIME_BLOCK, result:arr.length > 0 ? 1 : 0, History:arr}; + return Result; + } + return {result:0}; +}; + +function GetCopyBlock(Block) +{ + var Result = {BlockNum:Block.BlockNum, bSave:Block.bSave, TreeHash:GetHexFromAddres(Block.TreeHash), AddrHash:GetHexFromAddres(Block.AddrHash), + PrevHash:GetHexFromAddres(Block.PrevHash), SumHash:GetHexFromAddres(Block.SumHash), SumPow:Block.SumPow, TrDataPos:Block.TrDataPos, + TrDataLen:Block.TrDataLen, SeqHash:GetHexFromAddres(Block.SeqHash), Hash:GetHexFromAddres(Block.Hash), Power:GetPowPower(Block.PowHash), + TrCount:Block.TrCount, arrContent:Block.arrContent, }; + return Result; +}; +var AddrLength = 16; + +function GetHexFromAddresShort(Hash) +{ + return GetHexFromAddres(Hash).substr(0, AddrLength); +}; + +function GetHexFromStrShort(Str) +{ + if(Str === undefined) + return Str; + else + return Str.substr(0, AddrLength); +}; +var glid = 0; + +function GetGUID(Block) +{ + if(!Block) + return "------"; + if(!Block.guid) + { + glid++; + Block.guid = glid; + } + return Block.guid; +}; + +function CopyBlockDraw(Block,MainChains) +{ + var MinerID = 0; + var MinerName = ""; + if(Block.AddrHash) + { + var Num = ReadUintFromArr(Block.AddrHash, 0); + MinerID = Num; + var Item = DApps.Accounts.ReadState(Num); + if(Item && Item.Name) + MinerName = Item.Name.substr(0, 8); + } + var CheckPoint = 0; + if(Block.BlockNum === CHECK_POINT.BlockNum) + CheckPoint = 1; + var Mining; + if(SERVER.MiningBlock === Block) + Mining = 1; + else + Mining = 0; + GetGUID(Block); + var Item = {guid:Block.guid, Active:Block.Active, bSave:Block.bSave, Prepared:Block.Prepared, BlockNum:Block.BlockNum, Hash:GetHexFromAddresShort(Block.Hash), + SumHash:GetHexFromAddresShort(Block.SumHash), SeqHash:GetHexFromAddresShort(Block.SeqHash), TreeHash:GetHexFromAddresShort(Block.TreeHash), + AddrHash:GetHexFromAddresShort(Block.AddrHash), MinerID:MinerID, MinerName:MinerName, Comment1:Block.Comment1, Comment2:Block.Comment2, + SumPow:Block.SumPow, Info:Block.Info, TreeLoaded:Block.TreeEq, AddToLoad:Block.AddToLoad, LoadDB:Block.LoadDB, FindBlockDB:Block.FindBlockDB, + TrCount:Block.TrCount, ArrLength:0, TrDataLen:Block.TrDataLen, Power:GetPowPower(Block.PowHash), CheckPoint:CheckPoint, Mining:Mining, + TransferSize:Block.TransferSize, HasErr:Block.HasErr, }; + if(Block.chain) + Item.chainid = Block.chain.id; + if(Block.LoadDB !== undefined) + Item.bSave = Block.LoadDB; + if(Block.arrContent) + Item.TrCount = Block.arrContent.length; + Item.BlockDown = GetGUID(Block.BlockDown); + if(MainChains && Item.chainid) + { + Item.Main = MainChains[Item.chainid]; + } + return Item; +}; + +function CopyChainDraw(Chain,bWasRecursive,bMain) +{ + if(!Chain) + return Chain; + GetGUID(Chain); + var Item = {guid:Chain.guid, id:Chain.id, chainid:Chain.id, bSave:Chain.LoadDB, FindBlockDB:Chain.FindBlockDB, GetFindDB:Chain.GetFindDB(), + BlockNum:Chain.BlockNumStart, Hash:GetHexFromAddresShort(Chain.HashStart), Comment1:Chain.Comment1, Comment2:Chain.Comment2, + StopSend:Chain.StopSend, SumPow:0, Info:Chain.Info, IsSum:Chain.IsSum, Main:bMain, }; + if(Chain.IsSumStart) + { + Item.SumHash = Item.Hash; + Item.Hash = "-------"; + } + if(Chain.RootChain) + { + var rootChain = Chain.GetRootChain(); + if(rootChain) + { + Item.rootid = rootChain.id; + if(!bWasRecursive) + Item.root = CopyChainDraw(rootChain, true); + } + } + else + Item.rootid = ""; + if(Chain.BlockHead) + { + Item.HashMaxStr = GetGUID(Chain.BlockHead); + Item.BlockNumMax = Chain.BlockHead.BlockNum; + } + else + { + Item.HashMaxStr = "------"; + } + return Item; +}; + +function AddChainList(arrLoadedChainList,LoadedChainList,bMain) +{ + for(var chain of LoadedChainList) + { + if(chain) + { + arrLoadedChainList.push(CopyChainDraw(chain, false, bMain)); + } + } +}; + +function AddMapList(arrLoadedBlocks,type,MapMapLoaded,MainChains) +{ + for(var key in MapMapLoaded) + { + var map = MapMapLoaded[key]; + if(map) + { + for(var key in map) + { + var Block = map[key]; + if(key.substr(1, 1) === ":") + continue; + if(!Block.Send || type === "reload") + { + arrLoadedBlocks.push(CopyBlockDraw(Block, MainChains)); + Block.Send = true; + } + } + } + } +}; +var MapFileHTML5 = {}; + +function SendWebFile(response,name,StrCookie,bParsing) +{ + var type = name.substr(name.length - 4, 4); + if(type.substr(0, 1) === ".") + type = type.substr(1); + var Path = "./" + name; + if(!fs.existsSync(Path)) + { + if(type === "ico") + { + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(); + return ; + } + var data = "Not found: " + name; + response.end(data); + return ; + } + var StrContentType = ContenTypeMap[type]; + if(!StrContentType || StrContentType === "text/html") + { + var Headers = {'Content-Type':'text/html', "X-Frame-Options":"sameorigin"}; + if(StrCookie) + Headers['Set-Cookie'] = StrCookie; + response.writeHead(200, Headers); + } + else + { + if(StrContentType === "application/font-woff") + { + response.writeHead(200, {'Content-Type':StrContentType, 'Access-Control-Allow-Origin':"*"}); + } + else + response.writeHead(200, {'Content-Type':StrContentType}); + } + if(bParsing && StrContentType === "text/html") + { + var data = GetFileHTMLWithParsing(Path); + response.end(data); + return ; + } + const stream = fs.createReadStream(Path); + setTimeout(function () + { + stream.close(); + stream.push(null); + stream.read(0); + }, 100000); + stream.pipe(response); +}; + +function GetFileHTMLWithParsing(Path) +{ + var data = global.SendHTMLMap[Path]; + if(!data) + { + global.SendHTMLMap[Path] = "-recursion-"; + data = String(fs.readFileSync(Path)); + data = ParseTag(data, "File", 0); + data = ParseTag(data, "Edit", 1); + global.SendHTMLMap[Path] = data; + } + return data; +}; +var glEditNum = 0; + +function ParseTag(Str,TagName,bEdit) +{ + var bWasInject = 0; + var data = ""; + var index = 0; + while(index >= 0) + { + index = Str.indexOf("{{" + TagName + "=", index); + if(index >= 0) + { + var index2 = Str.indexOf("}}", index + 3 + TagName.length); + if(index2 < 0) + { + ToLog("Error teg " + TagName + " in " + Path); + break; + } + var Delta = index2 - index; + if(Delta > 210) + { + ToLog("Error length (more 200) teg File in " + Path); + break; + } + var Path2 = Str.substring(index + 3 + TagName.length, index + Delta); + data += Str.substring(0, index); + if(bEdit) + { + if(!bWasInject) + { + data = data + GetFileSimple("./SITE/JS/web-edit.html"); + bWasInject = 1; + } + glEditNum++; + data += "
"; + data += GetFileHTMLFromMarkdown(Path2); + data += "
"; + data += ""; + } + else + { + data += GetFileHTMLWithParsing(Path2); + } + Str = Str.substring(index2 + 2); + index = 0; + } + } + data += Str; + return data; +}; +var MarkLib; + +function GetFileHTMLFromMarkdown(Path) +{ + var data = global.SendHTMLMap[Path]; + if(!data) + { + if(MarkLib === undefined) + MarkLib = require("../HTML/JS/marked.js"); + var Str = ""; + if(fs.existsSync(Path)) + Str = String(fs.readFileSync(Path)); + data = MarkLib(Str); + global.SendHTMLMap[Path] = data; + } + return data; +}; + +function GetFileSimple(Path) +{ + var Key = "GetFileSimple-" + Path; + var data = global.SendHTMLMap[Key]; + if(!data) + { + data = String(fs.readFileSync(Path)); + global.SendHTMLMap[Key] = data; + } + return data; +}; + +function SaveFileSimple(Path,Str) +{ + global.SendHTMLMap = {}; + var Key = "GetFileSimple-" + Path; + global.SendHTMLMap[Key] = Str; + SaveToFile(Path, Str); +}; +global.SendHTMLMap = {}; +global.SendWebFile = SendWebFile; +global.GetFileHTMLWithParsing = GetFileHTMLWithParsing; +global.GetFileHTMLFromMarkdown = GetFileHTMLFromMarkdown; +global.GetFileSimple = GetFileSimple; +global.SaveFileSimple = SaveFileSimple; + +function GetStrTime(now) +{ + if(!now) + now = GetCurrentTime(0); + var Str = "" + now.getHours().toStringZ(2); + Str = Str + ":" + now.getMinutes().toStringZ(2); + Str = Str + ":" + now.getSeconds().toStringZ(2); + return Str; +}; + +function OnGetData(arg) +{ + var response = {end:function () + { + }, writeHead:function () + { + }, }; + var Path = arg.path; + var obj = arg.obj; + if(Path.substr(0, 1) === "/") + Path = Path.substr(1); + var params = Path.split('/', 5); + var Ret; + var F = HTTPCaller[params[0]]; + if(F) + { + if(obj) + Ret = F(obj); + else + Ret = F(params[1], params[2], params[3]); + } + else + { + Ret = {result:0}; + } + return Ret; +}; + +function parseCookies(rc) +{ + var list = {}; + rc && rc.split(';').forEach(function (cookie) + { + var parts = cookie.split('='); + list[parts.shift().trim()] = decodeURI(parts.join('=')); + }); + return list; +}; +global.SetSafeResponce = SetSafeResponce; + +function SetSafeResponce(response) +{ + if(!response.Safe) + { + response.Safe = 1; + response._end = response.end; + response._writeHead = response.writeHead; + response.end = function () + { + try + { + if(arguments && arguments[0] && arguments[0].length) + { + ADD_TO_STAT("HTTP_SEND", arguments[0].length); + if(response.DetailStatName) + ADD_TO_STAT("HTTP_SEND" + response.DetailStatName, arguments[0].length); + } + response._end.apply(response, arguments); + } + catch(e) + { + ToError("H##1"); + ToError(e); + } + }; + response.writeHead = function () + { + try + { + response._writeHead.apply(response, arguments); + } + catch(e) + { + ToError("H##2"); + ToError(e); + } + }; + } +}; +if(global.HTTP_PORT_NUMBER) +{ + var ClientTokenMap = {}; + var ClientIPMap = {}; + setInterval(function () + { + ClientTokenMap = {}; + }, 24 * 3600 * 1000); + var port = global.HTTP_PORT_NUMBER; + var HTTPServer = http.createServer(function (request,response) + { + if(!request.headers) + return ; + if(!request.socket || !request.socket.remoteAddress) + return ; + var remoteAddress = request.socket.remoteAddress.replace(/^.*:/, ''); + if(remoteAddress === "1") + remoteAddress = "127.0.0.1"; + var CheckPassword; + if(!global.HTTP_PORT_PASSWORD && remoteAddress.indexOf("127.0.0.1") < 0) + return ; + if(global.HTTP_IP_CONNECT && remoteAddress.indexOf("127.0.0.1") < 0 && global.HTTP_IP_CONNECT.indexOf(remoteAddress) < 0) + return ; + CheckPassword = 1; + SetSafeResponce(response); + if(!global.SERVER) + { + response.writeHead(404, {'Content-Type':'text/html'}); + response.end(""); + return ; + } + var fromURL = url.parse(request.url); + var Path = querystring.unescape(fromURL.path); + if(!ClientIPMap[remoteAddress]) + { + ClientIPMap[remoteAddress] = 1; + ToLog("CONNECT TO HTTP ACCESS FROM: " + remoteAddress, 0); + ToLog("Path: " + Path, 0); + } + if(CheckPassword && global.HTTP_PORT_PASSWORD) + { + var StrPort = ""; + if(global.HTTP_PORT_NUMBER !== 80) + StrPort = global.HTTP_PORT_NUMBER; + var cookies = parseCookies(request.headers.cookie); + var cookies_token = cookies["token" + StrPort]; + var cookies_hash = cookies["hash" + StrPort]; + if(cookies_token && cookies_hash && ClientTokenMap[cookies_token] === 0) + { + if(cookies_hash.substr(0, 4) !== "0000") + { + SendWebFile(response, "./HTML/password.html", "token" + StrPort + "=" + cookies_token + ";path=/"); + return ; + } + var nonce = 0; + var index = cookies_hash.indexOf("-"); + if(index > 0) + { + nonce = parseInt(cookies_hash.substr(index + 1)); + if(!nonce) + nonce = 0; + } + var hash = ClientHex(cookies_token + "-" + global.HTTP_PORT_PASSWORD, nonce); + if(hash === cookies_hash) + { + ClientTokenMap[cookies_token] = 1; + } + else + { + SendWebFile(response, "./HTML/password.html", "token" + StrPort + "=" + cookies_token + ";path=/"); + return ; + } + } + if(!cookies_token || !ClientTokenMap[cookies_token]) + { + var StrToken = GetHexFromArr(crypto.randomBytes(16)); + ClientTokenMap[StrToken] = 0; + SendWebFile(response, "./HTML/password.html", "token" + StrPort + "=" + StrToken + ";path=/"); + return ; + } + } + var params = Path.split('/', 6); + params.splice(0, 1); + var Type = request.method; + if(Type === "POST") + { + let Response = response; + let Params = params; + let postData = ""; + request.addListener("data", function (postDataChunk) + { + if(postData.length < 70000 && postDataChunk.length < 70000) + postData += postDataChunk; + }); + request.addListener("end", function () + { + var Data; + try + { + Data = JSON.parse(postData); + } + catch(e) + { + ToError("--------Error data parsing : " + Params[0] + " " + postData.substr(0, 200)); + Response.writeHead(405, {'Content-Type':'text/html'}); + Response.end("Error data parsing"); + } + if(Params[0] === "HTML") + DoCommand(response, Type, Path, [Params[1], Data], remoteAddress); + else + DoCommand(response, Type, Path, [Params[0], Data], remoteAddress); + }); + } + else + { + DoCommand(response, Type, Path, params, remoteAddress); + } + }).listen(port, LISTEN_IP, function () + { + global.HTTP_SERVER_START_OK = 1; + ToLog("Run HTTP-server on " + LISTEN_IP + ":" + port); + }); + HTTPServer.on('error', function (err) + { + ToError("H##3"); + ToError(err); + }); +} +if(global.ELECTRON) +{ + const ipcMain = require('electron').ipcMain; + ipcMain.on('GetData', function (event,arg) + { + event.returnValue = OnGetData(arg); + }); +} +exports.SendData = OnGetData; + +function RunConsole(StrRun) +{ + var Str = fs.readFileSync("./EXPERIMENTAL/_run-console.js", {encoding:"utf8"}); + if(StrRun) + Str += "\n" + StrRun; + try + { + var ret = eval(Str); + } + catch(e) + { + ret = e.message + "\n" + e.stack; + } + return ret; +}; +var WebWalletUser = {}; + +function GetUserContext(Params) +{ + if(typeof Params.Key !== "string") + Params.Key = "" + Params.Key; + var StrKey = Params.Key + "-" + Params.Session; + var Context = WebWalletUser[StrKey]; + if(!Context) + { + Context = {NumDappInfo:0, PrevDappInfo:"", NumAccountList:0, PrevAccountList:"", LastTime:0, FromEventNum:0}; + WebWalletUser[StrKey] = Context; + } + return Context; +}; +global.GetUserContext = GetUserContext; +global.EventNum = 0; +global.EventMap = {}; + +function AddDappEventToGlobalMap(Data) +{ + global.EventNum++; + Data.EventNum = global.EventNum; + Data.EventDate = Date.now(); + var Arr = global.EventMap[Data.Smart]; + if(!Arr) + { + Arr = []; + global.EventMap[Data.Smart] = Arr; + } + if(Arr.length < 1000) + { + Arr.push(Data); + } +}; +global.AddDappEventToGlobalMap = AddDappEventToGlobalMap; + +function DeleteOldEvents(SmartNum) +{ + var CurDate = Date.now(); + var Arr = global.EventMap[SmartNum]; + while(Arr && Arr.length) + { + var Event = Arr[0]; + if(!Event || CurDate - Event.EventDate < 5000) + { + break; + } + Arr.splice(0, 1); + } +}; + +function GetEventArray(SmartNum,Context) +{ + var Arr = global.EventMap[SmartNum]; + if(!Arr || Arr.length === 0) + { + return []; + } + var FromEventNum = Context.FromEventNum; + var ArrRet = []; + for(var i = 0; i < Arr.length; i++) + { + var Event = Arr[i]; + if(Event.EventNum >= FromEventNum) + ArrRet.push(Event); + } + if(ArrRet.length) + { + Context.FromEventNum = ArrRet[ArrRet.length - 1].EventNum + 1; + } + return ArrRet; +}; +HTTPCaller.GetHashRate = function (ArrParams) +{ + var CurBlockNum = GetCurrentBlockNumByTime(); + var ResArr = []; + for(var i = 0; i < ArrParams.length; i++) + { + var Item = ArrParams[i]; + if(!Item) + { + ResArr[i] = undefined; + continue; + } + if(Item.BlockNum1 < 0) + Item.BlockNum1 = 0; + if(Item.BlockNum2 > CurBlockNum) + Item.BlockNum2 = CurBlockNum; + var Delta = Item.BlockNum2 - Item.BlockNum1; + var Count = Delta; + if(Count > 20) + Count = 20; + else + if(Count <= 0) + Count = 1; + var StepDelta = Math.floor(Delta / Count); + if(StepDelta < 1) + StepDelta = 1; + var CountAvg = 10; + if(i === ArrParams.length - 1) + CountAvg = 3; + var ItervalArr = []; + for(var Num = Item.BlockNum1; Num < Item.BlockNum2; Num += StepDelta) + { + var Power; + var Item2 = BlockTree.LoadValue(Num, 1); + if(Item2 && i !== ArrParams.length - 1) + { + Power = Item2.Power; + } + else + { + var Sum = 0; + var CountSum = 0; + for(var d = 0; d < CountAvg; d++) + { + var Block = SERVER.ReadBlockHeaderDB(Num + d); + if(Block) + { + CountSum++; + Sum += GetPowPower(Block.PowHash); + } + } + if(!CountSum) + CountSum = 1; + Power = Math.floor(Sum / CountSum); + if(Power) + BlockTree.SaveValue(Num, {BlockNum:Num, Power:Power}); + } + ItervalArr.push(Power); + } + ResArr[i] = ItervalArr; + } + return {result:1, ItervalArr:ResArr}; +}; diff --git a/Source/core/library.js b/Source/core/library.js new file mode 100644 index 0000000..ceaf7f0 --- /dev/null +++ b/Source/core/library.js @@ -0,0 +1,483 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var fs = require('fs'); +const os = require('os'); +require("./constant.js"); +if(global.USE_PARAM_JS) +{ + var PathParams = GetCodePath("../extern-run.js"); + if(fs.existsSync(PathParams)) + try + { + require(PathParams); + } + catch(e) + { + console.log(e); + } +} +require("./log.js"); +Number.prototype.toStringZ = function (count) +{ + var strnum = this.toString(); + if(strnum.length > count) + count = strnum.length; + else + strnum = "0000000000" + strnum; + return strnum.substring(strnum.length - count, strnum.length); +}; +String.prototype.right = function (count) +{ + if(this.length > count) + return this.substr(this.length - count, count); + else + return this.substr(0, this.length); +}; +if(fs.existsSync("./lib/bintrees")) + global.RBTree = require("../lib/bintrees").RBTree; +else + global.RBTree = require('bintrees').RBTree; +if(fs.existsSync("./lib/ntp-client")) + global.ntpClient = require('../lib/ntp-client'); +else + global.ntpClient = require('ntp-client'); +global.Stun = require('stun'); +global.ZIP = require("zip"); +var strOS = os.platform() + "_" + os.arch(); +if(global.NWMODE) + strOS = strOS + "-nw" + global.NWVERSION; +if(fs.existsSync("./lib/secp256k1/" + strOS + "/secp256k1.node")) +{ + try + { + global.secp256k1 = require('../lib/secp256k1/' + strOS + '/secp256k1.node'); + } + catch(e) + { + } +} +if(!global.secp256k1) +{ + global.secp256k1 = require('secp256k1'); +} +require('../HTML/JS/terahashlib.js'); +require("./crypto-library"); +global.BufLib = require("../core/buffer"); +require('../HTML/JS/sha3.js'); +require('../HTML/JS/coinlib.js'); +global.GetCurrentBlockNumByTime = function GetCurrentBlockNumByTime() +{ + var CurTimeNum = GetCurrentTime() - FIRST_TIME_BLOCK; + var StartBlockNum = Math.trunc((CurTimeNum + CONSENSUS_PERIOD_TIME) / CONSENSUS_PERIOD_TIME); + return StartBlockNum; +}; +global.DelDir = function (Path) +{ + if(Path.substr(Path.length - 1, 1) === "/") + Path = Path.substr(0, Path.length - 1); + if(fs.existsSync(Path)) + { + var arr = fs.readdirSync(Path); + for(var i = 0; i < arr.length; i++) + { + var name = Path + "/" + arr[i]; + if(fs.statSync(name).isDirectory()) + { + DelDir(name); + } + else + { + if(name.right(9) == "const.lst") + continue; + if(name.right(7) == "log.log") + continue; + fs.unlinkSync(name); + } + } + } +}; +global.SliceArr = function (arr,start,end) +{ + var ret = []; + for(var i = start; i < end; i++) + { + ret[i - start] = arr[i]; + } + return ret; +}; +var nRand = Math.floor(123 + Math.random() * 1000); + +function random(max) +{ + return Math.floor(Math.random() * max); +}; +global.random = random; +global.AddrLevelArrFromBegin = function (arr1,arr2) +{ + var Level = 0; + for(var i = 0; i < arr1.length; i++) + { + var a1 = arr1[i]; + var a2 = arr2[i]; + for(var b = 0; b < 8; b++) + { + if((a1 & 128) !== (a2 & 128)) + return Level; + a1 = a1 << 1; + a2 = a2 << 1; + Level++; + } + } + return Level; +}; +global.AddrLevelArr = function (arr1,arr2) +{ + var Level = 0; + for(var i = arr1.length - 1; i >= 0; i--) + { + var a1 = arr1[i]; + var a2 = arr2[i]; + for(var b = 0; b < 8; b++) + { + if((a1 & 1) !== (a2 & 1)) + return Level; + a1 = a1 >> 1; + a2 = a2 >> 1; + Level++; + } + } + return Level; +}; +global.SaveToFile = function (name,buf) +{ + var fs = require('fs'); + var file_handle = fs.openSync(name, "w"); + fs.writeSync(file_handle, buf, 0, buf.length); + fs.closeSync(file_handle); +}; +global.LoadParams = function (filename,DefaultValue) +{ + try + { + if(fs.existsSync(filename)) + { + var Str = fs.readFileSync(filename); + if(Str.length > 0) + return JSON.parse(Str); + } + } + catch(err) + { + TO_ERROR_LOG("MAINLIB", 100, "Error in file:" + filename + "\n" + err); + } + return DefaultValue; +}; +global.SaveParams = function (filename,data) +{ + SaveToFile(filename, Buffer.from(JSON.stringify(data, "", 4))); +}; +global.StartTime = function () +{ + global.TimeStart = GetCurrentTime(); +}; +global.FinishTime = function (Str) +{ + Str = Str || ""; + var TimeFinish = GetCurrentTime(); + var delta = TimeFinish - TimeStart; + console.log(Str + " time: " + delta + " ms"); +}; +global.CompareItemBufFD = function (a,b) +{ + if(a.FD !== b.FD) + return a.FD - b.FD; + else + return a.Position - b.Position; +}; +global.CompareArr33 = function (a,b) +{ + for(var i = 0; i < 33; i++) + { + if(a[i] !== b[i]) + return a[i] - b[i]; + } + return 0; +}; +global.CompareItemHashSimple = function (a,b) +{ + if(a.hash < b.hash) + return - 1; + else + if(a.hash > b.hash) + return 1; + else + return 0; +}; +global.CompareItemHash = function (a,b) +{ + var hasha = a.hash; + var hashb = b.hash; + for(var i = 0; i < hasha.length; i++) + { + if(hasha[i] !== hashb[i]) + return hasha[i] - hashb[i]; + } + return 0; +}; +global.CompareItemHash32 = function (a,b) +{ + var hasha = a.hash; + var hashb = b.hash; + for(var i = 0; i < 32; i++) + { + if(hasha[i] !== hashb[i]) + return hasha[i] - hashb[i]; + } + return 0; +}; +global.CompareItemHASH32 = function (a,b) +{ + var hasha = a.HASH; + var hashb = b.HASH; + for(var i = 0; i < 32; i++) + { + if(hasha[i] !== hashb[i]) + return hasha[i] - hashb[i]; + } + return 0; +}; +global.CompareItemHash33 = function (a,b) +{ + var hasha = a.hash; + var hashb = b.hash; + for(var i = 0; i < 33; i++) + { + if(hasha[i] !== hashb[i]) + return hasha[i] - hashb[i]; + } + return 0; +}; +global.CompareItemHashPow = function (a,b) +{ + return CompareArr(a.HashPow, b.HashPow); +}; +global.CompareItemTimePow = function (a,b) +{ + if(b.TimePow !== a.TimePow) + return b.TimePow - a.TimePow; + else + return CompareArr(a.HashPow, b.HashPow); +}; +global.LOAD_CONST = function () +{ + var Count = 0; + var constants = LoadParams(GetDataPath("const.lst"), {}); + if(constants) + { + for(var i = 0; i < CONST_NAME_ARR.length; i++) + { + var key = CONST_NAME_ARR[i]; + if(constants[key] !== undefined) + { + Count++; + global[key] = constants[key]; + } + } + } + return Count; +}; +var WasStartSaveConst = false; + +function SaveConst() +{ + var constants = {}; + for(var i = 0; i < CONST_NAME_ARR.length; i++) + { + var key = CONST_NAME_ARR[i]; + if(global[key] !== undefined) + constants[key] = global[key]; + } + SaveParams(GetDataPath("const.lst"), constants); + WasStartSaveConst = false; +}; +global.SAVE_CONST = function (bForce) +{ + if(bForce) + { + SaveConst(); + } + else + { + if(!WasStartSaveConst) + setTimeout(SaveConst, 10 * 1000); + WasStartSaveConst = true; + } +}; + +function CheckGlobalTime() +{ + ntpClient.getNetworkTime("pool.ntp.org", 123, function (err,NetTime) + { + if(err) + { + TO_ERROR_LOG("MAINLIB", 110, err); + return ; + } + var curTime = new Date; + global.DELTA_CURRENT_TIME = NetTime - curTime; + if(isNaN(global.DELTA_CURRENT_TIME) || typeof global.DELTA_CURRENT_TIME !== "number") + global.DELTA_CURRENT_TIME = 0; + else + if(Math.abs(global.DELTA_CURRENT_TIME) > 24 * 3600 * 1000) + global.DELTA_CURRENT_TIME = 0; + ToLog("Get global time: " + NetTime); + SAVE_CONST(); + }); + SAVE_CONST(); +}; +global.CheckGlobalTime = CheckGlobalTime; +global.GetDeltaCurrentTime = function () +{ + if(isNaN(global.DELTA_CURRENT_TIME) || typeof global.DELTA_CURRENT_TIME !== "number") + global.DELTA_CURRENT_TIME = 0; + return global.DELTA_CURRENT_TIME; +}; +global.GetStrTimeUTC = function (now) +{ + if(!global.GetCurrentTime) + return ":::"; + if(!now) + now = GetCurrentTime(); + var Str = "" + now.getUTCDate(); + Str = Str + "." + (1 + now.getUTCMonth()); + Str = Str + "." + now.getUTCFullYear(); + Str = Str + " " + now.getUTCHours(); + Str = Str + ":" + now.getUTCMinutes(); + Str = Str + ":" + now.getUTCSeconds(); + return Str; +}; +global.GetStrOnlyTimeUTC = function (now) +{ + if(!global.GetCurrentTime) + return ":::"; + if(!now) + now = GetCurrentTime(); + var Str; + Str = "" + now.getUTCHours().toStringZ(2); + Str = Str + ":" + now.getUTCMinutes().toStringZ(2); + Str = Str + ":" + now.getUTCSeconds().toStringZ(2); + return Str; +}; + +function GetSecFromStrTime(Str) +{ + var arr = Str.split(":"); + var Mult = 3600; + var Sum = 0; + for(var i = 0; i < arr.length; i++) + { + Sum += Mult * parseInt(arr[i]); + Mult = Mult / 60; + } + return Sum; +}; +global.GetSecFromStrTime = GetSecFromStrTime; +global.GetCurrentTime = function (Delta_Time) +{ + if(Delta_Time === undefined) + Delta_Time = GetDeltaCurrentTime(); + var curTime = new Date; + var Time = new Date(curTime - ( - Delta_Time)); + return Time; +}; + +function DateFromBlock(BlockNum) +{ + var Str; + var now = new Date(FIRST_TIME_BLOCK + BlockNum * 1000); + Str = now.toISOString(); + Str = Str.substr(0, Str.indexOf(".")); + Str = Str.replace("T", " "); + return Str; +}; +global.DateFromBlock = DateFromBlock; +var code_base = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\u0402\u0403\u201a\u0453\u201e\u2026\u2020\u2021\u20ac\u2030\u0409\u2039\u040a\u040c\u040b\u040f\u0452\u2018\u2019\u201c\u201d\u2022\u2013\u2014\ufffd\u2122\u0459\u203a\u045a\u045c\u045b\u045f\xa0\u040e\u045e\u0408\xa4\u0490\xa6\xa7\u0401\xa9\u0404\xab\xac\xad\xae\u0407\xb0\xb1\u0406\u0456\u0491\xb5\xb6\xb7\u0451\u2116\u0454\xbb\u0458\u0405\u0455\u0457\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418\u0419\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0422\u0423\u0424\u0425\u0426\u0427\u0428\u0429\u042a\u042b\u042c\u042d\u042e\u042f\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438\u0439\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0442\u0443\u0444\u0445\u0446\u0447\u0448\u0449\u044a\u044b\u044c\u044d\u044e\u044f'; +global.NormalizeName = function (Name) +{ + var Str = ""; + for(var i = 0; i < Name.length; i++) + { + var code = Name.charCodeAt(i); + if(code >= 32) + Str += code_base.charAt(code - 32); + } + return Str; +}; +var glEvalMap = {}; + +function CreateEval(formula,StrParams) +{ + var Ret = glEvalMap[formula]; + if(!Ret) + { + eval("function M(" + StrParams + "){return " + formula + "}; Ret=M;"); + glEvalMap[formula] = Ret; + } + return Ret; +}; +global.CreateEval = CreateEval; +var CPU_Count = os.cpus().length; + +function GetCountMiningCPU() +{ + if(global.COUNT_MINING_CPU) + return global.COUNT_MINING_CPU; + else + { + return CPU_Count - 1; + } +}; +global.GetCountMiningCPU = GetCountMiningCPU; +var AddTrMap = {}; +AddTrMap[ - 6] = "Inner node error"; +AddTrMap[ - 5] = "Bad block num"; +AddTrMap[ - 4] = "Bad type transaction"; +AddTrMap[ - 3] = "Bad time"; +AddTrMap[ - 2] = "Bad PoW"; +AddTrMap[ - 1] = "Bad length"; +AddTrMap[0] = "Not add"; +AddTrMap[1] = "OK"; +AddTrMap[2] = "Update OK"; +AddTrMap[3] = "Was send"; +AddTrMap[4] = "Added to timer"; +global.AddTrMap = AddTrMap; + +function GrayConnect() +{ + if(global.NET_WORK_MODE && !NET_WORK_MODE.UseDirectIP) + return 1; + else + return 0; +}; +global.GrayConnect = GrayConnect; +var ResConst = LOAD_CONST(); +if(global.START_SERVER) +{ + if(!ResConst) + { + CheckGlobalTime(); + } + else + if(global.CHECK_GLOBAL_TIME) + { + CheckGlobalTime(); + } +} diff --git a/Source/core/log-strict.js b/Source/core/log-strict.js new file mode 100644 index 0000000..e22ac07 --- /dev/null +++ b/Source/core/log-strict.js @@ -0,0 +1,38 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var fs = require('fs'); + +function CheckSizeLogFile(file_name,file_name_prev) +{ + "use strict"; + let FILE_NAME_LOG = file_name; + let FILE_NAME_LOG_PREV = file_name_prev; + setInterval(function () + { + try + { + var stat = fs.statSync(FILE_NAME_LOG); + if(stat.size > MAX_SIZE_LOG) + { + if(fs.existsSync(FILE_NAME_LOG_PREV)) + { + fs.unlinkSync(FILE_NAME_LOG_PREV); + } + fs.renameSync(FILE_NAME_LOG, FILE_NAME_LOG_PREV); + ToLog("truncate logfile ok"); + } + } + catch(err) + { + } + }, 60000); +}; +global.CheckSizeLogFile = CheckSizeLogFile; diff --git a/Source/core/log.js b/Source/core/log.js new file mode 100644 index 0000000..37102d7 --- /dev/null +++ b/Source/core/log.js @@ -0,0 +1,267 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +require("./constant.js"); +var fs = require("fs"); +require("./log-strict.js"); +var file_name_info = GetDataPath("info.log"), file_name_infoPrev = GetDataPath("info-prev.log"); +CheckSizeLogFile(file_name_info, file_name_infoPrev); +var file_name_log = GetDataPath("log.log"), file_name_logPrev = GetDataPath("log-prev.log"); +CheckSizeLogFile(file_name_log, file_name_logPrev); +var file_name_log_web = GetDataPath("web.log"), file_name_log_webPrev = GetDataPath("web-prev.log"); +CheckSizeLogFile(file_name_log_web, file_name_log_webPrev); +var file_name_error = GetDataPath("err.log"), file_name_errorPrev = GetDataPath("err-prev.log"); +CheckSizeLogFile(file_name_error, file_name_errorPrev); +var StartStatTime, file_name_error_tx = GetDataPath("err-tx.log"), file_name_error_txPrev = GetDataPath("err-tx-prev.log"); + +function ToLogFile(e,t,r) +{ + t instanceof Error && (t = t.message + "\n" + t.stack), global.START_SERVER || (t = global.PROCESS_NAME + ": " + t), "MAIN" !== global.PROCESS_NAME && process.send ? process.send({cmd:"log", + message:t}) : (console.log(START_PORT_NUMBER + ": " + GetStrOnlyTime() + ": " + t), r || SaveToLogFileSync(e, t)); +}; + +function ToLogClient(e,t,r) +{ + e && (ToLogFile(file_name_log, e), t = t || "", ArrLogClient.push({text:GetStrOnlyTime() + " " + e, key:t, final:r}), 13 < ArrLogClient.length && ArrLogClient.shift()); +}; +CheckSizeLogFile(file_name_error_tx, file_name_error_txPrev), global.ToLog = function (e,t) +{ + void 0 === t && (t = 1), t && t > global.LOG_LEVEL || (global.ALL_LOG_TO_CLIENT ? ToLogClient(e, void 0, void 0) : ToLogFile(file_name_log, + e)); +}, global.WEB_LOG = 0, global.ToLogWeb = function (e) +{ + global.WEB_LOG && SaveToLogFileSync(file_name_log_web, e); +}, global.SmallAddr = function (e) +{ + return e.substr(0, 5); +}, global.ToErrorTrace = function (e) +{ + ToError(e + ":" + (new Error).stack); +}, global.ToLogTrace = function (e) +{ + ToErrorTrace(e); +}, global.ToInfo = function (e) +{ + ToLogFile(file_name_info, e, 1); +}, global.ToError = function (e) +{ + ToLogFile(file_name_error, e); +}, global.ToErrorTx = function (e) +{ + SaveToLogFileSync(file_name_error_tx, e), ToLog(e); +}, global.ArrLogClient = [], global.ToLogClient = ToLogClient, global.ToLogClient0 = ToLogClient; +var CONTEXT_STATS = {Total:{}, Interval:[]}, CONTEXT_ERRORS = {Total:{}, Interval:[]}, CurStatIndex = 0; + +function GetCurrentStatIndex() +{ + var e = 2 * MAX_STAT_PERIOD + 2; + return CurStatIndex % e; +}; + +function ResizeArrMax(e) +{ + for(var t = [], r = Math.trunc(e.length / 2), o = 0; o < r; o++) + t[o] = Math.max(e[2 * o], e[2 * o + 1]); + return t; +}; + +function ResizeArrAvg(e) +{ + for(var t = [], r = Math.trunc(e.length / 2), o = 0; o < r; o++) + t[o] = (e[2 * o] + e[2 * o + 1]) / 2; + return t; +}; + +function ResizeArr(e) +{ + for(var t = [], r = Math.trunc(e.length / 2), o = 0; o < r; o++) + t[o] = e[2 * o]; + return t; +}; + +function GetDiagramData(e,t) +{ + var r, o = 2 * MAX_STAT_PERIOD + 2; + r = "MAX:" === t.substr(0, 4); + for(var n, a = MAX_STAT_PERIOD, l = (GetCurrentStatIndex() - a + o) % o, i = (e.Total, []), T = void 0, g = l; g < l + a; g++) + { + var S = g % o; + if(n = e.Interval[S]) + { + var _ = n[t]; + void 0 !== _ ? r ? i.push(_) : (void 0 !== T ? i.push(_ - T) : i.push(_), T = _) : i.push(0); + } + } + return i; +}; + +function CalcInterval(e,t,r) +{ + for(var o, n = 2 * MAX_STAT_PERIOD + 2, a = {}, l = (t - r + n) % n, i = e.Total, T = l; T < l + r; T++) + { + var g = T % n; + if(o = e.Interval[g]) + break; + } + if(o) + for(var S in i) + "MAX:" === S.substr(0, 4) ? a[S] = 0 : void 0 === o[S] ? a[S] = i[S] : a[S] = i[S] - o[S]; + return a; +}; + +function AddToStatContext(e,t,r) +{ + void 0 === r && (r = 1); + var o = e.Total[t]; + o = o || 0, "MAX:" === t.substr(0, 4) ? o = Math.max(o, r) : o += r, e.Total[t] = o, StartStatTime = StartStatTime || GetCurrentTime(0); +}; + +function CopyStatInterval(e,t) +{ + var r = e.Interval[t]; + r || (r = {}, e.Interval[t] = r); + var o = e.Total; + for(var n in o) + r[n] = o[n], "MAX:" === n.substr(0, 4) && (o[n] = 0); +}; + +function SaveToLogFileAsync(e,o) +{ + fs.open(e, "a", void 0, function (e,r) + { + if(e) + console.log("Ошибка открытия лог-файла ошибок"); + else + { + var t = GetStrTime() + " : " + o + "\r\n"; + fs.write(r, t, null, "utf8", function (e,t) + { + e ? console.log("Ошибка записи в лог-файл ошибок!") : fs.close(r, function (e) + { + e && console.log(e); + }); + }); + } + }); +}; + +function SaveToLogFileSync(e,t) +{ + try + { + var r = GetStrTime() + " : " + t + "\r\n", o = fs.openSync(e, "a"); + fs.writeSync(o, r, null, "utf8"), fs.closeSync(o); + } + catch(e) + { + console.log(e.message); + } +}; +global.PrepareStatEverySecond = function () +{ + CurStatIndex++; + var e = GetCurrentStatIndex(); + CopyStatInterval(CONTEXT_STATS, e), CopyStatInterval(CONTEXT_ERRORS, e); +}, global.TO_ERROR_LOG = function (e,t,r,o,n,a) +{ + r instanceof Error && (r = r.message + "\n"), "rinfo" === o ? r += " from: " + n.address + ":" + n.port : "node" === o && (r += " from: " + n.ip + ":" + n.port); + var l = e + ":" + t; + ToError(" ==ERROR== " + l + " " + r), AddToStatContext(CONTEXT_ERRORS, l), ADD_TO_STAT("ERRORS"); +}, global.HASH_RATE = 0, global.ADD_HASH_RATE = function (e) +{ + e /= 1e6, global.HASH_RATE += e, ADD_TO_STAT("HASHRATE", e); +}, global.GET_STAT = function (e) +{ + var t = CONTEXT_STATS.Total[e]; + return t = t || 0; +}, global.ADD_TO_STAT_TIME = function (e,t,r) +{ + if(global.STAT_MODE) + { + if(r && 2 !== global.STAT_MODE) + return ; + var o = process.hrtime(t), n = 1e3 * o[0] + o[1] / 1e6; + ADD_TO_STAT(e, n); + } +}, global.ADD_TO_STAT = function (e,t,r) +{ + if(global.STAT_MODE) + { + if(r && 2 !== global.STAT_MODE) + return ; + AddToStatContext(CONTEXT_STATS, e, t); + } +}, global.GET_STATDIAGRAMS = function (e) +{ + GetCurrentTime(); + var t = GetCurrentStatIndex(); + if(!e || !e.length) + return []; + for(var r = [], o = 0; o < e.length; o++) + { + var n = e[o], a = GetDiagramData(CONTEXT_STATS, n); + r.push({name:n, maxindex:t, arr:a, starttime:StartStatTime - 0, steptime:1}); + } + var l = void 0; + for(o = 0; o < r.length; o++) + { + 0 < (T = r[o].arr).length && (void 0 === l || T.length < l) && (l = T.length); + } + for(o = 0; o < r.length; o++) + { + var i = r[o], T = i.arr; + l && T.length > l && (T = T.slice(T.length - l)), l && 0 <= ",POWER_MY_WIN,POWER_BLOCKCHAIN,".indexOf("," + i.name + ",") && (T = SERVER.GetStatBlockchain(i.name, + l)); + for(var g = 0, S = 0; S < T.length; S++) + T[S] && (g += T[S]); + 0 < T.length && (g /= T.length); + var _ = 1; + if("MAX:" === i.name.substr(0, 4)) + for(; 500 <= T.length; ) + T = ResizeArrMax(T), _ *= 2; + else + for(; 500 <= T.length; ) + T = ResizeArrAvg(T), _ *= 2; + i.AvgValue = g, i.steptime = _, i.arr = T.slice(1); + } + return r; +}, global.GET_STATS = function (e) +{ + var t = GetCurrentTime(), r = GetCurrentStatIndex(); + return {stats:{Counter:CONTEXT_STATS.Total, Counter10S:CalcInterval(CONTEXT_STATS, r, 10), Counter10M:CalcInterval(CONTEXT_STATS, + r, 600)}, errors:{Counter:CONTEXT_ERRORS.Total, Counter10S:CalcInterval(CONTEXT_ERRORS, r, 10), Counter10M:CalcInterval(CONTEXT_ERRORS, + r, 600)}, period:(t - StartStatTime) / 1e3, Confirmation:[]}; +}, global.StartCommonStat = function () +{ + for(var e in CONTEXT_STATS.Total) + return ; + ClearCommonStat(); +}, global.ClearCommonStat = function () +{ + StartStatTime = void (CurStatIndex = 0), CONTEXT_STATS = {Total:{}, Interval:[]}, CONTEXT_ERRORS = {Total:{}, Interval:[]}, + global.HASH_RATE = 0, SERVER.ClearStat(); +}, global.ResizeArrAvg = ResizeArrAvg, global.ResizeArrMax = ResizeArrMax, DEBUG_MODE ? global.TO_DEBUG_LOG = function (e,t,r,o) +{ + DEBUG_MODE && ("rinfo" === t && (e += " from: " + r.address + ":" + r.port + " - " + o.length), ToLog(e)); +} : global.TO_DEBUG_LOG = function (e,t,r,o) +{ +}, global.GetStrOnlyTime = function (e) +{ + if(!global.GetCurrentTime) + return ":::"; + var t = "" + (e = e || GetCurrentTime()).getHours().toStringZ(2); + return t = (t = (t = t + ":" + e.getMinutes().toStringZ(2)) + ":" + e.getSeconds().toStringZ(2)) + "." + e.getMilliseconds().toStringZ(3); +}, global.GetStrTime = function (e) +{ + if(!global.GetCurrentTime) + return ":::"; + var t = "" + (e = e || GetCurrentTime()).getDate().toStringZ(2); + return t = (t = (t = (t = (t = (t = t + "." + (1 + e.getMonth()).toStringZ(2)) + "." + e.getFullYear()) + " " + e.getHours().toStringZ(2)) + ":" + e.getMinutes().toStringZ(2)) + ":" + e.getSeconds().toStringZ(2)) + "." + e.getMilliseconds().toStringZ(3); +}; diff --git a/Source/core/node.js b/Source/core/node.js new file mode 100644 index 0000000..6ef1dd6 --- /dev/null +++ b/Source/core/node.js @@ -0,0 +1,614 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require("./library.js"); +const net = require("net"); +var ConnectIDCount = 1; +module.exports = class CNode +{ + constructor(addrStr, ip, port) + { + this.addrStr = addrStr + this.ip = ip.trim() + this.port = port + this.StartFindList = 0 + this.WhiteConnect = 0 + this.GrayConnect = 0 + this.POW = 0 + this.FirstTime = 0 + this.FirstTimeStr = "" + this.LastTime = 0 + this.LastTimeError = 0 + this.LastTimeTransfer = 0 + this.FromIP = undefined + this.FromPort = undefined + this.Active = false + this.Hot = false + this.CanHot = false + this.CountChildConnect = 0 + this.BlockProcessCount = 0 + this.VersionOK = false + this.VersionNum = 0 + this.Delete = 0 + this.DeltaBan = 300 + this.Name = "" + this.Info = "" + this.PrevInfo = "" + this.StartTimeHot = 0 + this.NextHotDelta = 1000 + this.ResetNode() + } + ResetNode() + { + this.LastTimeGetNode = 0 + this.DeltaGlobTime = 0 + this.CountDeltaTime = 0 + this.DeltaTime = 1000 + this.SumDeltaTime = 0 + this.TransferCount = 0 + this.StopGetBlock = 0 + this.LevelCount = 0 + this.LevelEnum = 100 + this.TimeMap = {} + this.bInit = 1 + this.INFO = {} + this.DoubleConnectCount = 0 + this.StartTimeConnect = 0 + this.NextConnectDelta = 1000 + this.StartTimeGetNodes = 0 + this.NextGetNodesDelta = 1000 + this.PingStart = 0 + this.NextPing = MIN_PERIOD_PING + this.SendBlockArr = [] + this.LoadBlockArr = [] + this.SendBlockCount = 0 + this.LoadBlockCount = 0 + this.SendBlockCountAll = 0 + this.LoadBlockCountAll = 0 + this.WantHardTrafficArr = [] + this.WantHardTraffic = 0 + this.CanHardTraffic = 0 + this.BufWriteLength = 0 + this.BufWrite = Buffer.alloc(0) + this.SendPacket = new RBTree(function (a,b) + { + return b.PacketNum - a.PacketNum; + }) + this.ConnectCount = 0 + this.TrafficArr = [] + this.SendTrafficCurrent = 0 + this.SendTrafficLimit = 0 + this.ErrCount = 0 + this.ErrCountAll = 0 + var Prioritet = this.BlockProcessCount; + SERVER.SetNodePrioritet(this, Prioritet) + this.SendPacketNum = 0 + } + ConnectStatus() + { + if(this.Socket) + return GetSocketStatus(this.Socket); + else + return 0; + } + CreateConnect() + { + delete SERVER.BAN_IP[this.ip] + let NODE = this; + if(NODE.ConnectStatus()) + { + if(NODE.ConnectStatus() === 100) + SERVER.AddNodeToActive(NODE) + return ; + } + AddNodeInfo(NODE, "===CreateConnect===") + CloseSocket(NODE.Socket, "CreateConnect") + NODE.SocketStart = Date.now() + NODE.Socket = net.createConnection(NODE.port, NODE.ip, function () + { + if(NODE.Socket) + { + socketInit(NODE.Socket, "s") + AddNodeInfo(NODE, "OK connected *" + NODE.Socket.ConnectID) + NODE.Socket.ConnectToServer = true + SetSocketStatus(NODE.Socket, 2) + } + }) + SetSocketStatus(NODE.Socket, 1) + NODE.Socket.Node = NODE + NODE.Socket.ConnectID = "~C" + ConnectIDCount + ConnectIDCount++ + this.SetEventsProcessing(NODE.Socket, 0) + } + CreateReconnection() + { + let NODE = this; + AddNodeInfo(NODE, "===CreateReconnection===") + CloseSocket(NODE.Socket2, "CreateReconnection") + NODE.SocketStart = Date.now() + NODE.Socket2 = net.createConnection(NODE.port, NODE.ip, function () + { + if(NODE.Socket2) + { + socketInit(NODE.Socket2, "s") + AddNodeInfo(NODE, "OK Reconnected *" + NODE.Socket2.ConnectID) + NODE.Socket2.ConnectToServer = true + SetSocketStatus(NODE.Socket2, 2) + } + }) + SetSocketStatus(NODE.Socket2, 1) + NODE.Socket2.Node = NODE + NODE.Socket2.ConnectID = "~R" + ConnectIDCount + ConnectIDCount++ + this.SetEventsProcessing(NODE.Socket2, 1) + } + SwapSockets() + { + if(!this.Socket2) + return ; + var SocketOld = this.Socket; + this.Socket = this.Socket2 + this.Socket2 = undefined + this.Socket.Node = this + SetSocketStatus(this.Socket, 100) + this.Socket.Buf = SocketOld.Buf + SERVER.LoadBufSocketList.remove(SocketOld) + SERVER.LoadBufSocketList.insert(this.Socket) + SocketOld.Buf = undefined + SocketOld.WasClose = 1 + SocketOld.Node = undefined + this.ErrCount = 0 + } + SetEventsProcessing(Socket, Reconnection) + { + let SOCKET = Socket; + let NODE = this; + let RECONNECTION = Reconnection; + SOCKET.on('data', function (data) + { + if(Socket.WasClose) + return ; + if(GetSocketStatus(SOCKET) === 2) + { + SetSocketStatus(SOCKET, 3) + var Buf = SERVER.GetDataFromBuf(data); + if(Buf) + { + var Res = NODE.SendPOWFromClientToServer(SOCKET, Buf.Data); + if(Res) + { + return ; + } + } + CloseSocket(SOCKET, Buf ? "Method=" + Buf.Method : "=CLIENT ON DATA=") + } + else + if(GetSocketStatus(SOCKET) === 3) + { + var Buf = SERVER.GetDataFromBuf(data); + if(Buf) + { + var Str = Buf.Data; + if(Str && Str.substr(0, 24) === "WAIT_CONNECT_FROM_SERVER") + { + AddNodeInfo(NODE, "2. CLIENT OK POW") + CloseSocket(SOCKET, "WAIT_CONNECT_FROM_SERVER") + NODE.WaitConnectFromServer = 1 + NODE.WaitConnectIP = NODE.ip + try + { + NODE.SecretForReconnect = GetArrFromHex(Str.substr(25)) + } + catch(e) + { + NODE.SecretForReconnect = [] + ToLog(e) + } + } + else + if(Str === "OK") + { + NODE.NextConnectDelta = 1000 + SetSocketStatus(SOCKET, 100) + AddNodeInfo(NODE, "4. CLIENT OK CONNECT") + if(RECONNECTION) + { + if(NODE.Socket) + SetSocketStatus(NODE.Socket, 200) + } + else + { + if(!NODE.Active) + SERVER.AddNodeToActive(NODE) + } + return ; + } + else + if(Str === "SELF") + { + NODE.Self = 1 + } + else + if(Str === "DOUBLE") + { + } + else + { + AddNodeInfo(NODE, "ERROR:" + Str) + } + } + CloseSocket(SOCKET, Buf ? "Method=" + Buf.Method + ":" + Str : "=CLIENT ON DATA=") + } + else + { + socketRead(Socket, data) + SERVER.OnGetFromTCP(NODE, Socket, data) + } + }) + SOCKET.on('end', function () + { + if(GetSocketStatus(SOCKET)) + AddNodeInfo(NODE, "Get socket end *" + SOCKET.ConnectID + " Stat: " + SocketStatistic(SOCKET)) + if(GetSocketStatus(SOCKET) === 200) + { + NODE.SwapSockets() + SOCKET.WasClose = 1 + } + }) + SOCKET.on('close', function (err) + { + if(SOCKET.ConnectID && GetSocketStatus(SOCKET)) + AddNodeInfo(NODE, "Get socket close *" + SOCKET.ConnectID + " Stat: " + SocketStatistic(SOCKET)) + if(!SOCKET.WasClose) + { + if(GetSocketStatus(SOCKET) >= 2) + { + CloseSocket(SOCKET, "GET CLOSE") + } + } + SetSocketStatus(SOCKET, 0) + }) + SOCKET.on('error', function (err) + { + if(GetSocketStatus(SOCKET) >= 2) + { + SERVER.AddCheckErrCount(NODE, 1, "ERR##1 : socket") + ADD_TO_STAT("ERRORS") + } + }) + } + SendPOWFromClientToServer(Socket, data) + { + var Node = this; + if(Node.ReconnectFromServer) + { + Node.ReconnectFromServer = 0 + var Info = this.GetPOWClientData(0); + Info.Reconnect = 1 + Info.SecretForReconnect = Node.SecretForReconnect + var BufWrite = BufLib.GetBufferFromObject(Info, FORMAT_POW_TO_SERVER, 1200, {}); + var BufAll = SERVER.GetBufFromData("POW_CONNECT7", BufWrite, 1); + Socket.write(BufAll) + return 1; + } + try + { + var Buf = BufLib.GetObjectFromBuffer(data, FORMAT_POW_TO_CLIENT, {}); + } + catch(e) + { + SERVER.SendCloseSocket(Socket, "FORMAT_POW_TO_CLIENT") + return 0; + } + if(CompareArr(Buf.addrArr, SERVER.addrArr) === 0) + { + Node.Self = true + AddNodeInfo(Node, "END: SELF") + SERVER.SendCloseSocket(Socket, "SELF") + return ; + } + var addrStr = GetHexFromAddres(Buf.addrArr); + if(!Node.StartFindList && addrStr !== Node.addrStr) + { + AddNodeInfo(Node, "END: CHANGED ADDR: " + Node.addrStr.substr(0, 16) + "->" + addrStr.substr(0, 16)) + SERVER.SendCloseSocket(Socket, "ADDRESS_HAS_BEEN_CHANGED") + return ; + } + if(Node.addrStrTemp) + { + AddNodeInfo(Node, "Set Addr = " + addrStr) + Node.addrStr = addrStr + SERVER.CheckNodeMap(Node) + } + var Result = false; + if(Buf.PubKeyType === 2 || Buf.PubKeyType === 3) + { + Result = secp256k1.verify(Buffer.from(shaarr(addrStr)), Buffer.from(Buf.Sign), Buffer.from([Buf.PubKeyType].concat(Buf.addrArr))) + if(!Result) + { + Result = secp256k1.verify(Buffer.from(sha3(addrStr)), Buffer.from(Buf.Sign), Buffer.from([Buf.PubKeyType].concat(Buf.addrArr))) + } + } + if(!Result) + { + ToLog("END: ERROR_SIGN_SERVER ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress) + AddNodeInfo(Node, "END: ERROR_SIGN_SERVER ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress) + SERVER.SendCloseSocket(Socket, "ERROR_SIGN_SERVER") + return ; + } + if(Buf.MIN_POWER_POW_HANDSHAKE > 1 + MIN_POWER_POW_HANDSHAKE) + { + ToLog("END: BIG_MIN_POWER_POW_HANDSHAKE ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress) + return 0; + } + var TestNode = SERVER.NodesMap[addrStr]; + if(TestNode && TestNode !== Node) + { + if(GetSocketStatus(TestNode.Socket)) + { + AddNodeInfo(Node, "DoubleConnection find") + Node.DoubleConnection = true + return 0; + } + else + { + AddNodeInfo(Node, "DoubleConnection find") + TestNode.DoubleConnection = true + } + } + Node.PubKey = Buffer.from([Buf.PubKeyType].concat(Buf.addrArr)) + Node.addrArr = Buf.addrArr + Node.addrStr = addrStr + if(CompareArr(SERVER.addrArr, Node.addrArr) === 0) + { + Node.Self = 1 + return 0; + } + var Hash = shaarr2(Buf.addrArr, Buf.HashRND); + var nonce = CreateNoncePOWExternMinPower(Hash, 0, Buf.MIN_POWER_POW_HANDSHAKE); + var Info; + if(WALLET.WalletOpen && IsDeveloperAccount(WALLET.PubKeyArr)) + { + Info = this.GetPOWClientData(0) + Info.Reconnect = 255 + Info.Sign = secp256k1.sign(SHA3BUF(Hash), WALLET.KeyPair.getPrivateKey('')).signature + Result = CheckDevelopSign(Hash, Info.Sign) + if(!Result) + { + throw "ERROR DEVELOPSIGN!"; + } + } + else + { + Info = this.GetPOWClientData(nonce) + Info.PubKeyType = SERVER.PubKeyType + Info.Sign = secp256k1.sign(Buffer.from(Hash), SERVER.KeyPair.getPrivateKey('')).signature + } + var BufWrite = BufLib.GetBufferFromObject(Info, FORMAT_POW_TO_SERVER, 1200, {}); + var BufAll = SERVER.GetBufFromData("POW_CONNECT6", BufWrite, 1); + Socket.write(BufAll) + return 1; + } + GetPOWClientData(nonce) + { + var Node = this; + var Info = {}; + Info.DEF_NETWORK = GetNetworkName() + Info.DEF_VERSION = DEF_VERSION + Info.DEF_CLIENT = DEF_CLIENT + Info.addrArr = SERVER.addrArr + Info.ToIP = Node.ip + Info.ToPort = Node.port + Info.FromIP = SERVER.ip + Info.FromPort = SERVER.port + Info.nonce = nonce + Info.Reconnect = 0 + Info.SendBytes = 0 + Info.SecretForReconnect = [] + Info.Reserv = [] + if(GrayConnect()) + Info.GrayConnect = 1 + return Info; + } + write(BufWrite) + { + if(!this.Socket) + return ; + socketWrite(this.Socket, BufWrite) + try + { + this.Socket.write(BufWrite) + } + catch(e) + { + ToError(e) + this.Socket.WasClose = 1 + this.Socket.SocketStatus = 0 + this.Socket.Node = undefined + } + } +}; +global.socketInit = function (Socket,Str) +{ + if(!Socket) + return ; + Socket.GetBytes = 0; + Socket.SendBytes = 0; + Socket.ConnectID = "" + ConnectIDCount + Str; + ConnectIDCount++; +}; +global.socketRead = function (Socket,Buf) +{ + Socket.GetBytes += Buf.length; +}; +global.socketWrite = function (Socket,Buf) +{ + Socket.SendBytes += Buf.length; +}; +global.CloseSocket = function (Socket,StrError,bHide) +{ + if(!Socket || Socket.WasClose) + { + if(Socket) + Socket.SocketStatus = 0; + return ; + } + var Node = Socket.Node; + if(Socket.Node && Socket.Node.Socket2 === Socket && Socket.Node.Socket && Socket.Node.Socket.SocketStatus === 200) + SetSocketStatus(Socket.Node.Socket, 100); + var StrNode = NodeInfo(Socket.Node); + Socket.WasClose = 1; + Socket.SocketStatus = 0; + Socket.Node = undefined; + Socket.end(); + if(!bHide) + AddNodeInfo(Node, "CLOSE " + StrNode + " *" + Socket.ConnectID + " - " + StrError); +}; + +function SetSocketStatus(Socket,Status) +{ + if(Socket && Socket.SocketStatus !== Status) + { + if(Status === 100 && (Socket.SocketStatus !== 3 && Socket.SocketStatus !== 200)) + { + ToLogTrace("===================ERROR=================== " + Status); + return ; + } + if(Status === 100 && Socket.Node) + Socket.Node.LastTime = GetCurrentTime() - 0; + Socket.SocketStatus = Status; + Socket.TimeStatus = Date.now(); + } +}; + +function GetSocketStatus(Socket) +{ + if(Socket && Socket.SocketStatus) + { + if(Socket.SocketStatus !== 100) + { + var Delta = Date.now() - Socket.TimeStatus; + if(Delta > MAX_WAIT_PERIOD_FOR_STATUS) + { + CloseSocket(Socket, "MAX_WAIT_PERIOD_FOR_STATUS = " + Socket.SocketStatus + " time = " + Delta); + } + } + return Socket.SocketStatus; + } + else + { + return 0; + } +}; + +function SocketInfo(Socket) +{ + if(Socket) + return "*" + Socket.ConnectID; + else + return ""; +}; + +function SocketStatistic(Socket) +{ + if(!Socket) + return ""; + var Str = ""; + if(!Socket.SendBytes) + Socket.SendBytes = 0; + if(!Socket.GetBytes) + Socket.GetBytes = 0; + if(Socket.SendBytes) + Str += " Send=" + Socket.SendBytes; + if(Socket.GetBytes) + Str += " Get=" + Socket.GetBytes; + if(GetSocketStatus(Socket)) + Str += " SocketStatus=" + GetSocketStatus(Socket); + if(Str === "") + Str = "0"; + return Str; +}; + +function NodeInfo(Node) +{ + if(Node) + return "" + Node.ip + ":" + Node.port + " " + SocketInfo(Node.Socket); + else + return ""; +}; + +function NodeName(Node) +{ + if(!Node) + return ""; + if(Node.Name) + return Node.Name; + if(LOCAL_RUN) + return "" + Node.port; + else + { + return "" + Node.ip + ":" + Node.addrStr.substr(0, 6); + } +}; + +function FindNodeByAddr(Addr,bConnect) +{ + var Node = SERVER.NodesMap[Addr.trim()]; + if(Node && Node.ConnectStatus() === 100) + return Node; + if(Node && bConnect) + { + Node.NextConnectDelta = 1000; + SERVER.StartConnectTry(Node); + return false; + } + return undefined; +}; + +function AddNodeInfo(Node,Str,bSet) +{ + if(!global.STAT_MODE) + return ; + if(!Node) + return ; + if(!Node.Info) + Node.Info = ""; + if(bSet) + { + Node.Info = ""; + } + else + { + if(Node.Socket && Node.Socket.Info) + { + Node.Info += Node.Socket.Info + "\n"; + Node.Socket.Info = ""; + } + } + if(Node.Info.length > 1000) + { + Node.PrevInfo = Node.Info; + Node.Info = ""; + } + { + var timesend = GetStrOnlyTimeUTC(); + Str = timesend + " " + Str; + Node.Info += Str + "\n"; + } +}; +global.SocketStatistic = SocketStatistic; +global.GetSocketStatus = GetSocketStatus; +global.SetSocketStatus = SetSocketStatus; +global.NodeInfo = NodeInfo; +global.NodeName = NodeName; +global.SocketInfo = SocketInfo; +global.FindNodeByAddr = FindNodeByAddr; +global.AddNodeInfo = AddNodeInfo; diff --git a/Source/core/rest-loader.js b/Source/core/rest-loader.js new file mode 100644 index 0000000..581b499 --- /dev/null +++ b/Source/core/rest-loader.js @@ -0,0 +1,503 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +module.exports = class CRest extends require("./db/block-db") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + } + CheckSyncRest() + { + var BlockNumTime = GetCurrentBlockNumByTime(); + var Delta = BlockNumTime - this.BlockNumDB; + if(Delta > REST_START_COUNT + DELTA_BLOCK_ACCOUNT_HASH + 500) + { + var BlockNumRest = GetCurrentRestNum(REST_START_COUNT + DELTA_BLOCK_ACCOUNT_HASH + 500); + if(this.BlockNumDB >= this.BlockNumDBMin && this.BlockNumDB <= this.BlockNumDBMin + BLOCK_PROCESSING_LENGTH2) + { + } + else + if(BlockNumRest > this.BlockNumDB) + { + } + else + { + this.LoadRestContext = undefined + return ; + } + this.LoadRestContext = {Mode:0, BlockNum:BlockNumRest, BlockNumRest:BlockNumRest, WasDelta:Delta, BlockNumProof:BlockNumRest + DELTA_BLOCK_ACCOUNT_HASH, + CountProof:COUNT_BLOCKS_FOR_LOAD, StartTimeHistory:Date.now(), MaxTimeOut:600 * 1000, LoopSyncRest:1, SendGetHeaderCount:0, + ReceiveHeaderCount:0, ArrProof:[], MapSend:{}} + for(var i = 0; i < this.NodesArr.length; i++) + { + this.NodesArr[i].SendRestGetHeader = 0 + } + ToLog("**********START REST MODE: " + this.LoadRestContext.BlockNumProof) + } + else + { + this.LoadRestContext = undefined + } + } + LoopSyncRest() + { + let Context = this.LoadRestContext; + switch(Context.Mode) + { + case 0: + var ArrNodes = this.GetActualNodes(); + for(var i = 0; i < ArrNodes.length; i++) + { + var Node = ArrNodes[i]; + if(!Node || Node.SendRestGetHeader) + { + continue; + } + Node.SendRestGetHeader = 1 + ToLog("Send rest get headers from " + Context.BlockNumProof + " to " + NodeName(Node), 2) + this.SendF(Node, {"Method":"GETBLOCKHEADER", "Data":{Foward:1, BlockNum:Context.BlockNumProof, Hash:[]}, "Context":{F:this.RETBLOCKHEADER_REST.bind(this)}, + }) + Context.SendGetHeaderCount++ + break; + } + if(Context.ReceiveHeaderCount >= COUNT_NODE_PROOF) + { + Context.Mode = 2 + ToLog("Next mode: " + Context.Mode + " Receive:" + Context.ReceiveHeaderCount + "/" + Context.SendGetHeaderCount, 2) + } + break; + case 1000: + break; + case 2: + var MapSumPower = {}; + for(var i = 0; i < Context.ArrProof.length; i++) + { + var Item = Context.ArrProof[i]; + if(!MapSumPower[Item.SumPower]) + MapSumPower[Item.SumPower] = 0 + MapSumPower[Item.SumPower]++ + } + var MaxCount = 0, MaxPow = 0; + for(var key in MapSumPower) + { + if(MapSumPower[key] >= MaxCount) + { + MaxCount = MapSumPower[key] + MaxPow = parseInt(key) + } + } + if(MaxCount < 2 || MaxPow === 0) + { + ToLog("****************************************************************** Error MaxPow=" + MaxPow + " - reload.") + this.CheckSyncRest() + return ; + } + for(var i = 0; i < Context.ArrProof.length; i++) + { + var Item = Context.ArrProof[i]; + if(Item.SumPower !== MaxPow) + { + var Str = "BAD SumPower: " + Item.SumPower + "/" + MaxPow; + ToLog(Str + " from: " + NodeName(Item.Node), 2) + } + else + if(Item.SumPower && Item.arr.length >= Context.CountProof) + { + Item.OK = 1 + Context.BlockProof = Item.arr[0] + } + } + Context.Mode++ + ToLog("Next mode: " + Context.Mode + " SumPower:" + MaxPow, 2) + break; + case 3: + if(global.TX_PROCESS && global.TX_PROCESS.RunRPC) + { + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + var Block = {BlockNum:Context.BlockNumRest}; + this.BlockNumDB = Block.BlockNum + this.BlockNumDBMin = Block.BlockNum + this.WriteBlockHeaderDB(Block) + this.UseTruncateBlockDB = undefined + ToLog("Start run TXPrepareLoadRest", 2) + global.TX_PROCESS.RunRPC("TXPrepareLoadRest", Block.BlockNum, function (Err,Params) + { + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + }) + } + break; + case 4: + break; + case 5: + let BlockProof = Context.BlockProof; + var SendCount = 0; + if(BlockProof) + for(var i = 0; i < Context.ArrProof.length; i++) + { + let Item = Context.ArrProof[i]; + if(Item.OK) + { + SendCount++ + ToLog("Send rest get block proof:" + BlockProof.BlockNum + " to " + NodeName(Item.Node), 2) + this.SendF(Item.Node, {"Method":"GETBLOCK", "Data":{BlockNum:BlockProof.BlockNum, TreeHash:BlockProof.TreeHash}, "Context":{F:function (Info) + { + if(Context.TxProof) + return ; + var Data = BufLib.GetObjectFromBuffer(Info.Data, FORMAT_BLOCK_TRANSFER, WRK_BLOCK_TRANSFER); + Info.Data = undefined + if(Data.BlockNum !== BlockProof.BlockNum || CompareArr(Data.TreeHash, BlockProof.TreeHash) !== 0) + { + ToLog("Error get proof block from " + NodeName(Item.Node), 2) + return ; + } + var TreeHash = CalcTreeHashFromArrBody(Data.BlockNum, Data.arrContent); + if(CompareArr(BlockProof.TreeHash, TreeHash) !== 0) + { + ToLog("Error TreeHash in proof block from " + NodeName(Item.Node), 2) + return ; + } + ToLog("GET BLOCK proof from " + NodeName(Item.Node), 2) + var FindTx = undefined; + for(var n = 0; n < Data.arrContent.length; n++) + { + var Body = Data.arrContent[n]; + if(Body[0] === TYPE_TRANSACTION_ACC_HASH) + { + try + { + FindTx = BufLib.GetObjectFromBuffer(Body, FORMAT_ACCOUNT_HASH3, {}) + } + catch(e) + { + ToLog("Error parsing Body[" + n + "] block proof: " + e, 2) + continue; + } + break; + } + } + if(!FindTx) + return ; + Context.TxProof = FindTx + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + Context.AccTaskList = [] + Context.AccTaskFinished = 0 + var AccCount = FindTx.AccountMax + 1; + for(var n = 0; n < AccCount; n += MAX_ACCOUNTS_TRANSFER) + { + var Task = {StartNum:n, Count:MAX_ACCOUNTS_TRANSFER, Time:0, MapSend:{}}; + if(Task.StartNum + Task.Count > AccCount) + Task.Count = AccCount - Task.StartNum + Context.AccTaskList.push(Task) + } + Context.SmartTaskList = [] + Context.SmartTaskFinished = 0 + for(var n = 0; n < FindTx.SmartCount; n += MAX_SMARTS_TRANSFER) + { + var Task = {StartNum:n, Count:MAX_SMARTS_TRANSFER, Time:0, MapSend:{}}; + if(Task.StartNum + Task.Count > FindTx.SmartCount) + Task.Count = FindTx.SmartCount - Task.StartNum + Context.SmartTaskList.push(Task) + } + }}, }) + if(SendCount >= 5) + break; + } + } + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + break; + case 6: + break; + case 7: + if(Context.AccTaskFinished === Context.AccTaskList.length) + { + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + break; + } + var CurTime = Date.now(); + for(var i = 0; i < Context.AccTaskList.length; i++) + { + let Task = Context.AccTaskList[i]; + var Delta = CurTime - Task.Time; + if(Delta > 5 * 1000 && !Task.OK) + { + var Ret = this.GetNextNode(Task, "", 1); + if(Ret.Result) + { + ToLog("Send GETREST Num:" + Task.StartNum + "-" + Task.Count + " to " + NodeName(Ret.Node), 2) + var SELF = this; + this.SendF(Ret.Node, {"Method":"GETREST", "Data":{BlockNum:Context.BlockNumRest, AccNum:Task.StartNum, Count:Task.Count, AccHash:Context.TxProof.AccHash}, + "Context":{F:function (Info) + { + if(Task.OK) + return ; + var Data = SELF.DataFromF(Info); + if(!Data.Result) + return ; + if(Data.Version !== 1) + { + ToLog("ERROR Version Result GETREST Num:" + Task.StartNum + " from " + NodeName(Info.Node), 2) + return ; + } + if(CompareArrL(Data.ProofHash, Context.TxProof.AccHash) !== 0) + { + ToLog("ERROR PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + GetHexFromArr(Data.ProofHash) + "/" + GetHexFromArr(Context.TxProof.AccHash) + " from " + NodeName(Info.Node), + 2) + return ; + } + var ArrM = []; + for(var i = 0; i < Data.Arr.length; i++) + { + ArrM[i] = shaarr(Data.Arr[i]) + } + var GetHash = CheckMerkleProof(Data.ProofArrL, ArrM, Data.ProofArrR); + if(CompareArrL(GetHash, Context.TxProof.AccHash) !== 0) + { + ToLog("ERROR CALC PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + GetHexFromArr(GetHash) + "/" + GetHexFromArr(Context.TxProof.AccHash) + " from " + NodeName(Info.Node), + 2) + return ; + } + ToLog("OK Result GETREST Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + NodeName(Info.Node), 2) + if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) + { + ToLog("ERROR global.TX_PROCESS") + return ; + } + Task.OK = 1 + global.TX_PROCESS.RunRPC("TXWriteAccArr", {StartNum:Task.StartNum, Arr:Data.Arr}, function (Err,Params) + { + Context.AccTaskFinished++ + }) + }}, }) + Task.Time = CurTime + break; + } + } + } + break; + case 8: + if(Context.SmartTaskFinished === Context.SmartTaskList.length) + { + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + break; + } + var CurTime = Date.now(); + for(var i = 0; i < Context.SmartTaskList.length; i++) + { + let Task = Context.SmartTaskList[i]; + var Delta = CurTime - Task.Time; + if(Delta > 3 * 1000 && !Task.OK) + { + var Ret = this.GetNextNode(Task, "", 1); + if(Ret.Result) + { + ToLog("Send GETSMART Num:" + Task.StartNum + "-" + Task.Count + " to " + NodeName(Ret.Node), 2) + var SELF = this; + this.SendF(Ret.Node, {"Method":"GETSMART", "Data":{BlockNum:Context.BlockNumRest, SmartNum:Task.StartNum, Count:Task.Count}, + "Context":{F:function (Info) + { + if(Task.OK) + return ; + var Data = SELF.DataFromF(Info); + if(!Data.Result) + return ; + ToLog("Result GETSMART Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + NodeName(Info.Node), 2) + Task.Node = Info.Node + if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) + return ; + Task.OK = 1 + global.TX_PROCESS.RunRPC("TXWriteSmartArr", {StartNum:Task.StartNum, Arr:Data.Arr}, function (Err,Params) + { + Context.SmartTaskFinished++ + }) + }}, }) + Task.Time = CurTime + break; + } + } + } + break; + case 9: + if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) + return ; + var ErrSmartNum = CheckHashSmarts(Context.TxProof.SmartHash); + if(ErrSmartNum > 0) + { + var Str = "Error hash in smart num: " + ErrSmartNum; + ToLog(Str, 2) + var t = Math.trunc(ErrSmartNum / MAX_SMARTS_TRANSFER); + var Task = Context.SmartTaskList[t]; + if(!Task) + { + ToLog("error task number: " + t) + Context.Mode = 100 + } + else + { + Task.OK = 0 + Context.Mode-- + Context.SmartTaskFinished-- + this.AddToBan(Task.Node, Str) + } + break; + } + var SELF = this; + global.TX_PROCESS.RunRPC("TXWriteAccHash", {}, function (Err,Params) + { + if(!Params) + return ; + if(CompareArr(Context.TxProof.AccHash, Params.AccHash) === 0 && CompareArr(Context.TxProof.SmartHash, Params.SmartHash) === 0) + { + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + } + else + { + ToLog("ERROR RESTS LOAD:") + ToLog("Must AccHash:" + GetHexFromArr(Context.TxProof.AccHash)) + ToLog("Must SmartHash:" + GetHexFromArr(Context.TxProof.SmartHash)) + ToLog("Write AccHash:" + GetHexFromArr(Params.AccHash)) + ToLog("Write SmartHash:" + GetHexFromArr(Params.SmartHash)) + SELF.BlockNumDB = 0 + SELF.BlockNumDBMin = 0 + SELF.UseTruncateBlockDB = undefined + global.TX_PROCESS.RunRPC("TXPrepareLoadRest", 0, function (Err,Params) + { + }) + Context.Mode = 100 + } + }) + Context.Mode++ + ToLog("Next mode: " + Context.Mode, 2) + break; + case 10: + break; + case 11: + var Context2 = this.LoadHistoryContext; + Context2.BlockNum = this.LoadRestContext.BlockNumRest + Context2.StartTimeHistory = Date.now() + Context.Mode = 200 + break; + case 200: + ToLog("Error state!") + break; + } + } + RETBLOCKHEADER_REST(Info, CurTime) + { + if(Info.Node.SendRestGetHeader === 2) + return ; + Info.Node.SendRestGetHeader = 2 + var Context = this.LoadRestContext; + var BufRead = BufLib.GetReadBuffer(Info.Data); + var arr = this.GetBlockArrFromBuffer_Load(BufRead, Info); + ToLog("RETBLOCKHEADER_FOWARD SyncRest from " + NodeName(Info.Node) + " arr=" + arr.length, 2) + Context.ReceiveHeaderCount++ + var MinSumPow = 10 * Context.CountProof; + var SumPower = 0; + if(arr.length >= Context.CountProof) + for(var i = 0; i < Context.CountProof; i++) + { + SumPower += arr[i].Power + } + if(SumPower <= MinSumPow) + SumPower = 0 + Context.ArrProof.push({Node:Info.Node, SumPower:SumPower, arr:arr, BufRead:BufRead}) + } + static + GETSMART_F() + { + return "{\ + SmartNum:uint,\ + Count:uint,\ + }"; + } + static + RETSMART_F() + { + return global.FORMAT_SMART_TRANSFER; + } + static + GETREST_F() + { + return "{\ + BlockNum:uint,\ + AccNum:uint,\ + Count:uint,\ + AccHash:hash,\ + }"; + } + static + RETREST_F() + { + return global.FORMAT_REST_TRANSFER; + } + SendLoadToBegin() + { + return ; + if(!this.BlockNumDBMin) + return ; + if(!this.ContextSendLoadToBegin) + this.ContextSendLoadToBegin = {Time:0, MapSend:{}} + var Context = this.ContextSendLoadToBegin; + var CurTime = Date.now(); + var Delta = CurTime - Context.Time; + if(Delta < 2 * 1000) + return ; + var BlockDB = this.ReadBlockHeaderDB(this.BlockNumDBMin + 1); + if(!BlockDB) + return ; + Context.BlockNum = BlockDB.BlockNum + var Ret = this.GetNextNode(Context, Context.BlockNum, 1); + if(Ret.Result) + { + var Node = Ret.Node; + ToLog("LOAD_TO_BEGIN - from: " + BlockDB.BlockNum + " to " + NodeName(Node), 2) + Context.Time = CurTime + this.SendF(Node, {"Method":"GETBLOCKHEADER", "Data":{Foward:0, BlockNum:Context.BlockNum, Hash:BlockDB.Hash, IsSum:0, Count:global.COUNT_HISTORY_BLOCKS_FOR_LOAD}, + "Context":{F:function (Info) + { + ToLog("GET LOAD_TO_BEGIN from " + NodeName(Info.Node) + " Length=" + Info.Data.length, 2) + }}}) + } + } +}; + +function CheckHashSmarts(LastSumHash) +{ + DApps.Smart.Close(); + var MaxNum = DApps.Smart.GetMaxNum(); + var Item = DApps.Smart.DBSmart.Read(MaxNum); + if(CompareArr(Item.SumHash, LastSumHash) !== 0) + return MaxNum; + var WorkStruct = {}; + for(var Num = MaxNum; Num >= 1; Num--) + { + var PrevItem = DApps.Smart.DBSmart.Read(Num - 1); + if(!PrevItem) + return Num; + var WasSumHash = Item.SumHash; + Item.SumHash = []; + var Buf = BufLib.GetBufferFromObject(Item, DApps.Smart.FORMAT_ROW, 20000, WorkStruct); + var Hash = sha3(Buf); + var SumHash = sha3arr2(PrevItem.SumHash, Hash); + if(CompareArr(SumHash, WasSumHash) !== 0) + return Num; + Item = PrevItem; + } + return 0; +}; diff --git a/Source/core/rest_tables.js b/Source/core/rest_tables.js new file mode 100644 index 0000000..7df51d3 --- /dev/null +++ b/Source/core/rest_tables.js @@ -0,0 +1,73 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + + +function DoRest(r,t,e) +{ + var u = r.Arr[0], o = Math.floor(e / REST_BLOCK_SCALE); + if(o !== Math.floor((u.BlockNum - 1) / REST_BLOCK_SCALE)) + { + for(var n = GetRestArr(o), l = [], a = n.length - 2; 0 <= a; a--) + l.push(n[a] * REST_BLOCK_SCALE); + RestPush(r, l, e, 1); + } + r.Arr[0] = {BlockNum:e, Value:t.Value}; +}; + +function RestPush(r,t,e,u) +{ + var o = r.Arr[u - 1], n = r.Arr[u]; + if(1 < u) + { + var l = t[u - 2]; + if(o.BlockNum > l) + return ; + } + if(n.BlockNum && n.BlockNum >= e || o.BlockNum >= e) + return n.BlockNum = 0, void (n.Value = {}); + n.BlockNum && u < r.Arr.length - 1 && RestPush(r, t, e, u + 1), r.Arr[u] = o; +}; + +function GetRestArr(r) +{ + for(var t = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], e = t.length, u = 0; u <= r; u++) + for(var o = 0, n = u, l = e - 1; 0 <= l; l--) + { + var a = t[l]; + if(t[l] = n, n = a, 0 == ((o = o << 4 | 15) & u)) + break; + if(0 != (o & n)) + break; + } + return t; +}; +var RestArrMap = {}; + +function GetCurrentRestArr() +{ + var r = GetCurrentBlockNumByTime(), t = Math.floor(r / REST_BLOCK_SCALE), e = RestArrMap[t]; + if(void 0 === e) + { + RestArrMap = {}, (e = GetRestArr(t)).length = e.length - 1; + for(var u = 0; u < e.length; u++) + e[u] = e[u] * REST_BLOCK_SCALE; + RestArrMap[t] = e; + } + return e; +}; + +function GetCurrentRestNum(r) +{ + for(var t = GetCurrentBlockNumByTime() - r, e = GetCurrentRestArr(), u = e.length - 1; 0 <= u; u--) + if(e[u] <= t) + return e[u]; + return 0; +}; +global.DoRest = DoRest, global.GetRestArr = GetRestArr, global.GetCurrentRestArr = GetCurrentRestArr, global.GetCurrentRestNum = GetCurrentRestNum; diff --git a/Source/core/server.js b/Source/core/server.js new file mode 100644 index 0000000..d217aef --- /dev/null +++ b/Source/core/server.js @@ -0,0 +1,1239 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const net = require("net"); +const dgram = require("dgram"); +const crypto = require('crypto'); +require("./library.js"); +require("./crypto-library"); +const HARD_PACKET_PERIOD = 20; +global.BUF_TYPE = 1; +global.STR_TYPE = 2; +global.MAX_STR_BUF_DATA = 200; +global.MAX_CONNECTION_ACTIVE = 40; +var MAX_CONNECTION_ANOTHER = 40; +const TRAFIC_LIMIT_NODE_1S = MAX_BLOCK_SIZE * 1.25; +const TRAFIC_LIMIT_1S = 8 * TRAFIC_LIMIT_NODE_1S; +global.STAT_PERIOD = CONSENSUS_PERIOD_TIME / 5; +const TRAFIC_LIMIT_SEND = TRAFIC_LIMIT_1S * STAT_PERIOD / 1000; +const TRAFIC_LIMIT_NODE = TRAFIC_LIMIT_NODE_1S * STAT_PERIOD / 1000; +const BUF_PACKET_SIZE = 32 * 1024; +global.FORMAT_POW_TO_CLIENT = "{addrArr:hash,HashRND:hash,MIN_POWER_POW_HANDSHAKE:uint,PubKeyType:byte,Sign:arr64,Reserve:arr33}"; +global.FORMAT_POW_TO_SERVER = "{\ + DEF_NETWORK:str15,\ + DEF_VERSION:str9,\ + DEF_CLIENT:str16, \ + addrArr:addres, \ + ToIP:str26,\ + ToPort:uint16, \ + FromIP:str26,\ + FromPort:uint16, \ + nonce:uint,\ + Reconnect:byte,\ + SendBytes:uint,\ + PubKeyType:byte,\ + Sign:arr64,\ + SecretForReconnect:arr20,\ + GrayConnect:byte,\ + Reserve:arr14\ + }"; +const WorkStructPacketSend = {}; +const FORMAT_PACKET_SEND_TCP = "{\ + PacketSize:uint,\ + NumXORRND:uint,\ + Method:str25,\ + NodeTime:time,\ + Length:uint,\ + ContextID:hash,\ + TypeData:byte,\ + Hash:hash,\ + Data:data,\ + }"; +module.exports = class CTransport extends require("./connect") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.UseRNDHeader = UseRNDHeader + this.BAN_IP = {} + this.ip = RunIP.trim() + this.port = RunPort + this.CanSend = 0 + this.SendFormatMap = {} + this.ActualNodes = new RBTree(function (a,b) + { + if(b.Prioritet !== a.Prioritet) + return b.Prioritet - a.Prioritet; + return CompareArr(a.addrArr, b.addrArr); + }) + this.SendTrafficFree = 0 + this.LoadedPacketNum = 0 + this.LoadedSocketNum = 0 + setInterval(this.DoLoadBuf.bind(this), 1) + this.LoadBufSocketList = new RBTree(function (a,b) + { + if(b.SocketPrioritet !== a.SocketPrioritet) + return b.SocketPrioritet - a.SocketPrioritet; + return a.SocketNum - b.SocketNum; + }) + this.BusyLevel = 0 + this.LastTimeHard = 0 + this.LastTimeHardOK = 0 + setInterval(this.DoHardPacketForSend.bind(this), HARD_PACKET_PERIOD) + this.HardPacketForSend = new RBTree(function (a,b) + { + if(b.BlockProcessCount === a.BlockProcessCount) + return a.PacketNum - b.PacketNum; + else + return b.BlockProcessCount - a.BlockProcessCount; + }) + setInterval(this.DoSendPacket.bind(this), 2) + setInterval(this.DoSendBuf.bind(this), 1) + var Map = {}; + this.MethodTiming = Map + MethodTiming: + { + Map["TRANSFER"] = {Period:700, Hot:1} + Map["TRANSFERTX"] = {Period:700, Hot:1} + Map["TIME"] = {Period:2000, LowVersion:1, Hard:1, Immediately:1} + Map["PING"] = {Period:4000, LowVersion:1, Hard:1, Immediately:1} + Map["PONG"] = {Period:0, LowVersion:1, Immediately:1} + Map["ADDLEVELCONNECT"] = {Period:1000, Hard:1} + Map["RETADDLEVELCONNECT"] = {Period:0} + Map["DISCONNECTHOT"] = {Period:1000, Hard:1} + Map["GETMESSAGE"] = {Period:1000, Hard:1} + Map["MESSAGE"] = {Period:1000, Hard:1} + Map["TRANSACTION"] = {Period:PERIOD_GET_BLOCK, Hard:1} + Map["GETBLOCKHEADER"] = {Period:PERIOD_GET_BLOCK, Hard:2, Process:global.STATIC_PROCESS} + Map["GETBLOCKHEADER100"] = {Period:PERIOD_GET_BLOCK, Hard:2, Process:global.STATIC_PROCESS} + Map["GETBLOCK"] = {Period:PERIOD_GET_BLOCK, Hard:2, Process:global.STATIC_PROCESS} + Map["GETNODES"] = {Period:1000, Hard:1, LowVersion:1, IsAddrList:1} + Map["RETGETNODES"] = {Period:0, IsAddrList:1} + Map["RETGETNODES2"] = {Period:0, IsAddrList:1} + Map["GETCODE"] = {Period:10000, Hard:1, LowVersion:1, Process:global.STATIC_PROCESS} + Map["RETBLOCKHEADER"] = {Period:0} + Map["RETBLOCKHEADER100"] = {Period:0} + Map["RETGETBLOCK"] = {Period:0} + Map["RETCODE"] = {Period:0} + Map["GETREST"] = {Period:1000, Hard:2, Process:global.STATIC_PROCESS} + Map["RETREST"] = {Period:0} + Map["GETSMART"] = {Period:1000, Hard:2, Process:global.STATIC_PROCESS} + Map["RETSMART"] = {Period:0} + } + if(!this.VirtualMode) + this.StartServer() + this.CurrentTimeStart = 0 + this.CurrentTimeValues = {} + this.LoadNodesFromFile() + } + GetF(Method, bSend) + { + var name = Method + "-" + bSend; + var format = this.SendFormatMap[name]; + if(!format) + { + var F = this.constructor[Method + "_F"]; + if(typeof F === "function") + { + format = {struct:F(bSend), length:8096, wrk:{}} + } + else + { + format = "{}" + } + this.SendFormatMap[name] = format + } + return format; + } + SendF(Node, Info, Length) + { + var format = this.GetF(Info.Method, true); + if(!Length) + Length = format.length + Info.Data = BufLib.GetBufferFromObject(Info.Data, format.struct, Length, format.wrk) + this.Send(Node, Info, 1) + } + DataFromF(Info, bSendFormat) + { + var format = this.GetF(Info.Method, bSendFormat); + try + { + var Data = BufLib.GetObjectFromBuffer(Info.Data, format.struct, format.wrk); + return Data; + } + catch(e) + { + ToLog(e) + return {}; + } + } + ADD_CURRENT_STAT_TIME(Key, Value) + { + var TimeNum = Math.floor(Date.now() / STAT_PERIOD); + if(this.CurrentTimeStart !== TimeNum) + this.CurrentTimeValues = {} + this.CurrentTimeStart = TimeNum + if(!this.CurrentTimeValues[Key]) + this.CurrentTimeValues[Key] = 0 + this.CurrentTimeValues[Key] += Value + } + GET_CURRENT_STAT_TIME(Key) + { + var TimeNum = Math.floor(Date.now() / STAT_PERIOD); + if(this.CurrentTimeStart === TimeNum) + { + var Value = this.CurrentTimeValues[Key]; + if(Value === undefined) + return 0; + else + return Value; + } + else + { + return 0; + } + } + RecalcSendStatictic() + { + var TimeNum = Math.floor(Date.now() / STAT_PERIOD); + if(this.SendStatNum === TimeNum) + return ; + this.SendStatNum = TimeNum + var Period = CONSENSUS_PERIOD_TIME / STAT_PERIOD; + this.SendTrafficFree = TRAFIC_LIMIT_SEND + var it = this.ActualNodes.iterator(), Node; + while((Node = it.next()) !== null) + { + { + var arr = Node.TrafficArr; + arr.push(Node.BufWriteLength) + Node.BufWriteLength = 0 + if(arr.length > 5 * Period) + { + arr.shift() + } + else + { + if(arr.length < 3 * Period) + continue; + } + var arrAvg = [], arrK = []; + var valNext = CalcStatArr(arr, arrAvg, arrK, Period); + valNext = Math.min(valNext, TRAFIC_LIMIT_NODE) + Node.SendTrafficLimit = Math.min(this.SendTrafficFree, valNext * 1.1) + this.SendTrafficFree -= Node.SendTrafficLimit + } + Node.SendTrafficCurrent = 0 + ADD_TO_STAT("MAX:NODE_TRAFFIC_LIMIT:" + NodeName(Node), 1000 / STAT_PERIOD * Node.SendTrafficLimit / 1024, 1) + } + this.SendTrafficFree += TRAFIC_LIMIT_NODE + ADD_TO_STAT("SEND_TRAFFIC_FREE", this.SendTrafficFree / 1024) + } + OnGetMethod(Info, CurTime) + { + if(DEBUG_MODE) + { + var Str = ""; + if(Info.Data && Info.Data.Length) + Str = " LENGTH=" + Info.Data.Length + TO_DEBUG_LOG("GET:" + Info.Method + Str + " from: Node=" + NodeInfo(Info.Node)) + } + if(global.ADDRLIST_MODE) + { + var StrOK = ",HAND,GETNODES,"; + if(StrOK.indexOf("," + Info.Method + ",") === - 1) + return ; + } + Info.Node.LastTime = CurTime - 0 + if(Info.Context && typeof Info.Context.F === "function") + { + Info.Context.F(Info, CurTime) + } + else + { + var F = this[Info.Method.toUpperCase()]; + if(typeof F === "function") + { + F.bind(this)(Info, CurTime) + } + else + { + TO_ERROR_LOG("TRANSPORT", 20, "Method '" + Info.Method + "' not found Socket=*" + Info.Socket.ConnectID, "node", Info.Node) + this.AddCheckErrCount(Info.Node, 1, "Method not found") + } + } + } + GetActualNodes() + { + var Arr = []; + var it = this.ActualNodes.iterator(), Item; + while((Item = it.next()) !== null) + { + if(GetSocketStatus(Item.Socket) >= 100) + Arr.push(Item) + else + { + this.DeleteNodeFromActive(Item) + } + } + return Arr; + } + NodeIp(Node) + { + if(Node.ip_arrival) + { + return {ip:Node.ip_arrival, port:Node.port_arrival}; + } + else + { + return {ip:Node.ip, port:Node.port}; + } + } + SetXORHeader(buf, bForce) + { + if(this.UseRNDHeader || bForce) + { + var HashHashSign = shaarr(buf.slice(buf.length - 32, buf.length)); + for(var i = 0; i < 32; i++) + buf[i] = HashHashSign[i] ^ buf[i] + } + } + WasBanIP(rinfo) + { + if(!rinfo || !rinfo.address) + return false; + var Key = "" + rinfo.address.trim(); + var Stat = this.BAN_IP[Key]; + if(Stat) + { + if(Stat.TimeTo > (GetCurrentTime(0) - 0)) + return true; + } + return false; + } + NodeInBan(Node) + { + return this.WasBanIP({address:Node.ip}); + } + DeleteNodeFromActiveByIP(ip) + { + var Arr = this.GetActualNodes(); + for(var i = 0; i < Arr.length; i++) + { + var Node = Arr[i]; + if(Node.ip === ip) + { + this.DeleteNodeFromActive(Node) + } + } + } + AddToBan(Node, Str) + { + if(global.NeedRestart) + return ; + this.DeleteNodeFromActive(Node) + if(!Node.ip) + return ; + var Key = "" + Node.ip.trim(); + if(!Node.DeltaBan) + Node.DeltaBan = 300 + if(Node.DeltaBan > 1000000) + Node.DeltaBan = 1000000 + Node.DeltaBan = Node.DeltaBan * 2 + var TimeTo = (GetCurrentTime(0) - 0) + Node.DeltaBan * 1000; + this.BAN_IP[Key] = {TimeTo:TimeTo} + Node.BlockProcessCount = 0 + this.DeleteNodeFromActiveByIP(Node.ip) + ADD_TO_STAT("AddToBan") + } + AddToBanIP(ip, Str, Period) + { + if(!Period) + Period = 600 * 1000 + var Key = "" + ip.trim(); + this.BAN_IP[Key] = {TimeTo:(GetCurrentTime(0) - 0) + Period} + this.DeleteNodeFromActiveByIP(ip) + ToLog("ADD TO BAN:: " + Key + " " + Str) + ADD_TO_STAT("AddToBanIP") + } + OnPacketTCP(Meta) + { + var startTime = process.hrtime(); + ADD_TO_STAT("USEPACKET") + var CurTime = GetCurrentTime(); + Meta.Node.LastTime = CurTime - 0 + if(Meta.MethodTiming.Process && Meta.MethodTiming.Process.Worker) + { + var Data = this.DataFromF(Meta); + Meta.MethodTiming.Process.Worker.send({cmd:Meta.Method, Data:Data, addrStr:Meta.Node.addrStr, Context:Meta.Context}) + } + else + { + this.OnGetMethod(Meta, CurTime) + } + ADD_TO_STAT_TIME("MAX:TIME_USE_PACKET", startTime) + ADD_TO_STAT_TIME("TIME_USE_PACKET", startTime) + ADD_TO_STAT_TIME("MAX:TIME_USE_PACKET:" + Meta.Method, startTime) + } + GetBufFromData(Method, Data, TypeData, ContextID) + { + var BufData; + if(TypeData === BUF_TYPE) + { + BufData = Data + } + else + if(TypeData === STR_TYPE) + { + BufData = Buffer.from(Data.substr(0, MAX_STR_BUF_DATA)) + } + else + { + if(Data === undefined) + { + TypeData = BUF_TYPE + BufData = Buffer.alloc(0) + } + else + { + throw "ERROR TYPE DATA"; + } + } + var BUF = {}; + BUF.PacketSize = 0 + BUF.NumXORRND = 0 + BUF.Method = Method + BUF.NodeTime = GetCurrentTime() + BUF.TypeData = TypeData + BUF.Length = BufData.length + BUF.Data = BufData + BUF.ContextID = ContextID + BUF.Hash = this.GetHashFromData(BUF) + var BufWrite = BufLib.GetBufferFromObject(BUF, FORMAT_PACKET_SEND_TCP, BufData.length + 300, WorkStructPacketSend); + BufWrite.len = 0 + BufLib.Write(BufWrite, BufWrite.length, "uint") + return BufWrite; + } + GetDataFromBuf(buf) + { + try + { + var Meta = BufLib.GetObjectFromBuffer(buf, FORMAT_PACKET_SEND_TCP, WorkStructPacketSend); + } + catch(e) + { + TO_ERROR_LOG("TRANSPORT", 640, "Error parsing Buffer") + return undefined; + } + var Hash = this.GetHashFromData(Meta); + if(CompareArr(Hash, Meta.Hash) !== 0) + { + if(global.WATCHDOG_DEV) + ToLog("TRANSPORT", 645, "Error hash Buffer", 2) + return undefined; + } + if(Meta.TypeData === STR_TYPE) + { + Meta.Data = Meta.Data.slice(0, MAX_STR_BUF_DATA).toString() + } + return Meta; + } + GetHashFromData(Info) + { + return shaarr(Info.Method + Info.Length + "-" + (Info.NodeTime - 0)); + } + OnGetFromTCP(Node, Socket, Buf) + { + if(!Node) + return ; + if(!Node.Socket) + Node.Socket = Socket + if(!Socket.Buf || Socket.Buf.length === 0) + { + Socket.Buf = Buf + } + else + { + Socket.Buf = Buffer.concat([Socket.Buf, Buf]) + } + if(!Socket.SocketNum) + { + this.LoadedSocketNum++ + Socket.SocketNum = this.LoadedSocketNum + Socket.SocketPrioritet = Node.BlockProcessCount + } + this.LoadBufSocketList.insert(Socket) + } + DoLoadBuf() + { + var Socket = this.LoadBufSocketList.min(); + if(!Socket) + return ; + this.LoadBufSocketList.remove(Socket) + if(Socket.WasClose) + return ; + while(true) + { + if(Socket.Buf && Socket.Buf.length > 6) + { + ADD_TO_STAT("MAX:BUFFE_LOAD_SIZE", Socket.Buf.length / 1024) + Socket.Buf.len = 0 + var PacketSize = BufLib.Read(Socket.Buf, "uint"); + if(PacketSize > MAX_PACKET_LENGTH) + { + this.SendCloseSocket(Socket, "MAX_PACKET_LENGTH") + break; + } + else + if(Socket.Buf.length >= PacketSize) + { + var data = Socket.Buf.slice(0, PacketSize); + Socket.Buf = Socket.Buf.slice(PacketSize, Socket.Buf.length) + var Res = this.DoDataFromTCP(Socket, data); + if(Res) + { + continue; + } + } + } + break; + } + } + DoDataFromTCP(Socket, buf) + { + this.LoadedPacketNum++ + var Node = Socket.Node; + if(!Node) + return 0; + var startTime = process.hrtime(); + ADD_TO_STAT("GETDATA(KB)", buf.length / 1024) + ADD_TO_STAT("GETDATA(KB):" + NodeName(Node), buf.length / 1024, 1) + if(!Node.TransferSize) + Node.TransferSize = 0 + Node.TransferSize += buf.length / 1024 + Node.TransferBlockNumFix = this.CurrentBlockNum + var Buf = this.GetDataFromBuf(buf); + if(!Buf) + { + this.AddCheckErrCount(Node, 1, "Err GetDataFromBuf") + this.SendCloseSocket(Socket, "FORMAT_PACKET_SEND_TCP") + return 0; + } + ADD_TO_STAT("GET:" + Buf.Method) + ADD_TO_STAT("GET:(KB)" + Buf.Method, buf.length / 1024) + ADD_TO_STAT("GET:" + Buf.Method + ":" + NodeName(Node), 1, 1) + var Param = this.MethodTiming[Buf.Method]; + if(this.StopDoSendPacket(Param, Node, Buf.Method)) + { + return 1; + } + if(!IsZeroArr(Buf.ContextID)) + { + Buf.Context = global.ContextPackets.LoadValue(Buf.ContextID) + } + if(!Buf.Context) + { + if(Param && Param.Period === 0 && Buf.Method !== "RETBLOCKHEADER") + { + this.AddCheckErrCount(Node, 1) + return ; + } + Buf.Context = {} + } + Buf.Context.ContextID = Buf.ContextID + Buf.Node = Node + Buf.Socket = Socket + Buf.MethodTiming = Param + if(!global.ADDRLIST_MODE || Param.IsAddrList) + { + if(Param.Hard) + { + if(Param.Immediately && this.HardPacketForSend.size <= 3) + { + this.OnPacketTCP(Buf) + } + else + { + Buf.PacketNum = this.LoadedPacketNum + Buf.BlockProcessCount = Node.BlockProcessCount + Buf.TimeLoad = Date.now() + this.HardPacketForSend.insert(Buf) + } + } + else + { + this.OnPacketTCP(Buf) + } + } + ADD_TO_STAT_TIME("MAX:TIMEDOGETDATA", startTime) + return 1; + } + StopDoSendPacket(Param, Node, Name) + { + var CurTime = GetCurrentTime(0) - 0; + if(!Param) + { + ADD_TO_STAT("STOP_METHOD") + ADD_TO_STAT("STOP_METHOD:NO") + this.AddCheckErrCount(Node, 1) + return 1; + } + if(Param.Hot && !Node.Hot) + { + this.AddCheckErrCount(Node, 1) + return 0; + } + if(Param.Period && !Node.VersionOK && !Param.LowVersion) + { + ADD_TO_STAT("STOP_METHOD") + ADD_TO_STAT("STOP_METHOD:LOWVERSION:" + Name) + return 1; + } + if(global.STOPGETBLOCK && Param.Hard === 2 && Node.BlockProcessCount < 1000000) + { + Node.NextPing = 1 * 1000 + ADD_TO_STAT("STOP_METHOD") + ADD_TO_STAT("STOP_METHOD:STOPGETBLOCK:" + Name) + this.AddCheckErrCount(Node, 0.5) + return 1; + } + var ArrTime = Node.TimeMap[Name]; + if(!ArrTime) + { + ArrTime = [0, 0, 0] + Node.TimeMap[Name] = ArrTime + } + ArrTime.sort(function (a,b) + { + return a - b; + }) + var Delta = CurTime - ArrTime[0]; + if(Delta < Param.Period) + { + ADD_TO_STAT("STOP_METHOD") + ADD_TO_STAT("STOP_METHOD:" + Name) + this.AddCheckErrCount(Node, 1) + return 1; + } + ArrTime[0] = CurTime + return 0; + } + DoHardPacketForSend() + { + ADD_TO_STAT("MAX:BUSY_LEVEL", this.BusyLevel) + ADD_TO_STAT("MAX:HARD_PACKET_SIZE", this.HardPacketForSend.size) + var Delta = Date.now() - this.LastTimeHard; + this.LastTimeHard = Date.now() + if(Delta > global.HARD_PACKET_PERIOD120 * HARD_PACKET_PERIOD / 100) + { + ADD_TO_STAT("HARD_PACKET_PERIOD120") + var Delta2 = Date.now() - this.LastTimeHardOK; + if(Delta2 > 100) + { + var Info = this.HardPacketForSend.min(); + this.RiseBusyLevelByInfo(Info) + } + return ; + } + if(this.BusyLevel) + this.BusyLevel = this.BusyLevel / 1.1 + this.LastTimeHardOK = Date.now() + ADD_TO_STAT("HARD_PACKET_PERIOD") + this.DoHardPacketForSendNext() + } + RiseBusyLevelByInfo(Info) + { + if(!Info) + return ; + if(!this.BusyLevel) + this.BusyLevel = 1 + if(Info.BlockProcessCount > this.BusyLevel) + this.BusyLevel = Info.BlockProcessCount + 1 + if(this.BusyLevel <= 0) + this.BusyLevel = 1 + } + DropBusyLevelByInfo(Info) + { + if(!Info) + return ; + if(this.BusyLevel > Info.BlockProcessCount) + this.BusyLevel = Info.BlockProcessCount - 1 + if(this.BusyLevel < 0) + this.BusyLevel = 0 + } + DoHardPacketForSendNext() + { + var Info = this.HardPacketForSend.min(); + if(!Info) + { + this.BusyLevel = 0 + return ; + } + this.DropBusyLevelByInfo(Info) + this.HardPacketForSend.remove(Info) + this.OnPacketTCP(Info) + ADD_TO_STAT("DO_HARD_PACKET") + ADD_TO_STAT("DO_HARD_PACKET:" + Info.Method) + var DeltaTime = Date.now() - Info.TimeLoad; + if(this.HardPacketForSend.size && DeltaTime > PACKET_ALIVE_PERIOD / 2) + { + ADD_TO_STAT("DELETE_HARD_PACKET_OLD", this.HardPacketForSend.size) + this.HardPacketForSend.clear() + return ; + } + var MaxCount = 20; + while(Info = this.HardPacketForSend.max()) + { + var DeltaTime = Date.now() - Info.TimeLoad; + if(DeltaTime > PACKET_ALIVE_PERIOD / 2 || !Info.Node.Socket || Info.Node.Socket.WasClose) + { + this.HardPacketForSend.remove(Info) + if(DeltaTime > PACKET_ALIVE_PERIOD / 2) + { + this.RiseBusyLevelByInfo(Info) + Info.Node.NextPing = 1 * 1000 + this.AddCheckErrCount(Info.Node, 0.2) + ADD_TO_STAT("DELETE_HARD_PACKET_OLD") + ADD_TO_STAT("DELETE_HARD_PACKET_OLD:" + Info.Method) + } + else + { + ADD_TO_STAT("DELETE_HARD_PACKET_NO_ALIVE") + } + } + MaxCount-- + if(MaxCount <= 0) + break; + } + } + Send(Node, Info, TypeData) + { + if(!Node.Socket) + { + this.DeleteNodeFromActive(Node) + return ; + } + if(Info.Context) + { + Info.ContextID = Info.Context.ContextID + if(!Info.ContextID) + { + Info.ContextID = crypto.randomBytes(32) + Info.Context.ContextID = Info.ContextID + } + global.ContextPackets.SaveValue(Info.ContextID, Info.Context) + } + else + { + Info.ContextID = [] + } + Node.SendPacketNum++ + Info.Node = Node + Info.TypeData = TypeData + Info.Prioritet = Node.Prioritet + Info.PacketNum = Node.SendPacketNum + Info.TimeNum = Date.now() + Node.SendPacket.insert(Info) + } + DoSendPacketNodeAll(Node) + { + while(this.DoSendPacketNode(Node) === 1); + } + DoSendPacketNode(Node) + { + var TimeNum = Date.now(); + var Info = Node.SendPacket.max(); + if(Info && TimeNum - Info.TimeNum > PACKET_ALIVE_PERIOD) + while(Info = Node.SendPacket.max()) + { + var DeltaTime = TimeNum - Info.TimeNum; + if(DeltaTime > PACKET_ALIVE_PERIOD / 2) + { + Node.SendPacket.remove(Info) + ADD_TO_STAT("DELETE_OLD_PACKET") + } + else + break; + } + Info = Node.SendPacket.min() + if(!Info) + return 0; + ADD_TO_STAT("MAX:NODE_BUF_WRITE:" + NodeName(Node), Node.BufWrite.length / 1024, 1) + ADD_TO_STAT("MAX:NODE_SEND_BUF_PACKET_COUNT:" + NodeName(Node), Node.SendPacket.size, 1) + if(Node.BufWrite.length > 2 * TRAFIC_LIMIT_1S) + { + return 2; + } + Node.SendPacket.remove(Info) + if(Info.Context) + { + if(!Info.Context.SendCount) + Info.Context.SendCount = 0 + Info.Context.SendCount++ + } + var BufWrite = this.GetBufFromData(Info.Method, Info.Data, Info.TypeData, Info.ContextID); + Node.BufWriteLength += BufWrite.length + if(Node.BufWrite.length === 0) + Node.BufWrite = BufWrite + else + Node.BufWrite = Buffer.concat([Node.BufWrite, BufWrite]) + ADD_TO_STAT("SEND:" + Info.Method) + ADD_TO_STAT("SEND:(KB)" + Info.Method, BufWrite.length / 1024) + ADD_TO_STAT("SEND:" + Info.Method + ":" + NodeName(Node), 1, 1) + TO_DEBUG_LOG("SEND " + Info.Method + " to " + NodeInfo(Node) + " LENGTH=" + BufWrite.length) + return 1; + } + DoSendPacket() + { + var it = this.ActualNodes.iterator(), Node; + while((Node = it.next()) !== null) + if(Node.ConnectStatus() === 100) + { + this.DoSendPacketNode(Node) + } + else + { + ADD_TO_STAT("SEND_ERROR") + this.AddCheckErrCount(Node, 0.005, "NODE STATUS=" + Node.ConnectStatus()) + } + } + DoSendBuf() + { + this.RecalcSendStatictic() + var CountNodeSend = 0; + var it = this.ActualNodes.iterator(), Node; + NEXT_NODE: + while((Node = it.next()) !== null) + if(Node.Socket && Node.ConnectStatus() === 100) + if(Node.BufWrite.length > 0) + { + CountNodeSend++ + var CountSend = Math.min(BUF_PACKET_SIZE, Node.BufWrite.length); + var Value = CountSend / 1024; + if(global.LIMIT_SEND_TRAFIC) + { + var CanCountSend = Node.SendTrafficLimit - Node.SendTrafficCurrent; + if(CanCountSend < CountSend) + { + if(this.SendTrafficFree < CountSend) + { + ADD_TO_STAT("LIMIT_SENDDATA:" + NodeName(Node), Value, 1) + continue NEXT_NODE; + } + this.SendTrafficFree -= CountSend + } + } + Node.write(Node.BufWrite.slice(0, CountSend)) + Node.SendTrafficCurrent += CountSend + Node.BufWrite = Node.BufWrite.slice(CountSend) + this.ADD_CURRENT_STAT_TIME("SEND_DATA", Value) + ADD_TO_STAT("SENDDATA(KB)", Value) + ADD_TO_STAT("SENDDATA(KB):" + NodeName(Node), Value, 1) + } + } + CheckPOWTicketConnect(Socket, data) + { + try + { + var Info = BufLib.GetObjectFromBuffer(data, FORMAT_POW_TO_SERVER, {}); + } + catch(e) + { + this.SendCloseSocket(Socket, "FORMAT_POW_TO_SERVER") + return ; + } + if(Info.DEF_NETWORK !== GetNetworkName()) + { + this.SendCloseSocket(Socket, "DEF_NETWORK=" + Info.DEF_NETWORK + " MUST:" + GetNetworkName()) + return ; + } + var Node = this.FindRunNodeContext(Info.addrArr, Info.FromIP, Info.FromPort, true); + if(CompareArr(Info.addrArr, this.addrArr) === 0) + { + AddNodeInfo(Node, "SERV: GET SELF") + this.SendCloseSocket(Socket, "SELF") + return ; + } + var Hash = shaarr2(this.addrArr, Socket.HashRND); + var hashInfo = GetHashWithValues(Hash, Info.nonce, 0); + var power = GetPowPower(hashInfo); + if(Info.Reconnect) + { + if((Node.SecretForReconnect && Node.WaitConnectFromServer && CompareArr(Node.SecretForReconnect, Info.SecretForReconnect) === 0) || Info.Reconnect === 255) + { + var Result = 1; + if(Info.Reconnect === 255) + { + Result = CheckDevelopSign(Hash, Info.Sign) + } + if(Result) + { + Node.NextConnectDelta = 1000 + Node.WaitConnectFromServer = 0 + Node.GrayConnect = 0 + AddNodeInfo(Node, "3. SERVER OK CONNECT for client node " + SocketInfo(Socket)) + this.AddNodeToActive(Node) + Node.Socket = Socket + SetSocketStatus(Socket, 3) + SetSocketStatus(Socket, 100) + Socket.Node = Node + Socket.write(this.GetBufFromData("POW_CONNECT0", "OK", 2)) + return ; + } + else + { + Node.NextConnectDelta = 60 * 1000 + ToLog("Error Sign Node from " + NodeInfo(Node)) + this.AddCheckErrCount(Node, 10, "Error Sign Node") + } + } + AddNodeInfo(Node, "SERV: ERROR_RECONNECT") + Socket.end(this.GetBufFromData("POW_CONNEC11", "ERROR_RECONNECT", 2)) + CloseSocket(Socket, "ERROR_RECONNECT") + return ; + } + else + { + if(power < MIN_POWER_POW_HANDSHAKE) + { + ToLog("END: MIN_POWER_POW_HANDSHAKE") + AddNodeInfo(Node, "SERV: ERR MIN_POWER_POW_HANDSHAKE") + Socket.end(this.GetBufFromData("POW_CONNECT2", "MIN_POWER_POW_HANDSHAKE", 2)) + CloseSocket(Socket, "MIN_POWER_POW_HANDSHAKE") + return ; + } + else + { + if(!Node.BlockProcessCount) + Node.BlockProcessCount = 0 + if(this.ActualNodes.size >= MAX_CONNECTIONS_COUNT && Node.BlockProcessCount < global.TRUST_PROCESS_COUNT) + { + AddNodeInfo(Node, "SERV: ERROR_MAX_CLIENTS") + Socket.end(this.GetBufFromData("POW_CONNECT8", "ERROR_MAX_CLIENTS", 2)) + CloseSocket(Socket, "ERROR_MAX_CLIENTS") + return ; + } + var Result = false; + if(Info.PubKeyType === 2 || Info.PubKeyType === 3) + Result = secp256k1.verify(Buffer.from(Hash), Buffer.from(Info.Sign), Buffer.from([Info.PubKeyType].concat(Info.addrArr))) + if(!Result) + { + AddNodeInfo(Node, "SERV: ERROR_SIGN_CLIENT") + Socket.end(this.GetBufFromData("POW_CONNECT8", "ERROR_SIGN_CLIENT", 2)) + CloseSocket(Socket, "ERROR_SIGN_CLIENT") + this.AddToBanIP(Socket.remoteAddress, "ERROR_SIGN_CLIENT") + return ; + } + AddNodeInfo(Node, "1. SERVER OK POW for client node " + SocketInfo(Socket)) + Node.FromIP = Info.FromIP + Node.FromPort = Info.FromPort + Node.SecretForReconnect = crypto.randomBytes(20) + Node.PubKey = Buffer.from([Info.PubKeyType].concat(Info.addrArr)) + if(Info.GrayConnect) + { + Node.NextConnectDelta = 1000 + Node.WaitConnectFromServer = 0 + Node.GrayConnect = 1 + AddNodeInfo(Node, "5. CLIENT OK GRAY CONNECT " + SocketInfo(Socket)) + this.AddNodeToActive(Node) + Node.Socket = Socket + SetSocketStatus(Socket, 3) + SetSocketStatus(Socket, 100) + Socket.Node = Node + Socket.write(this.GetBufFromData("POW_CONNECT0", "OK", 2)) + return ; + } + if(!Node.WasAddToReconnect) + { + Node.WasAddToReconnect = 1 + Node.ReconnectFromServer = 1 + global.ArrReconnect.push(Node) + } + Socket.write(this.GetBufFromData("POW_CONNECT4", "WAIT_CONNECT_FROM_SERVER:" + GetHexFromArr(Node.SecretForReconnect), 2)) + } + } + } + StopServer() + { + if(this.Server) + this.Server.close() + } + StartServer() + { + if(GrayConnect()) + { + this.CanSend++ + return ; + } + let SELF = this; + this.Server = net.createServer(function (sock) + { + if(SELF.WasBanIP({address:sock.remoteAddress})) + { + sock.ConnectID = "new" + CloseSocket(sock, "WAS BAN", true) + return ; + } + let SOCKET = sock; + socketInit(SOCKET, "c") + SetSocketStatus(SOCKET, 0) + AddNodeInfo(SOCKET, "Client *" + SOCKET.ConnectID + " connected from " + SOCKET.remoteAddress + ":" + SOCKET.remotePort, 1) + ADD_TO_STAT("ClientConnected") + SOCKET.HashRND = crypto.randomBytes(32) + var Data = {addrArr:SELF.addrArr, HashRND:SOCKET.HashRND, MIN_POWER_POW_HANDSHAKE:MIN_POWER_POW_HANDSHAKE, PubKeyType:SELF.PubKeyType, + Sign:SELF.ServerSign, Reserve:[]}; + var BufData = BufLib.GetBufferFromObject(Data, FORMAT_POW_TO_CLIENT, 300, {}); + var BufWrite = SELF.GetBufFromData("POW_CONNECT5", BufData, 1); + try + { + SOCKET.write(BufWrite) + } + catch(e) + { + ToError(e) + SOCKET = undefined + return ; + } + SOCKET.on('data', function (data) + { + if(SOCKET.WasClose) + { + return ; + } + if(!SOCKET.Node) + { + var Buf = SELF.GetDataFromBuf(data); + if(Buf) + { + SELF.CheckPOWTicketConnect(SOCKET, Buf.Data) + SOCKET.ConnectToServer = 0 + return ; + } + CloseSocket(SOCKET, "=SERVER ON DATA=") + } + else + { + socketRead(SOCKET, data) + SELF.OnGetFromTCP(SOCKET.Node, SOCKET, data) + } + }) + SOCKET.on('end', function () + { + ADD_TO_STAT("ClientEnd") + var Node = SOCKET.Node; + var Status = GetSocketStatus(SOCKET); + if(Status) + AddNodeInfo(Node, "Get socket end *" + SOCKET.ConnectID + " from client Stat: " + SocketStatistic(SOCKET)) + if(Node && Status === 200) + { + Node.SwapSockets() + SOCKET.WasClose = 1 + } + }) + SOCKET.on('close', function (err) + { + ADD_TO_STAT("ClientClose") + if(SOCKET.ConnectID && GetSocketStatus(SOCKET)) + AddNodeInfo(SOCKET.Node, "Get socket close *" + SOCKET.ConnectID + " from client Stat: " + SocketStatistic(SOCKET)) + if(!SOCKET.WasClose && SOCKET.Node) + { + CloseSocket(SOCKET, "GET CLOSE") + } + SetSocketStatus(SOCKET, 0) + }) + SOCKET.on('error', function (err) + { + ADD_TO_STAT("ERRORS") + CloseSocket(SOCKET, "ERRORS") + if(SOCKET.Node) + SELF.AddCheckErrCount(SOCKET.Node, 1, "ERR##2 : socket") + }) + }) + this.Server.on('close', function () + { + }) + this.Server.on('error', function (err) + { + if(err.code === 'EADDRINUSE') + { + ToLogClient('Port ' + SELF.port + ' in use, retrying...') + if(SELF.Server) + SELF.Server.close() + setTimeout(function () + { + SELF.RunListenServer() + }, 5000) + return ; + } + ADD_TO_STAT("ERRORS") + ToError("ERR##3") + }) + if(!SELF.ip) + { + this.FindInternetIP() + } + else + { + this.CanSend++ + this.RunListenServer() + } + } + RunListenServer() + { + if(!START_PORT_NUMBER || START_PORT_NUMBER === "undefined") + START_PORT_NUMBER = 30000 + let SELF = this; + SELF.port = START_PORT_NUMBER + ToLogClient("Prepare to run TCP server on " + LISTEN_IP + ":" + SELF.port) + this.Server.listen(SELF.port, LISTEN_IP, function () + { + if(SELF.CanSend < 2) + ToLogClient("Run TCP server on " + SELF.ip + ":" + SELF.port) + SELF.CanSend++ + var Hash; + Hash = sha3(SELF.addrStr) + SELF.ServerSign = secp256k1.sign(Buffer.from(Hash), SELF.KeyPair.getPrivateKey('')).signature + }) + } + FindInternetIP() + { + let SELF = this; + let server = Stun.createServer(); + const request = Stun.createMessage(Stun.constants.STUN_BINDING_REQUEST); + server.on('error', function (err) + { + SELF.CanSend++ + }) + server.once('bindingResponse', function (stunMsg) + { + var value = stunMsg.getAttribute(Stun.constants.STUN_ATTR_XOR_MAPPED_ADDRESS).value; + ToLog("INTERNET IP:" + value.address) + SELF.CanSend++ + global.INTERNET_IP_FROM_STUN = value.address + if(!SELF.ip) + SELF.ip = INTERNET_IP_FROM_STUN + if(server) + server.close() + SELF.RunListenServer() + }) + var StrStunAddr = 'stun.l.google.com'; + const dns = require('dns'); + dns.lookup(StrStunAddr, function (err,address,family) + { + if(!err) + server.send(request, 19302, StrStunAddr) + else + SELF.CanSend++ + }) + } + CLOSE_SOCKET(Context, CurTime) + { + AddNodeInfo(Context.Socket.Node, "GET CLOSE_SOCKET *" + Context.Socket.ConnectID + ": " + Context.Data.toString()) + CloseSocket(Context.Socket, "CLOSE_SOCKET") + } + SendCloseSocket(Socket, Str) + { + AddNodeInfo(Socket.Node, "CLOSE_SOCKET " + SocketInfo(Socket) + " - " + Str) + if(Socket.WasClose) + { + return ; + } + this.AddCheckErrCount(Socket.Node, 1, "SendCloseSocket") + if(Socket.Node && Socket.Node.BufWrite && Socket.Node.BufWrite.length > 0) + { + } + else + { + AddNodeInfo(Socket.Node, "END *" + Socket.ConnectID + ": " + Str) + Socket.end(this.GetBufFromData("CLOSE_SOCKET", Str, 2)) + } + CloseSocket(Socket, Str) + } + AddCheckErrCount(Node, Count, StrErr) + { + if(!Node) + return ; + if(!Count) + Count = 1 + var Delta = Date.now() - Node.LastTimeError; + if(Delta > 10 * 1000) + { + Node.ErrCount = 0 + } + Node.LastTimeError = Date.now() + Node.ErrCountAll += Count + Node.ErrCount += Count + if(Node.ErrCount >= 5) + { + Node.ErrCount = 0 + ADD_TO_STAT("ERRORS") + Node.BlockProcessCount-- + if(Node.BlockProcessCount < - 30) + { + if(!StrErr) + StrErr = "" + this.AddToBan(Node, StrErr + " BlockProcess:" + Node.BlockProcessCount) + } + else + { + } + } + } +}; +global.ContextPackets = new STreeBuffer(10 * 1000, CompareItemHash32, "object"); + +function CalcStatArr(arr,arrAvg,arrNext,Period) +{ + var arrSum = [arr[0]]; + for(var i = 1; i < arr.length; i++) + { + arrSum[i] = arrSum[i - 1] + arr[i]; + } + for(var i = 0; i < arrSum.length; i++) + { + if(i < Period) + arrAvg[i] = Math.floor(arrSum[i] / (i + 1)); + else + { + arrAvg[i] = Math.floor((arrSum[i] - arrSum[i - Period]) / Period); + } + } + arrNext[0] = 0; + for(var i = 1; i < arrAvg.length; i++) + { + var Avg = arrSum[i] / (i + 1); + var minValue = Avg / 20; + var Value1 = arrAvg[i - 1]; + var Value2 = arrAvg[i]; + if(Value1 < minValue) + Value1 = minValue; + if(Value2 < minValue) + Value2 = minValue; + var KLast = Math.floor(100 * (Value2 - Value1) / Value1) / 100; + var AvgLast = arrAvg[i]; + if(Avg > AvgLast) + AvgLast = Avg; + if(KLast > 2.0) + KLast = 2.0; + if(KLast < - 0.0) + KLast = - 0.0; + arrNext[i] = AvgLast * (1 + KLast); + var AvgMax = 0; + if(0) + if(i > 1 * Period) + { + for(var j = i - Period / 2; j <= i; j++) + if(arrAvg[j] > AvgMax) + AvgMax = arrAvg[j]; + } + } + return arrNext[arrNext.length - 1]; +}; diff --git a/Source/core/startlib.js b/Source/core/startlib.js new file mode 100644 index 0000000..5698d28 --- /dev/null +++ b/Source/core/startlib.js @@ -0,0 +1,50 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var fs = require("fs"); + +function CopyFiles(l,o,t) +{ + if(fs.existsSync(l)) + for(var e = fs.readdirSync(l), n = 0; n < e.length; n++) + { + var a = l + "/" + e[n], s = o + "/" + e[n]; + if(fs.statSync(a).isDirectory()) + t && (fs.existsSync(s) || fs.mkdirSync(s), CopyFiles(a, s, t)); + else + { + var r = fs.readFileSync(a), i = fs.openSync(s, "w"); + fs.writeSync(i, r, 0, r.length), fs.closeSync(i); + } + } +}; +global.GetDataPath = function (l) +{ + return "/" !== global.DATA_PATH.substr(global.DATA_PATH.length - 1, 1) && (global.DATA_PATH = global.DATA_PATH + "/"), GetNormalPathString(global.DATA_PATH + l); +}, global.GetCodePath = function (l) +{ + return "/" !== global.CODE_PATH.substr(global.CODE_PATH.length - 1, 1) && (global.CODE_PATH = global.CODE_PATH + "/"), GetNormalPathString(global.CODE_PATH + l); +}, global.GetNormalPathString = function (l) +{ + return l.split("\\").join("/"); +}, global.CheckCreateDir = function (l,o,t) +{ + if(l = GetNormalPathString(l), !fs.existsSync(l)) + { + o || console.log("Create: " + l); + var e = l.split("/"), n = e[0]; + t && e.length--; + for(var a = 1; a < e.length; a++) + n += "/" + e[a], fs.existsSync(n) || fs.mkdirSync(n); + } +}, global.CopyFiles = CopyFiles, global.ToLog || (global.ToLog = function (l) +{ + console.log(l); +}); diff --git a/Source/core/terahashmining.js b/Source/core/terahashmining.js new file mode 100644 index 0000000..7f3bbdf --- /dev/null +++ b/Source/core/terahashmining.js @@ -0,0 +1,227 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +var START_NONCE = 0; +const COUNT_FIND_HASH1 = 64; +const DELTA_LONG_MINING = 5000; +var BLOCKNUM_ALGO2 = 6560000; +if(global.LOCAL_RUN || global.TEST_NETWORK) +{ + BLOCKNUM_ALGO2 = 0; +} +require('./library.js'); +require('./crypto-library.js'); +require('../HTML/JS/terahashlib.js'); +var DELTA_NONCE = Math.pow(2, 40) * global.MINING_VERSION_NUM; +global.CreateHashMinimal = CreateHashMinimal; +global.CreatePOWVersionX = CreatePOWVersion3; + +function CreateHashMinimal(Block,MinerID) +{ + if(Block.BlockNum < BLOCKNUM_ALGO2) + { + throw "BlockNum < BLOCKNUM_ALGO2"; + return false; + } + var PrevHashNum = ReadUint32FromArr(Block.PrevHash, 28); + var Ret = GetHash(Block.SeqHash, PrevHashNum, Block.BlockNum, MinerID, 0, 0, 0, 0, 0); + Block.Hash = Ret.Hash; + Block.PowHash = Ret.PowHash; + Block.Power = GetPowPower(Block.PowHash); + Block.AddrHash = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + WriteUintToArrOnPos(Block.AddrHash, MinerID, 0); + WriteUint32ToArrOnPos(Block.AddrHash, PrevHashNum, 28); + return true; +}; +var MAX_MEMORY3 = 0, SHIFT_MASKA3; +var BufferNonce3, BufferBlockNum3; +var bWasInitVer3, bWasInitVerOK3; + +function InitVer3(Block) +{ + bWasInitVer3 = 1; + if(Block.ProcessMemorySize > 0) + { + var MAXARRAYSIZE = (1 << 30) * 2 - 1; + var MaxArrCount = Math.min(Math.trunc(Block.ProcessMemorySize / 8), MAXARRAYSIZE); + var BitCount = 0; + MAX_MEMORY3 = 1; + for(var b = 0; b < 32; b++) + { + if(MAX_MEMORY3 > MaxArrCount) + { + BitCount--; + MAX_MEMORY3 = MAX_MEMORY3 / 2; + break; + } + BitCount++; + MAX_MEMORY3 = MAX_MEMORY3 * 2; + } + SHIFT_MASKA3 = 32 - BitCount; + try + { + BufferNonce3 = new Uint32Array(MAX_MEMORY3); + BufferBlockNum3 = new Uint32Array(MAX_MEMORY3); + } + catch(e) + { + SHIFT_MASKA3 = SHIFT_MASKA3 + 1; + MAX_MEMORY3 = MAX_MEMORY3 / 2; + ToLog("WAS ALLOC MEMORY ERROR. NEW TRY: " + MAX_MEMORY3); + BufferNonce3 = new Uint32Array(MAX_MEMORY3); + BufferBlockNum3 = new Uint32Array(MAX_MEMORY3); + } + bWasInitVerOK3 = 1; + ToLog("MAX HASH ITEMS=" + Math.trunc(MAX_MEMORY3 / 1024 / 1024) + " M"); + } +}; + +function CreatePOWVersion3(Block,bHashPump) +{ + if(!bWasInitVer3) + InitVer3(Block); + if(!bWasInitVerOK3) + return 0; + if(!Block.LastNonce) + Block.LastNonce = 0; + if(!Block.HashCount) + Block.HashCount = 0; + if(!Block.LastNonce0) + Block.LastNonce0 = 0; + if(!Block.MaxLider) + { + Block.HashCount = 0; + Block.MaxLider = {Nonce0:0, Nonce1:0, Nonce2:0, DeltaNum1:0, DeltaNum2:0, Hash1:[255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255], Hash2:[255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255], }; + } + var MaxLider = Block.MaxLider; + var RunCount = Block.RunCount; + var BlockNum = Block.BlockNum; + var Miner = Block.MinerID; + var StartNonceRnd = DELTA_NONCE + Block.LastNonce + Math.trunc(3000000000 * Math.random()); + var List = GetNonceHashArr(BlockNum, Miner, StartNonceRnd, RunCount); + for(var n = 0; n < RunCount; n++) + { + var Nonce = List.ArrNonce[n]; + var HashNum = List.ArrHash[n] >>> SHIFT_MASKA3; + BufferNonce3[HashNum] = Nonce; + BufferBlockNum3[HashNum] = BlockNum; + } + Block.LastNonce += RunCount; + if(bHashPump) + return ; + var Ret = 0; + var PrevHashNum = ReadUint32FromArr(Block.PrevHash, 28); + var HashBase = GetHashFromNum2(BlockNum, PrevHashNum); + var Value1 = FindHashBuffer3(HashBase, BlockNum, Miner, 1); + if(Value1) + { + var Hash1 = XORArr(HashBase, Value1.Hash); + if(CompareArr(MaxLider.Hash1, Hash1) > 0) + { + MaxLider.Hash1 = Hash1; + MaxLider.Nonce1 = Value1.Nonce; + MaxLider.DeltaNum1 = Value1.DeltaNum; + Ret = 1; + } + } + START_NONCE = Block.LastNonce0; + var CountEnd = START_NONCE + 50000; + var Nonce0; + for(Nonce0 = START_NONCE; Nonce0 < CountEnd; Nonce0++) + { + var HashCurrent = GetHashFromArrNum2(Block.SeqHash, Miner, Nonce0); + var Value2 = FindHashBuffer3(HashCurrent, BlockNum, Miner, 1); + if(Value2) + { + var Hash2 = XORArr(HashCurrent, Value2.Hash); + if(CompareArr(MaxLider.Hash2, Hash2) > 0) + { + MaxLider.Nonce0 = Nonce0; + MaxLider.Hash2 = Hash2; + MaxLider.Nonce2 = Value2.Nonce; + MaxLider.DeltaNum2 = Value2.DeltaNum; + Ret = 1; + if(CompareArr(MaxLider.Hash1, Hash2) > 0) + { + break; + } + } + } + } + Block.LastNonce0 = Nonce0; + if(Ret) + { + Block.AddrHash = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + WriteUintToArrOnPos(Block.AddrHash, Miner, 0); + WriteUintToArrOnPos(Block.AddrHash, MaxLider.Nonce0, 6); + WriteUintToArrOnPos(Block.AddrHash, MaxLider.Nonce1, 12); + WriteUintToArrOnPos(Block.AddrHash, MaxLider.Nonce2, 18); + WriteUint16ToArrOnPos(Block.AddrHash, MaxLider.DeltaNum1, 24); + WriteUint16ToArrOnPos(Block.AddrHash, MaxLider.DeltaNum2, 26); + WriteUint32ToArrOnPos(Block.AddrHash, PrevHashNum, 28); + Block.Hash = MaxLider.Hash2; + if(CompareArr(MaxLider.Hash1, MaxLider.Hash2) > 0) + { + Block.PowHash = MaxLider.Hash1; + } + else + { + Block.PowHash = MaxLider.Hash2; + } + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + Block.Hash = sha3arr2(MaxLider.Hash1, MaxLider.Hash2); + else + Block.Hash = shaarr2(MaxLider.Hash1, MaxLider.Hash2); + var Power = GetPowPower(Block.PowHash); + Block.HashCount = (1 << Power) >>> 0; + } + return Ret; +}; + +function FindHashBuffer3(HashFind,BlockNum,Miner,CountFind) +{ + var HashNum = ReadIndexFromArr(HashFind); + for(var i = 0; i < CountFind; i++) + { + var Index = HashNum ^ i; + var BlockNum2 = BufferBlockNum3[Index]; + if(BlockNum2 && BlockNum2 > BlockNum - DELTA_LONG_MINING) + { + var Nonce2 = DELTA_NONCE + BufferNonce3[Index]; + var Hash2 = GetHashFromNum3(BlockNum2, Miner, Nonce2); + return {Hash:Hash2, DeltaNum:BlockNum - BlockNum2, Nonce:Nonce2}; + } + } + return undefined; +}; + +function ReadIndexFromArr(arr) +{ + var value = (arr[0] << 23) * 2 + (arr[1] << 16) + (arr[2] << 8) + arr[3]; + value = value >>> SHIFT_MASKA3; + return value; +}; +global.GetNonceHashArr = function (BlockNum,Miner,StartNonceRnd,CountNonce) +{ + var ArrNonce = []; + var ArrHash = []; + for(var n = 0; n < CountNonce; n++) + { + var Nonce = StartNonceRnd + n; + var HashNonce = GetHashFromNum3(BlockNum, Miner, Nonce); + var HashNum = (HashNonce[0] << 23) * 2 + (HashNonce[1] << 16) + (HashNonce[2] << 8) + HashNonce[3]; + ArrNonce[n] = Nonce; + ArrHash[n] = HashNum; + } + return {ArrNonce:ArrNonce, ArrHash:ArrHash}; +}; diff --git a/Source/core/transaction-validator.js b/Source/core/transaction-validator.js new file mode 100644 index 0000000..ee5e90c --- /dev/null +++ b/Source/core/transaction-validator.js @@ -0,0 +1,368 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require("../system/dapp"); +require("../system/accounts"); +require("../system/smart"); +require("../system/file"); +require("../system/messager"); +require("../system/names"); +if(global.PROCESS_NAME === "MAIN" || global.PROCESS_NAME === "TX") + require("./wallet"); +module.exports = class CSmartContract extends require("./block-exchange") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.BufHashTree = new RBTree(CompareArr) + this.BufHashTree.LastAddNum = 0 + this.SenderMap = {} + this.SenderBlockHashMap = {} + if(!global.ADDRLIST_MODE && !this.VirtualMode && global.START_SERVER) + { + setInterval(this.ClearOldSenderMapItem.bind(this), 10000) + } + } + AddBlockToHashTree(Block) + { + this.BufHashTree.LastAddNum = Block.BlockNum + var arr = Block.arrContent; + if(arr) + { + for(var i = 0; i < arr.length; i++) + { + var HASH = sha3(arr[i]); + this.BufHashTree.insert(HASH) + } + } + } + DeleteBlockFromHashTree(Block) + { + var arr = Block.arrContent; + if(arr) + { + for(var i = 0; i < arr.length; i++) + { + var HASH = sha3(arr[i]); + this.BufHashTree.remove(HASH) + } + } + } + OnWriteBlock(Block) + { + this.AddToSenderMap(Block) + } + OnDelete(Block) + { + } + BlockProcessTX(Block) + { + if(Block.BlockNum < 1) + return ; + var COUNT_MEM_BLOCKS = 0; + var NUM1 = 1240000; + var NUM2 = 1400000; + if(global.LOCAL_RUN) + { + NUM1 = 15 + NUM2 = 100 + } + if(Block.BlockNum > global.BLOCKNUM_TICKET_ALGO) + { + NUM1 = 1000000000000 + NUM2 = 1000000000000 + } + if(Block.BlockNum > NUM1) + { + COUNT_MEM_BLOCKS = 1 + if(Block.BlockNum > NUM2) + COUNT_MEM_BLOCKS = 60 + if(this.BufHashTree.LastAddNum !== Block.BlockNum - 1) + { + this.BufHashTree.clear() + for(var num = COUNT_MEM_BLOCKS; num >= 1; num--) + { + var Block2 = this.ReadBlockDB(Block.BlockNum - num); + if(Block2) + { + this.AddBlockToHashTree(Block2) + } + } + } + } + for(var key in DApps) + { + DApps[key].OnWriteBlockStart(Block) + } + var arrContentResult = []; + var BlockNum = Block.BlockNum; + var arr = Block.arrContent; + if(arr) + for(var i = 0; i < arr.length; i++) + { + var HASH = sha3(arr[i]); + if(this.BufHashTree.find(HASH)) + { + continue; + } + var type = arr[i][0]; + var App = DAppByType[type]; + if(App) + { + App.ResultTx = 0 + DApps.Accounts.BeginTransaction() + var StrHex = GetHexFromArr(HASH); + var item; + global.CurTrItem = undefined + if(global.TreeFindTX) + { + item = global.TreeFindTX.LoadValue(StrHex) + if(item) + global.CurTrItem = item.TX + } + var Result = App.OnWriteTransaction(Block, arr[i], BlockNum, i); + var SetResult = Result; + if(Result === true) + { + if(App.ResultTx) + SetResult = App.ResultTx + if(!DApps.Accounts.CommitTransaction(BlockNum, i)) + SetResult = 0 + } + else + { + DApps.Accounts.RollBackTransaction() + SetResult = 0 + } + if(SetResult === true) + SetResult = 1 + arrContentResult[i] = SetResult + if(item) + { + var ResultStr = Result; + if(Result === true || typeof Result === "number") + { + ResultStr = "Add to blockchain" + if(type === global.TYPE_TRANSACTION_FILE) + ResultStr += ": file/" + BlockNum + "/" + i + } + item.cmd = "RetFindTX" + item.ResultStr = "" + ResultStr + item.bFinal = 1 + item.Result = SetResult + process.send(item) + } + global.CurTrItem = undefined + } + } + if(COUNT_MEM_BLOCKS) + { + var Block2 = this.ReadBlockDB(Block.BlockNum - COUNT_MEM_BLOCKS); + if(Block2) + this.DeleteBlockFromHashTree(Block2) + this.AddBlockToHashTree(Block) + } + if(arrContentResult.length) + process.send({cmd:"WriteBodyResult", BlockNum:Block.BlockNum, arrContentResult:arrContentResult}) + for(var key in DApps) + { + DApps[key].OnWriteBlockFinish(Block) + } + } + BlockDeleteTX(Block) + { + this.BufHashTree.LastAddNum = 0 + for(var key in DApps) + { + DApps[key].OnDeleteBlock(Block) + } + } + IsValidTicket(Tr, BlockNum) + { + this.CheckCreateTicketObject(Tr, BlockNum) + if(Tr.power < MIN_POWER_POW_TR) + return - 2; + if(Tr.num !== BlockNum) + return - 3; + return 1; + } + IsValidTransaction(Tr, BlockNum) + { + if(!Tr.body || Tr.body.length < MIN_TRANSACTION_SIZE || Tr.body.length > MAX_TRANSACTION_SIZE) + return - 1; + this.CheckCreateTransactionObject(Tr) + if(Tr.power - Math.log2(Tr.body.length / 128) < MIN_POWER_POW_TR) + return - 2; + if(Tr.num !== BlockNum) + return - 3; + if(Tr.body[0] === TYPE_TRANSACTION_ACC_HASH) + return - 4; + return 1; + } + ReWriteDAppTransactions(Length) + { + if(!TX_PROCESS.Worker) + return 0; + if(!Length) + return 0; + var StartNum = this.BlockNumDB - Length + 1; + if(StartNum < 0) + StartNum = 0 + var EndNum = this.BlockNumDB; + var MinBlock = DApps.Accounts.GetMinBlockAct(); + if(MinBlock > StartNum) + { + ToLog("Cant rewrite transactions. Very long length of the rewriting chain. Max length=" + (this.BlockNumDB - MinBlock)) + return 0; + } + if(global.TX_PROCESS && global.TX_PROCESS.RunRPC) + global.TX_PROCESS.RunRPC("ReWriteDAppTransactions", {StartNum:StartNum, EndNum:EndNum}) + return 1; + } + AddDAppTransactions(BlockNum, Arr) + { + if(BlockNum % PERIOD_ACCOUNT_HASH !== 0) + return ; + var BlockNumHash = BlockNum - DELTA_BLOCK_ACCOUNT_HASH; + if(BlockNumHash < 0) + return ; + var Item = DApps.Accounts.GetAccountHashItem(BlockNumHash); + if(Item) + { + var Body = [TYPE_TRANSACTION_ACC_HASH]; + WriteUintToArr(Body, BlockNumHash) + WriteArrToArr(Body, Item.AccHash, 32) + if(BlockNumHash >= START_BLOCK_ACCOUNT_HASH3) + { + WriteUintToArr(Body, Item.AccountMax) + WriteArrToArr(Body, Item.SmartHash, 32) + WriteUintToArr(Body, Item.SmartCount) + WriteUintToArr(Body, BlockNum) + WriteUintToArr(Body, 0) + } + var Tr = {body:Body}; + this.CheckCreateTransactionObject(Tr) + Arr.unshift(Tr) + } + } + AddTransactionOwn(Tr) + { + if(!global.TX_PROCESS.Worker) + return - 6; + var StrHex = GetHexFromArr(sha3(Tr.body)); + global.TX_PROCESS.Worker.send({cmd:"FindTX", TX:StrHex}) + return this.AddTransaction(Tr, 1); + } + AddToSenderMap(Block) + { + if(!global.START_SERVER) + return ; + var BlockNum = Block.BlockNum; + var StrBlockHash = GetHexFromArr(Block.Hash); + this.SenderBlockHashMap[BlockNum] = StrBlockHash + var arr = Block.arrContent; + if(arr) + { + for(var i = 0; i < arr.length; i++) + { + var type = arr[i][0]; + var App = DAppByType[type]; + if(App) + { + var Body = arr[i]; + var SenderNum = App.GetSenderNum(BlockNum, Body); + if(SenderNum < 0) + continue; + var ItemArr = this.SenderMap[SenderNum]; + if(!ItemArr) + { + ItemArr = [] + this.SenderMap[SenderNum] = ItemArr + } + ItemArr.push({BlockNum:BlockNum, StrHash:StrBlockHash}) + } + } + } + } + GetSenderPrioritet(BlockNum, SenderNum) + { + if(!global.START_SERVER) + return 0; + if(!this.WasReloadSenderMapFromDB) + this.ReloadSenderMapFromDB() + if(SenderNum < 0) + return MAX_LENGTH_SENDER_MAP; + var MaxBlockNum = BlockNum - DELTA_START_SENDER_MAP; + if(MaxBlockNum > this.BlockNumDB) + return MAX_LENGTH_SENDER_MAP; + var ItemArr = this.SenderMap[SenderNum]; + if(!ItemArr) + { + return MAX_LENGTH_SENDER_MAP; + } + for(var i = ItemArr.length - 1; i--; i >= 0) + { + var Item = ItemArr[i]; + if(Item.BlockNum <= MaxBlockNum && this.SenderBlockHashMap[Item.BlockNum] === Item.StrHash) + { + var Delta = MaxBlockNum - Item.BlockNum; + if(Delta > MAX_LENGTH_SENDER_MAP) + Delta = MAX_LENGTH_SENDER_MAP + return Delta; + } + } + return MAX_LENGTH_SENDER_MAP; + } + ReloadSenderMapFromDB() + { + if(!global.START_SERVER) + return ; + this.SenderMap = {} + this.SenderBlockHashMap = {} + var EndNum = GetCurrentBlockNumByTime(); + var StartNum = EndNum - MAX_LENGTH_SENDER_MAP - DELTA_START_SENDER_MAP; + if(StartNum < 0) + StartNum = 0 + for(var Num = StartNum; Num < EndNum; Num++) + { + var Block = this.ReadBlockDB(Num); + if(!Block) + break; + this.AddToSenderMap(Block) + } + this.WasReloadSenderMapFromDB = 1 + } + ClearOldSenderMapItem() + { + if(!global.START_SERVER) + return ; + var MinBlockNum = GetCurrentBlockNumByTime() - (MAX_LENGTH_SENDER_MAP + COUNT_BLOCKS_FOR_LOAD); + var ArrForDel = []; + for(var key in this.SenderMap) + { + var ItemArr = this.SenderMap[key]; + while(ItemArr.length) + { + var Item = ItemArr[0]; + if(Item.BlockNum > MinBlockNum) + break; + ItemArr.shift() + } + if(ItemArr.length === 0) + ArrForDel.push(key) + } + for(var i = 0; i < ArrForDel.length; i++) + { + var key = ArrForDel[i]; + delete this.SenderMap[key] + } + } +}; diff --git a/Source/core/transfer-msg.js b/Source/core/transfer-msg.js new file mode 100644 index 0000000..c90390a --- /dev/null +++ b/Source/core/transfer-msg.js @@ -0,0 +1,258 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const MAX_MESSAGE_COUNT = 1000; +module.exports = class CMessages extends require("./transaction-validator") +{ + constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + { + super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) + this.MemPoolMsg = [] + for(var i = 0; i <= MAX_LEVEL_SPECIALIZATION; i++) + this.MemPoolMsg[i] = new RBTree(CompareItemTimePow) + } + AddMsgToQuote(Msg) + { + var Tree = this.MemPoolMsg[Msg.Level]; + if(Tree) + { + if(Tree.insert(Msg)) + { + if(Tree.size > MEM_POOL_MSG_COUNT) + { + var maxitem = Tree.max(); + Tree.remove(maxitem) + if(maxitem === Msg) + return 0; + } + return 1; + } + else + { + return 3; + } + } + return 0; + } + IsValidMsg(Msg) + { + this.CheckCreateMsgHASH(Msg) + if(Msg.power < MIN_POWER_POW_MSG) + return - 1; + if(Msg.time > this.CurrentBlockNum) + return - 1; + return 1; + } + CheckCreateMsgHASH(Msg) + { + if(!Msg.HashPow) + { + Msg.HASH = sha3(Msg.body) + Msg.HashPow = GetHashWithValues(Msg.HASH, Msg.nonce, Msg.time) + Msg.power = GetPowPower(Msg.HashPow) + Msg.TimePow = Msg.time + Msg.power - Math.log2(Msg.body.length / 128) + Msg.Level = AddrLevelArr(this.addrArr, Msg.addrArr) + if(Msg.Level >= MAX_LEVEL_SPECIALIZATION) + Msg.Level = MAX_LEVEL_SPECIALIZATION + } + } + CreateMsgFromBody(Body, ToAddr) + { + var HASH = sha3(Body); + var Msg = {HASH:HASH, body:Body, addrArr:ToAddr, nonce:CreateNoncePOWExtern(HASH, this.CurrentBlockNum, 3 * (1 << MIN_POWER_POW_MSG)), + time:this.CurrentBlockNum, }; + this.CheckCreateMsgHASH(Msg) + return Msg; + } + SendMessage(Body, ToAddr) + { + var Msg = this.CreateMsgFromBody(Body, ToAddr); + this.SendMessageNext(Msg) + } + SendMessageNext(Msg) + { + var CountNodes = 3; + var LevelStart = Msg.Level; + if(CompareArr(this.addrArr, Msg.addrArr) === 0) + return false; + for(var L = LevelStart; L >= 0; L--) + if(this.LevelNodes[L] && this.LevelNodes[L].length) + { + var arr = this.LevelNodes[L]; + for(var j = 0; arr && j < arr.length; j++) + { + var Node = arr[j]; + this.SendF(Node, {"Method":"MESSAGE", "Data":{Arr:[Msg]}}) + CountNodes-- + if(CountNodes <= 0) + break; + } + } + return true; + } + static + MESSAGE_F() + { + return "{Arr:[{addrArr:hash,body:tr,nonce:uint,time:uint}]}"; + } + MESSAGE(Info, CurTime) + { + var Data = this.DataFromF(Info); + var arr = Data.Arr; + for(var i = 0; i < arr.length; i++) + { + var Msg = arr[i]; + if(this.IsValidMsg(Msg)) + { + if(CompareArr(this.addrArr, Msg.addrArr) === 0) + { + var App = DAppByType[Msg.body[0]]; + if(App) + { + App.OnMessage(Msg, BlockNum, i) + } + } + else + { + if(this.AddMsgToQuote(Msg) === 1) + { + this.SendMessageNext(Msg) + } + } + } + } + } + SendGetMessage(Node) + { + var Context = {"SendGetMessage":1}; + this.Send(Node, {"Method":"GETMESSAGE", "Context":Context, "Data":undefined}) + } + GETMESSAGE(Info, CurTime) + { + var arr = []; + var BufLength = 300; + var Level = AddrLevelArr(this.addrArr, Info.Node.addrArr); + var Tree = this.MemPoolMsg[Level]; + if(Tree) + { + var it = Tree.iterator(), Item; + while((Item = it.next()) !== null) + { + if(arr.length >= MAX_MESSAGE_COUNT) + break; + arr.push(Item) + BufLength += Item.body.length + 50 + } + } + this.SendF(Info.Node, {"Method":"MESSAGE", "Context":Info.Context, "Data":{Arr:arr}}, BufLength) + } + AddTransaction(Tr, ToAll) + { + Tr.ToAll = ToAll + var Res = this.IsValidTransaction(Tr, this.CurrentBlockNum); + if(Res <= 0 && Res !== - 3) + return Res; + if(Tr.num < this.CurrentBlockNum) + return - 3; + var delta = Tr.num - this.CurrentBlockNum; + if(delta > 3) + { + if(delta < 15) + { + let TR = Tr; + let SELF = this; + setTimeout(function () + { + var Res = SELF.AddTransaction(TR, TR.ToAll); + if(TR.ToAll) + ToLogClient("#3 Added " + TrName(TR) + " for block: " + TR.num + " on timer Res=" + Res) + }, (delta - 3) * 1000) + if(Tr.ToAll) + ToLogClient("#2 Added " + TrName(Tr) + " for block: " + Tr.num + " to timer. Send transaction after " + (delta - 3) + " sec") + return 4; + } + return - 3; + } + var Block = this.GetBlockContext(Tr.num); + if(!Block) + return - 5; + if(Block.Active) + { + Res = - 3 + } + else + { + Res = this.AddTrToBlockQuote(Block, Tr) + if(Tr.ToAll) + this.SendTransaction(Tr) + } + ToLogContext("#1 Add " + TrName(Tr) + " for Block: " + Tr.num + " Res=" + Res) + return Res; + } + SendTransaction(Tr) + { + if(!Tr.ToAll) + return ; + var CurTime = GetCurrentTime(0) - 0; + var Count; + if(GrayConnect()) + Count = Math.trunc(MAX_GRAY_CONNECTIONS_TO_SERVER / 2) + else + Count = Math.min(this.ActualNodes.size, 16) + if(Count < 2) + Count = 2 + var ArrNodes = this.GetActualNodes(); + for(var i = 0; i < ArrNodes.length; i++) + { + var Node = ArrNodes[i]; + if(!Node) + continue; + if(Node.TaskLastSend) + { + var Delta = CurTime - Node.TaskLastSend; + if(Delta < global.PERIOD_GET_BLOCK || Node.StopGetBlock) + { + continue; + } + } + Node.TaskLastSend = CurTime + this.SendF(Node, {"Method":"TRANSACTION", "Data":Tr}, Tr.body.length + 1000) + ToLogContext("Send " + TrName(Tr) + " to " + NodeName(Node)) + Count-- + if(Count <= 0) + break; + } + } + static + TRANSACTION_F() + { + return "{body:tr}"; + } + TRANSACTION(Info, CurTime) + { + var Tr = this.DataFromF(Info); + ToLogContext("Receive " + TrName(Tr) + " from " + NodeName(Info.Node)) + this.AddTransaction(Tr, 0) + } +}; + +function ToLogContext(Str) +{ +}; + +function TrName(Tr) +{ + if(!Tr.HASH) + SERVER.CheckCreateTransactionObject(Tr); + var Str = GetHexFromArr(Tr.HASH); + return "Tx:" + Str.substr(0, 8); +}; +global.TrName = TrName; diff --git a/Source/core/update.js b/Source/core/update.js new file mode 100644 index 0000000..728c3f1 --- /dev/null +++ b/Source/core/update.js @@ -0,0 +1,189 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.RunOnUpdate = RunOnUpdate; + +function RunOnUpdate() +{ + var fname = GetDataPath("DB/update.lst"); + var UpdateInfo = LoadParams(fname, {UPDATE_NUM_COMPLETE:1071}); + if(!UpdateInfo.UPDATE_NUM_COMPLETE) + UpdateInfo.UPDATE_NUM_COMPLETE = 0; + var CurNum = UpdateInfo.UPDATE_NUM_COMPLETE; + if(CurNum !== UPDATE_CODE_VERSION_NUM) + { + UpdateInfo.UPDATE_NUM_COMPLETE = UPDATE_CODE_VERSION_NUM; + ToLog("UPDATER Start"); + SaveParams(fname, UpdateInfo); + if(global.TEST_NETWORK || global.LOCAL_RUN) + { + } + else + { + } + ToLog("UPDATER Finish"); + } +}; + +function CreateHeadersHash100() +{ + ToLog("CreateHeadersHash100"); + const DBRow = require("./db/db-row"); + global.UpdateMode = 1; + var DB = SERVER.DBHeader100; + var Num = 0; + var PrevHash100 = []; + while(1) + { + var Block = SERVER.ReadBlockHeaderDB(Num); + if(!Block) + break; + var Hash100; + if(Num === 0) + Hash100 = []; + else + Hash100 = sha3arr2(PrevHash100, Block.Hash); + DB.Write({Num:Num / 100, Hash100:Hash100, Hash:Block.Hash}); + if(Num % 1000000 === 0) + ToLog("Create Hash100:" + Num); + PrevHash100 = Hash100; + Num += 100; + } + global.UpdateMode = 0; +}; + +function CheckRewriteTr(Num,StrHash,StartRewrite) +{ + if(SERVER.BlockNumDB < StartRewrite) + return "NO"; + var AccountsHash = DApps.Accounts.GetHashOrUndefined(Num); + if(!AccountsHash || GetHexFromArr(AccountsHash) !== StrHash) + { + ToLog("START REWRITE ERR ACTS TRANSACTIONS"); + SERVER.ReWriteDAppTransactions(SERVER.BlockNumDB - StartRewrite); + return "Rewrite"; + } + else + { + return "OK"; + } +}; + +function CheckRewriteAllTr2(Num,StrHash,Num2,StrHash2) +{ + if(global.LOCAL_RUN || global.TEST_NETWORK) + return "NONE"; + var MaxNum = SERVER.GetMaxNumBlockDB(); + if(MaxNum < START_BLOCK_ACCOUNT_HASH) + return "NONE"; + var AccountsHash = DApps.Accounts.GetHashOrUndefined(Num); + var AccountsHash2 = DApps.Accounts.GetHashOrUndefined(Num2); + if(AccountsHash2 && GetHexFromArr(AccountsHash2) === StrHash2) + return "OK"; + if(AccountsHash && GetHexFromArr(AccountsHash) !== StrHash) + { + ToLog("***************** START REWRITE ALL DAPPS"); + global.UpdateMode = 1; + for(var key in DApps) + { + DApps[key].ClearDataBase(); + } + global.UpdateMode = 0; + return "Rewrite"; + } + else + { + return "OK"; + } +}; + +function CheckRewriteAllTr(Num,StrHash,Num2,StrHash2) +{ + if(global.LOCAL_RUN || global.TEST_NETWORK) + return "NONE"; + var MaxNum = SERVER.GetMaxNumBlockDB(); + if(MaxNum < START_BLOCK_ACCOUNT_HASH) + return "NONE"; + var AccountsHash = DApps.Accounts.GetHashOrUndefined(Num); + if(AccountsHash && GetHexFromArr(AccountsHash) !== StrHash) + { + ToLog("***************** START REWRITE ALL DAPPS"); + global.UpdateMode = 1; + for(var key in DApps) + { + DApps[key].ClearDataBase(); + } + global.UpdateMode = 0; + return "Rewrite"; + } + else + { + return "OK"; + } +}; +global.CheckRewriteTr = CheckRewriteTr; + +function RecreateAccountRest1() +{ + var name = "accounts-rest"; + var fname = GetDataPath("DB/" + name); + if(fs.existsSync(fname)) + { + ToLog("Delete " + fname); + fs.unlinkSync(fname); + } +}; + +function RecreateAccountHashDB3() +{ + var name = "accounts-hash2"; + var fname = GetDataPath("DB/" + name); + if(fs.existsSync(fname)) + { + global.UpdateMode = 1; + ToLog("Start updating " + name); + const DBRow = require("../core/db/db-row"); + var DB0 = new DBRow(name, 6 + 32 + 32 + 10, "{BlockNum:uint, Hash:hash, SumHash:hash, Reserve: arr10}"); + var DB3 = DApps.Accounts.DBAccountsHash; + for(var num = 0; true; num++) + { + var Item = DB0.Read(num); + if(!Item) + break; + Item.AccHash = Item.Hash; + DB3.Write(Item); + } + ToLog("Finish updating " + name); + DB0.Close(); + DB3.Close(); + global.UpdateMode = 0; + fs.unlinkSync(fname); + } +}; + +function ReWriteDBSmartWrite() +{ + global.UpdateMode = 1; + ToLog("Start ReWriteDBSmartWrite"); + require("../core/db/db-row"); + for(var num = 0; true; num++) + { + var Item = DApps.Smart.DBSmart.Read(num); + if(!Item) + break; + var Body = BufLib.GetBufferFromObject(Item, DApps.Smart.FORMAT_ROW, 20000, {}); + if(Body.length > 15000) + ToLog("Smart " + Item.Num + ". " + Item.Name + " length=" + Body.length); + DApps.Smart.DBSmartWrite(Item); + } + ToLog("Finish ReWriteDBSmartWrite"); + DApps.Smart.DBSmart.Close(); + global.UpdateMode = 0; +}; diff --git a/Source/core/wallet.js b/Source/core/wallet.js new file mode 100644 index 0000000..e43457a --- /dev/null +++ b/Source/core/wallet.js @@ -0,0 +1,269 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +const crypto = require('crypto'); +require("./library"); +require("./crypto-library"); +const WalletPath = "WALLET"; +const DBRow = require("./db/db-row"); +const CONFIG_NAME = GetDataPath(WalletPath + "/config.lst"); +class CApp +{ + constructor() + { + CheckCreateDir(GetDataPath(WalletPath)) + var bReadOnly = (global.PROCESS_NAME !== "TX"); + this.Password = "" + this.WalletOpen = undefined + var Params = LoadParams(CONFIG_NAME, undefined); + if(!Params) + { + Params = {} + if(global.TEST_NETWORK) + { + Params.Key = global.ARR_PUB_KEY[0] + } + else + { + Params.Key = GetHexFromArr(crypto.randomBytes(32)) + } + Params.AccountMap = {} + Params.MiningAccount = 0 + } + if(Params.MiningAccount) + global.GENERATE_BLOCK_ACCOUNT = Params.MiningAccount + this.AccountMap = Params.AccountMap + this.KeyPair = crypto.createECDH('secp256k1') + if(Params.Protect) + { + ToLogClient("Wallet protect by password") + this.KeyXOR = GetArrFromHex(Params.KeyXOR) + this.WalletOpen = false + this.SetPrivateKey(Params.PubKey) + } + else + { + this.SetPrivateKey(Params.Key) + } + } + SetMiningAccount(Account) + { + global.GENERATE_BLOCK_ACCOUNT = Account + this.SaveWallet() + } + AddTransaction(Tr) + { + if(!global.TX_PROCESS.Worker) + return 0; + var StrHex = GetHexFromArr(sha3(Tr.body)); + global.TX_PROCESS.Worker.send({cmd:"FindTX", TX:StrHex}) + return SERVER.AddTransaction(Tr, 1); + } + SetPrivateKey(KeyStr, bSetNew) + { + var bGo = 1; + if(this.WalletOpen === false) + { + bGo = 0 + } + if(KeyStr && KeyStr.length === 64 && bGo) + { + this.KeyPair.setPrivateKey(GetArr32FromHex(KeyStr)) + this.KeyPair.PubKeyArr = this.KeyPair.getPublicKey('', 'compressed') + this.KeyPair.PubKeyStr = GetHexFromArr(this.KeyPair.PubKeyArr) + this.KeyPair.PrivKeyStr = KeyStr.toUpperCase() + this.KeyPair.addrArr = this.KeyPair.PubKeyArr.slice(1) + this.KeyPair.addrStr = GetHexAddresFromPublicKey(this.KeyPair.addrArr) + this.KeyPair.addr = this.KeyPair.addrArr + this.KeyPair.WasInit = 1 + this.PubKeyArr = this.KeyPair.PubKeyArr + } + else + { + this.KeyPair.WasInit = 0 + if(KeyStr) + { + this.PubKeyArr = GetArrFromHex(KeyStr) + this.KeyPair.PubKeyStr = GetHexFromArr(this.PubKeyArr) + } + else + { + this.PubKeyArr = [] + this.KeyPair.PubKeyStr = "" + } + this.KeyPair.PrivKeyStr = "" + } + if(bSetNew) + { + this.AccountMap = {} + } + this.FindMyAccounts(0) + if(bGo) + this.SaveWallet() + } + CloseWallet() + { + this.Password = "" + this.WalletOpen = false + this.KeyPair = crypto.createECDH('secp256k1') + this.SetPrivateKey(GetHexFromArr(this.PubKeyArr), false) + ToLogClient("Wallet close") + return 1; + } + OpenWallet(StrPassword) + { + if(this.WalletOpen !== false) + { + ToLogClient("Wallet was open") + } + var Hash = this.HashProtect(StrPassword); + var TestPrivKey = this.XORHash(this.KeyXOR, Hash, 32); + if(!IsZeroArr(TestPrivKey)) + { + this.KeyPair.setPrivateKey(Buffer.from(TestPrivKey)) + var TestPubKey = this.KeyPair.getPublicKey('', 'compressed'); + if(CompareArr(TestPubKey, this.PubKeyArr) !== 0) + { + ToLogClient("Wrong password") + return 0; + } + this.Password = StrPassword + this.WalletOpen = true + this.SetPrivateKey(GetHexFromArr(TestPrivKey), false) + } + else + { + this.Password = StrPassword + this.WalletOpen = true + this.SetPrivateKey(GetHexFromArr(this.PubKeyArr), false) + } + ToLogClient("Wallet open") + return 1; + } + SetPasswordNew(StrPassword) + { + if(this.WalletOpen === false) + { + ToLogClient("Wallet is close by password") + return ; + } + this.Password = StrPassword + if(StrPassword) + this.WalletOpen = true + else + this.WalletOpen = undefined + this.SaveWallet() + } + HashProtect(Str) + { + var arr = shaarr(Str); + for(var i = 0; i < 10000; i++) + { + arr = shaarr(arr) + } + return arr; + } + XORHash(arr1, arr2, length) + { + var arr3 = []; + for(var i = 0; i < length; i++) + { + arr3[i] = arr1[i] ^ arr2[i] + } + return arr3; + } + SaveWallet() + { + if(this.WalletOpen === false) + { + return ; + } + var Params = {}; + if(this.Password) + { + Params.Protect = true + var Hash = this.HashProtect(this.Password); + if(this.KeyPair.WasInit) + { + Params.KeyXOR = GetHexFromArr(this.XORHash(this.KeyPair.getPrivateKey(), Hash, 32)) + } + else + { + var Key2 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + Params.KeyXOR = GetHexFromArr(this.XORHash(Key2, Hash, 32)) + } + Params.PubKey = GetHexFromArr(this.PubKeyArr) + this.KeyXOR = GetArrFromHex(Params.KeyXOR) + } + else + { + if(this.KeyPair.WasInit) + Params.Key = this.KeyPair.PrivKeyStr + else + Params.Key = GetHexFromArr(this.PubKeyArr) + } + Params.AccountMap = this.AccountMap + Params.MiningAccount = global.GENERATE_BLOCK_ACCOUNT + SaveParams(CONFIG_NAME, Params) + } + OnCreateAccount(Data) + { + this.AccountMap[Data.Num] = 0 + } + FindMyAccounts(bClean) + { + if(IsZeroArr(this.PubKeyArr)) + return ; + if(bClean) + this.AccountMap = {} + DApps.Accounts.FindAccounts([this.PubKeyArr], this.AccountMap, 0) + } + GetAccountKey(Num) + { + if(this.KeyPair.WasInit && global.TestTestWaletMode) + { + } + return this.KeyPair; + } + GetPrivateKey(Num) + { + if(!this.KeyPair.WasInit) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + var KeyPair; + if(Num) + { + KeyPair = this.GetAccountKey(Num) + } + else + { + KeyPair = this.KeyPair + } + return KeyPair.getPrivateKey(); + } + GetSignFromArr(Arr, Num) + { + if(!this.KeyPair.WasInit) + return "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + var PrivKey = this.GetPrivateKey(Num); + var sigObj = secp256k1.sign(SHA3BUF(Arr), Buffer.from(PrivKey)); + return GetHexFromArr(sigObj.signature); + } + GetSignTransaction(TR) + { + if(!this.KeyPair.WasInit) + return "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + var PrivKey = this.GetPrivateKey(this.AccountMap[TR.FromID]); + var Arr = DApps.Accounts.GetSignTransferTx(TR, PrivKey); + return GetHexFromArr(Arr); + } +}; +global.WALLET = new CApp; diff --git a/Source/package.json b/Source/package.json new file mode 100644 index 0000000..4013678 --- /dev/null +++ b/Source/package.json @@ -0,0 +1,27 @@ +{ + "name": "Tera", + "version": "1.0.0", + "description": "Tera wallet", + "main": "run-nw.js", + "main2": "run-electron.js", + "repository": "https://gitlab.com/terafoundation/tera", + "keywords": [ + "wallet", + "cryptowallet", + "quick", + "messenger", + "cryptocurrency", + "cryptomessenger" + ], + "author": "progr76@gmail.com", + "license": "MIT", + "dependencies": { + "bintrees": "^1.0.2", + "greenlock": "^2.6.7", + "ntp-client": "^0.5.3", + "secp256k1": "^3.6.1", + "stun": "^1.1.0", + "uglify-js": "^3.4.9", + "zip": "^1.2.0" + } +} diff --git a/Source/process/api-exchange.js b/Source/process/api-exchange.js new file mode 100644 index 0000000..88b24b5 --- /dev/null +++ b/Source/process/api-exchange.js @@ -0,0 +1,412 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +const crypto = require('crypto'); +WebApi2.GenerateKeys = function (Params) +{ + var KeyPair = crypto.createECDH('secp256k1'); + var PrivKey = sha3(crypto.randomBytes(32)); + KeyPair.setPrivateKey(Buffer.from(PrivKey)); + var PubKey = KeyPair.getPublicKey('', 'compressed'); + return {result:1, PrivKey:GetHexFromArr(PrivKey), PubKey:GetHexFromArr(PubKey), Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.CreateAccount = function (Params,response) +{ + if(typeof Params === "object" && Params.Name && Params.PrivKey) + { + var KeyPair = crypto.createECDH('secp256k1'); + KeyPair.setPrivateKey(Buffer.from(GetArrFromHex(Params.PrivKey))); + var PubKey = KeyPair.getPublicKey('', 'compressed'); + var TR = {Type:TYPE_TRANSACTION_CREATE, Currency:Params.Currency, PubKey:PubKey, Name:Params.Name, Smart:Params.Smart, }; + var Body = BufLib.GetBufferFromObject(TR, FORMAT_CREATE, 1000, {}, 1); + Body = Body.slice(0, Body.len + 12); + SendTransaction(Body, TR, Params.Wait, function (result,text) + { + var Result = {result:result, text:text, TxID:GetHexFromArr(TR._TxID.slice(0, TR_TICKET_HASH_LENGTH + 6)), BlockNum:TR._BlockNum, + Meta:Params.Meta, }; + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +var MapSendID = {}; +WebApi2.Send = function (Params,response,A,bJsonRet) +{ + if(typeof Params !== "object") + return {result:0}; + var Coin; + if(typeof Params.Amount === "number") + Coin = COIN_FROM_FLOAT(Params.Amount); + else + Coin = Params.Amount; + var FromNum = ParseNum(Params.FromID); + if(!Coin) + return {result:0, Meta:Params.Meta, text:"Params.Amount required"}; + if(!FromNum) + return {result:0, Meta:Params.Meta, text:"Params.FromID required"}; + if(!Params.ToID) + return {result:0, Meta:Params.Meta, text:"Params.ToID required"}; + var ToPubKeyArr = []; + var ToID = 0; + if(typeof Params.ToID === "string" && Params.ToID.length === 66) + ToPubKeyArr = GetArrFromHex(Params.ToID); + else + ToID = ParseNum(Params.ToID); + var DataFrom = DApps.Accounts.ReadState(FromNum); + if(!DataFrom) + return {result:0, Meta:Params.Meta, text:"Error read account: " + FromNum}; + var OperationID; + if(!MapSendID[FromNum]) + { + OperationID = DataFrom.Value.OperationID + 10; + MapSendID[FromNum] = {}; + } + else + { + OperationID = MapSendID[FromNum].OperationID; + if((new Date() - MapSendID[FromNum].Date) > 8 * 1000) + { + OperationID += 20; + } + } + OperationID++; + MapSendID[FromNum].OperationID = OperationID; + MapSendID[FromNum].Date = Date.now(); + var TR = {Type:111, Version:3, Reserve:0, FromID:FromNum, OperationID:OperationID, To:[{PubKey:ToPubKeyArr, ID:ToID, SumCOIN:Coin.SumCOIN, + SumCENT:Coin.SumCENT}], Description:Params.Description, Body:[], }; + if(bJsonRet) + return {result:1, Tx:TR}; + if(!Params.FromPrivKey) + return {result:0, Meta:Params.Meta, text:"Params.FromPrivKey required"}; + TR.Sign = DApps.Accounts.GetSignTransferTx(TR, GetArrFromHex(Params.FromPrivKey)); + var Body = BufLib.GetBufferFromObject(TR, FORMAT_MONEY_TRANSFER3, MAX_TRANSACTION_SIZE, {}, 1); + Body = Body.slice(0, Body.len + 12); + SendTransaction(Body, TR, Params.Wait, function (result,text) + { + var Result = {result:result, text:text, TxID:GetHexFromArr(TR._TxID.slice(0, TR_TICKET_HASH_LENGTH + 6)), BlockNum:TR._BlockNum, + Meta:Params.Meta, }; + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; +}; +WebApi2.GetBalance = function (Params,response) +{ + if(typeof Params === "object") + { + var arr = DApps.Accounts.GetRowsAccounts(ParseNum(Params.AccountID), 1); + if(arr.length) + { + var Account = arr[0]; + var Value = Account.Value; + var Result = {result:1, SumCOIN:Value.SumCOIN, SumCENT:Value.SumCENT, Currency:Account.Currency, PubKey:GetHexFromArr(Account.PubKey), + Meta:Params.Meta, }; + return Result; + } + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.GetTransaction = function (Params) +{ + if(typeof Params === "object" && Params.TxID) + { + var Arr = GetArrFromHex(Params.TxID); + var BlockNum = ReadUintFromArr(Arr, TR_TICKET_HASH_LENGTH); + var Block = SERVER.ReadBlockDB(BlockNum); + if(Block && Block.arrContent) + { + for(var i = 0; i < Block.arrContent.length; i++) + { + var Body = Block.arrContent[i]; + var Arr2 = GetTxID(BlockNum, Body); + if(CompareArr(Arr2, Arr) === 0) + { + return GetTransactionFromBody(Params, Block, i, Body); + } + } + } + } + else + { + if(typeof Params === "object" && Params.BlockNum && Params.TrNum !== undefined) + { + var Block = SERVER.ReadBlockDB(Params.BlockNum); + if(Block && Block.arrContent) + { + var Body = Block.arrContent[Params.TrNum]; + if(Body) + { + return GetTransactionFromBody(Params, Block, Params.TrNum, Body); + } + } + } + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.GetHistoryTransactions = function (Params) +{ + if(typeof Params === "object" && Params.AccountID) + { + if(!Params.Count) + Params.Count = 100; + if(Params.Confirm === undefined) + Params.Confirm = 8; + var arr = DApps.Accounts.GetHistory(Params.AccountID, Params.Count, Params.NextPos, Params.Confirm); + if(Params.GetTxID || Params.GetDescription) + { + for(var i = 0; i < arr.length; i++) + { + var Item = arr[i]; + var Block = SERVER.ReadBlockDB(Item.BlockNum); + if(!Block || (!Block.arrContent)) + continue; + var Body = Block.arrContent[Item.TrNum]; + if(!Body) + continue; + if(Params.GetTxID) + { + Item.TxID = GetHexFromArr(GetTxID(Item.BlockNum, Body)); + } + if(Params.GetDescription) + { + var TR = DApps.Accounts.GetObjectTransaction(Body); + if(TR) + { + Item.Description = TR.Description; + } + } + } + } + var Result = {result:arr.length > 0 ? 1 : 0, History:arr, Tail:DApps.Accounts.DBStateHistory.Read(Params.AccountID), Meta:Params ? Params.Meta : undefined}; + return Result; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.CreateRawTransaction = function (Params) +{ + if(typeof Params === "object") + { + var Ret = WebApi2.Send(Params, undefined, undefined, 1); + TxArrToHex(Ret.Tx); + Ret.Meta = Params.Meta; + return Ret; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.SignRawTransaction = function (Params) +{ + if(typeof Params === "object" && Params.Tx) + { + if(!Params.FromPrivKey) + return {result:0, Meta:Params.Meta, text:"Params.FromPrivKey required"}; + if(typeof Params.Tx !== "object") + return {result:0, Meta:Params.Meta, text:"Params.Tx required"}; + if(!Params.Tx.To || !Params.Tx.To.length) + return {result:0, Meta:Params.Meta, text:"Params.Tx.To required"}; + var TR = Params.Tx; + TxHexToArr(TR); + TR.Sign = DApps.Accounts.GetSignTransferTx(TR, GetArrFromHex(Params.FromPrivKey)); + TxArrToHex(TR); + var Ret = {result:1, Tx:TR, Meta:Params ? Params.Meta : undefined}; + return Ret; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; +WebApi2.SendRawTransaction = function (Params,response) +{ + if(typeof Params === "object" && Params.Tx) + { + if(typeof Params.Tx !== "object") + return {result:0, Meta:Params.Meta, text:"Params.Tx required"}; + if(!Params.Tx.To || !Params.Tx.To.length) + return {result:0, Meta:Params.Meta, text:"Params.Tx.To required"}; + if(!Params.Tx.Sign) + return {result:0, Meta:Params.Meta, text:"Params.Tx.Sign required"}; + var TR = Params.Tx; + TxHexToArr(TR); + var Body = BufLib.GetBufferFromObject(TR, FORMAT_MONEY_TRANSFER3, MAX_TRANSACTION_SIZE, {}, 1); + Body = Body.slice(0, Body.len + 12); + SendTransaction(Body, TR, Params.Wait, function (result,text) + { + var Result = {result:result, text:text, TxID:GetHexFromArr(TR._TxID.slice(0, TR_TICKET_HASH_LENGTH + 6)), BlockNum:TR._BlockNum, + Meta:Params.Meta, }; + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; + +function TxArrToHex(TR) +{ + if(TR && TR.To && TR.To[0].PubKey) + { + if(TR.To[0].PubKey.length) + TR.To[0].PubKey = GetHexFromArr(TR.To[0].PubKey); + else + TR.To[0].PubKey = ""; + if(TR.Sign && TR.Sign.length) + TR.Sign = GetHexFromArr(TR.Sign); + else + TR.Sign = ""; + TR.Body = undefined; + TR.Reserve = undefined; + } +}; + +function TxHexToArr(TR) +{ + TR.Body = []; + if(TR.Sign && TR.Sign.length) + TR.Sign = GetArrFromHex(TR.Sign); + else + TR.Sign = []; + for(var i = 0; i < TR.To.length; i++) + { + TR.To[i].PubKey = GetArrFromHex(TR.To[i].PubKey); + } +}; +var DELTA_FOR_TIME_TX = 1; + +function GetTxID(BlockNum,Body) +{ + var Nonce = ReadUintFromArr(Body, Body.length - 6); + var Arr2 = CreateTxID(Body, BlockNum, Nonce); + return Arr2.slice(0, TR_TICKET_HASH_LENGTH + 6); +}; + +function CreateTxID(body,BlockNum,Nonce) +{ + body.writeUIntLE(BlockNum, body.length - 12, 6); + body.writeUIntLE(Nonce, body.length - 6, 6); + var HASH = sha3(body); + var FullHashTicket = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for(var i = 0; i < TR_TICKET_HASH_LENGTH; i++) + FullHashTicket[i] = HASH[i]; + WriteUintToArrOnPos(FullHashTicket, BlockNum, TR_TICKET_HASH_LENGTH); + return FullHashTicket; +}; + +function GetBlockNumTr(arr) +{ + var BlockNum = DELTA_FOR_TIME_TX + GetCurrentBlockNumByTime(); + if(arr[0] === TYPE_TRANSACTION_CREATE) + { + var BlockNum2 = Math.floor(BlockNum / 10) * 10; + if(BlockNum2 < BlockNum) + BlockNum2 = BlockNum2 + 10; + BlockNum = BlockNum2; + } + return BlockNum; +}; + +function CreateHashBodyPOWInnerMinPower(TR,arr,MinPow,startnonce) +{ + var BlockNum = GetBlockNumTr(arr); + if(MinPow === undefined) + { + MinPow = MIN_POWER_POW_TR + Math.log2(arr.length / 128); + } + var nonce = startnonce; + while(1) + { + var TxID = CreateTxID(arr, BlockNum, nonce); + var power = GetPowPower(sha3(TxID)); + if(power >= MinPow) + { + TR._TxID = TxID; + TR._BlockNum = BlockNum; + ToLog("Find: " + power + " for block:" + BlockNum); + return nonce; + } + nonce++; + if(nonce % 2000 === 0) + { + BlockNum = GetBlockNumTr(arr); + } + } +}; + +function SendTransaction(Body,TR,Wait,F) +{ + if(Body.length > 16000) + { + TR._result = 0; + TR._text = "Error length transaction =" + Body.length + " (max size=16000)"; + F(1, TR, Body); + return ; + } + global.GlobalRunID++; + let WebID = global.GlobalRunID; + CreateNonceAndSend(0, 0); + +function CreateNonceAndSend(startnonce,NumNext) + { + if(!NumNext) + NumNext = 0; + if(NumNext > 10) + { + F(0, TR, Body); + return ; + } + var nonce = CreateHashBodyPOWInnerMinPower(TR, Body, undefined, startnonce); + process.RunRPC("AddTransactionFromWeb", {WebID:WebID, HexValue:GetHexFromArr(Body)}, function (Err,text) + { + TR._result = Err ? 0 : 1; + TR._text = text; + if(text === "Not add" || text === "Bad PoW") + { + CreateNonceAndSend(nonce + 1, NumNext + 1); + return ; + } + else + if(text === "Bad time") + { + if(DELTA_FOR_TIME_TX < 6) + { + DELTA_FOR_TIME_TX++; + ToLog("New set Delta time: " + DELTA_FOR_TIME_TX); + CreateNonceAndSend(0, NumNext + 1); + return ; + } + } + if(Wait && TR._result) + { + global.GlobalRunMap[WebID] = F; + } + else + { + F(TR._result < 1 ? 0 : 1, text); + } + }); + }; +}; + +function GetTransactionFromBody(Params,Block,TrNum,Body) +{ + var TR = DApps.Accounts.GetObjectTransaction(Body); + if(TR) + { + ConvertBufferToStr(TR); + TR.result = 1; + TR.Meta = Params.Meta; + if(Block.VersionBody === 1 && Block.arrContentResult) + { + TR.result = Block.arrContentResult[TrNum]; + } + TR.BlockNum = Block.BlockNum; + TR.TrNum = TrNum; + return TR; + } + return {result:0, Meta:Params ? Params.Meta : undefined}; +}; diff --git a/Source/process/dogs.js b/Source/process/dogs.js new file mode 100644 index 0000000..1e901a5 --- /dev/null +++ b/Source/process/dogs.js @@ -0,0 +1,10 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + diff --git a/Source/process/main-process.js b/Source/process/main-process.js new file mode 100644 index 0000000..a308d24 --- /dev/null +++ b/Source/process/main-process.js @@ -0,0 +1,789 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PROCESS_NAME = "MAIN"; +const fs = require('fs'); +const os = require('os'); +require("../core/constant"); +const crypto = require('crypto'); +global.START_SERVER = 1; +global.DATA_PATH = GetNormalPathString(global.DATA_PATH); +global.CODE_PATH = GetNormalPathString(global.CODE_PATH); +console.log("DATA DIR: " + global.DATA_PATH); +console.log("PROGRAM DIR: " + global.CODE_PATH); +require("../core/library"); +ToLog(os.platform() + " (" + os.arch() + ") " + os.release()); +var VerArr = process.versions.node.split('.'); +ToLog("nodejs: " + process.versions.node); +if(VerArr[0] < 8) +{ + ToError("Error version of NodeJS=" + VerArr[0] + " Pls, download new version from www.nodejs.org and update it. The minimum version must be 8"); + process.exit(); +} +var CServer = require("../core/server"); +const DEF_PERIOD_SIGN_LIB = 500; +setTimeout(function () +{ + TestSignLib(DEF_PERIOD_SIGN_LIB); +}, 4000); +global.glCurNumFindArr = 0; +global.ArrReconnect = []; +global.ArrConnect = []; +var FindList = [{"ip":"91.235.136.81", "port":30005}, {"ip":"149.154.70.158", "port":30000}, {"ip":"185.240.243.182", "port":30000}, + {"ip":"212.109.197.205", "port":30000}, {"ip":"212.109.197.209", "port":30000}, {"ip":"80.87.192.24", "port":30000}, ]; +if(global.LOCAL_RUN) +{ + FindList = [{"ip":"127.0.0.1", "port":50001}, {"ip":"127.0.0.1", "port":50002}]; +} +else + if(global.TEST_NETWORK) + { + FindList = [{"ip":"149.154.70.158", "port":40000}, ]; + } +global.SERVER = undefined; +global.NeedRestart = 0; +process.on('uncaughtException', function (err) +{ + if(global.PROCESS_NAME !== "MAIN") + { + process.send({cmd:"log", message:err}); + } + ToError(err.stack); + ToLog(err.stack); + if(err.code === "ENOTFOUND" || err.code === "ECONNRESET" || err.code === "EPIPE") + { + } + else + { + TO_ERROR_LOG("APP", 666, err); + ToLog("-----------------EXIT------------------"); + process.exit(); + } +}); +process.on('error', function (err) +{ + ToError(err.stack); + ToLog(err.stack); +}); +var ArrChildProcess = []; +var WebProcess = {Name:"WEB PROCESS", idInterval:0, idInterval1:0, idInterval2:0, LastAlive:Date.now(), Worker:undefined, Path:"./process/web-process.js", + OnMessage:OnMessageWeb, PeriodAlive:10 * 1000}; +global.WEB_PROCESS = WebProcess; +if(global.HTTP_HOSTING_PORT && !global.NWMODE) +{ + ArrChildProcess.push(WebProcess); + WebProcess.idInterval1 = setInterval(function () + { + if(WebProcess.Worker && WebProcess.Worker.connected) + { + try + { + WebProcess.Worker.send({cmd:"Stat", Name:"MAX:ALL_NODES", Value:global.CountAllNode}); + } + catch(e) + { + WebProcess.Worker = undefined; + } + } + }, 500); + WebProcess.idInterval2 = setInterval(function () + { + if(WebProcess.Worker && WebProcess.Worker.connected) + { + var arr = SERVER.GetDirectNodesArray(true, true).slice(1, 500); + var arr2 = []; + var CurTime = GetCurrentTime() - 0; + for(var i = 0; i < SERVER.NodesArr.length; i++) + { + var Item = SERVER.NodesArr[i]; + if(Item.LastTime && (CurTime - Item.LastTime) < NODES_DELTA_CALC_HOUR * 3600 * 1000) + arr2.push({ip:Item.ip, port:Item.port, webport:Item.webport}); + else + if(Item.LastTimeGetNode && (CurTime - Item.LastTimeGetNode) < NODES_DELTA_CALC_HOUR * 3600 * 1000) + arr2.push({ip:Item.ip, port:Item.port, webport:Item.webport}); + } + WebProcess.Worker.send({cmd:"NodeList", Value:arr, ValueAll:arr2}); + } + }, 5000); +} + +function OnMessageWeb(msg) +{ + switch(msg.cmd) + { + case "SetSmartEvent": + { + if(global.TX_PROCESS && global.TX_PROCESS.Worker) + { + global.TX_PROCESS.Worker.send(msg); + } + break; + } + } +}; + +function AddTransactionFromWeb(Params) +{ + var body = GetArrFromHex(Params.HexValue); + if(global.TX_PROCESS && global.TX_PROCESS.Worker) + { + var StrHex = GetHexFromArr(sha3(body)); + global.TX_PROCESS.Worker.send({cmd:"FindTX", TX:StrHex, Web:1, WebID:Params.WebID}); + } + var Res = SERVER.AddTransaction({body:body}, 1); + var text = AddTrMap[Res]; + var final = false; + if(Res <= 0 && Res !== - 3) + final = true; + ToLogClient("Send: " + text, GetHexFromArr(sha3(body)), final); + return text; +}; +global.AddTransactionFromWeb = AddTransactionFromWeb; +global.STATIC_PROCESS = {Name:"STATIC PROCESS", idInterval:0, idInterval1:0, idInterval2:0, LastAlive:Date.now(), Worker:undefined, + Path:"./process/static-process.js", OnMessage:OnMessageStatic, PeriodAlive:50000}; +ArrChildProcess.push(STATIC_PROCESS); + +function OnMessageStatic(msg) +{ + switch(msg.cmd) + { + case "Send": + { + var Node = SERVER.NodesMap[msg.addrStr]; + if(Node) + { + msg.Data = msg.Data.data; + SERVER.Send(Node, msg, 1); + } + break; + } + } +}; +global.TX_PROCESS = {Name:"TX PROCESS", idInterval:0, idInterval1:0, idInterval2:0, LastAlive:Date.now(), Worker:undefined, + Path:"./process/tx-process.js", OnMessage:OnMessageTX, PeriodAlive:100 * 1000}; +ArrChildProcess.push(TX_PROCESS); + +function OnMessageTX(msg) +{ + switch(msg.cmd) + { + case "DappEvent": + { + if(WebProcess && WebProcess.Worker) + { + WebProcess.Worker.send(msg); + } + AddDappEventToGlobalMap(msg.Data); + break; + } + } +}; + +function StartAllProcess(bClose) +{ + for(var i = 0; i < ArrChildProcess.length; i++) + { + var Item = ArrChildProcess[i]; + StartChildProcess(Item); + } + if(bClose) + setInterval(function () + { + if(global.DApps && DApps.Accounts) + { + DApps.Accounts.Close(); + DApps.Smart.DBSmart.Close(); + } + if(WALLET && WALLET.DBHistory) + WALLET.DBHistory.Close(); + }, 500); +}; +var GlobalRunID = 0; +var GlobalRunMap = {}; + +function StartChildProcess(Item) +{ + let ITEM = Item; + ITEM.idInterval = setInterval(function () + { + var Delta0 = Date.now() - ITEM.LastAlive; + if(Delta0 >= 0) + { + var Delta = Date.now() - ITEM.LastAlive; + if(ITEM.Worker && Delta > ITEM.PeriodAlive) + { + if(ITEM.Worker) + { + ToLog("KILL PROCESS " + ITEM.Name + ": " + ITEM.Worker.pid); + try + { + process.kill(ITEM.Worker.pid, 'SIGKILL'); + } + catch(e) + { + ToLog("ERR KILL"); + } + ITEM.Worker = undefined; + } + } + if(!ITEM.Worker) + { + ITEM.LastAlive = (Date.now()) + ITEM.PeriodAlive * 3; + ToLog("STARTING " + ITEM.Name); + ITEM.Worker = Fork(ITEM.Path, ["READONLYDB"]); + ITEM.pid = ITEM.Worker.pid; + ToLog("STARTED " + ITEM.Name + ":" + ITEM.pid); + ITEM.Worker.on('message', function (msg) + { + if(ITEM.LastAlive < Date.now()) + ITEM.LastAlive = Date.now(); + switch(msg.cmd) + { + case "call": + var Err = 0; + var Ret; + try + { + if(typeof msg.Params === "object" && msg.Params.F) + { + global[msg.Name](msg.Params, function (Err,Ret) + { + if(msg.id && ITEM.Worker) + ITEM.Worker.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret}); + }); + break; + } + else + { + Ret = global[msg.Name](msg.Params); + } + } + catch(e) + { + Err = 1; + Ret = "" + e; + } + if(msg.id && ITEM.Worker) + ITEM.Worker.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret}); + break; + case "retcall": + var F = GlobalRunMap[msg.id]; + if(F) + { + delete GlobalRunMap[msg.id]; + F(msg.Err, msg.Params); + } + break; + case "log": + ToLog(msg.message); + break; + case "ToLogClient": + if(WebProcess && WebProcess.Worker) + { + WebProcess.Worker.send(msg); + } + ToLogClient(msg.Str, msg.StrKey, msg.bFinal); + break; + case "RetFindTX": + if(WebProcess && WebProcess.Worker) + { + WebProcess.Worker.send(msg); + if(msg.Web) + break; + } + ToLogClient(msg.ResultStr, msg.TX, msg.bFinal); + break; + case "online": + if(ITEM.Worker) + ToLog("RUNNING " + ITEM.Name + " : " + msg.message + " pid: " + ITEM.Worker.pid); + break; + case "WriteBodyResult": + var Block = SERVER.ReadBlockDB(msg.BlockNum); + if(Block) + { + Block.arrContentResult = msg.arrContentResult; + SERVER.WriteBodyResultDB(Block); + } + break; + default: + if(ITEM.OnMessage) + { + ITEM.OnMessage(msg); + } + break; + } + }); + ITEM.Worker.on('error', function (err) + { + }); + ITEM.Worker.on('close', function (code) + { + ToError("CLOSE " + ITEM.Name); + }); + } + } + if(ITEM.Worker) + { + ITEM.Worker.send({cmd:"Alive"}); + } + }, 500); + ITEM.RunRPC = function (Name,Params,F) + { + if(!ITEM.Worker) + return ; + if(F) + { + GlobalRunID++; + try + { + ITEM.Worker.send({cmd:"call", id:GlobalRunID, Name:Name, Params:Params}); + GlobalRunMap[GlobalRunID] = F; + } + catch(e) + { + } + } + else + { + ITEM.Worker.send({cmd:"call", id:0, Name:Name, Params:Params}); + } + }; +}; +global.StopChildProcess = function () +{ + for(var i = 0; i < ArrChildProcess.length; i++) + { + var Item = ArrChildProcess[i]; + if(Item.idInterval) + clearInterval(Item.idInterval); + Item.idInterval = 0; + if(Item.idInterval1) + clearInterval(Item.idInterval1); + Item.idInterval1 = 0; + if(Item.idInterval2) + clearInterval(Item.idInterval2); + Item.idInterval2 = 0; + if(Item.Worker && Item.Worker.connected) + { + Item.Worker.send({cmd:"Exit"}); + Item.Worker = undefined; + } + } + RunStopPOWProcess("STOP"); +}; +require("../core/html-server"); +RunServer(); +setInterval(function run1() +{ + DoConnectToNodes(ArrReconnect, "RECONNECT"); +}, 200); +setInterval(function run2() +{ + DoGetNodes(); + DoConnectToNodes(ArrConnect, "CONNECT"); +}, 500); +var StartCheckMining = 0; +global.MiningPaused = 0; +var ProcessMemorySize = 0; +global.ArrMiningWrk = []; +var BlockMining; +if(global.ADDRLIST_MODE) +{ + return ; +} + +function AllAlive() +{ + for(var i = 0; i < ArrMiningWrk.length; i++) + { + ArrMiningWrk[i].send({cmd:"Alive"}); + } +}; + +function ClearArrMining() +{ + for(var i = 0; i < ArrMiningWrk.length; i++) + { + ArrMiningWrk[i].send({cmd:"Exit"}); + } + ArrMiningWrk = []; +}; + +function RunStopPOWProcess(Mode) +{ + if(!GetCountMiningCPU() || GetCountMiningCPU() <= 0) + return ; + if(!StartCheckMining) + { + StartCheckMining = 1; + setInterval(RunStopPOWProcess, CHECK_RUN_MINING); + setInterval(AllAlive, 1000); + } + if(global.NeedRestart) + return ; + if(global.USE_MINING && global.MINING_START_TIME && global.MINING_PERIOD_TIME) + { + var Time = GetCurrentTime(); + var TimeCur = Time.getUTCHours() * 3600 + Time.getUTCMinutes() * 60 + Time.getUTCSeconds(); + var StartTime = GetSecFromStrTime(global.MINING_START_TIME); + var RunPeriod = GetSecFromStrTime(global.MINING_PERIOD_TIME); + var TimeEnd = StartTime + RunPeriod; + global.MiningPaused = 1; + if(TimeCur >= StartTime && TimeCur <= TimeEnd) + { + global.MiningPaused = 0; + } + else + { + StartTime -= 24 * 3600; + TimeEnd -= 24 * 3600; + if(TimeCur >= StartTime && TimeCur <= TimeEnd) + { + global.MiningPaused = 0; + } + } + if(ArrMiningWrk.length && global.MiningPaused) + { + ToLog("------------ MINING MUST STOP ON TIME"); + ClearArrMining(); + return ; + } + else + if(!ArrMiningWrk.length && !global.MiningPaused) + { + ToLog("*********** MINING MUST START ON TIME"); + } + else + { + return ; + } + } + else + { + global.MiningPaused = 0; + } + if(!global.USE_MINING || Mode === "STOP") + { + ClearArrMining(); + return ; + } + if(global.USE_MINING && ArrMiningWrk.length) + return ; + if(SERVER.LoadHistoryMode) + return ; + if(GENERATE_BLOCK_ACCOUNT < 8) + return ; + var PathMiner = GetCodePath("../miner.js"); + if(!fs.existsSync(PathMiner)) + PathMiner = "./process/pow-process.js"; + if(ArrMiningWrk.length >= GetCountMiningCPU()) + return ; + if(GrayConnect()) + { + ToLog("CANNOT START MINER IN NOT DIRECT IP MODE"); + return ; + } + var Memory; + if(global.SIZE_MINING_MEMORY) + Memory = global.SIZE_MINING_MEMORY; + else + { + Memory = os.freemem() - (512 + GetCountMiningCPU() * 100) * 1024 * 1014; + if(Memory < 0) + { + ToLog("Not enough memory to start processes."); + return ; + } + } + ProcessMemorySize = Math.trunc(Memory / GetCountMiningCPU()); + ToLog("START MINER PROCESS COUNT: " + GetCountMiningCPU() + " Memory: " + ProcessMemorySize / 1024 / 1024 + " Mb for each process"); + for(var R = 0; R < GetCountMiningCPU(); R++) + { + let Worker = Fork(PathMiner); + ArrMiningWrk.push(Worker); + Worker.Num = ArrMiningWrk.length; + Worker.on('message', function (msg) + { + if(msg.cmd === "log") + { + ToLog(msg.message); + } + else + if(msg.cmd === "online") + { + Worker.bOnline = true; + ToLog("RUNNING PROCESS:" + Worker.Num + ":" + msg.message); + } + else + if(msg.cmd === "POW") + { + SERVER.MiningProcess(msg); + } + else + if(msg.cmd === "HASHRATE") + { + ADD_HASH_RATE(msg.CountNonce); + } + }); + Worker.on('error', function (err) + { + if(!ArrMiningWrk.length) + return ; + ToError('ERROR IN PROCESS: ' + err); + }); + Worker.on('close', function (code) + { + ToLog("STOP PROCESS: " + Worker.Num + " pid:" + Worker.pid); + for(var i = 0; i < ArrMiningWrk.length; i++) + { + if(ArrMiningWrk[i].pid === Worker.pid) + { + ToLog("Delete wrk from arr - pid:" + Worker.pid); + ArrMiningWrk.splice(i, 1); + } + } + }); + } +}; + +function SetCalcPOW(Block,cmd) +{ + if(!global.USE_MINING) + return ; + if(ArrMiningWrk.length !== GetCountMiningCPU()) + return ; + BlockMining = Block; + for(var i = 0; i < ArrMiningWrk.length; i++) + { + var CurWorker = ArrMiningWrk[i]; + if(!CurWorker.bOnline) + continue; + CurWorker.send({cmd:cmd, BlockNum:Block.BlockNum, Account:GENERATE_BLOCK_ACCOUNT, MinerID:GENERATE_BLOCK_ACCOUNT, SeqHash:Block.SeqHash, + Hash:Block.Hash, PrevHash:Block.PrevHash, Time:Date.now(), Num:CurWorker.Num, RunPeriod:global.POWRunPeriod, RunCount:global.POW_RUN_COUNT, + Percent:global.POW_MAX_PERCENT, CountMiningCPU:GetCountMiningCPU(), ProcessMemorySize:ProcessMemorySize, }); + } +}; +global.SetCalcPOW = SetCalcPOW; +global.RunStopPOWProcess = RunStopPOWProcess; + +function DoGetNodes() +{ + if(!SERVER) + return ; + if(!GrayConnect() && SERVER.CanSend < 2) + return ; + if(!SERVER.NodesArrUnSort || !SERVER.NodesArrUnSort.length) + return ; + var Num = glCurNumFindArr % SERVER.NodesArrUnSort.length; + var Node = SERVER.NodesArrUnSort[Num]; + if(Num === 0) + glCurNumFindArr = 0; + glCurNumFindArr++; + if(Node.Delete) + return ; + if(SERVER.NodeInBan(Node)) + return ; + if(SERVER.BusyLevel && Node.BlockProcessCount <= SERVER.BusyLevel) + return ; + if(GetSocketStatus(Node.Socket) === 100) + { + SERVER.StartGetNodes(Node); + } +}; + +function DoConnectToNodes(Arr,Mode) +{ + if(!SERVER) + return ; + if(!GrayConnect() && SERVER.CanSend < 2) + { + return ; + } + if(GrayConnect() && SERVER.ActualNodes.size > GetGrayServerConnections()) + return ; + if(Arr.length) + { + var MinProcessCount = SERVER.BusyLevel - 1; + for(var i = 0; i < Arr.length; i++) + { + var Node = Arr[i]; + if(Node.BlockProcessCount > MinProcessCount) + { + Arr.splice(i, 1); + if(Mode === "CONNECT") + { + Node.WasAddToConnect = undefined; + SERVER.StartConnectTry(Node); + } + else + if(Mode === "RECONNECT") + { + Node.WasAddToReconnect = undefined; + Node.CreateConnect(); + } + break; + } + } + } +}; +var idRunOnce; + +function RunServer() +{ + idRunOnce = setInterval(RunOnce, 1000); + ToLog("NETWORK: " + GetNetworkName()); + ToLog("VERSION: " + DEF_VERSION); + if(global.NET_WORK_MODE) + { + global.START_IP = NET_WORK_MODE.ip; + global.START_PORT_NUMBER = NET_WORK_MODE.port; + } + var KeyPair = crypto.createECDH('secp256k1'); + if(!global.SERVER_PRIVATE_KEY_HEX || global.NEW_SERVER_PRIVATE_KEY) + { + while(true) + { + var Arr = crypto.randomBytes(32); + KeyPair.setPrivateKey(Buffer.from(Arr)); + var Arr2 = KeyPair.getPublicKey('', 'compressed'); + if(Arr2[0] === 2) + break; + } + global.SERVER_PRIVATE_KEY_HEX = GetHexFromArr(Arr); + SAVE_CONST(true); + } + var ServerPrivKey = GetArrFromHex(global.SERVER_PRIVATE_KEY_HEX); + if(global.USE_NET_FOR_SERVER_ADDRES) + { + const os = require('os'); + var map = os.networkInterfaces(); + main: + for(var key in map) + { + var arr = map[key]; + for(var i = 0; i < arr.length; i++) + { + var item = arr[i]; + if(!item.internal && item.mac !== "00:00:00:00:00:00") + { + ServerPrivKey = sha3(global.SERVER_PRIVATE_KEY_HEX + ":" + item.mac + ":" + global.START_PORT_NUMBER); + break main; + } + } + } + } + KeyPair.setPrivateKey(Buffer.from(ServerPrivKey)); + new CServer(KeyPair, START_IP, START_PORT_NUMBER, false, false); + DoStartFindList(); +}; + +function DoStartFindList() +{ + var keyThisServer = SERVER.ip + ":" + SERVER.port; + for(var n = 0; n < FindList.length; n++) + { + var item = FindList[n]; + if(!item.ip) + continue; + var key = item.ip + ":" + item.port; + if(keyThisServer === key) + continue; + var addrStr = GetHexFromAddres(crypto.randomBytes(32)); + var Node = SERVER.GetNewNode(addrStr, item.ip, item.port); + Node.addrStrTemp = addrStr; + Node.StartFindList = 1; + } +}; + +function RunOnce() +{ + if(global.SERVER && global.SERVER.CheckOnStartComplete) + { + clearInterval(idRunOnce); + require("../core/update"); + RunOnUpdate(); + StartAllProcess(1); + require("./dogs"); + if(global.RESTART_PERIOD_SEC) + { + var Period = (random(600) + global.RESTART_PERIOD_SEC); + ToLog("SET RESTART NODE AFTER: " + Period + " sec"); + setInterval(function () + { + RestartNode(); + }, Period * 1000); + } + setTimeout(function () + { + RunStopPOWProcess(); + }, 10000); + } +}; +var glPortDebug = 49800; + +function Fork(Path,ArrArgs) +{ + const child_process = require('child_process'); + ArrArgs = ArrArgs || []; + if(global.LOCAL_RUN) + ArrArgs.push("LOCALRUN"); + else + if(global.TEST_NETWORK) + ArrArgs.push("TESTRUN"); + ArrArgs.push("PATH:" + global.DATA_PATH); + ArrArgs.push("HOSTING:" + global.HTTP_HOSTING_PORT); + if(!global.USE_PARAM_JS) + ArrArgs.push("NOPARAMJS"); + if(global.NWMODE) + ArrArgs.push("NWMODE"); + if(global.NOALIVE) + ArrArgs.push("NOALIVE"); + if(global.DEV_MODE) + ArrArgs.push("DEV_MODE"); + glPortDebug++; + var execArgv = []; + var Worker = child_process.fork(Path, ArrArgs, {execArgv:execArgv}); + return Worker; +}; +global.SpeedSignLib = 0; +global.TestSignLib = TestSignLib; + +function TestSignLib(MaxTime) +{ + if(!MaxTime) + MaxTime = DEF_PERIOD_SIGN_LIB; + var hash = Buffer.from(GetArrFromHex("A6B0914953F515F4686B2BA921B8FAC66EE6A6D3E317B43E981EBBA52393BFC6")); + var PubKey = Buffer.from(GetArrFromHex("026A04AB98D9E4774AD806E302DDDEB63BEA16B5CB5F223EE77478E861BB583EB3")); + var Sign = Buffer.from(GetArrFromHex("5D5382C65E4C1E8D412D5F30F87B8F72F371E9E4FC170761BCE583A961CF44966F92B38D402BC1CBCB7567335051A321B93F4E32112129AED4AB602E093A1187")); + var startTime = process.hrtime(); + var deltaTime = 1; + for(var Num = 0; Num < 1000; Num++) + { + var Result = secp256k1.verify(hash, Sign, PubKey); + if(!Result) + { + ToError("Error test sign"); + process.exit(0); + } + var Time = process.hrtime(startTime); + deltaTime = Time[0] * 1000 + Time[1] / 1e6; + if(deltaTime > MaxTime) + { + ToLog("*************** WARNING: VERY SLOW LIBRARY: secp256k1 ***************"); + ToLog("You can only process: " + Num + " transactions"); + ToLog("Install all dependent packages, see detail: https://www.npmjs.com/package/secp256k1"); + global.SpeedSignLib = Num; + return 0; + } + } + global.SpeedSignLib = Math.floor(Num * MaxTime / deltaTime); + ToLog("TestSignLib: " + global.SpeedSignLib + " per sec"); + return 1; +}; diff --git a/Source/process/pow-process.js b/Source/process/pow-process.js new file mode 100644 index 0000000..fd4c2f7 --- /dev/null +++ b/Source/process/pow-process.js @@ -0,0 +1,97 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PROCESS_NAME = "POW", global.POWPROCESS = 1, require("../core/library"), require("../core/crypto-library"), require("../core/terahashmining"); +var PROCESS = process; +process.send && !global.DEBUGPROCESS ? process.send({cmd:"online", message:"OK"}) : PROCESS = global.DEBUGPROCESS; +var LastAlive = Date.now(); +setInterval(CheckAlive, 1e3); +var idInterval = void 0, Block = {}; + +function CheckAlive() +{ + if(!global.NOALIVE) + { + var e = Date.now() - LastAlive; + Math.abs(e) > CHECK_STOP_CHILD_PROCESS && PROCESS.exit(0); + } +}; + +function CalcPOWHash() +{ + if(Block.SeqHash) + { + if(new Date - Block.Time > Block.Period) + return clearInterval(idInterval), void (idInterval = void 0); + try + { + CreatePOWVersionX(Block) && process.send({cmd:"POW", BlockNum:Block.BlockNum, SeqHash:Block.SeqHash, Hash:Block.Hash, PowHash:Block.PowHash, + AddrHash:Block.AddrHash, Num:Block.Num}); + } + catch(e) + { + ToError(e); + } + } +}; +PROCESS.on("message", function (e) +{ + if(LastAlive = Date.now(), "FastCalcBlock" === e.cmd) + { + var o = e; + StartHashPump(o), o.RunCount = 0; + try + { + CreatePOWVersionX(o) && process.send({cmd:"POW", BlockNum:o.BlockNum, SeqHash:o.SeqHash, Hash:o.Hash, PowHash:o.PowHash, AddrHash:o.AddrHash, + Num:o.Num}); + } + catch(e) + { + ToError(e); + } + } + else + if("SetBlock" === e.cmd) + { + var a = 1e6 * (1 + e.Num); + Block.HashCount && process.send({cmd:"HASHRATE", CountNonce:Block.HashCount, Hash:Block.Hash}), Block.HashCount = 0, (Block = e).Time = Date.now(), + Block.LastNonce = a, Block.Period = CONSENSUS_PERIOD_TIME * Block.Percent / 100, 0 < Block.Period && 0 < Block.RunPeriod && (CalcPOWHash(), + void 0 !== idInterval && clearInterval(idInterval), idInterval = setInterval(CalcPOWHash, Block.RunPeriod)); + } + else + "Alive" === e.cmd || "Exit" === e.cmd && PROCESS.exit(0); +}); +var idIntervalPump = global.BlockPump = void 0; + +function StartHashPump(e) +{ + (!BlockPump || BlockPump.BlockNum < e.BlockNum || BlockPump.MinerID !== e.MinerID || BlockPump.Percent !== e.Percent) && (global.BlockPump = {BlockNum:e.BlockNum, + RunCount:e.RunCount, MinerID:e.MinerID, Percent:e.Percent, LastNonce:0}), idIntervalPump = idIntervalPump || setInterval(PumpHash, + global.POWRunPeriod); +}; +var StartTime = 1, EndTime = 0; + +function PumpHash() +{ + if(BlockPump) + { + var e = Date.now(); + if(EndTime < StartTime) + { + if(100 * (e - StartTime) / CONSENSUS_PERIOD_TIME >= BlockPump.Percent) + return void (EndTime = e); + CreatePOWVersionX(BlockPump, 1); + } + else + { + 100 * (e - EndTime) / CONSENSUS_PERIOD_TIME > 100 - BlockPump.Percent && (StartTime = e); + } + } +}; diff --git a/Source/process/static-process.js b/Source/process/static-process.js new file mode 100644 index 0000000..f8bdf84 --- /dev/null +++ b/Source/process/static-process.js @@ -0,0 +1,425 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PROCESS_NAME = "STATIC"; +const crypto = require('crypto'); +const fs = require('fs'); +require("../core/constant"); +require('../core/block-loader-const'); +require('../core/rest_tables.js'); +require('../system/accounts.js'); +require('../system/smart.js'); +global.DATA_PATH = GetNormalPathString(global.DATA_PATH); +global.CODE_PATH = GetNormalPathString(global.CODE_PATH); +require("../core/library"); +global.READ_ONLY_DB = 1; +var LastAlive = Date.now(); +setTimeout(function () +{ + setInterval(CheckAlive, 1000); +}, 20000); +setInterval(function () +{ + process.send({cmd:"Alive"}); +}, 1000); +process.send({cmd:"online", message:"OK"}); +process.on('message', function (msg) +{ + LastAlive = Date.now(); + switch(msg.cmd) + { + case "ALive": + break; + case "Exit": + process.exit(0); + break; + case "call": + var Err = 0; + var Ret; + try + { + Ret = global[msg.Name](msg.Params); + } + catch(e) + { + Err = 1; + Ret = "" + e; + } + if(msg.id) + process.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret}); + break; + case "GETBLOCKHEADER": + GETBLOCKHEADER(msg); + break; + case "GETBLOCKHEADER100": + GETBLOCKHEADER100(msg); + break; + case "GETBLOCK": + GETBLOCK(msg); + break; + case "GETCODE": + GETCODE(msg); + break; + case "GETREST": + GETREST(msg); + break; + case "GETSMART": + GETSMART(msg); + break; + } +}); + +function CheckAlive() +{ + if(global.NOALIVE) + return ; + var Delta = Date.now() - LastAlive; + if(Delta > CHECK_STOP_CHILD_PROCESS) + { + ToLog("STATIC-DB: ALIVE TIMEOUT Stop and exit: " + Delta + "/" + global.CHECK_STOP_CHILD_PROCESS); + process.exit(0); + return ; + } +}; +process.on('uncaughtException', function (err) +{ + ToError(err.stack); + ToLog(err.stack); + TO_ERROR_LOG("STATIC-DB", 777, err); + ToLog("-----------------STATIC-DB EXIT------------------"); + process.exit(); +}); +process.on('error', function (err) +{ + ToError("STATIC-DB:\n" + err.stack); + ToLog(err.stack); +}); +var CServerDB = require("../core/db/block-db"); +var KeyPair = crypto.createECDH('secp256k1'); +KeyPair.setPrivateKey(Buffer.from([77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, +77, 77, 77, 77, 77, 77, 77, 77, 77, 77])); +global.SERVER = new CServerDB(KeyPair, undefined, undefined, false, true); +global.HTTP_PORT_NUMBER = 0; +setInterval(function () +{ + if(SERVER) + SERVER.Close(); + DApps.Accounts.Close(); + DApps.Smart.DBSmart.Close(); +}, 1000); + +function GETBLOCKHEADER100(msg) +{ + return ; + var Data = msg.Data; + var BlockNum = Data.BlockNum; + if(BlockNum % 100 !== 0) + return ; + var EndNum100 = BlockNum / 100; + var LoadHash100 = Data.Hash; + var Hash100; + var Count = Data.Count; + if(!Count || Count < 0 || !EndNum100) + return ; + if(Count > COUNT_BLOCKS_FOR_LOAD) + Count = COUNT_BLOCKS_FOR_LOAD; + var Arr = []; + var Data100 = SERVER.DBHeader100.Read(EndNum100); + if(Data100 && CompareArr(Data100.Hash100, LoadHash100) === 0) + { + var StartNum = EndNum100 - Count + 1; + if(StartNum < 0) + StartNum = 0; + for(var Num = StartNum; Num <= EndNum100; Num++) + { + Data100 = SERVER.DBHeader100.Read(Num); + if(Num === StartNum) + Arr.push(Data100.Hash100); + Arr.push(Data100.Hash); + } + } + var BufWrite = BufLib.GetBufferFromObject(Arr, "[hash]", MAX_PACKET_LENGTH, {}); + ToLog("GETBLOCKHEADER100 Send Arr=" + Arr.length + " - " + BlockNum); + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETBLOCKHEADER100", Context:msg.Context, Data:BufWrite}); +}; + +function GETBLOCKHEADER(msg) +{ + var Data = msg.Data; + var StartNum = undefined; + var BlockNum; + var LoadHash = Data.Hash; + var Foward = Data.Foward; + if(Foward) + { + var BlockDB = SERVER.ReadBlockHeaderDB(Data.BlockNum); + if(BlockDB && BlockDB.SumHash && (CompareArr(BlockDB.SumHash, LoadHash) === 0 || IsZeroArr(LoadHash))) + { + StartNum = Data.BlockNum - BLOCK_PROCESSING_LENGTH2; + if(StartNum < 0) + StartNum = 0; + BlockNum = StartNum + COUNT_BLOCKS_FOR_LOAD + BLOCK_PROCESSING_LENGTH2; + if(BlockNum > SERVER.GetMaxNumBlockDB()) + BlockNum = SERVER.GetMaxNumBlockDB(); + } + } + else + { + BlockNum = Data.BlockNum; + var IsSum = Data.IsSum; + var Count = Data.Count; + if(!Count || Count < 0 || BlockNum < 0) + return ; + if(Count > COUNT_BLOCKS_FOR_LOAD) + Count = COUNT_BLOCKS_FOR_LOAD; + Count += BLOCK_PROCESSING_LENGTH2; + var BlockDB = SERVER.ReadBlockHeaderDB(BlockNum); + if(BlockDB && (BlockDB.Prepared && (!IsSum) && BlockDB.Hash && CompareArr(BlockDB.Hash, LoadHash) === 0 || BlockDB.bSave && IsSum && BlockDB.SumHash && CompareArr(BlockDB.SumHash, + LoadHash) === 0)) + { + StartNum = BlockNum - Count + 1; + if(StartNum < 0) + StartNum = 0; + } + } + var BufWrite = SERVER.BlockChainToBuf(StartNum, StartNum, BlockNum); + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETBLOCKHEADER", Context:msg.Context, Data:BufWrite}); +}; + +function GETBLOCK(msg) +{ + var Data = msg.Data; + var BlockNum = Data.BlockNum; + var TreeHash = Data.TreeHash; + if(msg.Context.SendCount) + { + return ; + } + var BufWrite; + var BlockDB = SERVER.ReadBlockDB(BlockNum); + var StrSend; + if(BlockDB && (CompareArr(BlockDB.TreeHash, TreeHash) === 0 || IsZeroArr(TreeHash))) + { + var BufWrite = BufLib.GetBufferFromObject(BlockDB, FORMAT_BLOCK_TRANSFER, MAX_PACKET_LENGTH, WRK_BLOCK_TRANSFER); + StrSend = "OK"; + } + if(StrSend === "OK") + { + ADD_TO_STAT("BLOCK_SEND"); + } + else + { + BufWrite = BufLib.GetNewBuffer(100); + StrSend = "NO"; + } + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETGETBLOCK", Context:msg.Context, Data:BufWrite}); +}; + +function GETCODE(msg) +{ + var VersionNum = msg.Data; + var fname = GetDataPath("Update/wallet-" + VersionNum + ".zip"); + if(fs.existsSync(fname)) + { + var data = fs.readFileSync(fname); + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETCODE", Context:msg.Context, Data:data}); + } +}; + +function GETREST(msg) +{ + var Data = msg.Data; + if(!Data.BlockNum) + return ; + if(IsZeroArr(Data.AccHash)) + { + return ; + } + var BlockNumRest = Data.BlockNum; + var RestIndexArr = GetCurrentRestArr(); + var nResult = 0; + for(var i = 0; i < RestIndexArr.length; i++) + { + if(RestIndexArr[i] === BlockNumRest) + { + nResult = 1; + break; + } + } + var BufLength = 1000; + var ProofHash = []; + var ProofArrL = []; + var ProofArrR = []; + var ArrRest = []; + if(nResult) + { + var WorkStruct = {}; + var WorkFormat = DApps.Accounts.FORMAT_ACCOUNT_ROW; + var WorkFormatLength = DApps.Accounts.SIZE_ACCOUNT_ROW; + var Max = DApps.Accounts.DBState.GetMaxNum(); + var LengthAccount = Data.Count; + if(LengthAccount > MAX_ACCOUNTS_TRANSFER) + LengthAccount = MAX_ACCOUNTS_TRANSFER; + var StartAccount = Data.AccNum; + var EndAccount = StartAccount + LengthAccount - 1; + if(EndAccount > Max) + EndAccount = Max; + var Tree = GetRestMerkleTree(BlockNumRest, RestIndexArr); + if(CompareArr(Data.AccHash, Tree.Root) !== 0) + { + ToLog("Get bad rest acc hash: " + BlockNumRest + " = " + GetHexFromArr(Data.AccHash) + "/" + GetHexFromArr(Tree.Root), 2); + ArrRest = []; + nResult = 0; + } + else + { + ArrRest = GetArrRest(BlockNumRest, StartAccount, EndAccount); + ProofHash = Tree.Root; + var RetProof = GetMerkleProof(Tree.LevelsHash, StartAccount, EndAccount); + ProofArrL = RetProof.ArrL; + ProofArrR = RetProof.ArrR; + BufLength = 1000 + ArrRest.length * WorkFormatLength; + BufLength += ProofArrL.length * 32 + ProofArrR.length * 32 + 32; + } + } + var Data2 = {Result:nResult, Arr:ArrRest, Version:1, ProofHash:ProofHash, ProofArrL:ProofArrL, ProofArrR:ProofArrR}; + var BufWrite = BufLib.GetBufferFromObject(Data2, FORMAT_REST_TRANSFER, BufLength, {}); + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETREST", Context:msg.Context, Data:BufWrite}); +}; + +function GETSMART(msg) +{ + var Data = msg.Data; + if(!Data.Count) + return ; + var BufLength = 1000; + var SizeForSend = 200 * 1024; + var Arr = []; + for(var Num = Data.SmartNum; Num < Data.SmartNum + Data.Count; Num++) + { + var BufSmart = DApps.Smart.DBSmart.Read(Num, 1); + if(!BufSmart) + break; + SizeForSend = SizeForSend - BufSmart.length; + if(SizeForSend < 0) + break; + BufLength += BufSmart.length; + Arr.push(BufSmart); + } + var Data2 = {Result:Arr.length ? 1 : 0, Arr:Arr}; + var BufWrite = BufLib.GetBufferFromObject(Data2, FORMAT_SMART_TRANSFER, BufLength, {}); + process.send({cmd:"Send", addrStr:msg.addrStr, Method:"RETSMART", Context:msg.Context, Data:BufWrite}); +}; +var glMapForHash = {}; + +function GetArrRest(BlockNumRest,StartAccount,EndAccount,bHashOnly) +{ + var ArrRest = []; + var WorkStruct = {}; + var WorkFormat = DApps.Accounts.FORMAT_ACCOUNT_ROW; + var WorkFormatLength = DApps.Accounts.SIZE_ACCOUNT_ROW; + for(var Num = StartAccount; Num <= EndAccount; Num++) + { + var FindItem = undefined; + var RestData = DApps.Accounts.ReadRest(Num); + var CountZero = 0; + for(var i = RestData.Arr.length - 1; i >= 0; i--) + { + var Item = RestData.Arr[i]; + if(!Item.BlockNum) + { + CountZero++; + continue; + } + if(Item.BlockNum <= BlockNumRest) + { + if(!FindItem || Item.BlockNum > FindItem.BlockNum) + { + FindItem = Item; + } + } + } + var BlocNumMap = 0; + var StateDataValue = undefined; + if(FindItem) + { + StateDataValue = FindItem.Value; + BlocNumMap = Item.BlockNum; + } + else + { + if(CountZero !== RestData.Arr.length) + continue; + } + var StateData = DApps.Accounts.DBState.Read(Num); + if(!StateData) + break; + if(StateDataValue) + StateData.Value = StateDataValue; + if(bHashOnly) + { + var key = "" + Num + "-" + BlocNumMap; + var Hash = glMapForHash[key]; + if(!Hash) + { + var Buf = BufLib.GetBufferFromObject(StateData, WorkFormat, WorkFormatLength, WorkStruct); + Hash = shaarr(Buf); + glMapForHash[key] = Hash; + } + ArrRest.push(Hash); + } + else + { + var Buf = BufLib.GetBufferFromObject(StateData, WorkFormat, WorkFormatLength, WorkStruct); + ArrRest.push(Buf); + } + } + return ArrRest; +}; +var glMapRest = {}; + +function GetRestMerkleTree(BlockNumRest,RestIndexArr) +{ + var MerkleTree = glMapRest[BlockNumRest]; + if(!MerkleTree) + { + ToLog("Create new glMapRest key: " + BlockNumRest, 2); + var startTime = process.hrtime(); + var EndAccount = DApps.Accounts.DBState.GetMaxNum(); + var ArrHash = GetArrRest(BlockNumRest, 0, EndAccount, 1); + var Time1 = process.hrtime(startTime); + var MerkleCalc = {}; + MerkleTree = {LevelsHash:[ArrHash], RecalcCount:0}; + for(var Num = 0; Num < ArrHash.length; Num++) + { + MerkleCalc[Num] = 1; + } + UpdateMerklTree(MerkleTree, MerkleCalc, 0); + glMapRest[BlockNumRest] = MerkleTree; + var Time2 = process.hrtime(startTime); + var deltaTime1 = (Time1[0] * 1000 + Time1[1] / 1e6) / 1000; + var deltaTime2 = (Time2[0] * 1000 + Time2[1] / 1e6) / 1000; + ToLog("Create delta time: " + deltaTime1 + "/" + deltaTime2 + " s Tree.Root=" + GetHexFromArr(MerkleTree.Root), 2); + var MapIndex = {}; + for(var i = 0; i < RestIndexArr.length; i++) + { + MapIndex[RestIndexArr[i]] = 1; + } + for(var key in glMapRest) + { + if(!MapIndex[key]) + { + ToLog("Delete old glMapRest key: " + key, 2); + delete glMapRest[key]; + } + } + } + return MerkleTree; +}; diff --git a/Source/process/tx-process.js b/Source/process/tx-process.js new file mode 100644 index 0000000..73638ce --- /dev/null +++ b/Source/process/tx-process.js @@ -0,0 +1,411 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PROCESS_NAME = "TX"; +const crypto = require('crypto'); +const fs = require('fs'); +require("../core/constant"); +global.DATA_PATH = GetNormalPathString(global.DATA_PATH); +global.CODE_PATH = GetNormalPathString(global.CODE_PATH); +require("../core/library"); +global.READ_ONLY_DB = 0; +var LastAlive = Date.now(); +setTimeout(function () +{ + setInterval(CheckAlive, 1000); +}, 20000); +setInterval(PrepareStatEverySecond, 1000); +if(process.send) +{ + setInterval(function () + { + process.send({cmd:"Alive"}); + }, 1000); + process.send({cmd:"online", message:"OK"}); + global.ToLogClient = function (Str,StrKey,bFinal) + { + process.send({cmd:"ToLogClient", Str:"" + Str, StrKey:StrKey, bFinal:bFinal}); + }; +} +process.on('message', function (msg) +{ + LastAlive = Date.now(); + switch(msg.cmd) + { + case "ALive": + break; + case "Exit": + process.exit(0); + break; + case "call": + var Err = 0; + var Ret; + try + { + Ret = global[msg.Name](msg.Params); + } + catch(e) + { + Err = 1; + Ret = "" + e; + } + if(msg.id) + process.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret}); + break; + case "FindTX": + global.TreeFindTX.SaveValue(msg.TX, msg); + break; + case "SetSmartEvent": + global.TreeFindTX.SaveValue("Smart:" + msg.Smart, 1); + break; + case "Eval": + EvalCode(msg.Code); + break; + default: + break; + } +}); +global.SetStatMode = function (Val) +{ + global.STAT_MODE = Val; + return global.STAT_MODE; +}; + +function CheckAlive() +{ + if(global.NOALIVE) + return ; + var Delta = Date.now() - LastAlive; + if(Delta > 100 * 1000) + { + ToLog("TX-PROCESS: ALIVE TIMEOUT Stop and exit: " + Delta); + process.exit(0); + return ; + } +}; +process.on('uncaughtException', function (err) +{ + ToError(err.stack); + ToLog(err.stack); + TO_ERROR_LOG("TX-PROCESS", 777, err); + ToLog("-----------------TX-PROCESS EXIT------------------"); + process.exit(); +}); +process.on('error', function (err) +{ + ToError("TX-PROCESS:\n" + err.stack); + ToLog(err.stack); +}); +global.HTTP_PORT_NUMBER = 0; +var CServerDB = require("../core/transaction-validator"); +var KeyPair = crypto.createECDH('secp256k1'); +KeyPair.setPrivateKey(Buffer.from([77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, +77, 77, 77, 77, 77, 77, 77, 77, 77, 77])); +global.SERVER = new CServerDB(KeyPair, undefined, undefined, false, true); +global.TreeFindTX = new STreeBuffer(30 * 1000, CompareItemHashSimple, "string"); +setInterval(function () +{ + if(SERVER) + { + SERVER.Close(); + } + DoTXProcess(); +}, 10); +var BlockTree = new STreeBuffer(30 * 1000, CompareItemHashSimple, "number"); +global.bShowDetail = 0; +var StopTxProcess = 0; +var MinimalValidBlock = 0; +var LastBlockNum = undefined; + +function DoTXProcess() +{ + if(StopTxProcess) + return ; + if(LastBlockNum === undefined) + InitTXProcess(); + var BlockMin = FindMinimal(); + if(!BlockMin) + { + if(bShowDetail) + ToLog("!BlockMin"); + return ; + } + var StartTime = Date.now(); + if(bShowDetail) + ToLog("BlockMin: " + BlockMin.BlockNum + " LastBlockNum=" + LastBlockNum); + var CountTX = 0; + for(var Num = BlockMin.BlockNum; Num < BlockMin.BlockNum + 200; Num++) + { + var EndTime = Date.now(); + var Delta = EndTime - StartTime; + if(Delta >= 1000) + break; + var Block = SERVER.ReadBlockDB(Num); + if(!Block) + { + if(bShowDetail) + ToLog("!Block"); + break; + } + if(!IsValidSumHash(Block)) + { + break; + } + var Item = BlockTree.LoadValue(Block.BlockNum, 1); + if(Item && CompareArr(Item.SumHash, Block.SumHash) === 0) + { + if(bShowDetail) + ToLog("WAS CALC: " + Num + " SumHash: " + GetHexFromArr(Block.SumHash).substr(0, 12)); + continue; + } + if(Num > 0) + { + var Block0 = SERVER.ReadBlockDB(Num - 1); + if(Block0) + { + var Item0 = BlockTree.LoadValue(Block0.BlockNum, 1); + if(Item0 && CompareArr(Item0.SumHash, Block0.SumHash) !== 0) + { + break; + } + } + } + SERVER.BlockProcessTX(Block); + if(Num % 100000 === 0) + ToLog("CALC: " + Num); + CountTX++; + if(bShowDetail) + ToLog(" CALC: " + Num + " SumHash: " + GetHexFromArr(Block.SumHash).substr(0, 12)); + BlockTree.SaveValue(Block.BlockNum, {BlockNum:Block.BlockNum, SumHash:Block.SumHash}); + LastBlockNum = Block.BlockNum; + } +}; + +function FindMinimal() +{ + var MaxNumBlockDB = SERVER.GetMaxNumBlockDB(); + if(MaxNumBlockDB && MaxNumBlockDB < LastBlockNum) + { + if(bShowDetail) + ToLog("MaxNumBlockDB 0) + { + RewriteAllTransactions(); + } + Block = SERVER.ReadBlockHeaderDB(MinimalValidBlock); + return Block; +}; + +function IsValidSumHash(Block) +{ + if(Block.BlockNum <= MinimalValidBlock + BLOCK_PROCESSING_LENGTH2) + return 1; + if(Block.BlockNum < 16) + return 1; + if(IsZeroArr(Block.SumHash)) + return 0; + var PrevBlock = SERVER.ReadBlockHeaderDB(Block.BlockNum - 1); + if(!PrevBlock) + return 0; + var SumHash2 = shaarr2(PrevBlock.SumHash, Block.Hash); + if(CompareArr(SumHash2, Block.SumHash) === 0) + return 1; + return 0; +}; + +function InitTXProcess() +{ + var StateTX = DApps.Accounts.DBStateTX.Read(0); + if(!StateTX) + { + LastBlockNum = 0; + var MaxNum = DApps.Accounts.DBAccountsHash.GetMaxNum(); + if(MaxNum > 0) + { + var Item = DApps.Accounts.DBAccountsHash.Read(MaxNum); + if(Item) + { + LastBlockNum = Item.BlockNum; + } + } + ToLog("DETECT NEW VER on BlockNum=" + LastBlockNum, 2); + DApps.Accounts.DBStateTX.Write({Num:0, BlockNum:LastBlockNum, BlockNumMin:MinimalValidBlock}); + } + StateTX = DApps.Accounts.DBStateTX.Read(0); + LastBlockNum = StateTX.BlockNum; + MinimalValidBlock = StateTX.BlockNumMin; + LastBlockNum = PERIOD_ACCOUNT_HASH * Math.trunc(LastBlockNum / PERIOD_ACCOUNT_HASH); + if(LastBlockNum > 100) + { + LastBlockNum = 1 + LastBlockNum - 100; + } + ToLog("Start CalcMerkleTree", 2); + DApps.Accounts.CalcMerkleTree(1); + ToLog("Finsih CalcMerkleTree", 2); + if(LastBlockNum <= 0) + RewriteAllTransactions(); + else + ToLog("Start NUM = " + LastBlockNum, 2); +}; +global.ClearDataBase = ClearDataBase; + +function ClearDataBase() +{ + MinimalValidBlock = 0; + for(var key in DApps) + { + DApps[key].ClearDataBase(); + } + LastBlockNum = 0; + BlockTree.Clear(); +}; +global.RewriteAllTransactions = RewriteAllTransactions; + +function RewriteAllTransactions() +{ + if(MinimalValidBlock > 0) + { + ToLog("*************Cant run RewriteAllTransactions, MinimalValidBlock:" + MinimalValidBlock, 2); + return ; + } + ToLog("*************RewriteAllTransactions"); + for(var key in DApps) + { + DApps[key].ClearDataBase(); + } + LastBlockNum = 0; + BlockTree.Clear(); + ToLog("Start num = " + LastBlockNum, 2); +}; +global.ReWriteDAppTransactions = ReWriteDAppTransactions; + +function ReWriteDAppTransactions(Params) +{ + var StartNum = Params.StartNum; + var EndNum = Params.EndNum; + ToLog("ReWriteDAppTransactions: " + StartNum + " - " + EndNum); + BlockTree.Clear(); + if(StartNum < LastBlockNum) + LastBlockNum = StartNum; + ToLog("Start num = " + LastBlockNum, 2); +}; + +function TXPrepareLoadRest(BlockNum) +{ + StopTxProcess = 1; + MinimalValidBlock = BlockNum; + ToLog("*************TXPrepareLoadRest:" + BlockNum, 2); + for(var key in DApps) + { + DApps[key].ClearDataBase(); + } + LastBlockNum = BlockNum; + BlockTree.Clear(); + DApps.Accounts.DBStateTX.Write({Num:0, BlockNum:LastBlockNum, BlockNumMin:LastBlockNum}); +}; +global.TXPrepareLoadRest = TXPrepareLoadRest; + +function TXWriteAccArr(Params) +{ + var WorkStruct = {}; + var WorkFormat = DApps.Accounts.FORMAT_ACCOUNT_ROW; + ToLog("Write accounts: " + Params.StartNum + "-" + Params.Arr.length, 2); + for(var i = 0; i < Params.Arr.length; i++) + { + var Data = BufLib.GetObjectFromBuffer(Params.Arr[i], WorkFormat, WorkStruct); + Data.Num = Params.StartNum + i; + DApps.Accounts._DBStateWrite(Data, MinimalValidBlock); + } +}; +global.TXWriteAccArr = TXWriteAccArr; + +function TXWriteSmartArr(Params) +{ + var WorkStruct = {}; + var WorkFormat = DApps.Smart.FORMAT_ROW; + ToLog("Write smarts: " + Params.StartNum + "-" + Params.Arr.length, 2); + for(var i = 0; i < Params.Arr.length; i++) + { + var Data = BufLib.GetObjectFromBuffer(Params.Arr[i], WorkFormat, WorkStruct); + Data.Num = Params.StartNum + i; + DApps.Smart.DBSmart.Write(Data); + } +}; +global.TXWriteSmartArr = TXWriteSmartArr; + +function TXWriteAccHash() +{ + StopTxProcess = 0; + ToLog("Start TXWriteAccHash: " + MinimalValidBlock, 2); + for(var num = 0; true; num++) + { + var Item = DApps.Smart.DBSmart.Read(num); + if(!Item) + break; + var Body = BufLib.GetBufferFromObject(Item, DApps.Smart.FORMAT_ROW, 20000, {}); + DApps.Smart.DBSmartWrite(Item); + } + DApps.Accounts.CalcMerkleTree(1); + var Block = {BlockNum:MinimalValidBlock, SumHash:[]}; + var MaxAccount = DApps.Accounts.GetMaxAccount(); + var DataHash = DApps.Accounts.CalcHash(Block, MaxAccount); + return DataHash; +}; +global.TXWriteAccHash = TXWriteAccHash; +global.EvalCode = function (Code) +{ + var Result; + try + { + var ret = eval(Code); + Result = JSON.stringify(ret, "", 4); + } + catch(e) + { + Result = "" + e; + } + return Result; +}; diff --git a/Source/process/web-process.js b/Source/process/web-process.js new file mode 100644 index 0000000..753f41d --- /dev/null +++ b/Source/process/web-process.js @@ -0,0 +1,984 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +global.PROCESS_NAME = "WEB"; +const crypto = require('crypto'); +const http = require('http'), net = require('net'), url = require('url'), fs = require('fs'), querystring = require('querystring'); +global.MAX_STAT_PERIOD = 600; +require("../core/constant"); +global.MAX_STAT_PERIOD = 600; +global.DATA_PATH = GetNormalPathString(global.DATA_PATH); +global.CODE_PATH = GetNormalPathString(global.CODE_PATH); +require("../core/library"); +require("../core/geo"); +global.READ_ONLY_DB = 1; +global.MAX_STAT_PERIOD = 600; +var HostNodeList = []; +var AllNodeList = []; +var NodeBlockChain = []; +var LastAlive = Date.now(); +setTimeout(function () +{ + setInterval(CheckAlive, 1000); +}, 20000); +setInterval(function () +{ + process.send({cmd:"Alive"}); +}, 1000); +process.send({cmd:"online", message:"OK"}); +process.on('message', function (msg) +{ + LastAlive = Date.now(); + switch(msg.cmd) + { + case "ALive": + break; + case "Exit": + Exit(); + break; + case "call": + var Err = 0; + var Ret; + try + { + Ret = global[msg.Name](msg.Params); + } + catch(e) + { + Err = 1; + Ret = "" + e; + } + if(msg.id) + process.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret}); + break; + case "retcall": + var F = GlobalRunMap[msg.id]; + if(F) + { + delete GlobalRunMap[msg.id]; + F(msg.Err, msg.Params); + } + break; + case "Stat": + ADD_TO_STAT(msg.Name, msg.Value); + break; + case "NodeList": + HostNodeList = msg.Value; + AllNodeList = msg.ValueAll; + break; + case "NodeBlockChain": + NodeBlockChain = msg.Value; + break; + case "DappEvent": + { + AddDappEventToGlobalMap(msg.Data); + break; + } + case "ToLogClient": + { + ToLogClient0(msg.Str, msg.StrKey, msg.bFinal); + break; + } + case "RetFindTX": + { + if(msg.WebID) + { + var F = global.GlobalRunMap[msg.WebID]; + if(F) + { + delete global.GlobalRunMap[msg.WebID]; + F(msg.Result, msg.ResultStr); + break; + } + } + ArrLogClient.push({text:msg.ResultStr, key:msg.TX, final:msg.bFinal, }); + break; + } + case "Eval": + EvalCode(msg.Code); + break; + } +}); +var RedirectServer; +var HostingServer; + +function Exit() +{ + ToLogWeb("=Exit1="); + if(RedirectServer) + RedirectServer.close(); + if(HostingServer) + HostingServer.close(); + ToLogWeb("=Exit2="); + process.exit(0); +}; + +function CheckAlive() +{ + if(global.NOALIVE) + return ; + var Delta = Date.now() - LastAlive; + if(Delta > CHECK_STOP_CHILD_PROCESS) + { + Exit(); + return ; + } +}; +process.on('uncaughtException', function (err) +{ + ToError(err.stack); + ToLog(err.stack); + TO_ERROR_LOG("HOSTING", 777, err); + ToLog("-----------------HOSTING EXIT------------------", 0); + process.exit(); +}); +process.on('error', function (err) +{ + ToError("HOSTING:\n" + err.stack); + ToLog(err.stack); +}); +if(!global.HTTP_HOSTING_PORT) +{ + ToLogTrace("global.HTTP_HOSTING_PORT=" + global.HTTP_HOSTING_PORT); + process.exit(); +} +var CServerDB = require("../core/db/block-db"); +var KeyPair = crypto.createECDH('secp256k1'); +KeyPair.setPrivateKey(Buffer.from([77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, +77, 77, 77, 77, 77, 77, 77, 77, 77, 77])); +global.SERVER = new CServerDB(KeyPair, undefined, undefined, false, true); +global.HTTP_PORT_NUMBER = 0; +require("../core/html-server"); +require("../core/transaction-validator"); +global.STAT_MODE = 1; +setInterval(PrepareStatEverySecond, 1000); +var IndexName = "index.html"; +if(global.HTTPS_HOSTING_DOMAIN) +{ + var file_sert = GetDataPath("sertif.lst"); + CheckCreateDir(GetDataPath("tmp")); + var greenlock = require('greenlock').create({version:'draft-12', server:'https://acme-v02.api.letsencrypt.org/directory', configDir:GetDataPath('tmp'), + }); + var redir = require('redirect-https')(); + RedirectServer = require('http').createServer(greenlock.middleware(redir)); + RedirectServer.on('error', function (err) + { + ToError('RedirectServer: ' + err.code); + }); + RedirectServer.listen(80); + var GetNewSert = 1; + if(fs.existsSync(file_sert)) + { + var certs = LoadParams(file_sert, {}); + var Delta = certs.expiresAt - Date.now(); + if(Delta >= 10 * 24 * 3600 * 1000) + { + ToLog("USE EXIST SERT. ExpiresAt: " + new Date(certs.expiresAt)); + GetNewSert = 0; + var tlsOptions = {key:certs.privkey, cert:certs.cert + '\r\n' + certs.chain}; + HostingServer = require('https').createServer(tlsOptions, MainHTTPFunction); + RunListenServer(); + } + } + if(GetNewSert) + { + ToLog("Start get new SERT", 0); + var opts = {domains:[global.HTTPS_HOSTING_DOMAIN], email:'progr76@gmail.com', agreeTos:true, communityMember:true, }; + greenlock.register(opts).then(function (certs) + { + SaveParams(file_sert, certs); + var tlsOptions = {key:certs.privkey, cert:certs.cert + '\r\n' + certs.chain}; + HostingServer = require('https').createServer(tlsOptions, MainHTTPFunction); + RunListenServer(); + }, function (err) + { + ToError(err); + }); + } +} +else +{ + HostingServer = http.createServer(MainHTTPFunction); + RunListenServer(); +} + +function MainHTTPFunction(request,response) +{ + if(!request.headers) + return ; + if(!request.socket || !request.socket.remoteAddress) + return ; + SetSafeResponce(response); + var DataURL = url.parse(request.url); + var Params = querystring.parse(DataURL.query); + var Path = querystring.unescape(DataURL.pathname); + ToLogWeb("Get Path:" + Path); + ADD_TO_STAT("HTTP_ALL"); + if(global.STAT_MODE === 2) + response.DetailStatName = ":" + Path; + var Type = request.method; + if(Type === "POST") + { + let Response = response; + let postData = ""; + request.addListener("data", function (postDataChunk) + { + if(postData.length <= 12000 && postDataChunk.length <= 12000) + postData += postDataChunk; + else + { + var Str = "Error postDataChunk.length=" + postDataChunk.length; + ToLog(Str, 0); + Response.writeHead(405, {'Content-Type':'text/html'}); + Response.end(Str); + } + }); + request.addListener("end", function () + { + var Data; + if(postData && postData.length) + { + try + { + Data = JSON.parse(postData); + } + catch(e) + { + Response.writeHead(405, {'Content-Type':'text/html'}); + Response.end("Error data parsing"); + } + } + DoCommandNew(response, Type, Path, Data); + }); + } + else + { + DoCommandNew(response, Type, Path, Params); + } +}; +var bWasRun = 0; +var TimeToRerun = 3000; + +function RunListenServer() +{ + HostingServer.on('error', function (err) + { + if(err.code === 'EADDRINUSE') + { + TimeToRerun = Math.floor(TimeToRerun * 1.1); + if(TimeToRerun > 1000000 * 1000) + return ; + ToLogClient('Port ' + global.HTTP_HOSTING_PORT + ' in use, retrying...'); + if(HostingServer.Server) + HostingServer.Server.close(); + if(!bWasRun) + setTimeout(function () + { + RunListenServer(); + }, TimeToRerun); + return ; + } + ToError("H##6"); + ToError(err); + }); + ToLogClient("Prepare to run WEB-server on port: " + global.HTTP_HOSTING_PORT); + HostingServer.listen(global.HTTP_HOSTING_PORT, LISTEN_IP, function () + { + if(!bWasRun) + ToLogClient("Run WEB-server on " + LISTEN_IP + ":" + global.HTTP_HOSTING_PORT); + bWasRun = 1; + }); +}; +var SiteFolder = GetNormalPathString("./SITE"); +if(!fs.existsSync(SiteFolder)) +{ + IndexName = "web-wallet.html"; +} +var LangPathMap = {}; +LangPathMap["ru"] = 1; +LangPathMap["cn"] = 1; +LangPathMap["de"] = 1; +LangPathMap["blog"] = 1; +LangPathMap["docs"] = 1; +LangPathMap["game"] = 1; +var WalletFileMap = {}; +WalletFileMap["coinlib.js"] = 1; +WalletFileMap["client.js"] = 1; +WalletFileMap["diagram.js"] = 1; +WalletFileMap["sha3.js"] = 1; +WalletFileMap["terahashlib.js"] = 1; +WalletFileMap["wallet-web.js"] = 1; +WalletFileMap["wallet-lib.js"] = 1; +WalletFileMap["crypto-client.js"] = 1; +WalletFileMap["dapp-inner.js"] = 1; +WalletFileMap["marked.js"] = 1; +WalletFileMap["highlight.js"] = 1; +WalletFileMap["highlight-js.js"] = 1; +WalletFileMap["highlight-html.js"] = 1; +WalletFileMap["codes.css"] = 1; +WalletFileMap["sign-lib-min.js"] = 1; +WalletFileMap["buttons.css"] = 1; +WalletFileMap["style.css"] = 1; +WalletFileMap["wallet.css"] = 1; +WalletFileMap["history.html"] = 1; +WalletFileMap["blockviewer.html"] = 1; +WalletFileMap["web-wallet.html"] = 1; +WalletFileMap["address_book.png"] = 1; +WalletFileMap["mobile-wallet.html"] = 1; +WalletFileMap["mobile-wallet.js"] = 1; +WalletFileMap["mobile-wallet.css"] = 1; +WalletFileMap["reload.svg"] = 1; +WalletFileMap["T.svg"] = 1; +WalletFileMap["B.svg"] = 1; +WalletFileMap["blank.svg"] = 1; +WalletFileMap["info.svg"] = 1; +WalletFileMap["info.svg"] = 1; +WalletFileMap["check.svg"] = 1; +WalletFileMap["right-arrow.svg"] = 1; +WalletFileMap["down-arrow.svg"] = 1; +WalletFileMap["glass.svg"] = 1; +WalletFileMap["dapp-edit.html"] = 1; +WalletFileMap["TeraLogo.svg"] = 1; +WalletFileMap["mobile-wallet.html"] = "web-wallet.html"; +global.WebApi2 = {}; +global.HostingCaller = {}; + +function DoCommandNew(response,Type,Path,Params) +{ + if(Path.substring(0, 1) === "/") + Path = Path.substring(1); + var ArrPath = Path.split('/', 5); + var Caller = HostingCaller; + var Method = ArrPath[0]; + var APIv2 = 0; + if(ArrPath[0] === "api") + { + if(ArrPath[1] === "v2") + { + APIv2 = 1; + if(!global.USE_HARD_API_V2) + { + response.writeHead(200, {'Content-Type':'text/plain', 'Access-Control-Allow-Origin':"*"}); + response.end(JSON.stringify({result:0, text:"You must set const USE_HARD_API_V2:1"})); + return ; + } + Caller = WebApi2; + } + Method = ArrPath[2]; + } + ADD_TO_STAT("HTTP:" + Method); + if(global.STAT_MODE === 2) + response.DetailStatName = ":" + Method; + var F = Caller[Method]; + if(F) + { + response.writeHead(200, {'Content-Type':'text/plain', 'Access-Control-Allow-Origin':"*"}); + if(!global.USE_API_V1 && !APIv2) + { + response.end(JSON.stringify({result:0, text:"This node not use USE_API_V1"})); + return ; + } + var Ret; + try + { + Ret = F(Params, response, ArrPath); + } + catch(e) + { + Ret = {result:0, text:e.message, text2:e.stack}; + } + if(Ret === null) + return ; + try + { + var Str; + if(typeof Ret === "object") + Str = JSON.stringify(Ret); + else + Str = Ret; + response.end(Str); + } + catch(e) + { + ToLog("ERR PATH:" + Path); + ToLog(e); + response.end(); + } + return ; + } + Method = Method.toLowerCase(); + if(Method === "dapp" && ArrPath.length === 2) + Method = "DappTemplateFile"; + switch(Method) + { + case "index.html": + response.writeHead(301, {"Location":'/'}); + return response.end(); + case "file": + SendBlockFile(response, ArrPath[1], ArrPath[2]); + break; + case "DappTemplateFile": + DappTemplateFile(response, ArrPath[1]); + break; + case "smart": + DappSmartCodeFile(response, ArrPath[1]); + break; + case "client": + DappClientCodeFile(response, ArrPath[1]); + default: + { + var Name = ArrPath[ArrPath.length - 1]; + if(typeof Name !== "string") + Name = "ErrorPath"; + else + if(Name.indexOf("..") >= 0 || Name.indexOf("\\") >= 0 || Name.indexOf("/") >= 0) + Name = "ErrorFilePath"; + if(Name === "" || LangPathMap[Name]) + Name = IndexName; + if(Name.indexOf(".") < 0) + Name += ".html"; + var PrefixPath; + if(Method === "files") + { + PrefixPath = "../FILES"; + Name = ""; + for(var i = 1; i < ArrPath.length; i++) + if(ArrPath[i].indexOf("..") === - 1 && ArrPath[i].indexOf("\\") === - 1) + Name += "/" + ArrPath[i]; + Name = PrefixPath + Name; + SendWebFile(response, Name, Path); + return ; + } + else + if(LangPathMap[Method]) + { + PrefixPath = "./SITE/" + Method; + } + else + { + var Name2 = WalletFileMap[Name]; + if(!Name2) + PrefixPath = "./SITE"; + else + { + PrefixPath = "./HTML"; + if(typeof Name2 === "string") + Name = Name2; + } + } + var type = Path.substr(Path.length - 3, 3); + switch(type) + { + case ".js": + Name = PrefixPath + "/JS/" + Name; + break; + case "css": + Name = PrefixPath + "/CSS/" + Name; + break; + case "wav": + case "mp3": + Name = PrefixPath + "/SOUND/" + Name; + break; + case "svg": + case "png": + case "gif": + case "jpg": + case "ico": + Name = PrefixPath + "/PIC/" + Name; + break; + case "pdf": + case "zip": + case "exe": + case "msi": + Name = PrefixPath + "/FILES/" + Name; + break; + default: + Name = PrefixPath + "/" + Name; + break; + } + SendWebFile(response, Name, "", 1); + break; + } + } +}; +HostingCaller.GetCurrentInfo = function (Params) +{ + if(typeof Params === "object" && Params.BlockChain == 1) + { + if(!global.USE_API_WALLET) + return {result:0}; + } + var MaxNumBlockDB = SERVER.GetMaxNumBlockDB(); + var Ret = {result:1, VersionNum:global.UPDATE_CODE_VERSION_NUM, NETWORK:global.NETWORK, MaxNumBlockDB:MaxNumBlockDB, CurBlockNum:GetCurrentBlockNumByTime(), + MaxAccID:DApps.Accounts.GetMaxAccount(), MaxDappsID:DApps.Smart.GetMaxNum(), CurTime:Date.now(), DELTA_CURRENT_TIME:DELTA_CURRENT_TIME, + MIN_POWER_POW_TR:MIN_POWER_POW_TR, FIRST_TIME_BLOCK:FIRST_TIME_BLOCK, CONSENSUS_PERIOD_TIME:CONSENSUS_PERIOD_TIME, NEW_SIGN_TIME:NEW_SIGN_TIME, + PRICE_DAO:PRICE_DAO(MaxNumBlockDB), }; + if(typeof Params === "object" && Params.Diagram == 1) + { + var arrNames = ["MAX:ALL_NODES", "MAX:HASH_RATE_B"]; + Ret.arr = GET_STATDIAGRAMS(arrNames); + } + if(typeof Params === "object" && Params.BlockChain == 1) + { + Ret.BlockChain = NodeBlockChain; + } + if(typeof Params === "object" && Params.ArrLog == 1) + { + var ArrLog = []; + for(var i = 0; i < ArrLogClient.length; i++) + { + var Item = ArrLogClient[i]; + if(!Item.final) + continue; + ArrLog.push(Item); + } + Ret.ArrLog = ArrLog; + } + return Ret; +}; +var MaxCountViewRows = global.HTTP_MAX_COUNT_ROWS; +HostingCaller.GetAccountList = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Accounts.GetRowsAccounts(ParseNum(Params.StartNum), ParseNum(Params.CountNum)); + return {result:1, arr:arr}; +}; +HostingCaller.GetAccount = function (id) +{ + id = ParseNum(id); + var arr = DApps.Accounts.GetRowsAccounts(id, 1); + return {Item:arr[0], result:1}; +}; +HostingCaller.GetBlockList = function (Params,response) +{ + if(typeof Params !== "object") + return {result:0}; + Params.StartNum = ParseNum(Params.StartNum); + Params.CountNum = ParseNum(Params.CountNum); + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + return HTTPCaller.GetBlockList(Params, response); +}; +HostingCaller.GetTransactionList = function (Params,response) +{ + Params.Param3 = Params.BlockNum; + return HostingCaller.GetTransactionAll(Params, response); +}; +HostingCaller.GetTransactionAll = function (Params,response) +{ + if(typeof Params !== "object") + return {result:0}; + Params.Param3 = ParseNum(Params.Param3); + Params.StartNum = ParseNum(Params.StartNum); + Params.CountNum = ParseNum(Params.CountNum); + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + return HTTPCaller.GetTransactionAll(Params, response); +}; +HostingCaller.GetDappList = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Smart.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum), undefined, Params.Filter, 1); + return {result:1, arr:arr}; +}; +HostingCaller.GetNodeList = function (Params) +{ + var arr = []; + var List; + if(typeof Params === "object" && Params.All) + List = AllNodeList; + else + List = HostNodeList; + var MaxNodes = 20; + var len = List.length; + var UseRandom = 0; + if(len > MaxNodes) + { + UseRandom = 1; + len = MaxNodes; + } + var Geo = 0; + if(typeof Params === "object" && Params.Geo) + Geo = 1; + var mapWasAdd = {}; + for(var i = 0; i < len; i++) + { + var Item; + if(UseRandom) + { + var num = random(List.length); + Item = List[num]; + if(mapWasAdd[Item.ip]) + { + continue; + } + mapWasAdd[Item.ip] = 1; + } + else + { + Item = List[i]; + } + var Value = {ip:Item.ip, port:Item.portweb, }; + if(Geo) + { + if(!Item.Geo) + SetGeoLocation(Item); + Value.latitude = Item.latitude; + Value.longitude = Item.longitude; + Value.name = Item.name; + Value.port = Item.port; + } + arr.push(Value); + } + var Result = {result:1, arr:arr, VersionNum:global.UPDATE_CODE_VERSION_NUM, NETWORK:global.NETWORK, }; + return Result; +}; +var AccountKeyMap = {}; +var LastMaxNum = 0; +HostingCaller.GetAccountListByKey = function (Params,ppp,bRet) +{ + if(typeof Params !== "object" || !Params.Key) + return {result:0, arr:[]}; + if(!global.USE_API_WALLET) + return {result:0}; + var Accounts = DApps.Accounts; + for(var num = LastMaxNum; true; num++) + { + if(Accounts.IsHole(num)) + continue; + var Data = Accounts.ReadState(num); + if(!Data) + break; + var StrKey = GetHexFromArr(Data.PubKey); + Data.Next = AccountKeyMap[StrKey]; + AccountKeyMap[StrKey] = Data; + } + LastMaxNum = num; + var arr = []; + var Item = AccountKeyMap[Params.Key]; + while(Item) + { + var Data = Accounts.ReadState(Item.Num); + if(!Data) + continue; + if(!Data.PubKeyStr) + Data.PubKeyStr = GetHexFromArr(Data.PubKey); + if(Data.Currency) + Data.CurrencyObj = DApps.Smart.ReadSimple(Data.Currency); + if(Data.Value.Smart) + { + Data.SmartObj = DApps.Smart.ReadSimple(Data.Value.Smart); + try + { + Data.SmartState = BufLib.GetObjectFromBuffer(Data.Value.Data, Data.SmartObj.StateFormat, {}); + if(typeof Data.SmartState === "object") + Data.SmartState.Num = Item.Num; + } + catch(e) + { + Data.SmartState = {}; + } + } + arr.unshift(Data); + Item = Item.Next; + if(arr.length >= global.HTTP_MAX_COUNT_ROWS) + break; + } + var Ret = {result:1, arr:arr}; + if(bRet) + return Ret; + var Context = GetUserContext(Params); + var StrInfo = JSON.stringify(Ret); + if(!Params.AllData && Context.PrevAccountList === StrInfo) + { + return {result:0, cache:1}; + } + Context.PrevAccountList = StrInfo; + Context.NumAccountList++; + return StrInfo; +}; +var CategoryMap = {}; +var CategoryArr = []; +var CategoryDappMaxNumWas = 0; +HostingCaller.GetDappCategory = function (Params,response) +{ + CheckDappCategoryMap(); + return {result:1, arr:CategoryArr}; +}; + +function CheckDappCategoryMap() +{ + var MaxNumNow = DApps.Smart.GetMaxNum(); + if(MaxNumNow !== CategoryDappMaxNumWas) + { + for(var Num = CategoryDappMaxNumWas; Num <= MaxNumNow; Num++) + { + var Item = DApps.Smart.ReadSimple(Num); + for(var n = 1; n <= 3; n++) + { + var Name = "Category" + n; + var Value = Item[Name]; + if(Value) + { + var DappMap = CategoryMap[Value]; + if(!DappMap) + { + DappMap = {}; + CategoryMap[Value] = DappMap; + CategoryArr.push(Value); + } + DappMap[Num] = 1; + } + } + } + CategoryDappMaxNumWas = MaxNumNow; + } +}; +HostingCaller.SendTransactionHex = function (Params,response) +{ + if(typeof Params !== "object" || !Params.Hex) + return {result:0, text:"object requre"}; + process.RunRPC("AddTransactionFromWeb", {HexValue:Params.Hex}, function (Err,text) + { + var Result = {result:!Err, text:text}; + var Str = JSON.stringify(Result); + response.end(Str); + }); + return null; +}; +HostingCaller.DappSmartHTMLFile = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + return HTTPCaller.DappSmartHTMLFile(Params); +}; +HostingCaller.DappBlockFile = function (Params,responce) +{ + if(typeof Params !== "object") + return {result:0}; + return HTTPCaller.DappBlockFile(Params, responce); +}; +HostingCaller.DappInfo = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + var SmartNum = ParseNum(Params.Smart); + process.send({cmd:"SetSmartEvent", Smart:SmartNum}); + var Context = GetUserContext(Params); + var Ret = HTTPCaller.DappInfo(Params, undefined, 1); + Ret.PubKey = undefined; + var StrInfo = JSON.stringify(Ret); + if(!Params.AllData && Context.PrevDappInfo === StrInfo) + { + return {result:0, cache:1}; + } + Context.PrevDappInfo = StrInfo; + Context.NumDappInfo++; + Context.LastTime = Date.now(); + Ret.NumDappInfo = Context.NumDappInfo; + Ret.CurTime = Date.now(); + Ret.CurBlockNum = GetCurrentBlockNumByTime(); + Ret.BlockNumDB = SERVER.BlockNumDB; + Ret.MaxAccID = DApps.Accounts.GetMaxAccount(); + Ret.MaxDappsID = DApps.Smart.GetMaxNum(); + return Ret; +}; +HostingCaller.DappWalletList = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + var Ret = HostingCaller.GetAccountListByKey(Params, undefined, 1); + var Smart = ParseNum(Params.Smart); + var arr = []; + for(var i = 0; i < Ret.arr.length; i++) + { + if(Params.AllAccounts || Ret.arr[i].Value.Smart === Smart) + { + arr.push(Ret.arr[i]); + } + } + Ret.arr = arr; + return Ret; +}; +HTTPCaller.DappWalletList = HostingCaller.DappWalletList; +HostingCaller.DappAccountList = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Accounts.GetRowsAccounts(ParseNum(Params.StartNum), ParseNum(Params.CountNum), undefined, 1); + return {arr:arr, result:1}; +}; +HostingCaller.DappSmartList = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + var arr = DApps.Smart.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum), undefined, undefined, Params.GetAllData, + Params.TokenGenerate); + return {arr:arr, result:1}; +}; +HostingCaller.DappBlockList = function (Params,response) +{ + if(typeof Params !== "object") + return {result:0}; + Params.StartNum = ParseNum(Params.StartNum); + Params.CountNum = ParseNum(Params.CountNum); + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + return HTTPCaller.DappBlockList(Params, response); +}; +HostingCaller.DappTransactionList = function (Params,response) +{ + if(typeof Params !== "object") + return {result:0}; + Params.BlockNum = ParseNum(Params.BlockNum); + Params.StartNum = ParseNum(Params.StartNum); + Params.CountNum = ParseNum(Params.CountNum); + if(Params.CountNum > MaxCountViewRows) + Params.CountNum = MaxCountViewRows; + if(!Params.CountNum) + Params.CountNum = 1; + return HTTPCaller.DappTransactionList(Params, response); +}; +HostingCaller.DappStaticCall = function (Params,response) +{ + if(typeof Params !== "object") + return {result:0}; + return HTTPCaller.DappStaticCall(Params, response); +}; +HostingCaller.GetHistoryTransactions = function (Params) +{ + if(typeof Params !== "object") + return {result:0}; + return HTTPCaller.GetHistoryTransactions(Params); +}; +HostingCaller.GetSupply = function (Params) +{ + var Data = DApps.Accounts.ReadState(0); + if(!Data) + return ""; + else + { + return "" + (global.TOTAL_SUPPLY_TERA - Data.Value.SumCOIN); + } +}; +HostingCaller.GetTotalSupply = function (Params) +{ + return "" + global.TOTAL_SUPPLY_TERA; +}; +global.GlobalRunID = 0; +global.GlobalRunMap = {}; +process.RunRPC = function (Name,Params,F) +{ + if(F) + { + GlobalRunID++; + try + { + process.send({cmd:"call", id:GlobalRunID, Name:Name, Params:Params}); + GlobalRunMap[GlobalRunID] = F; + } + catch(e) + { + } + } + else + { + process.send({cmd:"call", id:0, Name:Name, Params:Params}); + } +}; +global.EvalCode = function (Code) +{ + var Result; + try + { + var ret = eval(Code); + Result = JSON.stringify(ret, "", 4); + } + catch(e) + { + Result = "" + e; + } + return Result; +}; +setInterval(function () +{ + if(SERVER) + SERVER.Close(); + DApps.Accounts.Close(); + DApps.Smart.DBSmart.Close(); +}, 500); +setInterval(function () +{ + var MaxNumBlockDB = SERVER.GetMaxNumBlockDB(); + var HASHARATE_BLOCK_LENGTH = 10; + var arr = SERVER.GetStatBlockchain("POWER_BLOCKCHAIN", HASHARATE_BLOCK_LENGTH); + if(arr.length) + { + var SumPow = 0; + var Count = 0; + var Value = 0; + for(var i = arr.length - HASHARATE_BLOCK_LENGTH; i < arr.length; i++) + if(arr[i]) + { + Value = arr[i]; + SumPow += Value; + Count++; + } + if(!Count) + Count = 1; + var AvgPow = SumPow / Count; + ADD_TO_STAT("MAX:HASH_RATE_B", AvgPow); + } + var Count = COUNT_BLOCK_PROOF + 16 - 1; + if(MaxNumBlockDB > Count) + { + var StartNum = MaxNumBlockDB - Count; + NodeBlockChain = SERVER.BlockChainToBuf(StartNum, StartNum, MaxNumBlockDB); + } +}, 700); +require("./api-exchange.js"); +try +{ + require("../SITE/JS/web-addon.js"); +} +catch(e) +{ +} +global.LoadBlockFromNetwork = function (Params,F) +{ + ToLog("RUN: LoadBlockFromNetwork:" + Params.BlockNum, 2); + process.RunRPC("LoadBlockFromNetwork", {BlockNum:Params.BlockNum, F:1}, function (Err,Block) + { + ToLog("RETURN: LoadBlockFromNetwork: " + Params.BlockNum, 2); + F(Err, Block); + }); +}; diff --git a/Source/run-node.bat b/Source/run-node.bat new file mode 100644 index 0000000..b0a5978 --- /dev/null +++ b/Source/run-node.bat @@ -0,0 +1,5 @@ + +:loop +node.exe run-node.js +goto :loop + diff --git a/Source/run-node.js b/Source/run-node.js new file mode 100644 index 0000000..519609e --- /dev/null +++ b/Source/run-node.js @@ -0,0 +1,10 @@ +const fs = require('fs'); +const os = require('os'); +if(!global.DATA_PATH || global.DATA_PATH==="") + global.DATA_PATH="../DATA"; +global.CODE_PATH=process.cwd(); +global.HTTP_PORT_NUMBER = 8080; +if(global.LOCAL_RUN===undefined) + global.LOCAL_RUN=0; + +require('./process/main-process'); diff --git a/Source/run-nw.js b/Source/run-nw.js new file mode 100644 index 0000000..ecae0e6 --- /dev/null +++ b/Source/run-nw.js @@ -0,0 +1,76 @@ + + +global.HTTP_PORT_NUMBER=Math.trunc(10000*Math.random())+50000; +if(!global.DATA_PATH || global.DATA_PATH==="") + global.DATA_PATH="../DATA"; +global.CODE_PATH=process.cwd(); + +global.NWMODE=1; +global.NWVERSION="0.37.4" + +global.START_IP=""; +global.START_PORT_NUMBER = 30000; +global.CREATE_ON_START=0; + +global.LOCAL_RUN=0; + +require('./core/library.js'); +if(!global.HTTP_PORT_NUMBER)//try 2 + global.HTTP_PORT_NUMBER=Math.trunc(10000*Math.random())+50000; + +require('./process/main-process'); + + +// global.NO_HISTORY_MODE=1;//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1 +// global.NO_CHECK_BLOCKNUM_ONSTART=1; + +setTimeout(function () +{ + var Path=""; + if(global.HTTP_SERVER_START_OK) + { + Path='http://localhost:'+global.HTTP_PORT_NUMBER; + } + else + { + ToLog("ERR HTTP-SERVER NOT STARTING"); + } + nw.Window.open(Path+'/HTML/wallet.html', + { + width: 840, + height: 1000, + icon: "../HTML/PIC/wallet.png", + }, function(win) + { + //win.showDevTools(); + + // Create a tray icon + let Visible=1; + var tray = new nw.Tray({ title: 'TERA', icon: '/HTML/PIC/wallet16.png'}); + tray.on('click',function () + { + Visible=!Visible; + if(Visible) + win.show(); + else + win.hide(); + }) + + // Give it a menu + var menu = new nw.Menu(); + menu.append(new nw.MenuItem({ label: 'Hide', click:function () {win.hide();Visible=0;}})); + menu.append(new nw.MenuItem({ label: 'Show', click:function () {win.show();Visible=1;}})); + menu.append(new nw.MenuItem({ type: "separator"})); + menu.append(new nw.MenuItem({ label: 'DevTools', click:function () {win.showDevTools();}})); + menu.append(new nw.MenuItem({ type: "separator"})); + menu.append(new nw.MenuItem({ label: 'Exit', click:function () {process.exit(0)}})); + tray.menu = menu; + + + // win.on('navigation',function (frame, url, policy) + // { + // console.log("url:"+url); + // }); + }); +},500); + diff --git a/Source/run-test.js b/Source/run-test.js new file mode 100644 index 0000000..701db5c --- /dev/null +++ b/Source/run-test.js @@ -0,0 +1,13 @@ +const fs = require('fs'); +const os = require('os'); +if(!global.DATA_PATH || global.DATA_PATH==="") + global.DATA_PATH="../DATA-TEST"; +global.CODE_PATH=process.cwd(); +global.HTTP_PORT_NUMBER = 8080; +global.START_PORT_NUMBER = 40000; +if(global.LOCAL_RUN===undefined) + global.LOCAL_RUN=0; + +global.TEST_NETWORK = 1; + +require('./process/main-process'); diff --git a/Source/set-test.js b/Source/set-test.js new file mode 100644 index 0000000..8c1429e --- /dev/null +++ b/Source/set-test.js @@ -0,0 +1,15 @@ + +global.TEST_NETWORK = 1; +global.DATA_PATH="../DATA-TEST"; +global.CODE_PATH=process.cwd(); + +require("./core/constant"); +require("./core/library"); + +CheckCreateDir(global.DATA_PATH); + +InitParamsArg(); +SAVE_CONST(true); + + +process.exit(); diff --git a/Source/set.js b/Source/set.js new file mode 100644 index 0000000..fcc3115 --- /dev/null +++ b/Source/set.js @@ -0,0 +1,14 @@ + +global.DATA_PATH="../DATA"; +global.CODE_PATH=process.cwd(); + +require("./core/constant"); +require("./core/library"); + +CheckCreateDir(global.DATA_PATH); + +InitParamsArg(); +SAVE_CONST(true); + + +process.exit(); diff --git a/Source/system/accounts.js b/Source/system/accounts.js new file mode 100644 index 0000000..4e5b4ca --- /dev/null +++ b/Source/system/accounts.js @@ -0,0 +1,1606 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +const DBRow = require("../core/db/db-row"); +require('../core/rest_tables.js'); +const MAX_SUM_TER = 1e9; +const MAX_SUM_CENT = 1e9; +const DBLib = require("../core/db/db"); +global.HistoryDB = new DBLib(); +const FILE_NAME_HISTORY = "history-body"; +var WorkStructHistory = {}; +const BLOCK_CREATE_INTERVAL = 10; +global.TYPE_TRANSACTION_CREATE = 100; +const TYPE_DEPRECATED_TRANSFER1 = 105; +const TYPE_DEPRECATED_TRANSFER2 = 110; +const TYPE_TRANSACTION_TRANSFER = 111; +global.TYPE_TRANSACTION_ACC_HASH = 119; +global.FORMAT_CREATE = "{\ + Type:byte,\ + Currency:uint,\ + PubKey:arr33,\ + Name:str40,\ + Adviser:uint,\ + Smart:uint32,\ + Reserve:arr3,\ + }"; +global.FORMAT_MONEY_TRANSFER = '{\ + Type:byte,\ + Currency:uint,\ + FromID:uint,\ + To:[{ID:uint,SumCOIN:uint,SumCENT:uint32}],\ + Description:str,\ + OperationID:uint,\ + Sign:arr64,\ + }'; +const WorkStructTransfer = {}; +global.FORMAT_MONEY_TRANSFER_BODY = FORMAT_MONEY_TRANSFER.replace("Sign:arr64,", ""); +global.FORMAT_MONEY_TRANSFER2 = "{\ + Type:byte,\ + Version:byte,\ + Currency:uint,\ + FromID:uint,\ + To:[{ID:uint,SumCOIN:uint,SumCENT:uint32}],\ + Description:str,\ + OperationID:uint,\ + Sign:arr64,\ + }"; +const WorkStructTransfer2 = {}; +global.FORMAT_MONEY_TRANSFER_BODY2 = FORMAT_MONEY_TRANSFER2.replace("Sign:arr64,", ""); +global.FORMAT_MONEY_TRANSFER3 = "{\ + Type:byte,\ + Version:byte,\ + Reserve:uint,\ + FromID:uint,\ + To:[{PubKey:tr,ID:uint,SumCOIN:uint,SumCENT:uint32}],\ + Description:str,\ + OperationID:uint,\ + Body:tr,\ + Sign:arr64,\ + }"; +const WorkStructTransfer3 = {}; +global.FORMAT_MONEY_TRANSFER_BODY3 = FORMAT_MONEY_TRANSFER3.replace("Sign:arr64,", ""); +global.FORMAT_ACCOUNT_HASH = "{\ + Type:byte,\ + BlockNum:uint,\ + AccHash:buffer32,\ + }"; +global.FORMAT_ACCOUNT_HASH3 = "{\ + Type:byte,\ + BlockNum:uint,\ + AccHash:buffer32,\ + AccountMax:uint,\ + SmartHash:buffer32,\ + SmartCount:uint,\ + }"; +class MerkleDBRow extends DBRow +{ + constructor(FileName, DataSize, Format, bReadOnly) + { + super(FileName, DataSize, Format, bReadOnly) + this.InitMerkleTree() + } + InitMerkleTree() + { + this.MerkleTree = undefined + this.MerkleArr = [] + this.MerkleCalc = {} + } + CalcMerkleTree(bForceUpdate) + { + if(!this.MerkleTree || bForceUpdate) + { + this.MerkleCalc = {} + this.MerkleTree = {LevelsHash:[this.MerkleArr], RecalcCount:0} + var GetMaxNum = this.GetMaxNum(); + for(var num = 0; num <= GetMaxNum; num++) + { + var Buf = this.Read(num, 1); + if(!Buf) + { + if(global.WATCHDOG_DEV) + ToErrorTx("CalcMerkleTree: Break account reading on num: " + num) + break; + } + this.MerkleArr[num] = shaarr(Buf) + this.MerkleCalc[num] = 1 + } + } + this.MerkleTree.RecalcCount = 0 + UpdateMerklTree(this.MerkleTree, this.MerkleCalc, 0) + this.MerkleCalc = {} + return this.MerkleTree.Root; + } + Write(Data) + { + var RetBuf = {}; + var bRes = DBRow.prototype.Write.call(this, Data, RetBuf); + if(bRes) + { + var Hash = shaarr(RetBuf.Buf); + this.MerkleArr[Data.Num] = Hash + this.MerkleCalc[Data.Num] = 1 + } + return bRes; + } + Truncate(LastNum) + { + DBRow.prototype.Truncate.call(this, LastNum) + if(this.MerkleArr.length !== LastNum + 1) + { + this.MerkleArr.length = LastNum + 1 + this.MerkleCalc[LastNum] = 1 + } + } +}; +class AccountApp extends require("./dapp") +{ + constructor() + { + super() + var bReadOnly = (global.PROCESS_NAME !== "TX"); + this.CreateTrCount = 0 + this.FORMAT_ACCOUNT_ROW = "{\ + Currency:uint,\ + PubKey:arr33,\ + Name:str40,\ + Value:{SumCOIN:uint,SumCENT:uint32, OperationID:uint,Smart:uint32,Data:arr80},\ + BlockNumCreate:uint,\ + Adviser:uint,\ + Reserve:arr9,\ + }" + this.SIZE_ACCOUNT_ROW = 6 + 33 + 40 + (6 + 4 + 6 + 84) + 6 + 6 + 9 + this.DBState = new MerkleDBRow("accounts-state", this.SIZE_ACCOUNT_ROW, this.FORMAT_ACCOUNT_ROW, bReadOnly) + this.FORMAT_ACCOUNT_ROW_REST = "{\ + Arr:[{\ + BlockNum:uint,\ + Value:{SumCOIN:uint,SumCENT:uint32, OperationID:uint,Smart:uint32,Data:arr80,Reserv:arr96},\ + }],\ + Reserv0:arr10,\ + }" + this.SIZE_ACCOUNT_ROW_REST = 1024 + this.DBRest = new DBRow("accounts-rest", this.SIZE_ACCOUNT_ROW_REST, this.FORMAT_ACCOUNT_ROW_REST, bReadOnly) + this.DBAct = new DBRow("accounts-act", 6 + 6 + (6 + 4 + 6 + 6 + 84) + 1 + 11, "{ID:uint, BlockNum:uint,PrevValue:{SumCOIN:uint,SumCENT:uint32, NextPos:uint, OperationID:uint,Smart:uint32,Data:arr80}, Mode:byte, TrNum:uint16, Reserve: arr9}", + bReadOnly) + this.DBActPrev = new DBRow("accounts-act-prev", this.DBAct.DataSize, this.DBAct.Format, bReadOnly) + this.FORMAT_STATE_HISTORY = "{NextPos:uint,Reserv:arr2}" + this.DBStateHistory = new DBRow("history-state", 8, this.FORMAT_STATE_HISTORY, bReadOnly) + HistoryDB.OpenDBFile(FILE_NAME_HISTORY, !bReadOnly) + this.HistoryFormatArr = ["{Type:byte, BlockNum:uint32,TrNum:uint16, NextPos:uint}", "{Type:byte, BlockNum:uint32,TrNum:uint16, NextPos:uint, Direct:str1,CorrID:uint, SumCOIN:uint,SumCENT:uint32}"] + this.DBStateTX = new DBRow("accounts-tx", 6 + 6 + 88, "{BlockNum:uint, BlockNumMin:uint, Reserve: arr88}", bReadOnly) + if(global.READ_ONLY_DB) + return ; + this.DBAccountsHash = new DBRow("accounts-hash3", 6 + 32 + 32 + 32 + 6 + 6 + 14, "{BlockNum:uint, AccHash:hash, SumHash:hash, SmartHash:hash, AccountMax:uint, SmartCount:uint, Reserve: arr14}", + bReadOnly) + if(global.START_SERVER) + return ; + if(!bReadOnly) + this.Start() + setInterval(this.ControlActSize.bind(this), 60 * 1000) + } + Start(bClean) + { + if(global.LOCAL_RUN) + bClean = 1 + if(!bClean && this.DBState.GetMaxNum() + 1 >= BLOCK_PROCESSING_LENGTH2) + return ; + this.DBState.MerkleTree = undefined + this.DBState.Truncate( - 1) + this.DBStateHistory.Truncate( - 1) + this.DBAct.Truncate( - 1) + this.DBActPrev.Truncate( - 1) + this.DBAccountsHash.Truncate( - 1) + this.DBStateTX.Truncate( - 1) + this.DBRest.Truncate( - 1) + this._DBStateWrite({Num:0, PubKey:[], Value:{BlockNum:1, SumCOIN:0.95 * TOTAL_SUPPLY_TERA}, Name:"System account"}, 1) + for(var i = 1; i < 8; i++) + this._DBStateWrite({Num:i, PubKey:[], Value:{BlockNum:1}, Name:""}) + this._DBStateWrite({Num:8, PubKey:GetArrFromHex(ARR_PUB_KEY[0]), Value:{BlockNum:1, SumCOIN:0.05 * TOTAL_SUPPLY_TERA}, Name:"Founder account"}) + this._DBStateWrite({Num:9, PubKey:GetArrFromHex(ARR_PUB_KEY[1]), Value:{BlockNum:1, SumCOIN:0}, Name:"Developer account"}) + for(var i = 10; i < BLOCK_PROCESSING_LENGTH2; i++) + this._DBStateWrite({Num:i, PubKey:GetArrFromHex(ARR_PUB_KEY[i - 8]), Value:{BlockNum:1}, Name:""}) + this.DBStateTX.Write({Num:0, BlockNum:0}) + this.CalcMerkleTree(1) + var FileItem = HistoryDB.OpenDBFile(FILE_NAME_HISTORY, 1); + fs.ftruncateSync(FileItem.fd, 0) + ToLog("MAX_NUM:" + this.DBState.GetMaxNum()) + } + Close() + { + this.DBState.Close() + this.DBActPrev.Close() + this.DBAct.Close() + if(this.DBAccountsHash) + this.DBAccountsHash.Close() + if(this.DBStateTX) + this.DBStateTX.Close() + this.DBRest.Close() + if(this.DBStateHistory) + this.DBStateHistory.Close() + } + ClearDataBase() + { + this.Start(1) + } + CheckRestDB() + { + if(!global.SERVER) + return ; + if(this.WasCheckRestDB) + return ; + this.WasCheckRestDB = 1 + var MaxNumBlock = SERVER.GetMaxNumBlockDB(); + if(this.DBState.GetMaxNum() >= 0 && this.DBRest.GetMaxNum() < 0 && MaxNumBlock > 0) + { + this.FillRestDB(MaxNumBlock) + } + } + FillRestDB(BlockNum) + { + ToLog("******************************START FillRestDB") + var Max = this.DBState.GetMaxNum(); + for(var Num = 0; Num <= Max; Num++) + { + var Data = this.DBState.Read(Num); + var RestData = this.ReadRest(Num); + if(Num % 10000 === 0) + ToLog("Fill Rest DB : " + Num) + RestData.Arr[0] = {BlockNum:BlockNum, Value:Data.Value} + this.DBRest.Write(RestData) + } + ToLog("******************************FINISH FillRestDB") + } + _DBStateWrite(Data, BlockNum) + { + this.CheckRestDB() + this.DBState.Write(Data) + if(Data.Num === undefined) + throw "Error undefined Num DBRest !!"; + var RestData = this.ReadRest(Data.Num); + DoRest(RestData, Data, BlockNum) + this.DBRest.Write(RestData) + } + _DBStateTruncate(Num) + { + this.DBState.Truncate(Num) + this.DBRest.Truncate(Num) + } + ReadRest(Num) + { + var COUNT_STATE = 5; + var RestData = this.DBRest.Read(Num); + if(!RestData || RestData.Arr.length !== COUNT_STATE) + { + RestData = {Num:Num, Arr:[]} + for(var i = 0; i < COUNT_STATE; i++) + RestData.Arr[i] = {BlockNum:0, Value:{}} + } + if(RestData.Arr.length !== COUNT_STATE) + throw "Error RestData.Arr.length = (" + RestData.Arr.length + ")"; + return RestData; + } + ControlActSize() + { + var MaxNum = this.DBAct.GetMaxNum(); + if(MaxNum >= TRANSACTION_PROOF_COUNT * 2) + { + ToLog("Rename act files") + this.DBActPrev.CloseDBFile(this.DBActPrev.FileName) + this.DBAct.CloseDBFile(this.DBAct.FileName) + if(fs.existsSync(this.DBActPrev.FileNameFull)) + { + var FileNameFull2 = this.DBActPrev.FileNameFull + "_del"; + try + { + fs.renameSync(this.DBActPrev.FileNameFull, FileNameFull2) + } + catch(e) + { + ToErrorTx("Can-t rename for delete act-file: " + FileNameFull2) + return ; + } + fs.unlinkSync(FileNameFull2) + } + try + { + fs.renameSync(this.DBAct.FileNameFull, this.DBActPrev.FileNameFull) + } + catch(e) + { + ToErrorTx("Can-t rename act-file!") + return ; + } + } + } + GetSenderNum(BlockNum, Body) + { + var Type = Body[0]; + if(Type && Body.length > 90) + { + switch(Type) + { + case TYPE_TRANSACTION_CREATE: + { + if(BlockNum % BLOCK_CREATE_INTERVAL !== 0) + return 0; + var Num = BlockNum; + return Num; + } + case TYPE_TRANSACTION_TRANSFER: + var Num = ReadUintFromArr(Body, 1 + 1 + 6); + return Num; + case TYPE_TRANSACTION_ACC_HASH: + return - 1; + } + } + return 0; + } + OnDeleteBlock(Block) + { + if(Block.BlockNum < 1) + return ; + this.DeleteAct(Block.BlockNum) + } + OnWriteBlockStart(Block) + { + this.CreateTrCount = 0 + if(Block.BlockNum < 1) + return ; + this.OnDeleteBlock(Block) + this.BeginBlock() + } + OnWriteBlockFinish(Block) + { + try + { + this.BeginTransaction() + this.DoCoinBaseTR(Block) + this.CommitTransaction(Block.BlockNum, 0xFFFF) + } + catch(e) + { + this.RollBackTransaction() + ToErrorTx("BlockNum:" + Block.BlockNum + " - DoCoinBaseTR: " + e) + } + this.CommitBlock(Block) + } + OnWriteTransaction(Block, Body, BlockNum, TrNum, ContextFrom) + { + var Result; + try + { + Result = this.OnWriteTransactionTR(Block, Body, BlockNum, TrNum, ContextFrom) + } + catch(e) + { + Result = "" + e + if(global.WATCHDOG_DEV) + ToErrorTx("BlockNum:" + BlockNum + ":" + e) + } + if(Result !== true) + { + this.RollBackTransaction() + } + return Result; + } + OnWriteTransactionTR(Block, Body, BlockNum, TrNum, ContextFrom) + { + var Type = Body[0]; + var Result; + switch(Type) + { + case TYPE_TRANSACTION_CREATE: + { + Result = this.TRCreateAccount(Body, BlockNum, TrNum, ContextFrom) + break; + } + case TYPE_DEPRECATED_TRANSFER1: + { + Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER, WorkStructTransfer) + break; + } + case TYPE_DEPRECATED_TRANSFER2: + { + Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER2, WorkStructTransfer2) + break; + } + case TYPE_TRANSACTION_TRANSFER: + { + Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER3, WorkStructTransfer3) + break; + } + case TYPE_TRANSACTION_ACC_HASH: + { + Result = 1 + if(global.LOCAL_RUN || global.TEST_NETWORK); + else + if(BlockNum < START_BLOCK_ACCOUNT_HASH + 200000) + break; + var BlockNumHash = BlockNum - DELTA_BLOCK_ACCOUNT_HASH; + if(!this.TRCheckAccountHash(Body, BlockNum, TrNum)) + { + Result = "BAD ACCOUNT HASH" + ToLog("2. ****FIND BAD ACCOUNT HASH IN BLOCK: " + BlockNumHash + " DO BLOCK=" + BlockNum) + } + else + { + Result = true + } + break; + } + } + return Result; + } + DoCoinBaseTR(Block) + { + if(Block.BlockNum < global.START_MINING) + return ; + var SysData = this.ReadStateTR(0); + var SysBalance = SysData.Value.SumCOIN; + const REF_PERIOD_START = global.START_MINING; + const REF_PERIOD_END = 30 * 1000 * 1000; + var AccountID = ReadUintFromArr(Block.AddrHash, 0); + if(AccountID < 8) + return ; + var Data = this.ReadStateTR(AccountID); + if(Data && Data.Currency === 0 && Data.BlockNumCreate < Block.BlockNum) + { + var Sum; + if(Block.BlockNum >= NEW_FORMULA_START) + { + if(Block.BlockNum <= NEW_FORMULA_TARGET1) + { + Sum = SysBalance * 43 * 43 / 100 / TOTAL_SUPPLY_TERA + var KMult = (NEW_FORMULA_TARGET2 - Block.BlockNum) / (NEW_FORMULA_TARGET2 - NEW_FORMULA_START); + Sum = KMult * Sum + } + else + { + Sum = NEW_FORMULA_KTERA * SysBalance / TOTAL_SUPPLY_TERA + } + } + else + { + var Power = GetPowPower(Block.PowHash); + if(Block.BlockNum >= NEW_BLOCK_REWARD1) + Power = 43 + Sum = Power * Power * SysBalance / TOTAL_SUPPLY_TERA / 100 + } + var CoinTotal = {SumCOIN:0, SumCENT:0}; + var CoinSum = COIN_FROM_FLOAT(Sum); + if(!ISZERO(CoinSum)) + { + if(Data.Adviser >= 8 && Block.BlockNum < REF_PERIOD_END) + { + var RefData = this.ReadStateTR(Data.Adviser); + if(RefData && RefData.BlockNumCreate < Block.BlockNum - REF_PERIOD_MINING) + { + var K = (REF_PERIOD_END - Block.BlockNum) / (REF_PERIOD_END - REF_PERIOD_START); + var CoinAdv = COIN_FROM_FLOAT(Sum * K); + this.SendMoneyTR(Block, 0, Data.Adviser, CoinAdv, Block.BlockNum, 0xFFFF, "", "Adviser coin base [" + AccountID + "]", 1) + ADD(CoinTotal, CoinAdv) + ADD(CoinSum, CoinAdv) + } + } + this.SendMoneyTR(Block, 0, AccountID, CoinSum, Block.BlockNum, 0xFFFF, "", "Coin base", 1) + ADD(CoinTotal, CoinSum) + var CoinDevelop = CopyObjValue(CoinTotal); + DIV(CoinDevelop, 100) + if(!ISZERO(CoinDevelop)) + this.SendMoneyTR(Block, 0, 9, CoinDevelop, Block.BlockNum, 0xFFFF, "", "Developers support", 1) + } + } + } + GetVerifyTransaction(Block, BlockNum, TrNum, Body) + { + if(Block.VersionBody === 1) + { + var Result = Block.arrContentResult[TrNum]; + if(!Result) + return - 1; + else + return Result; + } + return 0; + } + GetObjectTransaction(Body) + { + var Type = Body[0]; + var format; + switch(Type) + { + case TYPE_TRANSACTION_CREATE: + { + format = FORMAT_CREATE + break; + } + case TYPE_DEPRECATED_TRANSFER1: + { + format = FORMAT_MONEY_TRANSFER + break; + } + case TYPE_DEPRECATED_TRANSFER2: + { + format = FORMAT_MONEY_TRANSFER2 + break; + } + case TYPE_TRANSACTION_TRANSFER: + { + format = FORMAT_MONEY_TRANSFER3 + break; + } + case TYPE_TRANSACTION_ACC_HASH: + { + format = FORMAT_ACCOUNT_HASH3 + break; + } + default: + return ""; + } + var TR; + try + { + TR = BufLib.GetObjectFromBuffer(Body, format, {}) + } + catch(e) + { + } + return TR; + } + GetScriptTransaction(Body) + { + var TR = this.GetObjectTransaction(Body); + if(!TR) + return ""; + if(TR.Body && TR.Body.length) + { + var App = DAppByType[TR.Body[0]]; + if(App) + { + TR.Body = JSON.parse(App.GetScriptTransaction(TR.Body)) + } + } + ConvertBufferToStr(TR) + return JSON.stringify(TR, "", 2); + } + TRCheckAccountHash(Body, BlockNum, TrNum) + { + if(BlockNum % PERIOD_ACCOUNT_HASH !== 0) + return 1; + try + { + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_ACCOUNT_HASH3, {}); + } + catch(e) + { + return 0; + } + if(BlockNum < START_BLOCK_ACCOUNT_HASH + 200000) + return 1; + var Item = this.GetAccountHashItem(TR.BlockNum); + if(Item && Item.BlockNum === TR.BlockNum) + { + if(CompareArr(Item.AccHash, TR.AccHash) === 0) + { + if(TR.BlockNum >= START_BLOCK_ACCOUNT_HASH3) + { + if(CompareArr(Item.SmartHash, TR.SmartHash) === 0 && Item.AccountMax === TR.AccountMax && Item.SmartCount === TR.SmartCount) + { + return 1; + } + else + return 0; + } + return 1; + } + else + return 0; + } + else + return 2; + } + TRCreateAccount(Body, BlockNum, TrNum, ContextFrom) + { + if(Body.length < 90) + return "Error length transaction"; + var CheckMinPower = 1; + if(BlockNum >= 7000000 || global.LOCAL_RUN || global.TEST_NETWORK) + { + if(ContextFrom && ContextFrom.To.length === 1 && ContextFrom.To[0].ID === 0 && ContextFrom.To[0].SumCOIN >= PRICE_DAO(BlockNum).NewAccount) + { + CheckMinPower = 0 + } + else + { + if(BlockNum % BLOCK_CREATE_INTERVAL !== 0) + return "The create transaction is not possible in this block: " + BlockNum; + if(this.CreateTrCount > 0) + return "The account creation transaction was already in this block: " + BlockNum; + } + } + this.CreateTrCount++ + var power; + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + { + var Tr = {body:Body}; + SERVER.CheckCreateTransactionObject(Tr) + power = Tr.power + } + else + { + power = GetPowPower(shaarr(Body)) + } + if(global.TEST_NETWORK && BlockNum >= 3290000) + { + CheckMinPower = 0 + } + if(CheckMinPower && BlockNum < 19600000) + { + var MinPower; + if(BlockNum < 2500000) + MinPower = MIN_POWER_POW_ACC_CREATE + else + if(BlockNum < 2800000) + MinPower = MIN_POWER_POW_ACC_CREATE + 2 + else + MinPower = MIN_POWER_POW_ACC_CREATE + 3 + if(power < MinPower) + return "Error min power POW for create account (update client)"; + } + try + { + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_CREATE, {}); + } + catch(e) + { + return "Error transaction format"; + } + if(BlockNum >= 3500000 && !TR.Name) + return "Account name required"; + if(BlockNum >= 5700000 && !TR.Name.trim()) + return "Account name required"; + var Account = this.NewAccountTR(BlockNum, TrNum); + Account.Currency = TR.Currency + Account.PubKey = TR.PubKey + Account.Name = TR.Name + Account.Adviser = TR.Adviser + Account.Value.Smart = TR.Smart + this.WriteStateTR(Account, TrNum) + if(CompareArr(Account.PubKey, WALLET.PubKeyArr) === 0) + { + WALLET.OnCreateAccount(Account) + } + this.ResultTx = Account.Num + return true; + } + TRTransferMoney(Block, Body, BlockNum, TrNum, format_money_transfer, workstructtransfer) + { + if(Body.length < 103) + return "Error length transaction"; + try + { + var TR = BufLib.GetObjectFromBuffer(Body, format_money_transfer, workstructtransfer); + } + catch(e) + { + return "Error transaction format"; + } + if(!TR.Version) + TR.Version = 0 + var Data = this.ReadStateTR(TR.FromID); + if(!Data) + return "Error sender's account ID: " + TR.FromID; + if(TR.Version < 3 && TR.Currency !== Data.Currency) + return "Error sender's currency"; + if(TR.Version < 3) + { + if(TR.OperationID !== Data.Value.OperationID) + return "Error OperationID (expected: " + Data.Value.OperationID + " for ID: " + TR.FromID + ")"; + } + else + { + if(TR.OperationID < Data.Value.OperationID) + return "Error OperationID (expected: " + Data.Value.OperationID + " for ID: " + TR.FromID + ")"; + var MaxCountOperationID = 100; + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + MaxCountOperationID = 1000000 + if(TR.OperationID > Data.Value.OperationID + MaxCountOperationID) + return "Error too much OperationID (expected max: " + (Data.Value.OperationID + MaxCountOperationID) + " for ID: " + TR.FromID + ")"; + } + if(BlockNum >= SMART_BLOCKNUM_START) + { + if(TR.To.length > 10) + return "The number of recipients has been exceeded (max=5, current count=" + TR.To.length + ")"; + } + if(TR.Body && TR.Body.length && TR.To.length > 1) + { + return "Error - dapps transaction can not be used in a multiple transaction"; + } + var TotalSum = {SumCOIN:0, SumCENT:0}; + var MapItem = {}; + var bWas = 0; + for(var i = 0; i < TR.To.length; i++) + { + var Item = TR.To[i]; + if(Item.SumCOIN > MAX_SUM_TER) + return "Error MAX_SUM_COIN"; + if(Item.SumCENT >= MAX_SUM_CENT) + return "Error MAX_SUM_CENT"; + if(TR.Version < 3) + { + if(Item.ID === TR.FromID || MapItem[Item.ID]) + continue; + MapItem[Item.ID] = 1 + } + bWas = 1 + ADD(TotalSum, Item) + } + if(!bWas && TR.Version < 3) + return "No significant recipients"; + var ZeroSum = 0; + if(TotalSum.SumCOIN === 0 && TotalSum.SumCENT === 0) + { + if(TR.Version < 3) + return "No money transaction"; + else + ZeroSum = 1 + } + if(Data.Value.SumCOIN < TotalSum.SumCOIN || (Data.Value.SumCOIN === TotalSum.SumCOIN && Data.Value.SumCENT < TotalSum.SumCENT)) + return "Not enough money on the account"; + if(BlockNum >= global.NEW_ACCOUNT_INCREMENT) + Data.Value.OperationID = TR.OperationID + Data.Value.OperationID++ + TR.Value = TotalSum + var arr = []; + MapItem = {} + var arrpub = []; + for(var i = 0; i < TR.To.length; i++) + { + var Item = TR.To[i]; + var DataTo = this.ReadStateTR(Item.ID); + if(!DataTo) + return "Error receiver account ID: " + Item.ID; + if(!ZeroSum && Data.Currency !== DataTo.Currency) + return "Error receiver currency"; + for(var j = 0; j < 33; j++) + arrpub[arrpub.length] = DataTo.PubKey[j] + if(DataTo.Value.Smart) + { + if(TR.To.length > 1) + return "Error - smart accounts can not be used in a multiple transaction"; + } + if(TR.Version === 3 && Item.ID === 0 && Item.PubKey && Item.PubKey.length === 33) + { + if(Item.SumCOIN < PRICE_DAO(BlockNum).NewAccount) + return "Not enough money for create account with index: " + i; + var name = TR.Description; + var index = name.indexOf("\n"); + if(index !== - 1) + name = name.substr(0, index) + var Account = this.NewAccountTR(BlockNum, TrNum); + Account.PubKey = Item.PubKey + Account.Name = name + this.WriteStateTR(Account, TrNum) + this.ResultTx = Account.Num + Item.ID = Account.Num + this.SendMoneyTR(Block, Data.Num, Account.Num, {SumCOIN:Item.SumCOIN, SumCENT:Item.SumCENT}, BlockNum, TrNum, TR.Description, + TR.Description, 1) + this.SendMoneyTR(Block, Account.Num, 0, {SumCOIN:PRICE_DAO(BlockNum).NewAccount, SumCENT:0}, BlockNum, TrNum, "Fee for create account", + "", 1) + } + else + { + if(TR.Version < 3) + { + if(Item.ID === TR.FromID || MapItem[Item.ID]) + continue; + MapItem[Item.ID] = 1 + } + this.SendMoneyTR(Block, Data.Num, DataTo.Num, {SumCOIN:Item.SumCOIN, SumCENT:Item.SumCENT}, BlockNum, TrNum, TR.Description, + TR.Description, 0) + arr.push(DataTo) + } + } + if(TR.Version < 3 && arr.length === 0) + return "No recipients"; + var hash; + if(TR.Version === 2 || TR.Version === 3) + { + for(var j = 0; j < Body.length - 64 - 12; j++) + arrpub[arrpub.length] = Body[j] + hash = SHA3BUF(arrpub, BlockNum) + } + else + if(!TR.Version) + { + hash = SHA3BUF(Body.slice(0, Body.length - 64 - 12), BlockNum) + } + else + { + return "Error transaction version"; + } + var Result = 0; + if(Data.PubKey[0] === 2 || Data.PubKey[0] === 3) + try + { + Result = secp256k1.verify(hash, TR.Sign, Data.PubKey) + } + catch(e) + { + } + if(!Result) + { + return "Error sign transaction"; + } + if(TR.Body && TR.Body.length) + { + var App = DAppByType[TR.Body[0]]; + if(App) + { + TR.FromPubKey = Data.PubKey + var Result = App.OnWriteTransaction(Block, TR.Body, BlockNum, TrNum, TR); + if(Result !== true) + return Result; + } + } + return true; + } + ReadState(Num) + { + var Data = this.DBState.Read(Num); + if(Data) + Data.WN = "" + return Data; + } + GetMinBlockAct() + { + var DBAct; + var MaxNum = this.DBActPrev.GetMaxNum(); + if(MaxNum === - 1) + DBAct = this.DBAct + else + DBAct = this.DBActPrev + var Item = DBAct.Read(0); + if(!Item) + return - 1; + else + return Item.BlockNum; + } + DeleteAct(BlockNumFrom) + { + if(global.START_SERVER) + throw "DeleteAct START_SERVER"; + if(BlockNumFrom > 0) + { + var StateTX = this.DBStateTX.Read(0); + StateTX.BlockNum = BlockNumFrom - 1 + this.DBStateTX.Write(StateTX) + } + this.DeleteActOneDB(this.DBAct, BlockNumFrom) + this.DeleteActOneDB(this.DBActPrev, BlockNumFrom) + this.DBAccountsHash.Truncate(Math.trunc(BlockNumFrom / PERIOD_ACCOUNT_HASH)) + } + DeleteActOneDB(DBAct, BlockNum) + { + var MaxNum = DBAct.GetMaxNum(); + if(MaxNum === - 1) + return ; + for(var num = MaxNum; num >= 0; num--) + { + var ItemCheck = DBAct.Read(num); + if(!ItemCheck) + { + ToLogTrace("!ItemCheck") + throw "ERRR DeleteActOneDB"; + } + if(ItemCheck.BlockNum < BlockNum) + { + this.ProcessingDeleteAct(DBAct, num + 1) + return ; + } + } + this.ProcessingDeleteAct(DBAct, 0) + } + ProcessingDeleteAct(DBAct, StartNum) + { + var Map = {}; + var bWas = 0; + var NumTruncateState; + for(var num = StartNum; true; num++) + { + var Item = DBAct.Read(num); + if(!Item) + break; + bWas = 1 + if(Map[Item.ID]) + continue; + Map[Item.ID] = 1 + if(Item.Mode === 1) + { + if(!NumTruncateState) + NumTruncateState = Item.ID + } + else + { + var Data = this.DBState.Read(Item.ID); + Data.Value = Item.PrevValue + this._DBStateWrite(Data, Item.BlockNum, 1) + var History = this.DBStateHistory.Read(Item.ID); + if(History) + { + History.NextPos = Item.PrevValue.NextPos + this.DBStateHistory.Write(History) + } + } + } + if(bWas) + { + if(NumTruncateState) + { + this._DBStateTruncate(NumTruncateState - 1) + this.DBStateHistory.Truncate(NumTruncateState - 1) + } + DBAct.Truncate(StartNum - 1) + } + } + FindBlockInAct(DBAct, BlockNum) + { + return DBAct.FastFindBlockNum(BlockNum); + } + GetHole() + { + return [{s:8300, f:186478}]; + } + IsHole(num) + { + if(global.ALL_VIEW_ROWS) + return 0; + var ArrHole = this.GetHole(); + for(var i = 0; i < ArrHole.length; i++) + if(num >= ArrHole[i].s && num <= ArrHole[i].f) + return 1; + return 0; + } + FindAccounts(PubKeyArr, map, nSet) + { + var Count = 0; + for(var num = 0; true; num++) + { + if(this.IsHole(num)) + continue; + var Data = this.ReadState(num); + if(!Data) + break; + for(var i = 0; i < PubKeyArr.length; i++) + if(CompareArr(Data.PubKey, PubKeyArr[i]) === 0) + { + map[Data.Num] = i + Count++ + } + } + return Count; + } + GetWalletAccountsByMap(map) + { + var arr = []; + for(var key in map) + { + var Num = parseInt(key); + var Data = this.ReadState(Num); + if(Data) + { + if(!Data.PubKeyStr) + Data.PubKeyStr = GetHexFromArr(Data.PubKey) + arr.push(Data) + Data.WN = map[key] + Data.Name = NormalizeName(Data.Name) + if(Data.Currency) + Data.CurrencyObj = DApps.Smart.ReadSimple(Data.Currency) + if(Data.Value.Smart) + { + Data.SmartObj = DApps.Smart.ReadSimple(Data.Value.Smart) + if(Data.SmartObj) + Data.SmartState = this.GetSmartState(Data, Data.SmartObj.StateFormat) + else + Data.SmartState = {} + } + } + } + return arr; + } + GetMaxAccount() + { + return this.DBState.GetMaxNum(); + } + GetRowsAccounts(start, count, Filter, bGetState) + { + if(Filter) + { + Filter = Filter.trim() + } + var F; + if(Filter) + { + if(Filter.substring(0, 1) === "=") + { + Filter = Filter.substring(1) + try + { + F = CreateEval(Filter, "Cur,Currency,ID,Operation,Amount,Adviser,Name,PubKey,Smart,BlockNum") + } + catch(e) + { + F = undefined + ToLog("" + e) + } + } + else + { + Filter = Filter.toUpperCase() + } + } + var WasError = 0; + var arr = []; + for(var num = start; true; num++) + { + if(this.IsHole(num)) + continue; + var Data = this.ReadState(num); + if(!Data) + break; + if(!Data.PubKeyStr) + Data.PubKeyStr = GetHexFromArr(Data.PubKey) + Data.Name = NormalizeName(Data.Name) + if(F) + { + var Cur = Data.Currency; + var Currency = Data.Currency; + var ID = Data.Num; + var Operation = Data.Value.OperationID; + var Amount = FLOAT_FROM_COIN(Data.Value); + var Adviser = Data.Adviser; + var Name = Data.Name; + var PubKey = GetHexFromArr(Data.PubKey); + var Smart = Data.Value.Smart; + try + { + if(!F(Cur, Currency, ID, Operation, Amount, Adviser, Name, PubKey, Smart, Data.BlockNumCreate)) + continue; + } + catch(e) + { + if(!WasError) + ToLog("" + e) + WasError = 1 + } + } + else + if(Filter) + { + var Amount = FLOAT_FROM_COIN(Data.Value); + var PubKey = GetHexFromArr(Data.PubKey); + var Str = "" + Data.Num + " " + Data.Value.OperationID + " " + Data.Name.toUpperCase() + " " + Data.Adviser + " " + Amount + " " + PubKey + " " + Smart + " " + Data.BlockNumCreate; + if(Str.indexOf(Filter) < 0) + continue; + } + if(bGetState) + { + if(Data.Currency) + Data.CurrencyObj = DApps.Smart.ReadSimple(Data.Currency) + if(Data.Value.Smart) + { + Data.SmartObj = DApps.Smart.ReadSimple(Data.Value.Smart) + if(Data.SmartObj) + Data.SmartState = this.GetSmartState(Data, Data.SmartObj.StateFormat) + else + Data.SmartState = {} + } + } + arr.push(Data) + count-- + if(count < 1) + break; + } + return arr; + } + GetSmartState(StateData, StateFormat) + { + var SmartState; + try + { + SmartState = BufLib.GetObjectFromBuffer(StateData.Value.Data, StateFormat, {}) + if(typeof SmartState === "object") + SmartState.Num = StateData.Num + } + catch(e) + { + SmartState = {} + } + return SmartState; + } + GetActsMaxNum() + { + return this.DBActPrev.GetMaxNum() + this.DBAct.GetMaxNum(); + } + GetActList(start, count) + { + var arr = []; + var num; + for(num = start; num < start + count; num++) + { + var Item = this.DBActPrev.Read(num); + if(!Item) + break; + Item.Num = "Prev." + Item.Num + if(Item.TrNum === 0xFFFF) + Item.TrNum = "" + arr.push(Item) + if(arr.length > count) + return arr; + } + start = num - this.DBActPrev.GetMaxNum() - 1 + for(num = start; num < start + count; num++) + { + var Item = this.DBAct.Read(num); + if(!Item) + break; + Item.Num = Item.Num + if(Item.TrNum === 0xFFFF) + Item.TrNum = "" + arr.push(Item) + if(arr.length > count) + return arr; + } + return arr; + } + GetHashOrUndefined(BlockNum) + { + if(BlockNum % PERIOD_ACCOUNT_HASH !== 0) + return undefined; + var Item = this.GetAccountHashItem(BlockNum); + if(Item) + return Item.AccHash; + else + return undefined; + } + GetAccountHashItem(BlockNum) + { + var Item = this.DBAccountsHash.Read(Math.trunc(BlockNum / PERIOD_ACCOUNT_HASH)); + return Item; + } + GetHashedMaxBlockNum() + { + var Num = this.DBAccountsHash.GetMaxNum(); + if(Num >= 0) + { + var Data = this.DBAccountsHash.Read(Num); + return Data.BlockNum; + } + else + return 0; + } + CalcHash(Block, BlockMaxAccount) + { + if(Block.BlockNum % PERIOD_ACCOUNT_HASH !== 0) + return ; + if(this.DBState.WasUpdate || IsZeroArr(this.DBState.MerkleHash)) + { + this.CalcMerkleTree() + } + var Hash = this.DBState.MerkleHash; + var SmartHash; + var SmartCount = DApps.Smart.GetMaxNum() + 1; + if(SmartCount > 0) + { + var MaxSmart = DApps.Smart.DBSmart.Read(SmartCount - 1); + SmartHash = MaxSmart.SumHash + } + else + { + SmartHash = [] + } + if(Block.BlockNum > 1000 && IsZeroArr(Hash)) + { + ToErrorTx("BlockNum:" + Block.BlockNum + " AccHash = Zero") + throw "AccHash = Zero"; + } + var Data = {Num:Block.BlockNum / PERIOD_ACCOUNT_HASH, BlockNum:Block.BlockNum, AccHash:Hash, SumHash:Block.SumHash, AccountMax:BlockMaxAccount, + SmartHash:SmartHash, SmartCount:SmartCount}; + this.DBAccountsHash.Write(Data) + this.DBAccountsHash.Truncate(Block.BlockNum / PERIOD_ACCOUNT_HASH) + return Data; + } + CalcMerkleTree(bForce) + { + this.DBState.MerkleHash = this.DBState.CalcMerkleTree(bForce) + this.DBState.WasUpdate = 0 + } + GetAdviserByMiner(Map, Id) + { + var Adviser = Map[Id]; + if(Adviser === undefined) + { + var Item = this.ReadState(Id); + if(Item) + Adviser = Item.Adviser + else + Adviser = 0 + Map[Id] = Adviser + } + return Adviser; + } + BeginBlock() + { + this.DBChanges = {BlockMap:{}, BlockMaxAccount:this.GetMaxAccount(), BlockHistory:[], BlockEvent:[], } + } + BeginTransaction() + { + global.TickCounter = 35000 + this.DBChanges.TRMap = {} + this.DBChanges.TRMaxAccount = this.DBChanges.BlockMaxAccount + this.DBChanges.RollBackTransaction = 0 + this.DBChanges.TRHistory = [] + this.DBChanges.TREvent = [] + } + RollBackTransaction() + { + this.DBChanges.RollBackTransaction = 1 + } + CommitBlock(Block) + { + var BlockNum = Block.BlockNum; + var DBChanges = this.DBChanges; + for(var i = 0; i < DBChanges.BlockHistory.length; i++) + { + var Data = DBChanges.BlockHistory[i]; + var Account = DBChanges.BlockMap[Data.CurID]; + Data.Type = 1 + Data.NextPos = Account.Value.NextPos + Account.Value.NextPos = this.SaveHistory(Data) + } + var arr = []; + for(var key in DBChanges.BlockMap) + { + key = ParseNum(key) + var Data = DBChanges.BlockMap[key]; + if(Data.Changed) + { + arr.push(Data) + } + } + arr.sort(function (a,b) + { + return a.Num - b.Num; + }) + for(var i = 0; i < arr.length; i++) + { + var Account = arr[i]; + var BackLog = {Num:undefined, ID:Account.Num, BlockNum:BlockNum, PrevValue:Account.BackupValue, TrNum:Account.ChangeTrNum, + Mode:Account.New}; + this.DBAct.Write(BackLog) + } + for(var i = 0; i < arr.length; i++) + { + var Account = arr[i]; + this._DBStateWrite(Account, BlockNum, 0) + } + for(var i = 0; i < arr.length; i++) + { + var Account = arr[i]; + var History = {Num:Account.Num, NextPos:Account.Value.NextPos}; + this.DBStateHistory.Write(History) + } + for(var i = 0; i < DBChanges.BlockEvent.length; i++) + { + var Data = DBChanges.BlockEvent[i]; + var Has = global.TreeFindTX.LoadValue("Smart:" + Data.Smart, 1); + if(Has) + { + process.send({cmd:"DappEvent", Data:Data}) + } + } + global.TickCounter = 0 + this.DBChanges = undefined + this.CalcHash(Block, DBChanges.BlockMaxAccount) + var StateTX = this.DBStateTX.Read(0); + StateTX.BlockNum = BlockNum + this.DBStateTX.Write(StateTX) + } + CommitTransaction(BlockNum, TrNum) + { + var DBChanges = this.DBChanges; + if(DBChanges.RollBackTransaction) + return false; + DBChanges.BlockMaxAccount = DBChanges.TRMaxAccount + for(var key in DBChanges.TRMap) + { + key = ParseNum(key) + var Data = DBChanges.TRMap[key]; + if(Data.Changed) + { + DBChanges.BlockMap[key] = Data + if(Data.New) + this.OnWriteNewAccountTR(Data, BlockNum, TrNum) + } + } + for(var i = 0; i < DBChanges.TRHistory.length; i++) + DBChanges.BlockHistory.push(DBChanges.TRHistory[i]) + for(var i = 0; i < DBChanges.TREvent.length; i++) + { + DBChanges.BlockEvent.push(DBChanges.TREvent[i]) + } + global.TickCounter = 0 + return true; + } + OnWriteNewAccountTR(Data, BlockNum, TrNum) + { + if(BlockNum < SMART_BLOCKNUM_START) + Data.Value.Smart = 0 + Data.BlockNumCreate = BlockNum + if(Data.Adviser > this.GetMaxAccount()) + Data.Adviser = 0 + if(Data.Value.Smart > DApps.Smart.GetMaxNum()) + Data.Value.Smart = 0 + if(Data.Currency > DApps.Smart.GetMaxNum()) + Data.Currency = 0 + if(Data.Currency) + { + var Smart = DApps.Smart.ReadSmart(Data.Currency); + if(!Smart || !Smart.TokenGenerate) + Data.Currency = 0 + } + } + NewAccountTR(BlockNum, TrNum) + { + var DBChanges = this.DBChanges; + DBChanges.TRMaxAccount++ + var Data = {Num:DBChanges.TRMaxAccount, New:1, Changed:1, ChangeTrNum:TrNum, BackupValue:{}, PubKey:[], Currency:0, Adviser:0, + Value:{SumCOIN:0, SumCENT:0, OperationID:0, Smart:0, Data:[]}}; + this.DBChanges.TRMap[Data.Num] = Data + return Data; + } + ReadStateTR(Num) + { + Num = ParseNum(Num) + var TRMap = this.DBChanges.TRMap; + var Data = TRMap[Num]; + if(!Data) + { + var Value; + var BlockMap = this.DBChanges.BlockMap; + var BData = BlockMap[Num]; + if(!BData) + { + BData = this.DBState.Read(Num) + if(!BData) + return undefined; + BData.Num = Num + Value = BData.Value + var BHistory = this.DBStateHistory.Read(Num); + if(BHistory) + Value.NextPos = BHistory.NextPos + BData.BackupValue = {SumCOIN:Value.SumCOIN, SumCENT:Value.SumCENT, OperationID:Value.OperationID, Smart:Value.Smart, Data:Value.Data, + NextPos:Value.NextPos} + BlockMap[Num] = BData + } + Value = BData.Value + Data = {Num:Num, Currency:BData.Currency, PubKey:BData.PubKey, Name:BData.Name, BlockNumCreate:BData.BlockNumCreate, Adviser:BData.Adviser, + Value:{SumCOIN:Value.SumCOIN, SumCENT:Value.SumCENT, OperationID:Value.OperationID, Smart:Value.Smart, Data:CopyArr(Value.Data), + NextPos:Value.NextPos}, BackupValue:BData.BackupValue} + TRMap[Num] = Data + } + return Data; + } + WriteStateTR(Data, TrNum) + { + Data.Changed = 1 + Data.ChangeTrNum = TrNum + } + SendMoneyTR(Block, FromID, ToID, CoinSum, BlockNum, TrNum, DescriptionFrom, DescriptionTo, OperationCount) + { + FromID = ParseNum(FromID) + ToID = ParseNum(ToID) + if(CoinSum.SumCENT >= 1e9) + { + throw "ERROR SumCENT>=1e9"; + } + var FromData = this.ReadStateTR(FromID); + if(!FromData) + { + throw "Send: Error account FromNum: " + FromID; + } + if(!SUB(FromData.Value, CoinSum)) + { + throw "Not enough money on the account ID:" + FromID; + } + this.WriteStateTR(FromData, TrNum) + if(FromID > 15) + { + this.DBChanges.TRHistory.push({Direct:"-", Receive:0, CurID:FromID, CorrID:ToID, BlockNum:BlockNum, TrNum:TrNum, FromID:FromID, + ToID:ToID, SumCOIN:CoinSum.SumCOIN, SumCENT:CoinSum.SumCENT, Description:DescriptionFrom, FromOperationID:FromData.Value.OperationID, + Currency:FromData.Currency}) + } + var ToData = this.ReadStateTR(ToID); + if(!ToData) + { + throw "Send: Error account ToNum: " + ToID; + } + ADD(ToData.Value, CoinSum) + this.WriteStateTR(ToData, TrNum) + if(ToID > 15) + { + this.DBChanges.TRHistory.push({Direct:"+", Receive:1, CurID:ToID, CorrID:FromID, BlockNum:BlockNum, TrNum:TrNum, FromID:FromID, + ToID:ToID, SumCOIN:CoinSum.SumCOIN, SumCENT:CoinSum.SumCENT, Description:DescriptionTo, FromOperationID:FromData.Value.OperationID, + Currency:ToData.Currency}) + } + FromData.Value.OperationID += OperationCount + if(FromData.Value.Smart) + { + var Context = {FromID:FromID, ToID:ToID, Description:DescriptionFrom, Value:CoinSum}; + RunSmartMethod(Block, FromData.Value.Smart, FromData, BlockNum, TrNum, Context, "OnSend") + } + if(ToData.Value.Smart) + { + var Context = {FromID:FromID, ToID:ToID, Description:DescriptionTo, Value:CoinSum}; + RunSmartMethod(Block, ToData.Value.Smart, ToData, BlockNum, TrNum, Context, "OnGet") + } + } + GetSignTransferTx(TR, PrivKey) + { + var Arr; + if(TR.Version === 2 || TR.Version === 3) + { + var format; + if(TR.Version === 2) + format = FORMAT_MONEY_TRANSFER_BODY2 + else + format = FORMAT_MONEY_TRANSFER_BODY3 + Arr = [] + for(var i = 0; i < TR.To.length; i++) + { + var Item = TR.To[i]; + var DataTo = DApps.Accounts.ReadState(Item.ID); + if(!DataTo) + return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + for(var j = 0; j < 33; j++) + Arr[Arr.length] = DataTo.PubKey[j] + } + var Body = BufLib.GetBufferFromObject(TR, format, MAX_TRANSACTION_SIZE, {}); + for(var j = 0; j < Body.length; j++) + Arr[Arr.length] = Body[j] + } + else + { + Arr = BufLib.GetBufferFromObject(TR, FORMAT_MONEY_TRANSFER_BODY, MAX_TRANSACTION_SIZE, {}) + } + var sigObj = secp256k1.sign(SHA3BUF(Arr), Buffer.from(PrivKey)); + return sigObj.signature; + } + SaveHistory(Data) + { + var FileItem = HistoryDB.OpenDBFile(FILE_NAME_HISTORY, 1); + var FD = FileItem.fd; + var Position = FileItem.size; + if(!Position) + Position = 100 + var BufWrite = BufLib.GetBufferFromObject(Data, this.HistoryFormatArr[Data.Type], 100, WorkStructHistory); + var written = fs.writeSync(FD, BufWrite, 0, BufWrite.length, Position); + if(written !== BufWrite.length) + { + TO_ERROR_LOG("DB-HISTORY", 10, "Error write to file:" + written + " <> " + BufWrite.length) + throw "Error write to FILE_NAME_HISTORY"; + return false; + } + if(Position >= FileItem.size) + { + FileItem.size = Position + written + } + return Position; + } + GetHistory(Num, Count, StartPos, MinConfirm) + { + if(!MinConfirm) + MinConfirm = 0 + var MaxNumBlockDB = SERVER.GetMaxNumBlockDB(); + var Position = StartPos; + var FileItem = HistoryDB.OpenDBFile(FILE_NAME_HISTORY, 0); + var FD = FileItem.fd; + if(Position === undefined) + { + var Account = this.DBStateHistory.Read(Num); + if(!Account) + { + return []; + } + Position = Account.NextPos + } + var arr = []; + while(Count > 0 && Position) + { + Count-- + var BufRead = BufLib.GetNewBuffer(100); + var bytesRead = fs.readSync(FD, BufRead, 0, BufRead.length, Position); + if(bytesRead < 13) + { + ToLog("bytesRead<13 Position=" + Position) + break; + } + var Type = BufRead[0]; + var format = this.HistoryFormatArr[Type]; + if(!format) + { + ToLog("Error from history, type = " + Type) + break; + } + var Item = BufLib.GetObjectFromBuffer(BufRead, format, WorkStructHistory); + Item.Pos = Position + Position = Item.NextPos + if(MinConfirm) + { + if(Item.BlockNum + MinConfirm > MaxNumBlockDB) + { + continue; + } + } + arr.push(Item) + } + return arr; + } + CalcTotalSum(Currency) + { + var SumCoin = {SumCOIN:0, SumCENT:0}; + for(var num = 0; true; num++) + { + var Data = this.ReadState(num); + if(!Data) + break; + if(Data.Currency === Currency) + { + ADD(SumCoin, Data.Value) + } + } + return FLOAT_FROM_COIN(SumCoin); + } +}; +module.exports = AccountApp; +var App = new AccountApp; +DApps["Accounts"] = App; +DAppByType[TYPE_TRANSACTION_CREATE] = App; +DAppByType[TYPE_DEPRECATED_TRANSFER1] = App; +DAppByType[TYPE_DEPRECATED_TRANSFER2] = App; +DAppByType[TYPE_TRANSACTION_TRANSFER] = App; +DAppByType[TYPE_TRANSACTION_ACC_HASH] = App; + +function TestStateFiles(Size,Format) +{ + return ; + if(global.PROCESS_NAME !== "MAIN") + return ; + var DBState1 = new DBRow("state-ok", Size, Format, 0); + var DBState2 = new DBRow("state-no", Size, Format, 0); + for(var Num = 0; 1; Num++) + { + var Item1 = DBState1.Read(Num); + var Item2 = DBState2.Read(Num); + if(!Item1 && !Item2) + break; + var Str1 = JSON.stringify(Item1); + var Str2 = JSON.stringify(Item2); + if(Str1 !== Str2) + { + ToLog("Err item: " + Num); + ToLog("1: " + Str1); + ToLog("2: " + Str2); + } + } +}; diff --git a/Source/system/dapp.js b/Source/system/dapp.js new file mode 100644 index 0000000..4e825cc --- /dev/null +++ b/Source/system/dapp.js @@ -0,0 +1,78 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const fs = require('fs'); +class DApp +{ + constructor() + { + } + Name() + { + return ""; + } + SendMessage(Body, ToAddr) + { + SERVER.SendMessage(Body, ToAddr) + } + AddTransaction(Tr) + { + SERVER.AddTransaction(Tr) + } + GetScriptTransaction(Body) + { + return ""; + } + GetVerifyTransaction(Block, BlockNum, TrNum, Body) + { + return 1; + } + ClearDataBase() + { + } + GetSenderNum(BlockNum, Body) + { + return 0; + } + OnWriteBlockStart(Block) + { + } + OnWriteBlockFinish(Block) + { + } + OnDeleteBlock(Block) + { + } + OnWriteTransaction(Block, Body, BlockNum, TrNum) + { + } + OnMessage(Msg) + { + } +}; +module.exports = DApp; + +function ReqDir(Path) +{ + if(fs.existsSync(Path)) + { + var arr = fs.readdirSync(Path); + for(var i = 0; i < arr.length; i++) + { + var name = arr[i]; + ToLog("Reg: " + name); + var name2 = Path + "/" + arr[i]; + require(name2); + } + } +}; +global.DApps = {}; +global.DAppByType = {}; diff --git a/Source/system/file.js b/Source/system/file.js new file mode 100644 index 0000000..b2d4d3f --- /dev/null +++ b/Source/system/file.js @@ -0,0 +1,44 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +global.TYPE_TRANSACTION_FILE = 5; +global.FORMAT_FILE_CREATE = "{type:byte,Name:str,ContentType:str,Reserve:arr10,Data:tr}"; +const WorkStructRun = {}; +class FileApp extends require("./dapp") +{ + constructor() + { + super() + } + OnWriteTransaction(Block, Body, BlockNum, TrNum, ContextFrom) + { + return true; + } + GetObjectTransaction(Body) + { + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_FILE_CREATE, WorkStructRun); + return TR; + } + GetScriptTransaction(Body) + { + var TR = this.GetObjectTransaction(Body); + ConvertBufferToStr(TR) + return JSON.stringify(TR, "", 2); + } + GetVerifyTransaction(Block, BlockNum, TrNum, Body) + { + return 1; + } +}; +module.exports = FileApp; +var App = new FileApp; +DApps["File"] = App; +DAppByType[TYPE_TRANSACTION_FILE] = App; diff --git a/Source/system/messager.js b/Source/system/messager.js new file mode 100644 index 0000000..e6c3fd1 --- /dev/null +++ b/Source/system/messager.js @@ -0,0 +1,143 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +require('../core/crypto-library'); +const OPEN_TYPE_TRANSACTION = 11; +const MESSAGE_TYPE_TRANSACTION = 12; +const MAX_MSG_SIZE = 1024; +var TempArrayTr = new Uint8Array(MAX_MSG_SIZE); +const MESSAGE_START = 9; +const MESSAGE_END = MAX_MSG_SIZE - 5; +require("./names"); +class CApp extends require("./dapp") +{ + constructor() + { + super() + this.Channels = {} + this.NamesMap = NAMES.KeyValueMap + global.MESSAGER = this + } + Decrypt(Body) + { + for(var key in this.Channels) + { + var Node = this.Channels[key]; + Decrypt(Body, TempArrayTr, Node.Secret) + if(IsZeroArr(TempArrayTr.slice(MESSAGE_END))) + { + var Str = Utf8ArrayToStr(TempArrayTr.slice(MESSAGE_START)); + return Str; + } + } + return undefined; + } + Encrypt(StrMessage, StrTo) + { + var NameArr; + if(typeof StrTo === "string") + { + NameArr = GetArrFromStr(StrTo, 32) + } + else + { + NameArr = Str + } + var arrMessage = GetArrFromStr(StrMessage, MESSAGE_END); + var ArrayTr = new Uint8Array(MAX_MSG_SIZE); + ArrayTr[0] = MESSAGE_TYPE_TRANSACTION + for(var i = 5; i < MAX_MSG_SIZE; i++) + { + ArrayTr[i] = arrMessage[i - MESSAGE_START] + } + var Body = new Uint8Array(MAX_MSG_SIZE); + var Node = this.OpenChannel(NameArr); + Encrypt(ArrayTr, Body, Node.Secret) + return Body; + } + OpenChannel(FromNameArr) + { + var FromArr; + var StrKeyFrom = GetHexFromAddres(FromNameArr); + if(this.NamesMap[StrKeyFrom]) + { + FromArr = this.NamesMap[StrKeyFrom] + } + else + { + FromArr = FromNameArr + } + var StrKey = GetHexFromAddres(FromArr); + var Node = this.Channels[StrKey]; + if(!Node) + { + Node = {addrArr:FromArr} + CheckContextSecret(this.Server, Node) + this.Channels[StrKey] = Node + } + return Node; + } + OnWriteTransaction(Block, Body, BlockNum, TrNum) + { + var Type = Body[0]; + if(Type === OPEN_TYPE_TRANSACTION) + { + var ToArr = Body.slice(33); + if(CompareArr(ToArr, NAMES.CurrentNameArr) === 0 || CompareArr(ToArr, this.Server.addrArr) === 0) + { + var FromNameArr = Body.slice(1, 33); + this.OpenChannel(FromNameArr) + } + } + else + if(Type === MESSAGE_TYPE_TRANSACTION) + { + var Str = this.Decrypt(Body); + if(Str !== undefined) + ToLog("GET MESSAGE:" + Str) + } + } + OnMessage(Msg) + { + ToLog("GET MESSAGE:" + Msg.body) + return ; + var Body = Msg.body; + var Type = Body[0]; + if(Type === MESSAGE_TYPE_TRANSACTION) + { + var Str = this.Decrypt(Body); + if(Str !== undefined) + { + ToLog("GET MESSAGE:" + Str) + } + } + } +}; + +function TestEncryptDecrypt() +{ + const CMessager = module.exports; + var Server = {KeyPair:GetKeyPairTest("Test"), DApp:{Names:{KeyValueMap:{}}}, }; + var Test = new CMessager(Server); + var KeyPair2 = GetKeyPairTest("Test2"); + var StrTest1 = GetArrFromStr("Test2", 32); + var StrKey = GetHexFromAddres(StrTest1); + MESSAGER.NamesMap[StrKey] = KeyPair2.addrArr; + var Body = MESSAGER.Encrypt("This is a test message!", "Test2"); + var Str2 = MESSAGER.Decrypt(Body); + console.log("Decrypt:"); + console.log(Str2); +}; +module.exports = CApp; +var App = new CApp; +DApps["Messager"] = App; +DAppByType[OPEN_TYPE_TRANSACTION] = App; +DAppByType[MESSAGE_TYPE_TRANSACTION] = App; diff --git a/Source/system/names.js b/Source/system/names.js new file mode 100644 index 0000000..1823e62 --- /dev/null +++ b/Source/system/names.js @@ -0,0 +1,40 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const NAMES_TYPE_TRANSACTION = 10; +class CApp extends require("./dapp") +{ + constructor() + { + super() + this.KeyValueMap = {} + this.CurrentNameArr = "" + global.NAMES = this + } + OnWriteTransaction(Block, Body, BlockNum, TrNum) + { + return ; + if(Body[0] === NAMES_TYPE_TRANSACTION) + { + var StrKey = GetHexFromAddres(Body.slice(1, 33)); + if(!this.KeyValueMap[StrKey]) + { + this.KeyValueMap[StrKey] = Body.slice(33) + if(CompareArr(Body.slice(33), this.Server.addrArr) === 0) + this.CurrentNameArr = Body.slice(1, 33) + } + } + } +}; +module.exports = CApp; +var App = new CApp; +DApps["Names"] = App; +DAppByType[NAMES_TYPE_TRANSACTION] = App; diff --git a/Source/system/smart.js b/Source/system/smart.js new file mode 100644 index 0000000..f789ba3 --- /dev/null +++ b/Source/system/smart.js @@ -0,0 +1,1431 @@ +/* + * @project: TERA + * @version: Development (beta) + * @license: MIT (not for evil) + * @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com] + * Web: https://terafoundation.org + * Twitter: https://twitter.com/terafoundation + * Telegram: https://t.me/terafoundation +*/ + +"use strict"; +const LOC_ADD_NAME = "$"; +require("../HTML/JS/lexer.js"); +global.TickCounter = 0; +const DBRow = require("../core/db/db-row"); +const TYPE_TRANSACTION_SMART_CREATE = 130; +global.TYPE_TRANSACTION_SMART_RUN = 135; +const TYPE_TRANSACTION_SMART_CHANGE = 140; +global.FORMAT_SMART_CREATE = "{\ + Type:byte,\ + TokenGenerate:byte,\ + StartValue:uint,\ + OwnerPubKey:byte,\ + ISIN:str,\ + Zip:byte,\ + AccountLength:byte,\ + StateFormat:str,\ + Category1:byte,\ + Category2:byte,\ + Category3:byte,\ + Reserve:arr20,\ + IconBlockNum:uint,\ + IconTrNum:uint16,\ + ShortName:str5,\ + Name:str,\ + Description:str,\ + Code:str,\ + HTML:str,\ + }"; +const WorkStructCreate = {}; +global.FORMAT_SMART_RUN = "{\ + Type:byte,\ + Account:uint,\ + MethodName:str,\ + Params:str,\ + FromNum:uint,\ + OperationID:uint,\ + Reserve:arr10,\ + Sign:arr64,\ + }"; +const WorkStructRun = {}; +global.FORMAT_SMART_CHANGE = "{\ + Type:byte,\ + Account:uint,\ + Smart:uint32,\ + Reserve:arr10,\ + FromNum:uint,\ + OperationID:uint,\ + Sign:arr64,\ + }"; +const WorkStructChange = {}; +class SmartApp extends require("./dapp") +{ + constructor() + { + super() + var bReadOnly = (global.PROCESS_NAME !== "TX"); + this.FORMAT_ROW = "{\ + Version:byte,\ + TokenGenerate:byte,\ + ISIN:str12,\ + Zip:byte,\ + BlockNum:uint,\ + TrNum:uint16,\ + IconBlockNum:uint,\ + IconTrNum:uint16,\ + ShortName:str5,\ + Name:str40,\ + Account:uint,\ + AccountLength:byte,\ + Category1:byte,\ + Category2:byte,\ + Category3:byte,\ + Owner:uint,\ + Reserve:arr20,\ + StateFormat:str,\ + Description:str,\ + Code:str,\ + HTML:str,\ + SumHash:hash,\ + }" + this.ROW_SIZE = 2 * (1 << 13) + this.DBSmart = new DBRow("smart", this.ROW_SIZE, this.FORMAT_ROW, bReadOnly) + this.InitHole() + if(!bReadOnly) + this.Start() + } + Start() + { + if(this.GetMaxNum() + 1 >= 7) + return ; + this.DBSmartWrite({Num:0, ShortName:"TERA", Name:"TERA", Description:"TERA", BlockNum:0, TokenGenerate:1, Account:0, Category1:0}) + for(var i = 1; i < 8; i++) + this.DBSmartWrite({Num:i, ShortName:"", Name:"", Description:"", BlockNum:0, TokenGenerate:1, Account:i, Category1:0}) + } + Close() + { + this.DBSmart.Close() + } + ClearDataBase() + { + this.DBSmart.Truncate( - 1) + this.Start() + } + GetSenderNum(BlockNum, Body) + { + var Type = Body[0]; + if(Type && Body.length > 90) + { + switch(Type) + { + case TYPE_TRANSACTION_SMART_RUN: + var len = 1 + 6; + len += 2 + Body[len] + Body[len + 1] * 256 + if(len + 64 > Body.length) + return 0; + len += 2 + Body[len] + Body[len + 1] * 256 + if(len + 64 > Body.length) + return 0; + var Num = ReadUintFromArr(Body, len); + return Num; + case TYPE_TRANSACTION_SMART_CHANGE: + var Num = ReadUintFromArr(Body, 1); + return Num; + } + } + return 0; + } + OnDeleteBlock(Block) + { + if(Block.BlockNum < 1) + return ; + this.DBSmart.DeleteHistory(Block.BlockNum) + } + OnWriteBlockStart(Block) + { + if(Block.BlockNum < 1) + return ; + this.OnDeleteBlock(Block) + } + OnWriteBlockFinish(Block) + { + } + OnWriteTransaction(Block, Body, BlockNum, TrNum, ContextFrom) + { + var Type = Body[0]; + if(!ContextFrom) + { + DApps.Accounts.BeginTransaction() + } + var Result; + try + { + switch(Type) + { + case TYPE_TRANSACTION_SMART_CREATE: + Result = this.TRCreateSmart(Block, Body, BlockNum, TrNum, ContextFrom) + break; + case TYPE_TRANSACTION_SMART_RUN: + Result = this.TRRunSmart(Block, Body, BlockNum, TrNum, ContextFrom) + break; + case TYPE_TRANSACTION_SMART_CHANGE: + Result = this.TRChangeSmart(Block, Body, BlockNum, TrNum, ContextFrom) + break; + } + } + catch(e) + { + Result = "" + e + if(global.WATCHDOG_DEV) + ToErrorTx("BlockNum:" + BlockNum + ":" + e) + } + return Result; + } + GetScriptTransaction(Body) + { + var Type = Body[0]; + var format; + if(Type === TYPE_TRANSACTION_SMART_CREATE) + format = FORMAT_SMART_CREATE + else + if(Type === TYPE_TRANSACTION_SMART_RUN) + format = FORMAT_SMART_RUN + else + if(Type === TYPE_TRANSACTION_SMART_CHANGE) + format = FORMAT_SMART_CHANGE + if(!format) + return ""; + var TR = BufLib.GetObjectFromBuffer(Body, format, {}); + ConvertBufferToStr(TR) + return JSON.stringify(TR, "", 2); + } + GetVerifyTransaction(Block, BlockNum, TrNum, Body) + { + return 1; + } + TRCreateSmart(Block, Body, BlockNum, TrNum, ContextFrom) + { + if(!ContextFrom) + return "Pay context required"; + if(Body.length < 31) + return "Error length transaction (min size)"; + if(Body.length > 16000) + return "Error length transaction (max size)"; + if(BlockNum < SMART_BLOCKNUM_START) + return "Error block num"; + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_CREATE, WorkStructCreate); + if(!TR.Name.trim()) + return "Name required"; + if(TR.AccountLength > 50) + return "Error AccountLength=" + TR.AccountLength; + if(TR.AccountLength < 1) + TR.AccountLength = 1 + var AddAccount = TR.AccountLength - 1; + var Price; + if(TR.TokenGenerate) + Price = PRICE_DAO(BlockNum).NewTokenSmart + else + Price = PRICE_DAO(BlockNum).NewSmart + Price += AddAccount * PRICE_DAO(BlockNum).NewAccount + if(!(ContextFrom && ContextFrom.To.length === 1 && ContextFrom.To[0].ID === 0 && ContextFrom.To[0].SumCOIN >= Price)) + { + return "Not money in the transaction"; + } + ContextFrom.ToID = ContextFrom.To[0].ID + var Smart = TR; + Smart.Version = 0 + Smart.Zip = 0 + Smart.BlockNum = BlockNum + Smart.TrNum = TrNum + Smart.Reserve = [] + Smart.Num = undefined + Smart.Owner = ContextFrom.FromID + this.DBSmart.CheckNewNum(Smart) + var Account = DApps.Accounts.NewAccountTR(BlockNum, TrNum); + Account.Value.Smart = Smart.Num + Account.Name = TR.Name + if(Smart.TokenGenerate) + { + Account.Currency = Smart.Num + Account.Value.SumCOIN = TR.StartValue + } + if(TR.OwnerPubKey) + Account.PubKey = ContextFrom.FromPubKey + DApps.Accounts.WriteStateTR(Account, TrNum) + for(var i = 0; i < AddAccount; i++) + { + var CurAccount = DApps.Accounts.NewAccountTR(BlockNum, TrNum); + CurAccount.Value.Smart = Smart.Num + CurAccount.Name = TR.Name + if(Smart.TokenGenerate) + CurAccount.Currency = Smart.Num + if(TR.OwnerPubKey) + CurAccount.PubKey = ContextFrom.FromPubKey + DApps.Accounts.WriteStateTR(CurAccount, TrNum) + } + Smart.Account = Account.Num + this.DBSmart.DeleteMap("EVAL" + Smart.Num) + try + { + RunSmartMethod(Block, Smart, Account, BlockNum, TrNum, ContextFrom, "OnCreate") + } + catch(e) + { + this.DBSmart.DeleteMap("EVAL" + Smart.Num) + return e; + } + this.DBSmartWrite(Smart) + return true; + } + CheckSignFrom(Body, TR, BlockNum, TrNum) + { + var ContextFrom = {FromID:TR.FromNum}; + var AccountFrom = DApps.Accounts.ReadStateTR(TR.FromNum); + if(!AccountFrom) + return "Error account FromNum: " + TR.FromNum; + if(TR.OperationID < AccountFrom.Value.OperationID) + return "Error OperationID (expected: " + AccountFrom.Value.OperationID + " for ID: " + TR.FromNum + ")"; + var MaxCountOperationID = 100; + if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) + MaxCountOperationID = 1000000 + if(TR.OperationID > AccountFrom.Value.OperationID + MaxCountOperationID) + return "Error too much OperationID (expected max: " + (AccountFrom.Value.OperationID + MaxCountOperationID) + " for ID: " + TR.FromNum + ")"; + var hash = SHA3BUF(Body.slice(0, Body.length - 64 - 12), BlockNum); + var Result = 0; + if(AccountFrom.PubKey[0] === 2 || AccountFrom.PubKey[0] === 3) + try + { + Result = secp256k1.verify(hash, TR.Sign, AccountFrom.PubKey) + } + catch(e) + { + } + if(!Result) + { + return "Error sign transaction"; + } + if(BlockNum >= 13000000) + { + AccountFrom.Value.OperationID = TR.OperationID + 1 + DApps.Accounts.WriteStateTR(AccountFrom, TrNum) + } + else + if(AccountFrom.Value.OperationID !== TR.OperationID) + { + AccountFrom.Value.OperationID = TR.OperationID + DApps.Accounts.WriteStateTR(AccountFrom, TrNum) + } + return ContextFrom; + } + TRRunSmart(Block, Body, BlockNum, TrNum, ContextFrom) + { + if(Body.length < 100) + return "Error length transaction (min size)"; + if(BlockNum < SMART_BLOCKNUM_START) + return "Error block num"; + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_RUN, WorkStructRun); + var Account = DApps.Accounts.ReadStateTR(TR.Account); + if(!Account) + return "RunSmart: Error account Num: " + TR.Account; + if(!ContextFrom && TR.FromNum) + { + var ResultCheck = this.CheckSignFrom(Body, TR, BlockNum, TrNum); + if(typeof ResultCheck === "string") + return ResultCheck; + ContextFrom = ResultCheck + } + try + { + var Params = JSON.parse(TR.Params); + RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, TR.MethodName, Params, 1) + } + catch(e) + { + return e; + } + return true; + } + TRChangeSmart(Block, Body, BlockNum, TrNum, ContextFrom) + { + if(Body.length < 21) + return "Error length transaction (min size)"; + if(BlockNum < SMART_BLOCKNUM_START) + return "Error block num"; + var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_CHANGE, WorkStructChange); + if(!ContextFrom) + { + var ResultCheck = this.CheckSignFrom(Body, TR, BlockNum, TrNum); + if(typeof ResultCheck === "string") + return ResultCheck; + ContextFrom = ResultCheck + } + if(TR.Smart > this.GetMaxNum()) + TR.Smart = 0 + if(ContextFrom.FromID !== TR.Account) + return "ChangeSmart: Error account FromNum: " + TR.Account; + var Account = DApps.Accounts.ReadStateTR(TR.Account); + if(!Account) + return "Error read account Num: " + TR.Account; + if(BlockNum >= 13000000) + { + if(Account.Value.Smart === TR.Smart) + return "The value has not changed"; + } + if(Account.Value.Smart) + { + var Smart = this.ReadSmart(Account.Value.Smart); + if(Smart.Account === TR.Account) + return "Can't change base account"; + try + { + RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, "OnDeleteSmart") + } + catch(e) + { + return e; + } + } + Account.Value.Smart = TR.Smart + Account.Value.Data = [] + DApps.Accounts.WriteStateTR(Account, TrNum) + if(Account.Value.Smart) + { + try + { + RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, "OnSetSmart") + } + catch(e) + { + return e; + } + } + return true; + } + GetRows(start, count, Filter, Category, GetAllData, bTokenGenerate) + { + if(Filter) + { + Filter = Filter.trim() + Filter = Filter.toUpperCase() + } + if(Category) + Category = ParseNum(Category) + var WasError = 0; + var arr = []; + var Data; + for(var num = start; true; num++) + { + if(this.IsHole(num)) + continue; + if(GetAllData) + Data = this.ReadSmart(num) + else + Data = this.ReadSimple(num) + if(!Data) + break; + if(bTokenGenerate && !Data.TokenGenerate) + continue; + if(Category) + { + if(Data.Category1 !== Category && Data.Category2 !== Category && Data.Category3 !== Category) + continue; + } + if(Filter) + { + var Str = "" + Data.ShortName.toUpperCase() + Data.ISIN.toUpperCase() + Data.Name.toUpperCase() + Data.Description.toUpperCase(); + if(Data.TokenGenerate) + Str += "TOKEN GENERATE" + if(Str.indexOf(Filter) < 0) + continue; + } + var CanAdd = 1; + var DataState = DApps.Accounts.ReadState(Data.Account); + if(DataState && !global.ALL_VIEW_ROWS) + { + Data.BaseState = DApps.Accounts.GetSmartState(DataState, Data.StateFormat) + if(typeof Data.BaseState === "object" && Data.BaseState.HTMLBlock === 404) + CanAdd = 0 + } + if(CanAdd) + { + arr.push(Data) + } + count-- + if(count < 1) + break; + } + return arr; + } + GetMaxNum() + { + return this.DBSmart.GetMaxNum(); + } + DBSmartWrite(Item) + { + var PrevNum; + if(Item.Num === undefined) + PrevNum = this.GetMaxNum() + else + PrevNum = Item.Num - 1 + Item.SumHash = [] + var Buf = BufLib.GetBufferFromObject(Item, this.FORMAT_ROW, 20000, {}); + var Hash = sha3(Buf); + if(PrevNum < 0) + Item.SumHash = Hash + else + { + var PrevItem = this.DBSmart.Read(PrevNum); + if(!PrevItem) + { + throw "!PrevItem of Smart num = " + PrevNum; + } + Item.SumHash = sha3arr2(PrevItem.SumHash, Hash) + } + this.DBSmart.Write(Item) + } + ReadSmart(Num) + { + Num = ParseNum(Num) + var Smart = this.DBSmart.GetMap("ITEM" + Num); + if(!Smart) + { + Smart = this.DBSmart.Read(Num) + if(Smart) + { + if(!Smart.WorkStruct) + Smart.WorkStruct = {} + Smart.CodeLength = Smart.Code.length + Smart.HTMLLength = Smart.HTML.length + this.DBSmart.SetMap("ITEM" + Num, Smart) + } + } + return Smart; + } + ReadSimple(Num) + { + var Smart = this.DBSmart.GetMap("SIMPLE" + Num); + if(!Smart) + { + Smart = this.DBSmart.Read(Num) + if(Smart) + { + Smart.CodeLength = Smart.Code.length + Smart.HTMLLength = Smart.HTML.length + Smart.Code = undefined + Smart.HTML = undefined + Smart.Description = undefined + this.DBSmart.SetMap("SIMPLE" + Num, Smart) + } + } + return Smart; + } + InitHole() + { + if(global.LOCAL_RUN || global.TEST_NETWORK) + this.RowHole = {} + else + this.RowHole = {"10":1, "19":1, "22":1, "23":1, "24":1, "26":1, "27":1, "29":1, "30":1, "34":1, "56":1, "57":1} + for(var Num = 0; Num < 8; Num++) + this.RowHole[Num] = 1 + } + IsHole(num) + { + if(global.ALL_VIEW_ROWS) + return 0; + return this.RowHole[num]; + } +}; + +function GetParsing(Str) +{ + LexerJS.ParseCode(Str); + var Code = LexerJS.stream; + for(var key in LexerJS.FunctionMap) + { + Code += ";\nfunclist." + key + "=" + LOC_ADD_NAME + key; + } + for(var key in LexerJS.ExternMap) + { + Code += ";\npublist." + key + "=" + LOC_ADD_NAME + key; + } + Code += "\n\ + var context;\ + funclist.SetContext=function(cont){context=cont;};\ + "; + return Code; +}; + +function GetSmartEvalContext(Smart) +{ + var EvalContext = DApps.Smart.DBSmart.GetMap("EVAL" + Smart.Num); + if(0) + if(Smart.Num === 26) + { + const fs = require("fs"); + var Path = "./dapp-smart/test-test.js"; + Smart.Code = fs.readFileSync(Path, {encoding:"utf8"}); + EvalContext = undefined; + } + if(!EvalContext) + { + var CodeLex = GetParsing(Smart.Code); + var publist = {}; + var funclist = {}; + eval(CodeLex); + EvalContext = {publist:publist, funclist:funclist}; + for(var key in funclist) + { + Object.freeze(funclist[key]); + } + Object.freeze(funclist); + Object.freeze(publist); + DApps.Smart.DBSmart.SetMap("EVAL" + Smart.Num, EvalContext); + } + return EvalContext; +}; +var RunContext = undefined; +global.RunSmartMethod = RunSmartMethod; + +function RunSmartMethod(Block,SmartOrSmartID,Account,BlockNum,TrNum,PayContext,MethodName,Params,bPublic) +{ + var Smart = SmartOrSmartID; + if(typeof SmartOrSmartID === "number") + { + Smart = DApps.Smart.ReadSmart(SmartOrSmartID); + if(!Smart) + { + if(bPublic) + throw "Smart does not exist. Error id number: " + SmartOrSmartID; + else + return ; + } + } + var EvalContext = GetSmartEvalContext(Smart); + if(!EvalContext.funclist[MethodName] || (bPublic && !EvalContext.publist[MethodName])) + { + if(bPublic) + throw "Method '" + MethodName + "' not found in smart contract"; + else + return ; + } + var context = {}; + if(PayContext) + { + context.BlockNum = BlockNum; + context.BlockHash = CopyArr(Block.Hash); + context.BlockAddrHash = CopyArr(Block.AddrHash); + context.TrNum = TrNum; + context.Account = GET_ACCOUNT(Account); + context.Smart = GET_SMART(Smart); + context.FromNum = PayContext.FromID; + context.ToNum = PayContext.ToID; + context.Description = PayContext.Description; + if(PayContext.Value) + context.Value = {SumCOIN:PayContext.Value.SumCOIN, SumCENT:PayContext.Value.SumCENT}; + } + if(BlockNum === 0) + { + context.GetBlockHeader = StaticGetBlockHeader; + context.GetBlockNumDB = StaticGetBlockNumDB; + context.GetSmart = StaticGetSmart; + } + var LocalRunContext = {Block:Block, Smart:Smart, Account:Account, BlockNum:BlockNum, TrNum:TrNum, context:context}; + var RetValue; + var _RunContext = RunContext; + RunContext = LocalRunContext; + EvalContext.funclist.SetContext(RunContext.context); + try + { + RetValue = EvalContext.funclist[MethodName](Params); + } + catch(e) + { + throw e; + } + finally + { + RunContext = _RunContext; + } + return RetValue; +}; + +function GET_ACCOUNT(Obj) +{ + let Data = Obj; + var GET_PROP = {get Num() + { + return Data.Num; + }, get Currency() + { + return Data.Currency; + }, get PubKey() + { + return CopyArr(Data.PubKey); + }, get Name() + { + return Data.Name; + }, get BlockNumCreate() + { + return Data.BlockNumCreate; + }, get Adviser() + { + return Data.Adviser; + }, get Smart() + { + return Data.Smart; + }, get Value() + { + return {SumCOIN:Data.Value.SumCOIN, SumCENT:Data.Value.SumCENT, OperationID:Data.Value.OperationID, Smart:Data.Value.Smart}; + }, }; + return GET_PROP; +}; + +function GET_SMART(Obj) +{ + let Data = Obj; + var GET_PROP = {get Num() + { + return Data.Num; + }, get Version() + { + return Data.Version; + }, get TokenGenerate() + { + return Data.TokenGenerate; + }, get ISIN() + { + return Data.ISIN; + }, get Zip() + { + return Data.Zip; + }, get BlockNum() + { + return Data.BlockNum; + }, get TrNum() + { + return Data.TrNum; + }, get IconBlockNum() + { + return Data.IconBlockNum; + }, get IconTrNum() + { + return Data.IconTrNum; + }, get ShortName() + { + return Data.ShortName; + }, get Name() + { + return Data.Name; + }, get Description() + { + return Data.Description; + }, get Account() + { + return Data.Account; + }, get AccountLength() + { + return Data.AccountLength; + }, get Owner() + { + return Data.Owner; + }, get Code() + { + return Data.Code; + }, get HTML() + { + return Data.HTML; + }, }; + return GET_PROP; +}; + +function InitEval() +{ + $Math.abs = function () + { + DO(6); + return Math.abs.apply(Math, arguments); + }; + $Math.acos = function () + { + DO(16); + return Math.acos.apply(Math, arguments); + }; + $Math.acosh = function () + { + DO(9); + return Math.acosh.apply(Math, arguments); + }; + $Math.asin = function () + { + DO(19); + return Math.asin.apply(Math, arguments); + }; + $Math.asinh = function () + { + DO(32); + return Math.asinh.apply(Math, arguments); + }; + $Math.atan = function () + { + DO(13); + return Math.atan.apply(Math, arguments); + }; + $Math.atanh = function () + { + DO(30); + return Math.atanh.apply(Math, arguments); + }; + $Math.atan2 = function () + { + DO(15); + return Math.atan2.apply(Math, arguments); + }; + $Math.ceil = function () + { + DO(6); + return Math.ceil.apply(Math, arguments); + }; + $Math.cbrt = function () + { + DO(22); + return Math.cbrt.apply(Math, arguments); + }; + $Math.expm1 = function () + { + DO(18); + return Math.expm1.apply(Math, arguments); + }; + $Math.clz32 = function () + { + DO(5); + return Math.clz32.apply(Math, arguments); + }; + $Math.cos = function () + { + DO(12); + return Math.cos.apply(Math, arguments); + }; + $Math.cosh = function () + { + DO(20); + return Math.cosh.apply(Math, arguments); + }; + $Math.exp = function () + { + DO(16); + return Math.exp.apply(Math, arguments); + }; + $Math.floor = function () + { + DO(7); + return Math.floor.apply(Math, arguments); + }; + $Math.fround = function () + { + DO(6); + return Math.fround.apply(Math, arguments); + }; + $Math.hypot = function () + { + DO(56); + return Math.hypot.apply(Math, arguments); + }; + $Math.imul = function () + { + DO(3); + return Math.imul.apply(Math, arguments); + }; + $Math.log = function () + { + DO(10); + return Math.log.apply(Math, arguments); + }; + $Math.log1p = function () + { + DO(23); + return Math.log1p.apply(Math, arguments); + }; + $Math.log2 = function () + { + DO(19); + return Math.log2.apply(Math, arguments); + }; + $Math.log10 = function () + { + DO(16); + return Math.log10.apply(Math, arguments); + }; + $Math.max = function () + { + DO(6); + return Math.max.apply(Math, arguments); + }; + $Math.min = function () + { + DO(6); + return Math.min.apply(Math, arguments); + }; + $Math.pow = function () + { + DO(40); + return Math.pow.apply(Math, arguments); + }; + $Math.round = function () + { + DO(7); + return Math.round.apply(Math, arguments); + }; + $Math.sign = function () + { + DO(5); + return Math.sign.apply(Math, arguments); + }; + $Math.sin = function () + { + DO(10); + return Math.sin.apply(Math, arguments); + }; + $Math.sinh = function () + { + DO(24); + return Math.sinh.apply(Math, arguments); + }; + $Math.sqrt = function () + { + DO(6); + return Math.sqrt.apply(Math, arguments); + }; + $Math.tan = function () + { + DO(13); + return Math.tan.apply(Math, arguments); + }; + $Math.tanh = function () + { + DO(24); + return Math.tanh.apply(Math, arguments); + }; + $Math.trunc = function () + { + DO(6); + return Math.trunc.apply(Math, arguments); + }; + $Math.random = function () + { + DO(1); + return 0; + }; + Object.freeze($SetValue); + Object.freeze($Send); + Object.freeze($Move); + Object.freeze($Event); + Object.freeze($ReadAccount); + Object.freeze($ReadState); + Object.freeze($WriteState); + Object.freeze($GetMaxAccount); + Object.freeze($ADD); + Object.freeze($SUB); + Object.freeze($ISZERO); + Object.freeze($FLOAT_FROM_COIN); + Object.freeze($COIN_FROM_FLOAT); + Object.freeze($COIN_FROM_STRING); + Object.freeze($GetHexFromArr); + Object.freeze($GetArrFromHex); + Object.freeze($sha); + Object.freeze($isFinite); + Object.freeze($isNaN); + Object.freeze($parseFloat); + Object.freeze($parseInt); + Object.freeze($parseUint); + Object.freeze($String); + Object.freeze($Number); + Object.freeze($Boolean); + var arr = Object.getOwnPropertyNames(JSON); + for(var name of arr) + { + $JSON[name] = JSON[name]; + } + FreezeObjectChilds($Math); + Object.freeze($Math); + FreezeObjectChilds($JSON); + Object.freeze($JSON); + FreezeObjectChilds(Number.prototype); + FreezeObjectChilds(String.prototype); + FreezeObjectChilds(Boolean.prototype); + FreezeObjectChilds(Array.prototype); + FreezeObjectChilds(Object.prototype); +}; + +function FreezeObjectChilds(Value) +{ + var arr = Object.getOwnPropertyNames(Value); + for(var name of arr) + { + Object.freeze(Value[name]); + } +}; + +function ChangePrototype() +{ + var Array_prototype_concat = Array.prototype.concat; + var Array_prototype_toString = Array.prototype.toString; + Array.prototype.concat = function () + { + if(RunContext) + throw "Error Access denied: concat"; + else + return Array_prototype_concat.apply(this, arguments); + }; + Array.prototype.toString = function () + { + if(RunContext) + throw "Error Access denied: toString"; + else + return Array_prototype_toString.apply(this, arguments); + }; + Array.prototype.toLocaleString = Array.prototype.toString; + Number.prototype.toLocaleString = function () + { + return this.toString(); + }; + String.prototype.toLocaleLowerCase = String.prototype.toLowerCase; + String.prototype.toLocaleUpperCase = String.prototype.toUpperCase; + var String_prototype_localeCompare = String.prototype.localeCompare; + String.prototype.localeCompare = function () + { + if(RunContext) + throw "Error Access denied: localeCompare"; + else + return String_prototype_localeCompare.apply(this, arguments); + }; + var String_prototype_match = String.prototype.match; + String.prototype.match = function () + { + if(RunContext) + throw "Error Access denied: match"; + else + return String_prototype_match.apply(this, arguments); + }; + var String_prototype_repeat = String.prototype.repeat; + String.prototype.repeat = function () + { + if(RunContext) + throw "Error Access denied: repeat"; + else + return String_prototype_repeat.apply(this, arguments); + }; + var String_prototype_search = String.prototype.search; + String.prototype.search = function () + { + if(RunContext) + throw "Error Access denied: search"; + else + return String_prototype_search.apply(this, arguments); + }; + var String_prototype_padStart = String.prototype.padStart; + String.prototype.padStart = function () + { + if(RunContext) + throw "Error Access denied: padStart"; + else + return String_prototype_padStart.apply(this, arguments); + }; + var String_prototype_padEnd = String.prototype.padEnd; + String.prototype.padEnd = function () + { + if(RunContext) + throw "Error Access denied: padEnd"; + else + return String_prototype_padEnd.apply(this, arguments); + }; + String.prototype.right = function (count) + { + if(this.length > count) + return this.substr(this.length - count, count); + else + return this.substr(0, this.length); + }; +}; +const MAX_LENGTH_STRING = 5000; +const $Math = {}; +const $JSON = {}; + +function DO(Count) +{ + global.TickCounter -= Count; + if(global.TickCounter < 0) + throw new Error("Stop the execution code. The limit of ticks is over."); +}; + +function $SetValue(ID,CoinSum) +{ + DO(3000); + ID = ParseNum(ID); + if(!RunContext.Smart.TokenGenerate) + { + throw "The smart-contract is not token generate, access to change values is denied"; + } + var ToData = DApps.Accounts.ReadStateTR(ID); + if(!ToData) + { + throw "Account does not exist.Error id number: " + ID; + } + if(ToData.Currency !== RunContext.Smart.Num) + { + throw "The account currency does not belong to the smart-contract, access to change values is denied"; + } + if(typeof CoinSum === "number") + { + CoinSum = COIN_FROM_FLOAT(CoinSum); + } + if(CoinSum.SumCENT >= 1e9) + { + throw "ERROR SumCENT>=1e9"; + } + if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) + { + throw "ERROR Sum<0"; + } + ToData.Value.SumCOIN = Math.trunc(CoinSum.SumCOIN); + ToData.Value.SumCENT = Math.trunc(CoinSum.SumCENT); + DApps.Accounts.WriteStateTR(ToData, RunContext.TrNum); + return true; +}; + +function $Send(ToID,CoinSum,Description) +{ + DO(3000); + ToID = ParseNum(ToID); + if(typeof CoinSum === "number") + CoinSum = COIN_FROM_FLOAT(CoinSum); + if(CoinSum.SumCENT >= 1e9) + { + throw "ERROR SumCENT>=1e9"; + } + if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) + { + throw "ERROR Sum<0"; + } + var ToData = DApps.Accounts.ReadStateTR(ToID); + if(!ToData) + { + throw "Error ToID - the account number does not exist."; + } + if(RunContext.Account.Currency !== ToData.Currency) + { + throw "Different currencies"; + } + DApps.Accounts.SendMoneyTR(RunContext.Block, RunContext.Account.Num, ToID, CoinSum, RunContext.BlockNum, RunContext.TrNum, + Description, Description, 1); +}; + +function $Move(FromID,ToID,CoinSum,Description) +{ + DO(3000); + FromID = ParseNum(FromID); + ToID = ParseNum(ToID); + var FromData = DApps.Accounts.ReadStateTR(FromID); + if(!FromData) + { + throw "Error FromID - the account number does not exist."; + } + var ToData = DApps.Accounts.ReadStateTR(ToID); + if(!ToData) + { + throw "Error ToID - the account number does not exist."; + } + if(FromData.Currency !== ToData.Currency) + { + throw "Different currencies"; + } + if(FromData.Value.Smart !== RunContext.Smart.Num) + { + throw "The account smart does not belong to the smart-contract, access is denied"; + } + if(typeof CoinSum === "number") + { + CoinSum = COIN_FROM_FLOAT(CoinSum); + } + if(CoinSum.SumCENT >= 1e9) + { + throw "ERROR SumCENT>=1e9"; + } + if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) + { + throw "ERROR Sum<0"; + } + CoinSum.SumCOIN = Math.trunc(CoinSum.SumCOIN); + CoinSum.SumCENT = Math.trunc(CoinSum.SumCENT); + DApps.Accounts.SendMoneyTR(RunContext.Block, FromID, ToID, CoinSum, RunContext.BlockNum, RunContext.TrNum, Description, Description, + 1); +}; + +function $Event(Description) +{ + DO(50); + DApps.Accounts.DBChanges.TREvent.push({Description:Description, Smart:RunContext.Smart.Num, Account:RunContext.Account.Num, + BlockNum:RunContext.BlockNum, TrNum:RunContext.TrNum}); + if(global.DebugEvent) + DebugEvent(Description); + if(global.CurTrItem) + { + ToLogClient(Description, global.CurTrItem, false); + } +}; + +function $ReadAccount(ID) +{ + DO(900); + ID = ParseNum(ID); + var Account = DApps.Accounts.ReadStateTR(ID); + if(!Account) + throw "Error read account Num: " + ID; + return GET_ACCOUNT(Account); +}; + +function $ReadState(ID) +{ + DO(900); + ID = ParseNum(ID); + var Account = DApps.Accounts.ReadStateTR(ID); + if(!Account) + throw "Error read state account Num: " + ID; + var Smart; + if(Account.Value.Smart === RunContext.Smart.Num) + { + Smart = RunContext.Smart; + } + else + { + DO(100); + var Smart = DApps.Smart.ReadSmart(Account.Value.Smart); + if(!Smart) + { + throw "Error smart ID: " + Account.Value.Smart; + } + } + var Data; + if(Smart.StateFormat) + Data = BufLib.GetObjectFromBuffer(Account.Value.Data, Smart.StateFormat, Smart.WorkStruct, 1); + else + Data = {}; + if(typeof Data === "object") + Data.Num = ID; + return Data; +}; + +function $WriteState(Obj,ID) +{ + DO(3000); + if(ID === undefined) + ID = Obj.Num; + ID = ParseNum(ID); + var Account = DApps.Accounts.ReadStateTR(ID); + if(!Account) + throw "Error write account Num: " + ID; + var Smart = RunContext.Smart; + if(Account.Value.Smart !== Smart.Num) + { + throw "The account does not belong to the smart-contract, access to change state is denied"; + } + Account.Value.Data = BufLib.GetBufferFromObject(Obj, Smart.StateFormat, 80, Smart.WorkStruct, 1); + DApps.Accounts.WriteStateTR(Account, RunContext.TrNum); +}; + +function $GetMaxAccount() +{ + DO(20); + return DApps.Accounts.DBChanges.TRMaxAccount; +}; + +function $ADD(Coin,Value2) +{ + DO(5); + return ADD(Coin, Value2); +}; + +function $SUB(Coin,Value2) +{ + DO(5); + return SUB(Coin, Value2); +}; + +function $ISZERO(Coin) +{ + DO(5); + if(Coin.SumCOIN === 0 && Coin.SumCENT === 0) + return true; + else + return false; +}; + +function $FLOAT_FROM_COIN(Coin) +{ + DO(5); + return FLOAT_FROM_COIN(Coin); +}; + +function $COIN_FROM_FLOAT(Sum) +{ + DO(20); + return COIN_FROM_FLOAT(Sum); +}; + +function $COIN_FROM_STRING(Sum) +{ + DO(20); + return COIN_FROM_STRING(Sum); +}; + +function $require(SmartNum) +{ + DO(2000); + SmartNum = ParseNum(SmartNum); + var Smart = DApps.Smart.ReadSmart(SmartNum); + if(!Smart) + { + throw "Smart does not exist. Error id number: " + SmartNum; + } + var EvalContext = GetSmartEvalContext(Smart); + EvalContext.funclist.SetContext(RunContext.context); + return EvalContext.publist; +}; + +function $GetHexFromArr(Arr) +{ + DO(20); + return GetHexFromArr(Arr); +}; + +function $GetArrFromHex(Str) +{ + DO(20); + return GetArrFromHex(Str); +}; + +function $sha(Str) +{ + DO(1000); + return shaarr(Str); +}; + +function $isFinite(a) +{ + DO(5); + return isFinite(a); +}; + +function $isNaN(a) +{ + DO(5); + return isNaN(a); +}; + +function $parseFloat(a) +{ + DO(10); + var Num = parseFloat(a); + if(!Num) + Num = 0; + if(isNaN(Num)) + Num = 0; + return Num; +}; + +function $parseInt(a) +{ + DO(10); + var Num = parseInt(a); + if(!Num) + Num = 0; + if(isNaN(Num)) + Num = 0; + return Num; +}; + +function $parseUint(a) +{ + DO(10); + return ParseNum(a); +}; + +function $String(a) +{ + DO(5); + return String(a); +}; + +function $Number(a) +{ + DO(5); + return Number(a); +}; + +function $Boolean(a) +{ + DO(5); + return Boolean(a); +}; + +function CHKL(Str) +{ + if(typeof Str === "string" && Str.length > MAX_LENGTH_STRING) + throw new Error("Invalid string length:" + Str.length); + return Str; +}; +var BlockRandomInit; +var m_w = 123456789; +var m_z = 987654321; +var mask = 0xffffffff; + +function MathRandom() +{ + DO(5); + +function seed(i) + { + m_w = i; + m_z = 987654321; + }; + +function random() + { + m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; + m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; + var result = ((m_z << 16) + m_w) & mask; + result /= 4294967296; + return result + 0.5; + }; + if(BlockRandomInit === RunContext.Block.BlockNum) + return random(); + BlockRandomInit = RunContext.Block.BlockNum; + RunContext.Block.Hash; + return 0; +}; + +function StaticGetBlockHeader(BlockNum) +{ + DO(100); + return SERVER.ReadBlockHeaderDB(BlockNum); +}; + +function StaticGetBlockNumDB() +{ + return SERVER.GetMaxNumBlockDB(); +}; + +function StaticGetSmart(Num) +{ + DO(100); + var Smart = DApps.Smart.ReadSmart(Num); + return GET_SMART(Smart); +}; +ChangePrototype(); +InitEval(); +module.exports = SmartApp; +var App = new SmartApp; +DApps["Smart"] = App; +DAppByType[TYPE_TRANSACTION_SMART_CREATE] = App; +DAppByType[TYPE_TRANSACTION_SMART_RUN] = App; +DAppByType[TYPE_TRANSACTION_SMART_CHANGE] = App; diff --git a/run-node.bat b/run-node.bat new file mode 100644 index 0000000..e0e91d9 --- /dev/null +++ b/run-node.bat @@ -0,0 +1,7 @@ +cd Source + +:loop +node.exe run-node.js +goto :loop + + diff --git a/run-nw.bat b/run-nw.bat new file mode 100644 index 0000000..96c43ff --- /dev/null +++ b/run-nw.bat @@ -0,0 +1,2 @@ +cd Source +start "" "..\nwjs\nw.exe" . \ No newline at end of file