261 Commits

Author SHA1 Message Date
Ario Sius
95021a636d Merge branch 'arionum:master' into master 2022-08-18 12:37:56 +02:00
arionum
15844fa580 bump version 2022-06-12 18:45:04 +03:00
arionum
2a4c674bec Fix masternode voting removal 2022-06-12 18:43:34 +03:00
Ario Sius
6b8e2e21c0 Merge branch 'arionum:master' into master 2021-08-03 13:40:31 +02:00
admin@arionum.com
7b484b2539 block local peering attempts 2021-07-21 17:54:13 +03:00
admin@arionum.com
657350c603 changed sanity lock mechanism to flock 2021-07-21 15:30:10 +03:00
Ario Sius
7b9df611be Merge remote-tracking branch 'upstream/master' 2021-03-15 13:49:47 +01:00
Ario Sius
e15de7b9f0 remove api.php 2021-03-15 13:09:43 +01:00
arionum
8e11ad314b Update api.php 2021-03-13 19:39:48 +02:00
arionum
62de55deb6 Merge pull request #50 from ariochain/patch-1
Update api.php
2021-03-13 19:38:41 +02:00
Ario Sius
8fc4acad3c Update api.php
update assetBalance to return either results for account or asset
2021-03-12 11:39:28 +01:00
Ario Sius
ea2df3dfa6 Update api.php 2021-03-12 11:35:10 +01:00
Ario Sius
726262a561 Update api.php 2021-03-12 11:31:57 +01:00
Ario Sius
6f4b091068 Change api.php - node 2021-03-12 10:47:58 +01:00
Ario Sius
9e9c3951b3 Change api.php 2021-03-12 10:32:02 +01:00
Ario Sius
bf668f4327 Change api.php 2021-03-12 10:29:06 +01:00
57cf6faf21 Change api.php 2021-03-12 10:17:40 +01:00
Ario Sius
248fb0cc54 Merge pull request #10 from arionum/master
update node
2020-12-29 18:54:13 +01:00
arionum
2f09d9e065 Update sanity.php
sync fixes
2020-11-10 15:30:48 +02:00
arionum
a5356346e6 Update init.inc.php
moving to alpha 6
2020-09-17 21:15:10 +03:00
arionum
0d2bd3b419 Update functions.inc.php 2020-09-17 21:13:17 +03:00
Ario Sius
f966d6e0ff Merge pull request #9 from arionum/master
automatic forking resolution
2020-06-15 11:17:23 +02:00
admin@arionum.com
79a5fd4e28 automatic forking resolution typo 2020-06-13 02:25:18 +03:00
admin@arionum.com
0e4da0cb11 automatic forking resolution 2020-06-13 02:23:37 +03:00
Ario Sius
f7b507cec5 Merge pull request #8 from arionum/master
alpha 5
2020-02-02 13:12:06 +01:00
admin@arionum.com
cd873b537f version 2019-12-26 14:54:26 +02:00
admin@arionum.com
16d205ff06 resync if stale 2019-12-26 14:52:12 +02:00
Ario
e56c9b25ce 1.0.0-alpha.4 2019-11-15 13:52:48 +01:00
admin@arionum.com
8a9715f306 bugfix for mn voting at 320000 2019-11-15 13:36:40 +02:00
Ario
96beabf954 node-info restriction 2019-11-11 20:07:20 +01:00
Ario
afca393e75 node-info restriction 2019-11-11 19:58:34 +01:00
admin@arionum.com
73be045de9 auto resync fix 2019-10-18 07:42:57 +03:00
admin@arionum.com
09d3dd51ba auto resync 2019-10-18 07:38:52 +03:00
Ario
66310e5679 blacklisted 2019-10-09 11:22:37 +02:00
Ario
26243a60f8 mn reward 2019-09-28 16:25:13 +02:00
Ario
82ed34bb92 paused 2019-09-28 12:58:30 +02:00
Ario
ecad33e761 remove bootstrap 2019-09-27 09:36:42 +02:00
Ario
89dfd2d547 remove remover 2019-09-27 02:14:38 +02:00
Ario
c4aa35b692 mkn rewards 2019-09-26 13:05:59 +02:00
Ario
0b9ad9c537 mkn rewards 2019-09-26 13:02:42 +02:00
Ario
3e5481a41a propagate 2019-09-18 22:23:17 +02:00
Ario
1d0378ffef accept block 2019-09-18 18:25:33 +02:00
Ario
1a30d8e915 propagate 2019-09-18 08:17:51 +02:00
Ario Sius
7501d217e9 Merge pull request #6 from arionum/master
typo ==
2019-09-18 08:10:33 +02:00
Ario
c2752a740a Merge branch 'master' of github.com:ariochain/node 2019-09-18 08:08:47 +02:00
Ario
ababb65c1d propagate 2019-09-18 08:08:30 +02:00
admin@arionum.com
0d99c67605 typo == 2019-09-18 06:35:19 +03:00
Ario Sius
8854a29a23 Merge pull request #5 from arionum/master
blacklist
2019-09-18 05:32:18 +02:00
admin@arionum.com
67748de103 default setting recheck blocks 2019-09-18 06:22:11 +03:00
admin@arionum.com
8adab781a1 single new peer per sanity 2019-09-18 05:59:40 +03:00
admin@arionum.com
67e02cb498 peer blacklist fix 2019-09-18 05:23:25 +03:00
Ario
61c0c11160 log 2019-09-17 12:51:12 +02:00
Ario
046d4e7b39 log 2019-09-17 12:49:27 +02:00
Ario
d89cc480d7 log 2019-09-17 12:44:52 +02:00
Ario
1533d78978 log 2019-09-17 12:43:59 +02:00
Ario
468ec7fb1d log 2019-09-17 12:40:51 +02:00
Ario
a9f62b8d6d log 2019-09-17 12:20:37 +02:00
Ario
2d05a8334b log 2019-09-17 12:11:13 +02:00
Ario
ce9af721f7 Merge branch 'master' of github.com:ariochain/node 2019-09-17 12:05:11 +02:00
Ario
b8119df9fe log 2019-09-17 12:04:50 +02:00
Ario Sius
55348cc945 Merge pull request #4 from arionum/master
voting stale height
2019-09-17 11:51:37 +02:00
admin@arionum.com
bb22e45d40 voting stale height 2019-09-17 12:41:52 +03:00
Ario Sius
2ffd1de928 Merge pull request #3 from arionum/master
216070 cold bugfix
2019-09-17 11:35:33 +02:00
admin@arionum.com
a64d349a15 216070 cold bugfix 2019-09-17 12:16:50 +03:00
Ario
7efc49336b log 2019-09-17 11:09:03 +02:00
Ario
f744778b1d log 2019-09-17 09:47:20 +02:00
Ario
ce82b38e84 push 2019-09-17 07:04:22 +02:00
Ario
95687ea257 Merge branch 'arionum-master' 2019-09-17 06:56:32 +02:00
Ario
d2a5c13106 Merge branch 'master' of https://github.com/arionum/node into arionum-master 2019-09-17 06:56:00 +02:00
admin@arionum.com
e36b43246b bootstrapping fix 2019-09-17 03:58:50 +03:00
Ario Sius
8303376372 Update sanity.php
Add support for db host name in bootstraping
2019-09-17 00:36:53 +02:00
Ario Sius
ac4822d507 Merge pull request #1 from arionum/master
sql dump bootstrapping
2019-09-17 00:21:11 +02:00
Ario
008edc848a log 2019-09-16 12:40:12 +02:00
Ario
65c392c084 log 2019-09-16 12:13:58 +02:00
Ario
00b6464c3c log 2019-09-16 12:04:32 +02:00
admin@arionum.com
4697718bdc sql dump bootstrapping 2019-09-16 06:16:35 +03:00
Ario
51a5d9ad3c log 2019-09-15 14:10:29 +02:00
Ario
b1c72ddaf6 log 2019-09-15 02:22:01 +02:00
Ario
a563b8656a log 2019-09-15 02:11:46 +02:00
Ario
59fe17fcc9 log 2019-09-15 01:55:54 +02:00
Ario
a60938ae45 log 2019-09-15 01:36:44 +02:00
Ario
a017b2d0ba nette index 2019-09-15 01:27:56 +02:00
Ario
11fe75c102 add node-index.php 2019-09-15 01:20:20 +02:00
Ario
77be0a2716 stats on 2019-09-15 01:11:56 +02:00
Ario
7cbe87e8cd nette index 2019-09-15 01:08:54 +02:00
Ario
858621bb0c stats off 2019-09-15 00:53:25 +02:00
e81acf2f46 stats off 2019-09-15 00:52:10 +02:00
Ario
2d293c81c9 config 2019-09-15 00:20:35 +02:00
Ario
27a62b0fd2 stats 2019-09-15 00:14:55 +02:00
arionum
55816af407 Update db-update 2019-09-15 00:01:24 +03:00
admin@arionum.com
f86ececf92 typo 2019-09-14 02:13:44 +03:00
arionum
b77018de6f Merge pull request #48 from arionum/assets
Assets and hf
2019-09-14 02:04:34 +03:00
arionum
246c84c91c Merge branch 'master' into assets 2019-09-14 02:04:09 +03:00
admin@arionum.com
c2b32207f4 blocktime votes bugfix 2019-09-13 21:45:40 +03:00
admin@arionum.com
41223f5a87 version update and mn voting ratio 2019-09-13 18:42:31 +03:00
admin@arionum.com
e9768b0d10 tested hf 2019-09-13 03:42:39 +03:00
admin@arionum.com
b3729a9895 tested hf 2019-09-13 03:42:03 +03:00
admin@arionum.com
027b36fce8 untested hf 2019-09-05 03:52:42 +03:00
arionum
c9a1e60b57 Update sanity.php
resync bugfix for masternode release
2019-06-27 00:57:31 +03:00
arionum
05ec0a1b38 Update util.php
bugfix resync
2019-06-27 00:55:36 +03:00
admin@arionum.com
751ca8dd19 Merge branch 'assets' of https://github.com/arionum/node into assets 2019-05-03 01:51:05 +03:00
admin@arionum.com
c5142655a7 assets api 2019-05-03 01:50:06 +03:00
arionum
cab1ed3ea8 Merge pull request #46 from pxgamer/bugfix/transaction-limit-typo
Fix reference to transaction limit in the API
2019-05-03 01:44:01 +03:00
arionum
99e5c412de bid ask logic bug 2019-04-17 04:42:51 +03:00
admin@arionum.com
1baba2ab3a assets system 2019-04-14 22:55:16 +03:00
pxgamer
b3bec0961b Remove limit 1000 reference from ApiDoc comments 2019-04-09 17:11:08 +01:00
pxgamer
5dced856e2 Fix reference to transaction limit in the API 2019-04-09 16:51:13 +01:00
arionum
7e28ce9246 Merge pull request #45 from pxgamer/release/0.4.5
Bump version number to v0.4.5
2019-04-05 19:53:11 +03:00
pxgamer
9791689a10 Bump version number to v0.4.5 2019-03-29 16:47:04 +00:00
arionum
489f048438 typo 2019-03-24 09:44:31 +02:00
arionum
cbe27cf45a local repropagation issue - by hendrik 2019-03-24 09:42:55 +02:00
arionum
bda11255ed Merge pull request #44 from bogdanadnan/master
Add mining rewards to getBlockTransactions
2019-03-24 09:37:01 +02:00
arionum
471d88e3cc Merge pull request #42 from pxgamer/feature/php7.3-travis
Add Travis support for PHP 7.3
2019-03-24 09:35:06 +02:00
arionum
837254e21b Merge pull request #43 from pxgamer/feature/ignore-composer-lock
Add Composer lock to the Gitignore
2019-03-24 09:34:43 +02:00
bogdanadnan
d1af58ccf9 Add mining rewards to getBlockTransactions - review fixes 2019-02-08 12:31:49 +02:00
bogdanadnan
12fa045ba3 Add mining rewards to getBlockTransactions 2019-02-07 21:03:23 +02:00
admin@arionum.com
d03cd9e9f6 version 2018-12-26 01:12:00 +02:00
admin@arionum.com
f700c3a1c7 Merge branch 'master' of https://github.com/arionum/node
hf recovery
2018-12-25 22:44:33 +02:00
admin@arionum.com
f564e7efa1 hf recovery 2018-12-25 22:44:29 +02:00
pxgamer
fd8be6aa1a Add Composer lock to the Gitignore 2018-12-14 22:57:08 +00:00
pxgamer
8cf132e1ba Fix the Travis configuration indentation 2018-12-14 22:03:07 +00:00
pxgamer
df3dc61885 Add PHP 7.3 to Travis test suite 2018-12-14 22:02:45 +00:00
pxgamer
0e902d3e87 Remove Travis containerisation 2018-12-14 22:01:36 +00:00
arionum
55c2ccc12c Merge pull request #41 from pxgamer/feature/temp-ignore
Add Git ignore file to the tmp directory
2018-12-13 21:23:44 +02:00
pxgamer
0d181f5512 Add Git ignore file to the tmp directory
The sanity process generates a lot of temporary files in the tmp
directory. These display as untracked changes in Git and make it a lot
harder to view only actual changes to the repository.
2018-12-07 11:31:52 +00:00
arionum
cf6811d5c0 Merge pull request #37 from pxgamer/bugfix/public-key-transactions
Add missing public_key to getPendingBalance
2018-11-13 12:21:56 +02:00
pxgamer
d3d20f596b Add missing public_key to getPendingBalance 2018-11-07 11:58:07 +00:00
arionum
54c9d063be Merge pull request #36 from pxgamer/bugfix/public-key-transactions
Add public_key variable where it was undefined
2018-11-05 14:40:22 +02:00
pxgamer
be078f910f Add public_key variable where it was undefined
Previously the public_key variable is being referenced but it is never
set in this API call. This allows the correct functionality to occur
when the public_key query parameter is set.
2018-11-05 11:58:44 +00:00
admin@arionum.com
6252d4e3fe api typo 2018-11-05 12:13:45 +02:00
admin@arionum.com
90fb711d2a 10x sanity lock timeout 2018-11-05 11:47:50 +02:00
arionum
271815e731 Merge pull request #35 from pxgamer/feature/sanity-orphan
Update to use the sanity interval for the lock
2018-11-05 11:45:23 +02:00
admin@arionum.com
1a8b491fba checkAddress API 2018-11-05 11:42:41 +02:00
pxgamer
d6235f4121 Set a sanity interval fallback if it isn't set 2018-10-30 09:18:20 +00:00
pxgamer
dacd9420a1 Standardise the sanity path 2018-10-29 17:49:38 +00:00
pxgamer
7e11df5a28 Update to use the sanity interval for the lock 2018-10-29 17:44:15 +00:00
admin@arionum.com
88f50fcae1 passive peering 2018-10-25 21:11:24 +03:00
arionum
3251f98ebf Merge pull request #34 from pxgamer/feature/config-peers
Add initial peers list to configuration
2018-10-25 20:33:27 +03:00
pxgamer
98dba661b1 Change namespace to Arionum\Node 2018-10-20 18:15:14 +01:00
pxgamer
ca9e95e7af Move loading of new files to the init file 2018-10-20 18:04:33 +01:00
pxgamer
5bbbca15a9 Update the sanity process to use InitialPeers 2018-10-19 10:01:07 +01:00
pxgamer
4c79b7f460 Add InitialPeers class for manging initial peers 2018-10-19 09:58:32 +01:00
pxgamer
5906372fc0 Add initial peers from the peer file 2018-10-19 09:49:53 +01:00
pxgamer
30c76cab5c Add Arionum exception class 2018-10-19 09:30:11 +01:00
pxgamer
e70d422da5 Add peers array to the configuration sample 2018-10-18 23:05:08 +01:00
arionum
754017f634 Merge pull request #33 from pxgamer/feature/api-docs
Add missing API documentation
2018-10-13 21:06:36 +03:00
admin@arionum.com
4aeff314bb ignored file 2018-10-13 21:05:41 +03:00
admin@arionum.com
c25ccfe064 json content 2018-10-13 21:03:24 +03:00
pxgamer
5fbb3c3ac2 Update the API documentation output 2018-10-12 12:11:42 +01:00
pxgamer
d6c6d3420e Add apiDoc comments for the node-info endpoint 2018-10-12 12:01:56 +01:00
pxgamer
e389988bda Update the formatting of the node-info API code 2018-10-12 11:58:08 +01:00
pxgamer
c8a8d3b9d0 Add apiDoc comments for the sanity endpoint 2018-10-12 11:55:24 +01:00
pxgamer
3f42a53991 Update the formatting of the sanity API code 2018-10-12 11:38:20 +01:00
pxgamer
6383aade6e Fix doc numbering typo for getAlias 2018-10-12 11:35:57 +01:00
arionum
17acd5830e Merge pull request #27 from pxgamer/feature/travis
Add a configuration file for Travis CI
2018-10-10 17:40:49 +03:00
arionum
f33402382b Merge pull request #32 from pxgamer/feature/json-headers
Add JSON header setting to the api functions
2018-10-10 17:40:30 +03:00
pxgamer
3ddc0dd90e Add JSON header setting to the api functions 2018-10-08 12:39:16 +01:00
arionum
33587258a8 Merge pull request #31 from pxgamer/feature/blacklist-addresses
Add support for blacklisting addresses
2018-09-14 22:16:33 +03:00
pxgamer
49df2cf71f Add Blacklist check for addresses 2018-09-14 15:49:10 +01:00
pxgamer
975e602088 Blacklist the new Octaex wallet address 2018-09-14 15:46:52 +01:00
pxgamer
6483f377b5 Add Blacklist::checkAddress() method 2018-09-14 15:45:51 +01:00
arionum
843d1d0fef Merge pull request #29 from pxgamer/feature/blacklist-refactor
Refactor the blacklist to a class
2018-09-09 15:52:39 +03:00
admin@arionum.com
91f6e1f5d4 tag 2018-09-09 15:50:17 +03:00
pxgamer
33aa9cb6c5 Merge 'master' into feature/blacklist-refactor 2018-09-06 13:54:45 +01:00
admin@arionum.com
29c27c426e typo 2018-09-06 14:57:52 +03:00
pxgamer
2202811745 Update blacklist checks to use the class 2018-09-06 12:07:17 +01:00
pxgamer
30f35944ea Require the Blaclist class in init.inc 2018-09-06 12:02:10 +01:00
pxgamer
7146cfd3c0 Add current blacklisted public keys 2018-09-06 12:00:42 +01:00
pxgamer
024042abab Add Blacklist class 2018-09-06 12:00:08 +01:00
admin@arionum.com
0b73adb3d8 paranoid and blacklist 2018-09-05 16:06:23 +03:00
Arionum
a03484adee accounts-hash 2018-08-30 01:33:09 +03:00
Arionum
99f17a3e7d util tools 2018-08-30 01:28:57 +03:00
Arionum
3549da461d Merge branch 'master' of https://github.com/arionum/node
Conflicts:
	util.php
2018-08-30 01:28:07 +03:00
Arionum
616dde11ea sync fix 2018-08-30 01:24:39 +03:00
pxgamer
31f5ff0bd1 Remove progress from Travis testing 2018-08-29 00:01:29 +01:00
pxgamer
3cad1162ef Fix local configuration to prevent recursing 2018-08-28 23:57:37 +01:00
pxgamer
7a83f1ab10 Add test Travis configuration 2018-08-28 23:55:27 +01:00
arionum
b974dc4c9a Update util.php 2018-08-27 15:18:40 +03:00
arionum
55a1dc750c Merge pull request #25 from KyleFromOhio/master
FIXED: formatting
2018-08-24 22:56:31 +03:00
Kyle Anderson
4b47d0d88e FIXED: formatting 2018-08-24 12:45:34 -07:00
Arionum
6a6005ca4c tuning 2018-08-19 23:02:06 +03:00
Arionum
9dd74285f7 limit peers per sanity 2018-08-18 01:55:18 +03:00
Arionum
788f438114 alias transactions 2018-08-18 00:42:36 +03:00
Arionum
f3e430fee5 sanity rollback 2018-08-15 14:16:59 +03:00
Arionum
faf8cc05f7 test 2018-08-14 05:35:43 +03:00
Arionum
de732ee7f9 db link 2018-08-14 05:21:00 +03:00
Arionum
74190d40ba filter 2018-08-14 05:16:31 +03:00
Arionum
65e801989d resync check 2018-08-14 02:52:01 +03:00
Arionum
d688423d3e block rechecl restore 2018-08-14 02:39:18 +03:00
Arionum
ffc87d69bf mine logic error 2018-08-14 02:33:07 +03:00
Arionum
2a49e6c70a dif fix 2018-08-13 06:45:59 +03:00
Arionum
6c8bc7b97c argon typo 2018-08-13 05:57:59 +03:00
Arionum
7efb4df493 string 2018-08-13 05:33:29 +03:00
Arionum
beff53e938 80458 2018-08-13 05:15:02 +03:00
Arionum
5ca54479a7 hf extend 2018-08-13 04:56:01 +03:00
Arionum
547cac4afb diff split 2018-08-13 03:31:43 +03:00
Arionum
bc9c945646 faster sanity 2018-08-13 03:09:21 +03:00
Arionum
8be4270e16 20block diff 2018-08-13 03:03:07 +03:00
Arionum
971c385042 difficulty 2018-08-13 03:01:20 +03:00
Arionum
984aedc940 version 2018-08-13 02:51:49 +03:00
Arionum
e59bb416b4 80460 hard fork 2018-08-13 02:36:59 +03:00
Arionum
57d2257c43 80460 hf 2018-08-13 02:36:20 +03:00
Arionum
8c32d1c71b 80500 hf mn 2018-08-12 21:39:38 +03:00
Arionum
b5110bf01f protection 2018-08-12 16:47:52 +03:00
Arionum
0567899edd temp fix 2018-08-12 05:44:03 +03:00
Arionum
66b1221e22 blacklist time 83000 2018-08-12 01:47:56 +03:00
Arionum
9e9c12fbbb accounts resync option 2018-08-11 23:00:33 +03:00
Arionum
c5002508d6 internal transaction 2018-08-10 22:19:22 +03:00
Arionum
fbc48921f3 mn fix 2018-08-10 21:49:34 +03:00
Arionum
07dcebd895 limit peers 2018-08-10 18:19:19 +03:00
arionum
6651ff54fc Update db-update 2018-08-09 15:32:09 +03:00
Arionum
38745111ef Merge branch 'master' of https://github.com/arionum/node 2018-08-07 22:36:19 +03:00
Arionum
c83895d384 homepage version 2018-08-07 22:36:04 +03:00
arionum
62cc290b50 Update README.md 2018-08-07 18:19:17 +03:00
Arionum
dab6648ae8 log fix 2018-08-07 18:09:26 +03:00
arionum
5c476be954 Merge pull request #23 from pxgamer/feature/config-formatting
Beautify the sample configuration with sections
2018-08-07 15:57:17 +03:00
pxgamer
0d5f79fc20 Beautify the sample configuration with sections 2018-08-07 11:38:47 +01:00
arionum
5fc167cfbf Update README.md 2018-08-07 13:12:32 +03:00
arionum
5f4b6d9d51 Rename config.inc.php to config-sample.inc.php 2018-08-07 13:11:09 +03:00
Arionum
e707f8daa4 external mansternode miner support 2018-08-07 09:32:57 +03:00
Arionum
a09279d091 upgrade info 2018-08-06 00:23:00 +03:00
Arionum
d9e5ad94de docs 2018-08-06 00:17:28 +03:00
Arionum
8b006fc26a hf alias and mining 2018-08-06 00:13:36 +03:00
Arionum
066211b2c0 transaction indexes 2018-06-28 17:52:19 +03:00
arionum
06960e64e7 Merge pull request #21 from pxgamer/feature/custom-index-page
Create a custom index page
2018-06-28 12:47:36 +03:00
pxgamer
b276fde6da Update the order of CSS properties 2018-06-25 09:00:51 +01:00
pxgamer
7ebf3e0e8a Update the styling of the index 2018-06-22 12:23:21 +01:00
arionum
3a4aaf973c Update sanity.php 2018-06-19 22:09:36 +03:00
arionum
81bd36d2c5 Merge pull request #6 from pxgamer/feature/psr-2
Update to follow PSR-2
2018-06-19 22:05:22 +03:00
Arionum
85cf950fca bugfix send 2018-06-01 18:18:00 +03:00
pxgamer
2f4e95f22d Update files to use class constructor parentheses 2018-05-29 15:50:30 +01:00
pxgamer
c6393d7e8d Update util file to use PSR-2 2018-05-29 15:49:35 +01:00
pxgamer
aae845548f Update sanity file to PSR-2 2018-05-29 15:47:59 +01:00
pxgamer
b68f316029 Update propagate file to PSR-2 2018-05-29 15:47:18 +01:00
pxgamer
ffd3a55144 Update peer file to PSR-2 2018-05-29 15:46:52 +01:00
pxgamer
aa3459181e Update mine file to PSR-2 2018-05-29 15:46:16 +01:00
pxgamer
28807ef9ca Update index to use PSR-2 2018-05-29 15:44:23 +01:00
pxgamer
2fded5d4eb Update api file to PSR-2 2018-05-29 15:43:26 +01:00
pxgamer
f95d94f205 Update Transaction class to use PSR-2 2018-05-29 15:41:58 +01:00
pxgamer
c82e68c9b2 Update schema to PSR-2 2018-05-29 15:40:28 +01:00
pxgamer
bff8025b15 Update init file to PSR-2 2018-05-29 15:39:30 +01:00
pxgamer
636f4013dc Update functions to PSR-2 2018-05-29 15:38:24 +01:00
pxgamer
d8f1f4ea57 Update DB class to PSR-2 2018-05-29 15:37:05 +01:00
pxgamer
44c9eb5ce6 Update Block class to PSR-2 2018-05-29 15:35:37 +01:00
pxgamer
425bdcb504 Update Account class to PSR-2 2018-05-29 15:35:14 +01:00
pxgamer
af98de2e86 Add the include directory for linting 2018-05-29 15:31:48 +01:00
pxgamer
5d5e4d1f3c Add Composer config and PHPCS dependency 2018-05-29 15:31:05 +01:00
Arionum
e1d52c0e31 testnet 2018-05-18 15:59:40 +03:00
Arionum
58a815e068 testnet mining info 2018-05-18 15:50:19 +03:00
arionum
6bf10c408c Merge pull request #5 from pxgamer/feature/readme-restructure
Update the README layout to match the other repos
2018-05-18 15:35:20 +03:00
pxgamer
128241a3d9 Update the README layout to match the other repos 2018-05-17 09:42:48 +01:00
arionum
0a5be52570 Merge pull request #4 from pxgamer/feature/readme-donation-table
Change donations to be in a neater table
2018-05-11 16:23:49 +03:00
pxgamer
4ee780578b Change donations to be in a neater table 2018-05-10 09:05:33 +01:00
Arionum
0e561e5407 random number 2018-05-10 02:06:32 +03:00
Arionum
82ef1e9d10 Merge branch 'master' of https://github.com/arionum/node 2018-05-03 21:56:32 +03:00
Arionum
f2dbc02e93 spelling mistake 2018-05-03 21:56:01 +03:00
arionum
a5a1ff7f7a Merge pull request #3 from nunomendes/patch-1
Update api_data.js
2018-05-03 21:54:25 +03:00
Arionum
1115e02e5d ip fix 2018-05-01 13:13:02 +03:00
Arionum
54aea30232 wildcard hosts 2018-04-26 02:53:41 +03:00
Arionum
3898b7c7e0 new functions 2018-04-26 02:45:09 +03:00
nunomendes
70966df297 Update api_data.js
field name correction on getTransactions api call
2018-04-25 12:06:26 +01:00
31 changed files with 7784 additions and 2701 deletions

3
.gitignore vendored
View File

@@ -1 +1,2 @@
include/config.inc.php
/include/config.inc.php
/composer.lock

22
.travis.yml Normal file
View File

@@ -0,0 +1,22 @@
dist: trusty
language: php
php:
- 7.2
- 7.3
- nightly
matrix:
allow_failures:
- php: nightly
## Cache composer
cache:
directories:
- $HOME/.composer/cache
before_script:
- travis_retry composer update --no-interaction --prefer-dist
script:
- vendor/bin/phpcs -l --standard=psr2 . include

View File

@@ -1,55 +1,54 @@
# node
Arionum (ARO) cryptocurrency node
-----------------------------------------------------
Block 10800 Hard fork - Code name: Resistance
The Arionum (ARO) cryptocurrency node.
In order to increase the fairness of Arionum and stay true to the inital claim of being a cpu coin, we will be changing the mining algorithm starting the block 10800.
## Install
The new mining alghoritm uses:
**Hardware Requirements:**
```
2GB RAM
1 CPU Core
50GB DISK
```
**Requirements:**
threads=1
- PHP 7.2
- PDO extension
- GMP extension
- BCMath extension
- MySQL/MariaDB
memory=524288
time=1
Also, starting block 10800, the maximum transaction fee will be 10 ARO.
-----------------------------------------------------
Requires php 7.2, mysql/mariadb, php-pdo, php-bcmath and php-gmp
This app should only be run in the main directory of the domain/subdomain, ex: http://111.111.111.111/
The node should have a public IP and be accessible over internet.
Installation:
1. Install MySQL or MariaDB and create a database and a user.
2. Edit include/config.inc.php and set the DB login data
3. Change permissions to tmp and tmp/db-update to 777 (chmod 777 tmp -R)
2. Rename `include/config-sample.inc.php` to `include/config.inc.php` and set the DB login data
3. Change permissions to tmp and `tmp/db-update` to 777 (`chmod 777 tmp -R`)
4. Access the http://ip-or-domain and refresh once
Official website: https://www.arionum.com
## Usage
Block explorer: https://arionum.info
This app should only be run in the main directory of the domain/subdomain, ex: http://111.111.111.111
Forums: https://forum.arionum.com
The node should have a public IP and be accessible over internet.
## Links
Development Fund
- Official website: https://www.arionum.com
- Block explorer: https://arionum.info
- Forums: https://forum.arionum.com
ARO: 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
## Development Fund
LTC: LWgqzbXGeucKaMmJEvwaAWPFrAgKiJ4Y4m
BTC: 1LdoMmYitb4C3pXoGNLL1VRj7xk3smGXoU
ETH: 0x4B904bDf071E9b98441d25316c824D7b7E447527
BCH: qrtkqrl3mxzdzl66nchkgdv73uu3rf7jdy7el2vduw
Coin | Address
---- | --------
[ARO]: | 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
[LTC]: | LWgqzbXGeucKaMmJEvwaAWPFrAgKiJ4Y4m
[BTC]: | 1LdoMmYitb4C3pXoGNLL1VRj7xk3smGXoU
[ETH]: | 0x4B904bDf071E9b98441d25316c824D7b7E447527
[BCH]: | qrtkqrl3mxzdzl66nchkgdv73uu3rf7jdy7el2vduw
If you'd like to support the Arionum development, you can donate to the addresses listed above.
[aro]: https://arionum.com
[ltc]: https://litecoin.org
[btc]: https://bitcoin.org
[eth]: https://ethereum.org
[bch]: https://www.bitcoincash.org

6
UPGRADE Normal file
View File

@@ -0,0 +1,6 @@
In order to upgrade your Arionum Node, follow the next steps:
1. Download the source code from https://github.com/arionum/node
2. Edit the include/config.inc.php with the DB details
3. Replace your old node folder with the new one.
4. chmod 777 tmp -R
5. Load the node's address in your browser.

1130
api.php

File diff suppressed because it is too large Load Diff

19
composer.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "arionum/node",
"description": "The Arionum (ARO) cryptocurrency node.",
"license": "MIT",
"require": {
"php": "^7.2",
"ext-bcmath": "*",
"ext-gmp": "*",
"ext-openssl": "*",
"ext-pdo": "*"
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.2"
},
"scripts": {
"check-style": "phpcs -p -l --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 . include",
"fix-style": "phpcbf -p -l --standard=PSR2 --runtime-set ignore_errors_on_exit 1 --runtime-set ignore_warnings_on_exit 1 . include"
}
}

View File

@@ -101,6 +101,101 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=checkAddress",
"title": "22. checkAddress",
"name": "checkAddress",
"group": "API",
"description": "<p>Checks the validity of an address.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "account",
"description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>True if the address is valid, false otherwise.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=checkSignature",
"title": "17. checkSignature",
"name": "checkSignature",
"group": "API",
"description": "<p>Checks a signature against a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "signature",
"description": "<p>signature</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "data",
"description": "<p>signed data</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>true or false</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=currentBlock",
@@ -249,6 +344,50 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=getAlias",
"title": "19. getAlias",
"name": "getAlias",
"group": "API",
"description": "<p>Returns the alias of an account</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data",
"description": "<p>alias</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=getBalance",
@@ -272,6 +411,13 @@ define({ "api": [
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "alias",
"description": "<p>alias</p>"
}
]
}
@@ -402,6 +548,13 @@ define({ "api": [
"optional": true,
"field": "block",
"description": "<p>Block id</p>"
},
{
"group": "Parameter",
"type": "boolean",
"optional": true,
"field": "includeMiningRewards",
"description": "<p>Include mining rewards</p>"
}
]
}
@@ -420,7 +573,7 @@ define({ "api": [
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "confirmation",
"field": "confirmations",
"description": "<p>Number of confirmations</p>"
},
{
@@ -752,7 +905,7 @@ define({ "api": [
"type": "numeric",
"optional": true,
"field": "limit",
"description": "<p>Number of confirmed transactions, max 1000, min 1</p>"
"description": "<p>Number of confirmed transactions, max 100, min 1</p>"
}
]
}
@@ -865,6 +1018,30 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=masternodes",
"title": "18. masternodes",
"name": "masternodes",
"group": "API",
"description": "<p>Returns all the masternode data</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>masternode date</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=mempoolSize",
@@ -889,6 +1066,189 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=node-info",
"title": "21. node-info",
"name": "node_info",
"group": "API",
"description": "<p>Returns details about the node.</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>A collection of data about the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.hostname",
"description": "<p>The hostname of the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.version",
"description": "<p>The current version of the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.dbversion",
"description": "<p>The database schema version for the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.accounts",
"description": "<p>The number of accounts known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.transactions",
"description": "<p>The number of transactions known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.mempool",
"description": "<p>The number of transactions in the mempool.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.masternodes",
"description": "<p>The number of masternodes known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.peers",
"description": "<p>The number of valid peers.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=randomNumber",
"title": "16. randomNumber",
"name": "randomNumber",
"group": "API",
"description": "<p>Returns a random number based on an ARO block id.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "height",
"description": "<p>The height of the block on which the random number will be based on (should be a future block when starting)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "min",
"description": "<p>Minimum number (default 1)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "max",
"description": "<p>Maximum number</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "seed",
"description": "<p>A seed to generate different numbers for each use cases.</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "data",
"description": "<p>The random number</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=sanity",
"title": "20. sanity",
"name": "sanity",
"group": "API",
"description": "<p>Returns details about the node's sanity process.</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>A collection of data about the sanity process.</p>"
},
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data.sanity_running",
"description": "<p>Whether the sanity process is currently running.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.last_sanity",
"description": "<p>The timestamp for the last time the sanity process was run.</p>"
},
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data.sanity_sync",
"description": "<p>Whether the sanity process is currently synchronising.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=send",
@@ -1211,6 +1571,24 @@ define({ "api": [
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "clean-blacklist",
"title": "Clean-Blacklist",
"name": "clean_blacklist",
"group": "UTIL",
"description": "<p>Removes all the peers from blacklist</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean-blacklist",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "current",

View File

@@ -101,6 +101,101 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=checkAddress",
"title": "22. checkAddress",
"name": "checkAddress",
"group": "API",
"description": "<p>Checks the validity of an address.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "account",
"description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>True if the address is valid, false otherwise.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=checkSignature",
"title": "17. checkSignature",
"name": "checkSignature",
"group": "API",
"description": "<p>Checks a signature against a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "signature",
"description": "<p>signature</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "data",
"description": "<p>signed data</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>true or false</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=currentBlock",
@@ -249,6 +344,50 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=getAlias",
"title": "19. getAlias",
"name": "getAlias",
"group": "API",
"description": "<p>Returns the alias of an account</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data",
"description": "<p>alias</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=getBalance",
@@ -272,6 +411,13 @@
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "alias",
"description": "<p>alias</p>"
}
]
}
@@ -402,6 +548,13 @@
"optional": true,
"field": "block",
"description": "<p>Block id</p>"
},
{
"group": "Parameter",
"type": "boolean",
"optional": true,
"field": "includeMiningRewards",
"description": "<p>Include mining rewards</p>"
}
]
}
@@ -420,7 +573,7 @@
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "confirmation",
"field": "confirmations",
"description": "<p>Number of confirmations</p>"
},
{
@@ -752,7 +905,7 @@
"type": "numeric",
"optional": true,
"field": "limit",
"description": "<p>Number of confirmed transactions, max 1000, min 1</p>"
"description": "<p>Number of confirmed transactions, max 100, min 1</p>"
}
]
}
@@ -865,6 +1018,30 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=masternodes",
"title": "18. masternodes",
"name": "masternodes",
"group": "API",
"description": "<p>Returns all the masternode data</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>masternode date</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=mempoolSize",
@@ -889,6 +1066,189 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=node-info",
"title": "21. node-info",
"name": "node_info",
"group": "API",
"description": "<p>Returns details about the node.</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>A collection of data about the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.hostname",
"description": "<p>The hostname of the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.version",
"description": "<p>The current version of the node.</p>"
},
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data.dbversion",
"description": "<p>The database schema version for the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.accounts",
"description": "<p>The number of accounts known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.transactions",
"description": "<p>The number of transactions known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.mempool",
"description": "<p>The number of transactions in the mempool.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.masternodes",
"description": "<p>The number of masternodes known by the node.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.peers",
"description": "<p>The number of valid peers.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=randomNumber",
"title": "16. randomNumber",
"name": "randomNumber",
"group": "API",
"description": "<p>Returns a random number based on an ARO block id.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "height",
"description": "<p>The height of the block on which the random number will be based on (should be a future block when starting)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "min",
"description": "<p>Minimum number (default 1)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "max",
"description": "<p>Maximum number</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "seed",
"description": "<p>A seed to generate different numbers for each use cases.</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "data",
"description": "<p>The random number</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=sanity",
"title": "20. sanity",
"name": "sanity",
"group": "API",
"description": "<p>Returns details about the node's sanity process.</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "object",
"optional": false,
"field": "data",
"description": "<p>A collection of data about the sanity process.</p>"
},
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data.sanity_running",
"description": "<p>Whether the sanity process is currently running.</p>"
},
{
"group": "Success 200",
"type": "number",
"optional": false,
"field": "data.last_sanity",
"description": "<p>The timestamp for the last time the sanity process was run.</p>"
},
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data.sanity_sync",
"description": "<p>Whether the sanity process is currently synchronising.</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "get",
"url": "/api.php?q=send",
@@ -1211,6 +1571,24 @@
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "clean-blacklist",
"title": "Clean-Blacklist",
"name": "clean_blacklist",
"group": "UTIL",
"description": "<p>Removes all the peers from blacklist</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean-blacklist",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "current",

View File

@@ -7,7 +7,7 @@ define({
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-02-22T16:13:41.713Z",
"time": "2018-11-05T10:13:13.657Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@@ -7,7 +7,7 @@
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-02-22T16:13:41.713Z",
"time": "2018-11-05T10:13:13.657Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

50
include/Blacklist.php Normal file
View File

@@ -0,0 +1,50 @@
<?php
namespace Arionum;
/**
* Class Blacklist
*/
final class Blacklist
{
/**
* The official list of blacklisted public keys
*/
public const PUBLIC_KEYS = [
// phpcs:disable Generic.Files.LineLength
'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCvVQcHHCNLfiP9LmzWhhpCHx39Bhc67P5HMQM9cctEFvcsUdgrkGqy18taz9ZMrAGtq7NhBYpQ4ZTHkKYiZDaSUqQ' => 'Faucet Abuser',
'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCxYDeQHk7Ke66UB2Un3UMmMoJ7RF5vDZXihdEXi8gk8ZBRAi35aFrER2ZLX1mgND7sLFXKETGTjRYjoHcuRNiJN1g' => 'Octaex Exchange',
// phpcs:enable
];
/**
* The official list of blacklisted addresses
*/
public const ADDRESSES = [
// phpcs:disable Generic.Files.LineLength
'xuzyMbEGA1tmx1o7mcxSXf2nXuuV1GtKbA4sAqjcNq2gh3shuhwBT5nJHez9AynCaxpJwL6dpkavmZBA3JkrMkg' => 'Octaex Exchange',
// phpcs:enable
];
/**
* Check if a public key is blacklisted
*
* @param string $publicKey
* @return bool
*/
public static function checkPublicKey(string $publicKey): bool
{
return key_exists($publicKey, static::PUBLIC_KEYS);
}
/**
* Check if an address is blacklisted
*
* @param string $address
* @return bool
*/
public static function checkAddress(string $address): bool
{
return key_exists($address, static::ADDRESSES);
}
}

11
include/Exception.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
namespace Arionum\Node;
/**
* Class Exception
* A custom exception for Arionum error handling.
*/
class Exception extends \Exception
{
}

81
include/InitialPeers.php Normal file
View File

@@ -0,0 +1,81 @@
<?php
namespace Arionum\Node;
/**
* Class InitialPeers
*/
final class InitialPeers
{
public const MINIMUM_PEERS_REQUIRED = 2;
public const PRELOAD_ERROR = 'Unable to retrieve peers from the preload list.';
public const PRELOAD_LIST = 'https://www.arionum.com/peers.txt';
/**
* @var array
*/
private $peerList = [];
/**
* InitialPeers constructor.
* @param array|null $peerList
* @return void
*/
public function __construct(?array $peerList = [])
{
$this->peerList = $peerList;
}
/**
* Retrieve a peer from the initial peer list.
* @return string
* @throws Exception
*/
public function get(): string
{
if (!$this->peerList || count($this->peerList) < self::MINIMUM_PEERS_REQUIRED) {
$this->retrieveFromPreloadList();
}
return $this->selectPeer();
}
/**
* Retrieve all available initial peers.
* @return array
* @throws Exception
*/
public function getAll(): array
{
if (!$this->peerList || count($this->peerList) < self::MINIMUM_PEERS_REQUIRED) {
$this->retrieveFromPreloadList();
}
return $this->peerList;
}
/**
* @return string
*/
private function selectPeer(): string
{
return $this->peerList[array_rand($this->peerList)];
}
/**
* Retrieve a peer from
*
* @return void
* @throws Exception
*/
private function retrieveFromPreloadList(): void
{
$peerList = file(self::PRELOAD_LIST, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
if (!$peerList || count($peerList) < self::MINIMUM_PEERS_REQUIRED) {
throw new Exception(self::PRELOAD_ERROR);
}
$this->peerList = $peerList;
}
}

View File

@@ -1,162 +1,350 @@
<?php
class Account {
// inserts the account in the DB and updates the public key if empty
public function add($public_key, $block){
global $db;
$id=$this->get_address($public_key);
$bind=array(":id"=>$id, ":public_key"=>$public_key, ":block"=>$block,":public_key2"=>$public_key );
$db->run("INSERT INTO accounts SET id=:id, public_key=:public_key, block=:block, balance=0 ON DUPLICATE KEY UPDATE public_key=if(public_key='',:public_key2,public_key)",$bind);
}
// inserts just the account without public key
public function add_id($id, $block){
global $db;
$bind=array(":id"=>$id, ":block"=>$block);
$db->run("INSERT ignore INTO accounts SET id=:id, public_key='', block=:block, balance=0",$bind);
}
// generates Account's address from the public key
public function get_address($hash){
class Account
{
// inserts the account in the DB and updates the public key if empty
public function add($public_key, $block)
{
global $db;
$id = $this->get_address($public_key);
$bind = [":id" => $id, ":public_key" => $public_key, ":block" => $block, ":public_key2" => $public_key];
//broken base58 addresses, which are block winners, missing the first 0 bytes from the address.
if($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwCpspGFGQSaF9yVGLamBgymdf8M7FafghmP3oPzQb3W4PZsZApVa41uQrrHRVBH5p9bdoz7c6XeRQHK2TkzWR45e') return '22SoB29oyq2JhMxtBbesL7JioEYytyC6VeFmzvBH6fRQrueSvyZfEXR5oR7ajSQ9mLERn6JKU85EAbVDNChke32';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzbRyyz5oDNDKhk5jyjg4caRjkbqegMZMrUkuBjVMuYcVfPyc3aKuLmPHS4QEDjCrNGks7Z5oPxwv4yXSv7WJnkbL') return 'AoFnv3SLujrJSa2J7FDTADGD7Eb9kv3KtNAp7YVYQEUPcLE6cC6nLvvhVqcVnRLYF5BFF38C1DyunUtmfJBhyU';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyradtFFJoaYB4QdcXyBGSXjiASMMnofsT4f5ZNaxTnNDJt91ubemn3LzgKrfQh8CBpqaphkVNoRLub2ctdMnrzG1') return 'RncXQuc7S7aWkvTUJSHEFvYoV3ntAf7bfxEHjSiZNBvQV37MzZtg44L7GAV7szZ3uV8qWqikBewa3piZMqzBqm';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyjKMBY4ihhJ2G25EVezg7KnoCBVbhdvWfqzNA4LC5R7wgu3VNfJgvqkCq9sKKZcCoCpX6Qr9cN882MoXsfGTvZoj') return 'Rq53oLzpCrb4BdJZ1jqQ2zsixV2ukxVdM4H9uvUhCGJCz1q2wagvuXV4hC6UVwK7HqAt1FenukzhVXgzyG1y32';
$db->run(
"INSERT INTO accounts SET id=:id, public_key=:public_key, block=:block, balance=0 ON DUPLICATE KEY UPDATE public_key=if(public_key='',:public_key2,public_key)",
$bind
);
}
// hashes 9 times in sha512 (binary) and encodes in base58
for($i=0;$i<9;$i++) $hash=hash('sha512',$hash, true);
return base58_encode($hash);
// inserts just the account without public key
public function add_id($id, $block)
{
global $db;
$bind = [":id" => $id, ":block" => $block];
$db->run("INSERT ignore INTO accounts SET id=:id, public_key='', block=:block, balance=0", $bind);
}
}
// checks the ecdsa secp256k1 signature for a specific public key
public function check_signature($data, $signature, $public_key){
return ec_verify($data ,$signature, $public_key);
}
// generates a new account and a public/private key pair
public function generate_account(){
// using secp256k1 curve for ECDSA
$args = array(
"curve_name" => "secp256k1",
"private_key_type" => OPENSSL_KEYTYPE_EC,
);
// generates a new key pair
$key1 = openssl_pkey_new($args);
// exports the private key encoded as PEM
openssl_pkey_export($key1, $pvkey);
// converts the PEM to a base58 format
$private_key= pem2coin($pvkey);
// exports the private key encoded as PEM
$pub = openssl_pkey_get_details($key1);
// converts the PEM to a base58 format
$public_key= pem2coin($pub['key']);
// generates the account's address based on the public key
$address=$this->get_address($public_key);
return array("address"=>$address, "public_key"=>$public_key,"private_key"=>$private_key);
// generates Account's address from the public key
public function get_address($hash)
{
//broken base58 addresses, which are block winners, missing the first 0 bytes from the address.
if ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwCpspGFGQSaF9yVGLamBgymdf8M7FafghmP3oPzQb3W4PZsZApVa41uQrrHRVBH5p9bdoz7c6XeRQHK2TkzWR45e') {
return '22SoB29oyq2JhMxtBbesL7JioEYytyC6VeFmzvBH6fRQrueSvyZfEXR5oR7ajSQ9mLERn6JKU85EAbVDNChke32';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzbRyyz5oDNDKhk5jyjg4caRjkbqegMZMrUkuBjVMuYcVfPyc3aKuLmPHS4QEDjCrNGks7Z5oPxwv4yXSv7WJnkbL') {
return 'AoFnv3SLujrJSa2J7FDTADGD7Eb9kv3KtNAp7YVYQEUPcLE6cC6nLvvhVqcVnRLYF5BFF38C1DyunUtmfJBhyU';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyradtFFJoaYB4QdcXyBGSXjiASMMnofsT4f5ZNaxTnNDJt91ubemn3LzgKrfQh8CBpqaphkVNoRLub2ctdMnrzG1') {
return 'RncXQuc7S7aWkvTUJSHEFvYoV3ntAf7bfxEHjSiZNBvQV37MzZtg44L7GAV7szZ3uV8qWqikBewa3piZMqzBqm';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyjKMBY4ihhJ2G25EVezg7KnoCBVbhdvWfqzNA4LC5R7wgu3VNfJgvqkCq9sKKZcCoCpX6Qr9cN882MoXsfGTvZoj') {
return 'Rq53oLzpCrb4BdJZ1jqQ2zsixV2ukxVdM4H9uvUhCGJCz1q2wagvuXV4hC6UVwK7HqAt1FenukzhVXgzyG1y32';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzZtEj6zAW8WVB6AbDLndbQZrnH2R5Nmpk1sLyHXzqyp4P5cyJAbnUpR5UdG8sBCCuZekWSBHgWNMaGS317vPsVuG'){
// mixed keys badly generated address
return '3CWXXqpzuda85MaPpgYRee8d7a44wzemqztfFfeZDyEysQ15cN6gZNsPT32MHwjrzbENDvkqKtADoCBgVVqXWP2g';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwYhtqidHRVigBiQiun5csb9YnZzcvmSt7aCVS6nH2gYykLr9pQfJHP8bTtYTMkU1WLdmeTkNPGDujYWKjPSGU8XX'){
// broken wallet due to webwallet bug
return '4JstC5anTNMpY2zmUHt2LDmQXsMQvkh7d9qHBjBhRahAsWVTyyS9RPYMRdmcqdVPSDUQsXJfGyPFMn6y6R9M5QQ7';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwoUzVYoaDZGausEDEg5JMtiRQvdmuuv1FKvCxUp6m8iMJhKbohtH75wXPBgi4wYPMSUw4himHeJ3qnDpAQnKSsJU'){
// stolen coins hf recovery
return '4VLRngC3U24YdusKQ4rGfCX4DDfBUcNemH419wUS5xe2uUf3ku1CXq6RCfGoiZvDK61upmLfrb64YWubyh5eUG4Y';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCvgAVPik2t9yfM63rDCy13opmvejvMXadRb6qKjEoaiFkGCJHKBUSzggaEtgyDVkRk7ajREQjRrN6J4EcoXTzGM3p'){
// stolen coins hf recovery
return 'SBWPS7Yu4X4ZQFY9n27bkBL5AnDRveUBbfbZWwhEN1tWZnTCEc8kvx7ddfoaqhjw7tw8rQULk2fEgSB1r2vWqKp';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCz1Adkpo6MRTogSUKLtEh5NKo1Y8sumnNKqhRW8w2bZtVfMT4sfbStg8ycqtmQNDztvUE39MVHnXmCDtpmv9KKdQN'){
// stolen coins hf recovery
return 'reMoRxfiUxtoj7RD1aDH5yNfeuXEyTj2XiHaUBdVUhk8Wt5f4VLWrqge6J5yE9BGkVLFPqfS6ZYvj4ocWhbZhru';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCxXrhv7wkhX4R2YjyRNfssf15wTksz5Ev4FFGfCmhcUL7kt5aXRvh6xknQf5HHzDo4GsG523wBYcFhiAkmBL1kkUo'){
// stolen coins hf recovery
return '3goaif939N4xy5ThT7iq2GhBgwjrS4buhhTtTQkXrzarKcsYvyg5PU8KVgzrSfgFfhnZGNx9WseaR2JSUpYn1Vch';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzhTJ5G37ijQcgdzUsMMVyAPFaxLCyssQjqYF1g1Zsr3XkzYux3Dt75y3DRmXmx6wiNTf7uKxAGFaQA7qao7TXp9j'){
// stolen coins hf recovery
return 'LtjGjTTurwLNZLbexAbusCRS5SNYhFydMuFdPTaYFRD3WoL2q67tidsrd7qnX8czmTBhrDyrdheP5gCwCbAHBBd';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwrgAsdSZtv9e4Ldw2mzg7HwQMHzG1FFVzoRtENwDRZxhRTwSGYU4oUXVLzNWNqpRZ6iEZXenxUANScwr7yDET7xA'){
// stolen coins hf recovery
return 'aSpfMMbxA8U1rMqBgYoQtXhUyAKyhBJHY17CEW4V3ttgRsvpuZ1Dg3xYc1rcMeKnP2gT2sxnn7vHpmLAVVPQv7w';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzPi4ZmDK5E7vNVz14AiJmvcT9UbxXDDWzNVYt973Sqgt6p6BQtuqkJ3X3UM92mbjxVLg3xzmhZricuUSx5J811nW'){
// stolen coins hf recovery
return '98MsWpiv3fcutf4Mm94wYKZeeS556EAvMWEBLc12y5nf5QzNtD6hDfCuWcJMUr9Q9qmbj8kS326EGuiiTW7YJDo';
} elseif ($hash == 'PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCz9iKvAxdReMiDYmBWJb3GnjeTnNLwE8y7fuyBwFDbRLpECwnscbXSLGXMmvPMbMMRYSnnc7aTyLJBzw7tqxGNw4K'){
// stolen coins hf recovery
return '2xQGMH8qQuaTeKSYya5wYPWA9dgqiKBBDSeDWu1aUuBhKi8LnsukknqcUDRdzS7VVeC7aezK6Azhkx6L7H24pUjo';
}
}
// check the validity of a base58 encoded key. At the moment, it checks only the characters to be base58.
public function valid_key($id){
$chars = str_split("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
for($i=0;$i<strlen($id);$i++) if(!in_array($id[$i],$chars)) return false;
return true;
// hashes 9 times in sha512 (binary) and encodes in base58
for ($i = 0; $i < 9;
$i++) {
$hash = hash('sha512', $hash, true);
}
return base58_encode($hash);
}
}
// check the validity of an address. At the moment, it checks only the characters to be base58 and the length to be >=70 and <=128.
public function valid($id){
if(strlen($id)<70||strlen($id)>128) return false;
$chars = str_split("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
for($i=0;$i<strlen($id);$i++) if(!in_array($id[$i],$chars)) return false;
// checks the ecdsa secp256k1 signature for a specific public key
public function check_signature($data, $signature, $public_key)
{
return ec_verify($data, $signature, $public_key);
}
return true;
// generates a new account and a public/private key pair
public function generate_account()
{
// using secp256k1 curve for ECDSA
$args = [
"curve_name" => "secp256k1",
"private_key_type" => OPENSSL_KEYTYPE_EC,
];
}
// returns the current account balance
public function balance($id){
global $db;
$res=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$id));
if($res===false) $res="0.00000000";
return number_format($res,8,".","");
}
// returns the account balance - any pending debits from the mempool
public function pending_balance($id){
global $db;
$res=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$id));
if($res===false) $res="0.00000000";
// generates a new key pair
$key1 = openssl_pkey_new($args);
// if the original balance is 0, no mempool transactions are possible
if($res=="0.00000000") return $res;
$mem=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:id",array(":id"=>$id));
$rez=$res-$mem;
return number_format($rez,8,".","");
}
// returns all the transactions of a specific address
public function get_transactions($id,$limit=100){
global $db;
$block=new Block;
$current=$block->current();
$public_key=$this->public_key($id);
$limit=intval($limit);
if($limit>100||$limit<1) $limit=100;
$res=$db->run("SELECT * FROM transactions WHERE dst=:dst or public_key=:src ORDER by height DESC LIMIT :limit",array(":src"=>$public_key, ":dst"=>$id, ":limit"=>$limit));
$transactions=array();
foreach($res as $x){
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
$trans['src']=$this->get_address($x['public_key']);
$trans['confirmations']=$current['height']-$x['height'];
// version 0 -> reward transaction, version 1 -> normal transaction
if($x['version']==0) $trans['type']="mining";
elseif($x['version']==1){
if($x['dst']==$id) $trans['type']="credit";
else $trans['type']="debit";
} else {
$trans['type']="other";
}
ksort($trans);
$transactions[]=$trans;
}
return $transactions;
}
// returns the transactions from the mempool
public function get_mempool_transactions($id){
global $db;
$transactions=array();
$res=$db->run("SELECT * FROM mempool WHERE src=:src ORDER by height DESC LIMIT 100",array(":src"=>$id, ":dst"=>$id));
foreach($res as $x){
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"src"=>$x['src'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
$trans['type']="mempool";
// they are unconfirmed, so they will have -1 confirmations.
$trans['confirmations']=-1;
ksort($trans);
$transactions[]=$trans;
}
return $transactions;
}
// returns the public key for a specific account
public function public_key($id){
global $db;
$res=$db->single("SELECT public_key FROM accounts WHERE id=:id",array(":id"=>$id));
return $res;
}
// exports the private key encoded as PEM
openssl_pkey_export($key1, $pvkey);
// converts the PEM to a base58 format
$private_key = pem2coin($pvkey);
// exports the private key encoded as PEM
$pub = openssl_pkey_get_details($key1);
// converts the PEM to a base58 format
$public_key = pem2coin($pub['key']);
// generates the account's address based on the public key
$address = $this->get_address($public_key);
return ["address" => $address, "public_key" => $public_key, "private_key" => $private_key];
}
// check the validity of a base58 encoded key. At the moment, it checks only the characters to be base58.
public function valid_key($id)
{
$chars = str_split("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
for ($i = 0; $i < strlen($id);
$i++) {
if (!in_array($id[$i], $chars)) {
return false;
}
}
return true;
}
//check alias validity
public function free_alias($id)
{
global $db;
$orig=$id;
$id=strtoupper($id);
$id = san($id);
if (strlen($id)<4||strlen($id)>25) {
return false;
}
if ($orig!=$id) {
return false;
}
// making sure the same alias can only be used in one place
if ($db->single("SELECT COUNT(1) FROM accounts WHERE alias=:alias", [":alias"=>$id])==0) {
return true;
} else {
return false;
}
}
//check if an account already has an alias
public function has_alias($public_key)
{
global $db;
$public_key=san($public_key);
$res=$db->single("SELECT COUNT(1) FROM accounts WHERE public_key=:public_key AND alias IS NOT NULL", [":public_key"=>$public_key]);
if ($res!=0) {
return true;
} else {
return false;
}
}
//check alias validity
public function valid_alias($id)
{
global $db;
$orig=$id;
$banned=["MERCURY","DEVS","DEVELOPMENT", "MARKETING", "MERCURY80","DEVARO", "DEVELOPER","DEVELOPERS","ARODEV", "DONATION","MERCATOX", "OCTAEX", "MERCURY", "ARIONUM", "ESCROW","OKEX","BINANCE","CRYPTOPIA","HUOBI","ITFINEX","HITBTC","UPBIT","COINBASE","KRAKEN","BITSTAMP","BITTREX","POLONIEX"];
$id=strtoupper($id);
$id = san($id);
if (in_array($id, $banned)) {
return false;
}
if (strlen($id)<4||strlen($id)>25) {
return false;
}
if ($orig!=$id) {
return false;
}
return $db->single("SELECT COUNT(1) FROM accounts WHERE alias=:alias", [":alias"=>$id]);
}
//returns the account of an alias
public function alias2account($alias)
{
global $db;
$alias=strtoupper($alias);
$res=$db->single("SELECT id FROM accounts WHERE alias=:alias LIMIT 1", [":alias"=>$alias]);
return $res;
}
//returns the alias of an account
public function account2alias($id)
{
global $db;
$id=san($id);
$res=$db->single("SELECT alias FROM accounts WHERE id=:id LIMIT 1", [":id"=>$id]);
return $res;
}
// check the validity of an address. At the moment, it checks only the characters to be base58 and the length to be >=70 and <=128.
public function valid($id)
{
if (strlen($id) < 70 || strlen($id) > 128) {
return false;
}
$chars = str_split("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
for ($i = 0; $i < strlen($id);
$i++) {
if (!in_array($id[$i], $chars)) {
return false;
}
}
return true;
}
// returns the current account balance
public function balance($id)
{
global $db;
$res = $db->single("SELECT balance FROM accounts WHERE id=:id", [":id" => $id]);
if ($res === false) {
$res = "0.00000000";
}
return number_format($res, 8, ".", "");
}
// returns the account balance - any pending debits from the mempool
public function pending_balance($id)
{
global $db;
$res = $db->single("SELECT balance FROM accounts WHERE id=:id", [":id" => $id]);
if ($res === false) {
$res = "0.00000000";
}
// if the original balance is 0, no mempool transactions are possible
if ($res == "0.00000000") {
return $res;
}
$mem = $db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:id", [":id" => $id]);
$rez = $res - $mem;
return number_format($rez, 8, ".", "");
}
// returns all the transactions of a specific address
public function get_transactions($id, $limit = 100)
{
global $db;
$block = new Block();
$current = $block->current();
$public_key = $this->public_key($id);
$alias = $this->account2alias($id);
$limit = intval($limit);
if ($limit > 100 || $limit < 1) {
$limit = 100;
}
$res = $db->run(
"SELECT * FROM transactions WHERE dst=:dst or public_key=:src or dst=:alias ORDER by height DESC LIMIT :limit",
[":src" => $public_key, ":dst" => $id, ":limit" => $limit, ":alias"=>$alias]
);
$transactions = [];
foreach ($res as $x) {
$trans = [
"block" => $x['block'],
"height" => $x['height'],
"id" => $x['id'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $this->get_address($x['public_key']);
$trans['confirmations'] = $current['height'] - $x['height'];
// version 0 -> reward transaction, version 1 -> normal transaction
if ($x['version'] == 0) {
$trans['type'] = "mining";
} elseif ($x['version'] == 1) {
if ($x['dst'] == $id) {
$trans['type'] = "credit";
} else {
$trans['type'] = "debit";
}
} else {
$trans['type'] = "other";
}
ksort($trans);
$transactions[] = $trans;
}
return $transactions;
}
// returns the transactions from the mempool
public function get_mempool_transactions($id)
{
global $db;
$transactions = [];
$res = $db->run(
"SELECT * FROM mempool WHERE src=:src ORDER by height DESC LIMIT 100",
[":src" => $id, ":dst" => $id]
);
foreach ($res as $x) {
$trans = [
"block" => $x['block'],
"height" => $x['height'],
"id" => $x['id'],
"src" => $x['src'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['type'] = "mempool";
// they are unconfirmed, so they will have -1 confirmations.
$trans['confirmations'] = -1;
ksort($trans);
$transactions[] = $trans;
}
return $transactions;
}
// returns the public key for a specific account
public function public_key($id)
{
global $db;
$res = $db->single("SELECT public_key FROM accounts WHERE id=:id", [":id" => $id]);
return $res;
}
public function get_masternode($public_key)
{
global $db;
$res = $db->row("SELECT * FROM masternode WHERE public_key=:public_key", [":public_key" => $public_key]);
if (empty($res['public_key'])) {
return false;
}
return $res;
}
}
?>

File diff suppressed because it is too large Load Diff

162
include/config-sample.inc.php Executable file
View File

@@ -0,0 +1,162 @@
<?php
/*
|--------------------------------------------------------------------------
| Database Configuration
|--------------------------------------------------------------------------
*/
// The database DSN
$_config['db_connect'] = 'mysql:host=localhost;dbname=ENTER-DB-NAME';
// The database username
$_config['db_user'] = 'ENTER-DB-USER';
// The database password
$_config['db_pass'] = 'ENTER-DB-PASS';
/*
|--------------------------------------------------------------------------
| General Configuration
|--------------------------------------------------------------------------
*/
// Maximum number of connected peers
$_config['max_peers'] = 30;
// Enable testnet mode for development
$_config['testnet'] = false;
// To avoid any problems if other clones are made
$_config['coin'] = 'arionum';
// Allow others to connect to the node api (if set to false, only the below 'allowed_hosts' are allowed)
$_config['public_api'] = true;
// Hosts that are allowed to mine on this node
$_config['allowed_hosts'] = [
'127.0.0.1',
];
// Disable transactions and block repropagation
$_config['disable_repropagation'] = false;
/*
|--------------------------------------------------------------------------
| Peer Configuration
|--------------------------------------------------------------------------
*/
// The number of peers to send each new transaction to
$_config['transaction_propagation_peers'] = 5;
// How many new peers to check from each peer
$_config['max_test_peers'] = 5;
// The initial peers to sync from in sanity
$_config['initial_peer_list'] = [
'http://peer1.arionum.com',
'http://peer2.arionum.com',
'http://peer3.arionum.com',
'http://peer4.arionum.com',
'http://peer5.arionum.com',
'http://peer6.arionum.com',
'http://peer7.arionum.com',
'http://peer8.arionum.com',
'http://peer9.arionum.com',
'http://peer10.arionum.com',
'http://peer11.arionum.com',
'http://peer12.arionum.com',
'http://peer13.arionum.com',
'http://peer14.arionum.com',
'http://peer15.arionum.com',
'http://peer16.arionum.com',
'http://peer17.arionum.com',
'http://peer18.arionum.com',
'http://peer19.arionum.com',
'http://peer20.arionum.com',
'http://peer21.arionum.com',
'http://peer22.arionum.com',
'http://peer23.arionum.com',
'http://peer24.arionum.com',
'http://peer25.arionum.com',
'http://peer26.arionum.com',
'http://peer27.arionum.com',
];
// does not peer with any of the peers. Uses the seed peers and syncs only from those peers. Requires a cronjob on sanity.php
$_config['passive_peering'] = false;
/*
|--------------------------------------------------------------------------
| Mempool Configuration
|--------------------------------------------------------------------------
*/
// The maximum transactions to accept from a single peer
$_config['peer_max_mempool'] = 100;
// The maximum number of mempool transactions to be rebroadcasted
$_config['max_mempool_rebroadcast'] = 5000;
// The number of blocks between rebroadcasting transactions
$_config['sanity_rebroadcast_height'] = 30;
// Block accepting transfers from addresses blacklisted by the Arionum devs
$_config['use_official_blacklist'] = true;
/*
|--------------------------------------------------------------------------
| Sanity Configuration
|--------------------------------------------------------------------------
*/
// Recheck the last blocks on sanity
$_config['sanity_recheck_blocks'] = 30;
// The interval to run the sanity in seconds
$_config['sanity_interval'] = 900;
// Enable setting a new hostname (should be used only if you want to change the hostname)
$_config['allow_hostname_change'] = false;
// Rebroadcast local transactions when running sanity
$_config['sanity_rebroadcast_locals'] = true;
// Get more peers?
$_config['get_more_peers'] = true;
// Allow automated resyncs if the node is stuck. Enabled by default
$_config['auto_resync'] = true;
/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
*/
// Enable log output to the specified file
$_config['enable_logging'] = false;
// The specified file to write to (this should not be publicly visible)
$_config['log_file'] = '/var/log/aro.log';
// Log verbosity (default 0, maximum 3)
$_config['log_verbosity'] = 0;
/*
|--------------------------------------------------------------------------
| Masternode Configuration
|--------------------------------------------------------------------------
*/
// Enable this node as a masternode
$_config['masternode'] = false;
// The public key for the masternode
$_config['masternode_public_key'] = '';
$_config['masternode_voting_public_key'] = '';
$_config['masternode_voting_private_key'] = '';

View File

@@ -1,40 +0,0 @@
<?php
// Database connection
$_config['db_connect']="mysql:host=localhost;dbname=ENTER-DB-NAME";
$_config['db_user']="ENTER-DB-USER";
$_config['db_pass']="ENTER-DB-PASS";
// Maximum number of connected peers
$_config['max_peers']=30;
// Testnet, used for development
$_config['testnet']=false;
// To avoid any problems if other clones are made
$_config['coin']="arionum";
// maximum transactions accepted from a single peer
$_config['peer_max_mempool']=100;
// maximum mempool transactions to be rebroadcasted
$_config['max_mempool_rebroadcast']=5000;
// after how many blocks should the transactions be rebroadcasted
$_config['sanity_rebroadcast_height']=30;
// each new received transaction is sent to X peers
$_config['transaction_propagation_peers']=5;
// how many new peers to check from each peer.
$_config['max_test_peers']=5;
// recheck the last blocks on sanity
$_config['sanity_recheck_blocks']=10;
// allow others to connect to node api. If set to false, only allowed_hosts are allowed
$_config['public_api']=true;
// hosts allowed to mine on this node
$_config['allowed_hosts']=array("127.0.0.1");
// sanity is run every X seconds
$_config['sanity_interval']=900;
// accept the setting of new hostnames / should be used only if you want to change the hostname
$_config['allow_hostname_change']=false;
// rebroadcast local transactions on each sanity
$_config['sanity_rebroadcast_locals']=true;
// write logs to file
$_config['enable_logging']=false;
// log file, should not be publicly viewable
$_config['log_file']="/var/log/aro.log";
?>

View File

@@ -1,122 +1,138 @@
<?php
// a simple wrapper for pdo
class db extends PDO {
/**
* Class DB
*
* A simple wrapper for PDO.
*/
class DB extends PDO
{
private $error;
private $sql;
private $bind;
private $debugger = 0;
public $working = "yes";
private $error;
private $sql;
private $bind;
private $debugger=0;
public $working="yes";
public function __construct($dsn, $user = "", $passwd = "", $debug_level = 0)
{
$options = [
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];
$this->debugger = $debug_level;
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
die("Could not connect to the DB - ".$this->error);
}
}
public function __construct($dsn, $user="", $passwd="",$debug_level=0) {
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
$this->debugger=$debug_level;
try {
parent::__construct($dsn, $user, $passwd, $options);
} catch (PDOException $e) {
$this->error = $e->getMessage();
die("Could not connect to the DB - ".$this->error);
private function debug()
{
global $_config;
if (!$this->debugger) {
return;
}
$error = ["Error" => $this->error];
if (!empty($this->sql)) {
$error["SQL Statement"] = $this->sql;
}
if (!empty($this->bind)) {
$error["Bind Parameters"] = trim(print_r($this->bind, true));
}
$backtrace = debug_backtrace();
if (!empty($backtrace)) {
foreach ($backtrace as $info) {
if ($info["file"] != __FILE__) {
$error["Backtrace"] = $info["file"]." at line ".$info["line"];
}
}
}
$msg = "";
$msg .= "SQL Error\n".str_repeat("-", 50);
foreach ($error as $key => $val) {
$msg .= "\n\n$key:\n$val";
}
private function debug() {
if(!$this->debugger) return;
$error = array("Error" => $this->error);
if(!empty($this->sql))
$error["SQL Statement"] = $this->sql;
if(!empty($this->bind))
$error["Bind Parameters"] = trim(print_r($this->bind, true));
_log($msg);
}
$backtrace = debug_backtrace();
if(!empty($backtrace)) {
foreach($backtrace as $info) {
if($info["file"] != __FILE__)
$error["Backtrace"] = $info["file"] . " at line " . $info["line"];
}
}
$msg = "";
$msg .= "SQL Error\n" . str_repeat("-", 50);
foreach($error as $key => $val)
$msg .= "\n\n$key:\n$val";
if($this->debugger){
echo nl2br($msg);
}
private function cleanup($bind, $sql = "")
{
if (!is_array($bind)) {
if (!empty($bind)) {
$bind = [$bind];
} else {
$bind = [];
}
}
private function cleanup($bind,$sql="") {
if(!is_array($bind)) {
if(!empty($bind))
$bind = array($bind);
else
$bind = array();
}
foreach ($bind as $key => $val) {
if (str_replace($key, "", $sql) == $sql) {
unset($bind[$key]);
}
}
return $bind;
}
foreach($bind as $key=>$val){
if(str_replace($key,"",$sql)==$sql) unset($bind[$key]);
}
return $bind;
public function single($sql, $bind = "")
{
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind, $sql);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if ($pdostmt->execute($this->bind) !== false) {
return $pdostmt->fetchColumn();
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
}
public function run($sql, $bind = "")
{
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind, $sql);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if ($pdostmt->execute($this->bind) !== false) {
if (preg_match("/^(".implode("|", ["select", "describe", "pragma"]).") /i", $this->sql)) {
return $pdostmt->fetchAll(PDO::FETCH_ASSOC);
} elseif (preg_match("/^(".implode("|", ["delete", "insert", "update"]).") /i", $this->sql)) {
return $pdostmt->rowCount();
}
public function single($sql,$bind="") {
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind,$sql);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if($pdostmt->execute($this->bind) !== false) {
return $pdostmt->fetchColumn();
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
}
public function run($sql, $bind="") {
$this->sql = trim($sql);
$this->bind = $this->cleanup($bind,$sql);
$this->error = "";
try {
$pdostmt = $this->prepare($this->sql);
if($pdostmt->execute($this->bind) !== false) {
if(preg_match("/^(" . implode("|", array("select", "describe", "pragma")) . ") /i", $this->sql))
return $pdostmt->fetchAll(PDO::FETCH_ASSOC);
elseif(preg_match("/^(" . implode("|", array("delete", "insert", "update")) . ") /i", $this->sql))
return $pdostmt->rowCount();
}
} catch (PDOException $e) {
$this->error = $e->getMessage();
$this->debug();
return false;
}
public function row($sql, $bind = "")
{
$query = $this->run($sql, $bind);
if (count($query) == 0) {
return false;
}
public function row($sql,$bind=""){
$query=$this->run($sql,$bind);
if(count($query)==0) return false;
if(count($query)>1) return $query;
if(count($query)==1){
foreach($query as $row) $result=$row;
return $result;
}
if (count($query) > 1) {
return $query;
}
if (count($query) == 1) {
foreach ($query as $row) {
$result = $row;
}
return $result;
}
}
}
?>

View File

@@ -1,257 +1,313 @@
<?php
// simple santization function to accept only alphanumeric characters
function san($a,$b=""){
function san($a, $b = "")
{
$a = preg_replace("/[^a-zA-Z0-9".$b."]/", "", $a);
return $a;
}
// api error and exit
function api_err($data){
global $_config;
echo json_encode(array("status"=>"error","data"=>$data, "coin"=>$_config['coin']));
exit;
function san_ip($a)
{
$a = preg_replace("/[^a-fA-F0-9\[\]\.\:]/", "", $a);
return $a;
}
// api print ok and exit
function api_echo($data){
function san_host($a)
{
$a = preg_replace("/[^a-zA-Z0-9\.\-\:\/]/", "", $a);
return $a;
}
// api error and exit
function api_err($data)
{
global $_config;
echo json_encode(array("status"=>"ok","data"=>$data, "coin"=>$_config['coin']));
if (!headers_sent()) {
header('Content-Type: application/json');
}
echo json_encode(["status" => "error", "data" => $data, "coin" => $_config['coin']]);
exit;
}
// api print ok and exit
function api_echo($data)
{
global $_config;
if (!headers_sent()) {
header('Content-Type: application/json');
}
echo json_encode(["status" => "ok", "data" => $data, "coin" => $_config['coin']]);
exit;
}
// log function, shows only in cli atm
function _log($data){
$date=date("[Y-m-d H:i:s]");
$trace=debug_backtrace();
$loc=count($trace)-1;
$file=substr($trace[$loc]['file'],strrpos($trace[$loc]['file'],"/")+1);
$res="$date ".$file.":".$trace[$loc]['line'];
if(!empty($trace[$loc]['class'])) $res.="---".$trace[$loc]['class'];
if(!empty($trace[$loc]['function'])&&$trace[$loc]['function']!='_log') $res.='->'.$trace[$loc]['function'].'()';
$res.=" $data \n";
if(php_sapi_name() === 'cli') echo $res;
global $_config;
if($_config['enable_logging']==true){
@file_put_contents($_config['log_file'],$res, FILE_APPEND);
}
function _log($data, $verbosity = 0)
{
global $_config;
if ($_config['log_verbosity'] < $verbosity) {
return;
}
$date = date("[Y-m-d H:i:s]");
$trace = debug_backtrace();
$loc = count($trace) - 1;
$file = substr($trace[$loc]['file'], strrpos($trace[$loc]['file'], "/") + 1);
$res = "$date ".$file.":".$trace[$loc]['line'];
if (!empty($trace[$loc]['class'])) {
$res .= "---".$trace[$loc]['class'];
}
if (!empty($trace[$loc]['function']) && $trace[$loc]['function'] != '_log') {
$res .= '->'.$trace[$loc]['function'].'()';
}
$res .= " $data \n";
if (php_sapi_name() === 'cli') {
echo $res;
}
if ($_config['enable_logging'] == true && $_config['log_verbosity'] >= $verbosity) {
@file_put_contents($_config['log_file'], $res, FILE_APPEND);
}
}
// converts PEM key to hex
function pem2hex ($data) {
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
$data=str_replace("-----END PUBLIC KEY-----","",$data);
$data=str_replace("-----BEGIN EC PRIVATE KEY-----","",$data);
$data=str_replace("-----END EC PRIVATE KEY-----","",$data);
$data=str_replace("\n","",$data);
$data=base64_decode($data);
$data=bin2hex($data);
function pem2hex($data)
{
$data = str_replace("-----BEGIN PUBLIC KEY-----", "", $data);
$data = str_replace("-----END PUBLIC KEY-----", "", $data);
$data = str_replace("-----BEGIN EC PRIVATE KEY-----", "", $data);
$data = str_replace("-----END EC PRIVATE KEY-----", "", $data);
$data = str_replace("\n", "", $data);
$data = base64_decode($data);
$data = bin2hex($data);
return $data;
}
// converts hex key to PEM
function hex2pem ($data, $is_private_key=false) {
$data=hex2bin($data);
$data=base64_encode($data);
if($is_private_key) return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----";
function hex2pem($data, $is_private_key = false)
{
$data = hex2bin($data);
$data = base64_encode($data);
if ($is_private_key) {
return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----";
}
return "-----BEGIN PUBLIC KEY-----\n".$data."\n-----END PUBLIC KEY-----";
}
// Base58 encoding/decoding functions - all credits go to https://github.com/stephen-hill/base58php
function base58_encode($string)
{
$alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base=strlen($alphabet);
// Type validation
if (is_string($string) === false) {
return false;
}
// If the string is empty, then the encoded string is obviously empty
if (strlen($string) === 0) {
return '';
}
// Now we need to convert the byte array into an arbitrary-precision decimal
// We basically do this by performing a base256 to base10 conversion
$hex = unpack('H*', $string);
$hex = reset($hex);
$decimal = gmp_init($hex, 16);
// This loop now performs base 10 to base 58 conversion
// The remainder or modulo on each loop becomes a base 58 character
$output = '';
while (gmp_cmp($decimal, $base) >= 0) {
list($decimal, $mod) = gmp_div_qr($decimal, $base);
$output .= $alphabet[gmp_intval($mod)];
}
// If there's still a remainder, append it
if (gmp_cmp($decimal, 0) > 0) {
$output .= $alphabet[gmp_intval($decimal)];
}
// Now we need to reverse the encoded data
$output = strrev($output);
// Now we need to add leading zeros
$bytes = str_split($string);
foreach ($bytes as $byte) {
if ($byte === "\x00") {
$output = $alphabet[0] . $output;
continue;
}
break;
}
return (string) $output;
// Base58 encoding/decoding functions - all credits go to https://github.com/stephen-hill/base58php
function base58_encode($string)
{
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base = strlen($alphabet);
// Type validation
if (is_string($string) === false) {
return false;
}
function base58_decode($base58)
{
$alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base=strlen($alphabet);
// Type Validation
if (is_string($base58) === false) {
return false;
}
// If the string is empty, then the decoded string is obviously empty
if (strlen($base58) === 0) {
return '';
}
$indexes = array_flip(str_split($alphabet));
$chars = str_split($base58);
// Check for invalid characters in the supplied base58 string
foreach ($chars as $char) {
if (isset($indexes[$char]) === false) {
return false;
}
}
// Convert from base58 to base10
$decimal = gmp_init($indexes[$chars[0]], 10);
for ($i = 1, $l = count($chars); $i < $l; $i++) {
$decimal = gmp_mul($decimal, $base);
$decimal = gmp_add($decimal, $indexes[$chars[$i]]);
}
// Convert from base10 to base256 (8-bit byte array)
$output = '';
while (gmp_cmp($decimal, 0) > 0) {
list($decimal, $byte) = gmp_div_qr($decimal, 256);
$output = pack('C', gmp_intval($byte)) . $output;
}
// Now we need to add leading zeros
foreach ($chars as $char) {
if ($indexes[$char] === 0) {
$output = "\x00" . $output;
continue;
}
break;
}
return $output;
// If the string is empty, then the encoded string is obviously empty
if (strlen($string) === 0) {
return '';
}
// Now we need to convert the byte array into an arbitrary-precision decimal
// We basically do this by performing a base256 to base10 conversion
$hex = unpack('H*', $string);
$hex = reset($hex);
$decimal = gmp_init($hex, 16);
// This loop now performs base 10 to base 58 conversion
// The remainder or modulo on each loop becomes a base 58 character
$output = '';
while (gmp_cmp($decimal, $base) >= 0) {
list($decimal, $mod) = gmp_div_qr($decimal, $base);
$output .= $alphabet[gmp_intval($mod)];
}
// If there's still a remainder, append it
if (gmp_cmp($decimal, 0) > 0) {
$output .= $alphabet[gmp_intval($decimal)];
}
// Now we need to reverse the encoded data
$output = strrev($output);
// Now we need to add leading zeros
$bytes = str_split($string);
foreach ($bytes as $byte) {
if ($byte === "\x00") {
$output = $alphabet[0].$output;
continue;
}
break;
}
return (string)$output;
}
function base58_decode($base58)
{
$alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base = strlen($alphabet);
// Type Validation
if (is_string($base58) === false) {
return false;
}
// If the string is empty, then the decoded string is obviously empty
if (strlen($base58) === 0) {
return '';
}
$indexes = array_flip(str_split($alphabet));
$chars = str_split($base58);
// Check for invalid characters in the supplied base58 string
foreach ($chars as $char) {
if (isset($indexes[$char]) === false) {
return false;
}
}
// Convert from base58 to base10
$decimal = gmp_init($indexes[$chars[0]], 10);
for ($i = 1, $l = count($chars); $i < $l; $i++) {
$decimal = gmp_mul($decimal, $base);
$decimal = gmp_add($decimal, $indexes[$chars[$i]]);
}
// Convert from base10 to base256 (8-bit byte array)
$output = '';
while (gmp_cmp($decimal, 0) > 0) {
list($decimal, $byte) = gmp_div_qr($decimal, 256);
$output = pack('C', gmp_intval($byte)).$output;
}
// Now we need to add leading zeros
foreach ($chars as $char) {
if ($indexes[$char] === 0) {
$output = "\x00".$output;
continue;
}
break;
}
return $output;
}
// converts PEM key to the base58 version used by ARO
function pem2coin ($data) {
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
$data=str_replace("-----END PUBLIC KEY-----","",$data);
$data=str_replace("-----BEGIN EC PRIVATE KEY-----","",$data);
$data=str_replace("-----END EC PRIVATE KEY-----","",$data);
$data=str_replace("\n","",$data);
$data=base64_decode($data);
function pem2coin($data)
{
$data = str_replace("-----BEGIN PUBLIC KEY-----", "", $data);
$data = str_replace("-----END PUBLIC KEY-----", "", $data);
$data = str_replace("-----BEGIN EC PRIVATE KEY-----", "", $data);
$data = str_replace("-----END EC PRIVATE KEY-----", "", $data);
$data = str_replace("\n", "", $data);
$data = base64_decode($data);
return base58_encode($data);
}
// converts the key in base58 to PEM
function coin2pem ($data, $is_private_key=false) {
function coin2pem($data, $is_private_key = false)
{
$data = base58_decode($data);
$data = base64_encode($data);
$data=base58_decode($data);
$data=base64_encode($data);
$dat = str_split($data, 64);
$data = implode("\n", $dat);
$dat=str_split($data,64);
$data=implode("\n",$dat);
if($is_private_key) return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----\n";
if ($is_private_key) {
return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----\n";
}
return "-----BEGIN PUBLIC KEY-----\n".$data."\n-----END PUBLIC KEY-----\n";
}
// sign data with private key
function ec_sign($data, $key){
// transform the base58 key format to PEM
$private_key=coin2pem($key,true);
$pkey=openssl_pkey_get_private($private_key);
$k=openssl_pkey_get_details($pkey);
function ec_sign($data, $key)
{
// transform the base58 key format to PEM
$private_key = coin2pem($key, true);
openssl_sign($data,$signature,$pkey,OPENSSL_ALGO_SHA256);
// the signature will be base58 encoded
$pkey = openssl_pkey_get_private($private_key);
$k = openssl_pkey_get_details($pkey);
openssl_sign($data, $signature, $pkey, OPENSSL_ALGO_SHA256);
// the signature will be base58 encoded
return base58_encode($signature);
}
function ec_verify($data, $signature, $key){
function ec_verify($data, $signature, $key)
{
// transform the base58 key to PEM
$public_key=coin2pem($key);
$signature=base58_decode($signature);
$pkey=openssl_pkey_get_public($public_key);
$res=openssl_verify($data,$signature,$pkey,OPENSSL_ALGO_SHA256);
if($res===1) return true;
$public_key = coin2pem($key);
$signature = base58_decode($signature);
$pkey = openssl_pkey_get_public($public_key);
$res = openssl_verify($data, $signature, $pkey, OPENSSL_ALGO_SHA256);
if ($res === 1) {
return true;
}
return false;
}
// verify the validity of an url
function isValidURL($url)
{
return preg_match('|^(ht)?(f)?tp(s)?://[a-z0-9-]+(.[a-z0-9-]+)*(:[0-9]+)?(/.*)?$|i', $url);
}
// POST data to an URL (usualy peer). The data is an array, json encoded with is sent as $_POST['data']
function peer_post($url, $data=array(),$timeout=60,$debug=false){
function peer_post($url, $data = [], $timeout = 60, $debug = false)
{
global $_config;
if($debug) echo "\nPeer post: $url\n";
if ($debug) {
echo "\nPeer post: $url\n";
}
if (!isValidURL($url)) {
return false;
}
$postdata = http_build_query(
array(
[
'data' => json_encode($data),
"coin"=>$_config['coin']
)
"coin" => $_config['coin'],
]
);
$opts = array('http' =>
array(
'timeout' => $timeout,
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata
)
);
$context = stream_context_create($opts);
$opts = [
'http' =>
[
'timeout' => $timeout,
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $postdata,
],
];
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
if($debug) echo "\nPeer response: $result\n";
$res=json_decode($result,true);
if ($debug) {
echo "\nPeer response: $result\n";
}
$res = json_decode($result, true);
// the function will return false if something goes wrong
if($res['status']!="ok"||$res['coin']!=$_config['coin']) return false;
if ($res['status'] != "ok" || $res['coin'] != $_config['coin']) {
return false;
}
return $res['data'];
}
// convers hex to base58
function hex2coin($hex){
$data=hex2bin($hex);
return base58_encode($data);
}
function hex2coin($hex)
{
$data = hex2bin($hex);
return base58_encode($data);
}
// converts base58 to hex
function coin2hex($data){
$bin= base58_decode($data);
return bin2hex($bin);
}
?>
function coin2hex($data)
{
$bin = base58_decode($data);
return bin2hex($bin);
}

View File

@@ -1,89 +1,119 @@
<?php
// ARO version
define("VERSION", "0.2b");
// Amsterdam timezone by default, should probably be moved to config
date_default_timezone_set("Europe/Amsterdam");
define("VERSION", "1.0.0-alpha.7");
// UTC timezone by default
date_default_timezone_set("UTC");
//error_reporting(E_ALL & ~E_NOTICE);
// error_reporting(E_ALL & ~E_NOTICE);
error_reporting(0);
ini_set('display_errors',"off");
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
// not accessible directly
if(php_sapi_name() !== 'cli'&&substr_count($_SERVER['PHP_SELF'],"/")>1){
die("This application should only be run in the main directory /");
if (php_sapi_name() !== 'cli' && substr_count($_SERVER['PHP_SELF'], "/") > 1) {
die("This application should only be run in the main directory /");
}
require_once __DIR__.'/Exception.php';
require_once __DIR__.'/config.inc.php';
require_once __DIR__.'/db.inc.php';
require_once __DIR__.'/functions.inc.php';
require_once __DIR__.'/Blacklist.php';
require_once __DIR__.'/InitialPeers.php';
require_once __DIR__.'/block.inc.php';
require_once __DIR__.'/account.inc.php';
require_once __DIR__.'/transaction.inc.php';
require_once("include/config.inc.php");
require_once("include/db.inc.php");
require_once("include/functions.inc.php");
require_once("include/block.inc.php");
require_once("include/account.inc.php");
require_once("include/transaction.inc.php");
if($_config['db_pass']=="ENTER-DB-PASS") die("Please update your config file and set your db password");
if ($_config['db_pass'] == "ENTER-DB-PASS") {
die("Please update your config file and set your db password");
}
// initial DB connection
$db=new DB($_config['db_connect'],$_config['db_user'],$_config['db_pass'],0);
if(!$db) die("Could not connect to the DB backend.");
$db = new DB($_config['db_connect'], $_config['db_user'], $_config['db_pass'], $_config['enable_logging']);
if (!$db) {
die("Could not connect to the DB backend.");
}
// checks for php version and extensions
if (!extension_loaded("openssl") && !defined("OPENSSL_KEYTYPE_EC")) api_err("Openssl php extension missing");
if (!extension_loaded("gmp")) api_err("gmp php extension missing");
if (!extension_loaded('PDO')) api_err("pdo php extension missing");
if (!extension_loaded("bcmath")) api_err("bcmath php extension missing");
if (!defined("PASSWORD_ARGON2I")) api_err("The php version is not compiled with argon2i support");
if(floatval(phpversion())<7.2) api_err("The minimum php version required is 7.2");
// Getting extra configs from the database
$query=$db->run("SELECT cfg, val FROM config");
foreach($query as $res){
$_config[$res['cfg']]=trim($res['val']);
if (!extension_loaded("openssl") && !defined("OPENSSL_KEYTYPE_EC")) {
api_err("Openssl php extension missing");
}
if (!extension_loaded("gmp")) {
api_err("gmp php extension missing");
}
if (!extension_loaded('PDO')) {
api_err("pdo php extension missing");
}
if (!extension_loaded("bcmath")) {
api_err("bcmath php extension missing");
}
if (!defined("PASSWORD_ARGON2I")) {
api_err("The php version is not compiled with argon2i support");
}
if (floatval(phpversion()) < 7.2) {
api_err("The minimum php version required is 7.2");
}
// Getting extra configs from the database
$query = $db->run("SELECT cfg, val FROM config");
foreach ($query as $res) {
$_config[$res['cfg']] = trim($res['val']);
}
// current hostname
$hostname = (!empty($_SERVER['HTTPS']) ? 'https' : 'http')."://".san_host($_SERVER['HTTP_HOST']);
// set the hostname to the current one
if ($hostname != $_config['hostname'] && $_SERVER['HTTP_HOST'] != "localhost" && $_SERVER['HTTP_HOST'] != "127.0.0.1" && $_SERVER['hostname'] != '::1' && php_sapi_name() !== 'cli' && ($_config['allow_hostname_change'] != false || empty($_config['hostname']))) {
$db->run("UPDATE config SET val=:hostname WHERE cfg='hostname' LIMIT 1", [":hostname" => $hostname]);
$_config['hostname'] = $hostname;
}
// Getting extra configs from the database
$query = $db->run("SELECT cfg, val FROM config");
foreach ($query as $res) {
$_config[$res['cfg']] = trim($res['val']);
}
// nothing is allowed while in maintenance
if($_config['maintenance']==1) api_err("under-maintenance");
if ($_config['maintenance'] == 1) {
api_err("under-maintenance");
}
// update the db schema, on every git pull or initial install
if(file_exists("tmp/db-update")){
$res=unlink("tmp/db-update");
if($res){
echo "Updating db schema! Please refresh!\n";
require_once("include/schema.inc.php");
exit;
}
echo "Could not access the tmp/db-update file. Please give full permissions to this file\n";
if (file_exists("tmp/db-update")) {
//checking if the server has at least 2GB of ram
$ram=file_get_contents("/proc/meminfo");
$ramz=explode("MemTotal:",$ram);
$ramb=explode("kB",$ramz[1]);
$ram=intval(trim($ramb[0]));
if($ram<1700000) {
die("The node requires at least 2 GB of RAM");
}
$res = unlink("tmp/db-update");
if ($res) {
echo "Updating db schema! Please refresh!\n";
require_once __DIR__.'/schema.inc.php';
exit;
}
echo "Could not access the tmp/db-update file. Please give full permissions to this file\n";
}
// something went wront with the db schema
if($_config['dbversion']<2) exit;
if ($_config['dbversion'] < 2) {
exit;
}
// separate blockchain for testnet
if($_config['testnet']==true) $_config['coin'].="-testnet";
// current hostname
$hostname=(!empty($_SERVER['HTTPS'])?'https':'http')."://".$_SERVER['HTTP_HOST'];
// set the hostname to the current one
if($hostname!=$_config['hostname']&&$_SERVER['HTTP_HOST']!="localhost"&&$_SERVER['HTTP_HOST']!="127.0.0.1"&&$_SERVER['hostname']!='::1'&&php_sapi_name() !== 'cli' && ($_config['allow_hostname_change']!=false||empty($_config['hostname']))){
$db->run("UPDATE config SET val=:hostname WHERE cfg='hostname' LIMIT 1",array(":hostname"=>$hostname));
$_config['hostname']=$hostname;
if ($_config['testnet'] == true) {
$_config['coin'] .= "-testnet";
}
if (empty($_config['hostname']) || $_config['hostname'] == "http://" || $_config['hostname'] == "https://") {
api_err("Invalid hostname");
}
if(empty($_config['hostname'])||$_config['hostname']=="http://"||$_config['hostname']=="https://") api_err("Invalid hostname");
// run sanity
$t=time();
if($t-$_config['sanity_last']>$_config['sanity_interval']&& php_sapi_name() !== 'cli') system("php sanity.php > /dev/null 2>&1 &");
?>
$t = time();
if ($t - $_config['sanity_last'] > $_config['sanity_interval'] && php_sapi_name() !== 'cli') {
system("php sanity.php > /dev/null 2>&1 &");
}

View File

@@ -1,18 +1,18 @@
<?php
// when db schema modifications are done, this function is run.
$dbversion = intval($_config['dbversion']);
$dbversion=intval($_config['dbversion']);
$db->beginTransaction();
if($dbversion==0){
$db->run("
if ($dbversion == 0) {
$db->run("
CREATE TABLE `accounts` (
`id` varbinary(128) NOT NULL,
`public_key` varbinary(1024) NOT NULL,
`block` varbinary(128) NOT NULL,
`balance` decimal(20,8) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;");
$db->run("CREATE TABLE `blocks` (
`id` varbinary(128) NOT NULL,
`generator` varbinary(128) NOT NULL,
@@ -24,16 +24,16 @@ if($dbversion==0){
`argon` varbinary(128) NOT NULL,
`transactions` INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `config` (
`cfg` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`val` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
('hostname', '');");
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
('dbversion', '1');");
@@ -51,7 +51,7 @@ if($dbversion==0){
`date` bigint(20) NOT NULL,
`peer` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `peers` (
`id` int(11) NOT NULL,
`hostname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
@@ -60,8 +60,8 @@ if($dbversion==0){
`reserve` tinyint(4) NOT NULL DEFAULT 1,
`ip` varchar(45) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `transactions` (
`id` varbinary(128) NOT NULL,
`block` varbinary(128) NOT NULL,
@@ -75,75 +75,196 @@ if($dbversion==0){
`date` int(11) NOT NULL,
`public_key` varbinary(1024) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("ALTER TABLE `peers`
ADD PRIMARY KEY (`id`);");
$db->run("ALTER TABLE `peers`
$db->run("ALTER TABLE `peers`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;");
$db->run("ALTER TABLE `accounts`
ADD PRIMARY KEY (`id`),
ADD KEY `accounts` (`block`);");
$db->run("ALTER TABLE `blocks`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `height` (`height`);");
$db->run("ALTER TABLE `config` ADD PRIMARY KEY (`cfg`);");
$db->run("ALTER TABLE `mempool`
$db->run("ALTER TABLE `config` ADD PRIMARY KEY (`cfg`);");
$db->run("ALTER TABLE `mempool`
ADD PRIMARY KEY (`id`),
ADD KEY `height` (`height`);");
$db->run("ALTER TABLE `peers`
$db->run("ALTER TABLE `peers`
ADD UNIQUE KEY `hostname` (`hostname`),
ADD UNIQUE KEY `ip` (`ip`),
ADD KEY `blacklisted` (`blacklisted`),
ADD KEY `ping` (`ping`),
ADD KEY `reserve` (`reserve`);");
$db->run("ALTER TABLE `transactions`
$db->run("ALTER TABLE `transactions`
ADD PRIMARY KEY (`id`),
ADD KEY `block_id` (`block`);");
$db->run("ALTER TABLE `accounts`
$db->run("ALTER TABLE `accounts`
ADD CONSTRAINT `accounts` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
$db->run("ALTER TABLE `transactions`
$db->run("ALTER TABLE `transactions`
ADD CONSTRAINT `block_id` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
$dbversion++;
}
if($dbversion==1){
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_last', '0');");
$dbversion++;
if ($dbversion == 1) {
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_last', '0');");
$dbversion++;
}
if($dbversion==2){
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_sync', '0');");
$dbversion++;
if ($dbversion == 2) {
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_sync', '0');");
$dbversion++;
}
if($dbversion==3){
$dbversion++;
if ($dbversion == 3) {
$dbversion++;
}
if($dbversion==4){
$db->run("ALTER TABLE `mempool` ADD INDEX(`src`);");
$db->run("ALTER TABLE `mempool` ADD INDEX(`peer`); ");
$db->run("ALTER TABLE `mempool` ADD INDEX(`val`); ");
$dbversion++;
if ($dbversion == 4) {
$db->run("ALTER TABLE `mempool` ADD INDEX(`src`);");
$db->run("ALTER TABLE `mempool` ADD INDEX(`peer`); ");
$db->run("ALTER TABLE `mempool` ADD INDEX(`val`); ");
$dbversion++;
}
if($dbversion==5){
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
$dbversion++;
if ($dbversion == 5) {
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
$dbversion++;
}
if($dbversion==6){
$db->run("ALTER TABLE `peers` ADD `stuckfail` TINYINT(4) NOT NULL DEFAULT '0' AFTER `fails`, ADD INDEX (`stuckfail`); ");
$db->run("ALTER TABLE `accounts` ADD `alias` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL AFTER `balance`; ");
$dbversion++;
if ($dbversion == 6) {
$db->run("ALTER TABLE `peers` ADD `stuckfail` TINYINT(4) NOT NULL DEFAULT '0' AFTER `fails`, ADD INDEX (`stuckfail`); ");
$db->run("ALTER TABLE `accounts` ADD `alias` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL AFTER `balance`; ");
$dbversion++;
}
if ($dbversion == 7) {
$db->run("ALTER TABLE `accounts` ADD INDEX(`alias`); ");
$db->run("ALTER TABLE `transactions` ADD KEY `dst` (`dst`), ADD KEY `height` (`height`), ADD KEY `public_key` (`public_key`);");
$dbversion++;
}
if ($dbversion == 8) {
$db->run("CREATE TABLE `masternode` (
`public_key` varchar(128) COLLATE utf8mb4_bin NOT NULL,
`height` int(11) NOT NULL,
`ip` varchar(16) COLLATE utf8mb4_bin NOT NULL,
`last_won` int(11) NOT NULL DEFAULT '0',
`blacklist` int(11) NOT NULL DEFAULT '0',
`fails` int(11) NOT NULL DEFAULT '0',
`status` tinyint(4) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;");
$db->run("ALTER TABLE `masternode`
ADD PRIMARY KEY (`public_key`),
ADD KEY `last_won` (`last_won`),
ADD KEY `status` (`status`),
ADD KEY `blacklist` (`blacklist`),
ADD KEY `height` (`height`);");
$dbversion++;
}
if ($dbversion == 9) {
//dev only
$dbversion++;
}
if ($dbversion == 10) {
//assets system
$db->run("
CREATE TABLE `assets` (
`id` varbinary(128) NOT NULL,
`max_supply` bigint(18) NOT NULL DEFAULT '0',
`tradable` tinyint(1) NOT NULL DEFAULT '1',
`price` decimal(20,8) NOT NULL DEFAULT '0.00000000',
`dividend_only` tinyint(1) NOT NULL DEFAULT '0',
`auto_dividend` tinyint(1) NOT NULL DEFAULT '0',
`allow_bid` tinyint(1) NOT NULL DEFAULT '1',
`height` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
");
$db->run("
ALTER TABLE `assets`
ADD PRIMARY KEY (`id`)
");
$db->run("
CREATE TABLE `assets_market` (
`id` varchar(128) COLLATE utf8mb4_bin NOT NULL,
`account` varbinary(128) NOT NULL,
`asset` varbinary(128) NOT NULL,
`price` decimal(20,8) NOT NULL,
`date` int(11) NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0',
`type` enum('bid','ask') COLLATE utf8mb4_bin NOT NULL DEFAULT 'bid',
`val` bigint(18) NOT NULL,
`val_done` bigint(18) NOT NULL DEFAULT '0',
`cancelable` tinyint(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
");
$db->run("
ALTER TABLE `assets_market`
ADD PRIMARY KEY (`id`);
");
$db->run("CREATE TABLE `assets_balance` (
`account` varbinary(128) NOT NULL,
`asset` varbinary(128) NOT NULL,
`balance` bigint(128) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
");
$db->run("
ALTER TABLE `assets_balance`
ADD PRIMARY KEY (`account`,`asset`);
");
$dbversion++;
}
if ($dbversion == 11) {
$db->run("ALTER TABLE `transactions` ADD INDEX(`version`); ");
$db->run("ALTER TABLE `transactions` ADD INDEX(`message`); ");
$db->run("
CREATE TABLE `logs` (
`id` int(11) NOT NULL,
`transaction` varbinary(128) NULL DEFAULT NULL,
`block` VARBINARY(128) NULL DEFAULT NULL,
`json` text DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;");
$db->run("ALTER TABLE `logs`
ADD PRIMARY KEY (`id`),
ADD INDEX(`transaction`),
ADD INDEX(`block`);");
$db->run("ALTER TABLE `logs` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;");
$db->run("ALTER TABLE `masternode` ADD `vote_key` VARCHAR(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL AFTER `status`, ADD INDEX(`vote_key`);");
$db->run("ALTER TABLE `masternode` ADD `cold_last_won` INT NOT NULL DEFAULT '0' AFTER `vote_key`, ADD INDEX(`cold_last_won`); ");
$db->run("ALTER TABLE `masternode` ADD `voted` TINYINT NOT NULL DEFAULT '0' AFTER `cold_last_won`, ADD INDEX (`voted`); ");
$db->run("CREATE TABLE `votes` (
`id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`nfo` varchar(64) NOT NULL,
`val` int(11) NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=latin1;");
$db->run("INSERT INTO `votes` (`id`, `nfo`, `val`) VALUES
('coldstacking', 'Enable cold stacking for inactive masternodes', 1),
('emission30', 'Emission reduction by 30 percent', 1),
('endless10reward', 'Minimum reward to be 10 aro forever', 0),
('masternodereward50', 'Masternode reward to be 50 percent of the block reward', 1);");
$db->run("ALTER TABLE `votes` ADD PRIMARY KEY (`id`);");
$dbversion++;
}
// update the db version to the latest one
if($dbversion!=$_config['dbversion']) $db->run("UPDATE config SET val=:val WHERE cfg='dbversion'",array(":val"=>$dbversion));
if ($dbversion != $_config['dbversion']) {
$db->run("UPDATE config SET val=:val WHERE cfg='dbversion'", [":val" => $dbversion]);
}
$db->commit();
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,14 @@
<?php
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
www.arionum.com
if (file_exists(__DIR__ . '/../app/bootstrap.php')) {
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:
$container = require __DIR__ . '/../app/bootstrap.php';
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
$container->getByType(Nette\Application\Application::class)
->run();
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.
*/
require_once("include/init.inc.php");
$block=new Block;
$current=$block->current();
echo "<h3>Arionum Node</h3>";
echo "System check complete.<br><br> Current block: $current[height]";
?>
} else {
require_once __DIR__.'/node-index.php';
}

244
mine.php
View File

@@ -1,7 +1,7 @@
<?php
/*
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
Copyright (c) 2018 AroDev
www.arionum.com
@@ -23,52 +23,206 @@ 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.
*/
require_once("include/init.inc.php");
$block=new Block();
$acc=new Account();
require_once __DIR__.'/include/init.inc.php';
$block = new Block();
$acc = new Account();
set_time_limit(360);
$q=$_GET['q'];
$q = $_GET['q'];
$ip=$_SERVER['REMOTE_ADDR'];
$ip=filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
$ip = san_ip($_SERVER['REMOTE_ADDR']);
$ip = filter_var($ip, FILTER_VALIDATE_IP);
// in case of testnet, all IPs are accepted for mining
if($_config['testnet']==false&&!in_array($ip,$_config['allowed_hosts'])) api_err("unauthorized");
if($q=="info"){
// provides the mining info to the miner
$diff=$block->difficulty();
$current=$block->current();
api_echo(array("difficulty"=>$diff, "block"=>$current['id'], "height"=>$current['height']));
exit;
} elseif($q=="submitNonce"){
// in case the blocks are syncing, reject all
if($_config['sanity_sync']==1) api_err("sanity-sync");
$nonce = san($_POST['nonce']);
$argon=$_POST['argon'];
$public_key=san($_POST['public_key']);
$private_key=san($_POST['private_key']);
// check if the miner won the block
$result=$block->mine($public_key, $nonce, $argon);
if($result) {
// generate the new block
$res=$block->forge($nonce,$argon, $public_key, $private_key);
if($res){
//if the new block is generated, propagate it to all peers in background
$current=$block->current();
system("php propagate.php block $current[id] > /dev/null 2>&1 &");
api_echo("accepted");
}
}
api_err("rejected");
} else {
api_err("invalid command");
if ($_config['testnet'] == false && !in_array($ip, $_config['allowed_hosts']) && !empty($ip) && !in_array(
'*',
$_config['allowed_hosts']
)) {
api_err("unauthorized");
}
?>
if ($q == "info") {
// provides the mining info to the miner
$diff = $block->difficulty();
$current = $block->current();
$current_height=$current['height'];
$recommendation="mine";
$argon_mem=16384;
$argon_threads=4;
$argon_time=4;
if ($current_height<80000) {
if ($current_height > 10800) {
$argon_mem=524288;
$argon_threads=1;
$argon_time=1;
}
} elseif($current_height>=80458){
if($current_height%2==0){
$argon_mem=524288;
$argon_threads=1;
$argon_time=1;
} else {
$argon_mem=16384;
$argon_threads=4;
$argon_time=4;
}
} else {
if ($current_height%3==0) {
$argon_mem=524288;
$argon_threads=1;
$argon_time=1;
} elseif ($current_height%3==2) {
global $db;
$winner=$db->single(
"SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 1",
[":current"=>$current_height, ":start"=>$current_height-360]
);
//$recommendation="pause";
if ($winner===false) {
$recommendation="mine";
}
}
}
$res = [
"difficulty" => $diff,
"block" => $current['id'],
"height" => $current['height'],
"testnet" => $_config['testnet'],
"recommendation"=> $recommendation,
"argon_mem" => $argon_mem,
"argon_threads" => $argon_threads,
"argon_time" => $argon_time,
];
api_echo($res);
exit;
} elseif ($q == "submitNonce") {
// in case the blocks are syncing, reject all
if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
}
$nonce = san($_POST['nonce']);
$argon = $_POST['argon'];
$public_key = san($_POST['public_key']);
$private_key = san($_POST['private_key']);
// check if the miner won the block
$result = $block->mine($public_key, $nonce, $argon);
if ($result) {
// generate the new block
$res = $block->forge($nonce, $argon, $public_key, $private_key);
if ($res) {
//if the new block is generated, propagate it to all peers in background
$current = $block->current();
$current['id']=escapeshellarg(san($current['id']));
system("php propagate.php block $current[id] > /dev/null 2>&1 &");
api_echo("accepted");
}
}
api_err("rejected");
} elseif ($q == "submitBlock") {
// in case the blocks are syncing, reject all
if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
}
$nonce = san($_POST['nonce']);
$argon = $_POST['argon'];
$public_key = san($_POST['public_key']);
// check if the miner won the block
$result = $block->mine($public_key, $nonce, $argon);
if ($result) {
// generate the new block
$date = intval($_POST['date']);
if ($date <= $current['date']) {
api_err("rejected - date");
}
// get the mempool transactions
$txn = new Transaction();
$current = $block->current();
$height = $current['height'] += 1;
// get the mempool transactions
$txn = new Transaction();
$difficulty = $block->difficulty();
$acc = new Account();
$generator = $acc->get_address($public_key);
$data=json_decode($_POST['data'], true);
// sign the block
$signature = san($_POST['signature']);
// reward transaction and signature
$reward = $block->reward($height, $data);
$msg = '';
$transaction = [
"src" => $generator,
"dst" => $generator,
"val" => $reward,
"version" => 0,
"date" => $date,
"message" => $msg,
"fee" => "0.00000000",
"public_key" => $public_key,
];
ksort($transaction);
$reward_signature = san($_POST['reward_signature']);
// add the block to the blockchain
$res = $block->add(
$height,
$public_key,
$nonce,
$data,
$date,
$signature,
$difficulty,
$reward_signature,
$argon
);
if ($res) {
//if the new block is generated, propagate it to all peers in background
$current = $block->current();
$current['id']=escapeshellarg(san($current['id']));
system("php propagate.php block $current[id] > /dev/null 2>&1 &");
api_echo("accepted");
} else {
api_err("rejected - add");
}
}
api_err("rejected");
} elseif ($q == "getWork") {
if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
}
$block = new Block();
$current = $block->current();
$height = $current['height'] += 1;
$date = time();
// get the mempool transactions
$txn = new Transaction();
$data = $txn->mempool($block->max_transactions());
$difficulty = $block->difficulty();
// always sort the transactions in the same way
ksort($data);
// reward transaction and signature
$reward = $block->reward($height, $data);
api_echo(["height"=>$height, "data"=>$data, "reward"=>$reward, "block"=>$current['id'], "difficulty"=>$difficulty]);
} else {
api_err("invalid command");
}

274
node-index.php Executable file
View File

@@ -0,0 +1,274 @@
<?php
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
www.arionum.com
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.
*/
require_once __DIR__.'/include/init.inc.php';
$block = new Block();
$current = $block->current();
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Arionum Node</title>
<style>
.title:not(:last-child) {
margin-bottom: 1.5rem;
}
body, h1, html {
margin: 0;
padding: 0;
}
h1 {
font-size: 100%;
font-weight: 400;
}
html {
box-sizing: border-box;
background-color: #fff;
font-size: 16px;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
min-width: 300px;
overflow-x: hidden;
overflow-y: scroll;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
text-size-adjust: 100%;
}
*, ::after, ::before {
box-sizing: inherit;
}
section {
display: block;
}
body {
font-family: "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #4a4a4a;
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
}
span {
font-style: inherit;
font-weight: inherit;
}
strong {
color: #363636;
font-weight: 700;
}
.container {
position: relative;
margin: 0 auto;
}
.field.is-grouped {
display: flex;
justify-content: flex-start;
}
.field.is-grouped > .control {
flex-shrink: 0;
}
.field.is-grouped > .control:not(:last-child) {
margin-bottom: 0;
margin-right: .75rem;
}
.field.is-grouped.is-grouped-multiline {
flex-wrap: wrap;
}
.field.is-grouped.is-grouped-multiline > .control:last-child, .field.is-grouped.is-grouped-multiline > .control:not(:last-child) {
margin-bottom: .75rem;
}
.field.is-grouped.is-grouped-multiline:last-child {
margin-bottom: -.75rem;
}
.control {
font-size: 1rem;
position: relative;
text-align: left;
}
.tags {
align-items: center;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.tags .tag {
margin-bottom: .5rem;
}
.tags .tag:not(:last-child) {
margin-right: .5rem;
}
.tags:last-child {
margin-bottom: -.5rem;
}
.tags.has-addons .tag {
margin-right: 0;
}
.tags.has-addons .tag:not(:first-child) {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
.tags.has-addons .tag:not(:last-child) {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
.tag:not(body) {
align-items: center;
background-color: #f5f5f5;
border-radius: 4px;
color: #4a4a4a;
display: inline-flex;
font-size: .75rem;
height: 2em;
justify-content: center;
line-height: 1.5;
padding-left: .75em;
padding-right: .75em;
white-space: nowrap;
}
.tag:not(body).is-light {
background-color: #f5f5f5;
color: #363636;
}
.tag:not(body).is-info {
background-color: #209cee;
color: #fff;
}
.tag:not(body).is-danger {
background-color: #f48f42;
color: #fff;
}
.tag:not(body).is-success {
background-color: #23d160;
color: #fff;
}
.title {
word-break: break-word;
color: #363636;
font-size: 2rem;
font-weight: 600;
line-height: 1.125;
}
.hero {
align-items: stretch;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.hero.is-dark {
background-color: #363636;
color: #f5f5f5;
}
.hero.is-dark strong {
color: inherit;
}
.hero.is-dark .title {
color: #f5f5f5;
}
.hero.is-fullheight .hero-body {
align-items: center;
display: flex;
}
.hero.is-fullheight .hero-body > .container {
flex-grow: 1;
flex-shrink: 1;
}
.hero.is-fullheight {
min-height: 100vh;
}
.hero-body {
flex-grow: 1;
flex-shrink: 0;
padding: 3rem 1.5rem;
}
a {
color: #3273dc;
cursor: pointer;
text-decoration: none;
}
a:hover {
color: #363636;
}
a.is-dark {
color: #fff;
}
@media screen and (min-width: 1088px) {
.container {
max-width: 960px;
width: 960px;
}
}
@media screen and (min-width: 1280px) {
.container {
max-width: 1152px;
width: 1152px;
}
}
@media screen and (min-width: 1472px) {
.container {
max-width: 1344px;
width: 1344px;
}
}
</style>
</head>
<body>
<section class="hero is-dark is-fullheight">
<div class="hero-body">
<div class="container">
<h1 class="title">Arionum Node</h1>
<div class="field is-grouped is-grouped-multiline">
<div class="control">
<div class="tags has-addons">
<strong class="tag is-success">Current Block</strong>
<span class="tag is-light"><?= $current['height']; ?></span>
</div>
</div>
<div class="control">
<div class="tags has-addons">
<strong class="tag is-danger">Version</strong>
<span class="tag is-light"><?= VERSION; ?></span>
</div>
</div>
<div class="control">
<div class="tags has-addons">
<strong class="tag is-info">Public API</strong>
<span class="tag is-light"><?= ($_config['public_api']) ? 'yes' : 'no'; ?></span>
</div>
</div>
<div class="control">
<a class="tags is-dark" href="./doc/" target="_blank">
<strong class="tag is-info">Documentation</strong>
</a>
</div>
</div>
</div>
</div>
</section>
</body>
</html>

527
peer.php
View File

@@ -1,228 +1,299 @@
<?php
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
www.arionum.com
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.
*/
require_once("include/init.inc.php");
$trx = new Transaction;
$block=new Block;
$q=$_GET['q'];
// the data is sent as json, in $_POST['data']
if(!empty($_POST['data'])){
$data=json_decode(trim($_POST['data']),true);
}
// make sure it's the same coin and not testnet
if($_POST['coin']!=$_config['coin']) api_err("Invalid coin");
$ip=$_SERVER['REMOTE_ADDR'];
$ip=filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
// peer with the current node
if($q=="peer"){
// sanitize the hostname
$hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL);
if (!filter_var($hostname, FILTER_VALIDATE_URL)) api_err("invalid-hostname");
// if it's already peered, only repeer on request
$res=$db->single("SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",array(":hostname"=>$hostname,":ip"=>$ip));
if($res==1){
if($data['repeer']==1){
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
if($res!==false) api_echo("re-peer-ok");
else api_err("re-peer failed - $result");
}
api_echo("peer-ok-already");
}
// if we have enough peers, add it to DB as reserve
$res=$db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0");
$reserve=1;
if($res<$_config['max_peers']) $reserve=0;
$db->run("INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",array(":ip"=>$ip, ":hostname2"=>$hostname,":hostname"=>$hostname, ":reserve"=>$reserve));
// re-peer to make sure the peer is valid
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
if($res!==false) api_echo("re-peer-ok");
else{
$db->run("DELETE FROM peers WHERE ip=:ip",array(":ip"=>$ip));
api_err("re-peer failed - $result");
}
}
elseif($q=="ping"){
// confirm peer is active
api_echo("pong");
} elseif($q=="submitTransaction"){
// receive a new transaction from a peer
$current=$block->current();
// no transactions accepted if the sanity is syncing
if($_config['sanity_sync']==1) api_err("sanity-sync");
$data['id']=san($data['id']);
// validate transaction data
if(!$trx->check($data)) api_err("Invalid transaction");
$hash=$data['id'];
// make sure it's not already in mempool
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash));
if($res!=0) api_err("The transaction is already in mempool");
// make sure the peer is not flooding us with transactions
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE src=:src",array(":src"=>$data['src']));
if($res>25) api_err("Too many transactions from this address in mempool. Please rebroadcast later.");
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer",array(":peer"=>$ip));
if($res>$_config['peer_max_mempool']) api_error("Too many transactions broadcasted from this peer");
// make sure the transaction is not already on the blockchain
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$hash));
if($res!=0) api_err("The transaction is already in a block");
$acc=new Account;
$src=$acc->get_address($data['public_key']);
// make sure the sender has enough balance
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src));
if($balance<$val+$fee) api_err("Not enough funds");
// make sure the sender has enough pending balance
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src));
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)");
// add to mempool
$trx->add_mempool($data, $ip);
// rebroadcast the transaction to some peers unless the transaction is smaller than the average size of transactions in mempool - protect against garbage data flooding
$res=$db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ",array(":src"=>$data['src']));
if($res['c']<$_config['max_mempool_rebroadcast']&&$res['v']/$res['c']<$data['val']) system("php propagate.php transaction '$data[id]' > /dev/null 2>&1 &");
api_echo("transaction-ok");
}
elseif($q=="submitBlock"){
// receive a new block from a peer
// if sanity sync, refuse all
if($_config['sanity_sync']==1){ _log('['.$ip."] Block rejected due to sanity sync"); api_err("sanity-sync"); }
$data['id']=san($data['id']);
$current=$block->current();
// block already in the blockchain
if($current['id']==$data['id']) api_echo("block-ok");
if($data['date']>time()+30) api_err("block in the future");
if($current['height']==$data['height']&&$current['id']!=$data['id']){
// different forks, same height
$accept_new=false;
if($current['transactions']<$data['transactions']){
// accept the one with most transactions
$accept_new=true;
} elseif($current['transactions']==$data['transactions']) {
// convert the first 12 characters from hex to decimal and the block with the largest number wins
$no1=hexdec(substr(coin2hex($current['id']),0,12));
$no2=hexdec(substr(coin2hex($data['id']),0,12));
if(gmp_cmp($no1,$no2)==1){
$accept_new=true;
}
}
if($accept_new){
// if the new block is accepted, run a microsanity to sync it
_log('['.$ip."] Starting microsanity - $data[height]");
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
api_echo("microsanity");
} else {
_log('['.$ip."] suggesting reverse-microsanity - $data[height]");
api_echo("reverse-microsanity"); // if it's not, suggest to the peer to get the block from us
}
}
// if it's not the next block
if($current['height']!=$data['height']-1) {
// if the height of the block submitted is lower than our current height, send them our current block
if($data['height']<$current['height']){
$pr=$db->row("SELECT * FROM peers WHERE ip=:ip",array(":ip"=>$ip));
if(!$pr) api_err("block-too-old");
$peer_host=base58_encode($pr['hostname']);
$pr['ip']=escapeshellcmd($pr['ip']);
system("php propagate.php block current '$peer_host' '$pr[ip]' > /dev/null 2>&1 &");
_log('['.$ip."] block too old, sending our current block - $data[height]");
api_err("block-too-old");
}
// if the block difference is bigger than 150, nothing should be done. They should sync via sanity
if($data['height']-$current['height']>150) {
_log('['.$ip."] block-out-of-sync - $data[height]");
api_err("block-out-of-sync");
}
// request them to send us a microsync with the latest blocks
_log('['.$ip."] requesting microsync - $current[height] - $data[height]");
api_echo(array("request"=>"microsync","height"=>$current['height'], "block"=>$current['id']));
}
// check block data
if(!$block->check($data)){
_log('['.$ip."] invalid block - $data[height]");
api_err("invalid-block");
}
$b=$data;
// add the block to the blockchain
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
if(!$res) {
_log('['.$ip."] invalid block data - $data[height]");
api_err("invalid-block-data");
}
_log('['.$ip."] block ok, repropagating - $data[height]");
// send it to all our peers
system("php propagate.php block '$data[id]' all all linear > /dev/null 2>&1 &");
api_echo("block-ok");
}
// return the current block, used in syncing
elseif($q=="currentBlock"){
$current=$block->current();
api_echo($current);
}
// return a specific block, used in syncing
elseif($q=="getBlock"){
$height=intval($data['height']);
$export=$block->export("",$height);
if(!$export) api_err("invalid-block");
api_echo($export);
}
elseif($q=="getBlocks"){
// returns X block starting at height, used in syncing
$height=intval($data['height']);
$r=$db->run("SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",array(":height"=>$height));
foreach($r as $x){
$blocks[$x['height']]=$block->export($x['id']);
}
api_echo($blocks);
}
// returns a full list of unblacklisted peers in a random order
elseif($q=="getPeers"){
$peers=$db->run("SELECT ip,hostname FROM peers WHERE blacklisted<UNIX_TIMESTAMP() ORDER by RAND()");
api_echo($peers);
} else {
api_err("Invalid request");
}
?>
<?php
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
www.arionum.com
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.
*/
const SANITY_LOCK_PATH = __DIR__.'/tmp/sanity-lock';
require_once __DIR__.'/include/init.inc.php';
header('Content-Type: application/json');
$trx = new Transaction();
$block = new Block();
$q = $_GET['q'];
// the data is sent as json, in $_POST['data']
if (!empty($_POST['data'])) {
$data = json_decode(trim($_POST['data']), true);
}
// make sure it's the same coin and not testnet
if ($_POST['coin'] != $_config['coin']) {
api_err("Invalid coin");
}
$ip = san_ip($_SERVER['REMOTE_ADDR']);
$ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
// there should not be any local peering attempts
if($_SERVER['REMOTE_ADDR']==$_SERVER['SERVER_ADDR']){
api_err("invalid-ip");
}
// peer with the current node
if ($q == "peer") {
// sanitize the hostname
$hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL);
$bad_peers = ["127.", "localhost", "10.", "192.168.","172.16.","172.17.","172.18.","172.19.","172.20.","172.21.","172.22.","172.23.","172.24.","172.25.","172.26.","172.27.","172.28.","172.29.","172.30.","172.31."];
$tpeer=str_replace(["https://","http://","//"], "", $hostname);
foreach ($bad_peers as $bp) {
if (strpos($tpeer, $bp)===0) {
api_err("invalid-hostname");
}
}
if (!filter_var($hostname, FILTER_VALIDATE_URL)) {
api_err("invalid-hostname");
}
$hostname = san_host($hostname);
// if it's already peered, only repeer on request
$res = $db->single(
"SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",
[":hostname" => $hostname, ":ip" => $ip]
);
if ($res == 1) {
if ($data['repeer'] == 1) {
$res = peer_post($hostname."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
if ($res !== false) {
api_echo("re-peer-ok");
} else {
api_err("re-peer failed - $result");
}
}
api_echo("peer-ok-already");
}
// if we have enough peers, add it to DB as reserve
$res = $db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0");
$reserve = 1;
if ($res < $_config['max_peers']) {
$reserve = 0;
}
$db->run(
"INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",
[":ip" => $ip, ":hostname2" => $hostname, ":hostname" => $hostname, ":reserve" => $reserve]
);
// re-peer to make sure the peer is valid
$res = peer_post($hostname."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
if ($res !== false) {
api_echo("re-peer-ok");
} else {
$db->run("DELETE FROM peers WHERE ip=:ip", [":ip" => $ip]);
api_err("re-peer failed - $result");
}
} elseif ($q == "ping") {
// confirm peer is active
api_echo("pong");
} elseif ($q == "submitTransaction") {
// receive a new transaction from a peer
$current = $block->current();
// no transactions accepted if the sanity is syncing
if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
}
$data['id'] = san($data['id']);
// validate transaction data
if (!$trx->check($data)) {
api_err("Invalid transaction");
}
$hash = $data['id'];
// make sure it's not already in mempool
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE id=:id", [":id" => $hash]);
if ($res != 0) {
api_err("The transaction is already in mempool");
}
// make sure the peer is not flooding us with transactions
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE src=:src", [":src" => $data['src']]);
if ($res > 25) {
api_err("Too many transactions from this address in mempool. Please rebroadcast later.");
}
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer", [":peer" => $ip]);
if ($res > $_config['peer_max_mempool']) {
api_error("Too many transactions broadcasted from this peer");
}
// make sure the transaction is not already on the blockchain
$res = $db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $hash]);
if ($res != 0) {
api_err("The transaction is already in a block");
}
$acc = new Account();
$src = $acc->get_address($data['public_key']);
// make sure the sender has enough balance
$balance = $db->single("SELECT balance FROM accounts WHERE id=:id", [":id" => $src]);
if ($balance < $val + $fee) {
api_err("Not enough funds");
}
// make sure the sender has enough pending balance
$memspent = $db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src", [":src" => $src]);
if ($balance - $memspent < $val + $fee) {
api_err("Not enough funds (mempool)");
}
// add to mempool
$trx->add_mempool($data, $ip);
// rebroadcast the transaction to some peers unless the transaction is smaller than the average size of transactions in mempool - protect against garbage data flooding
$res = $db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ", [":src" => $data['src']]);
if ($res['c'] < $_config['max_mempool_rebroadcast'] && $res['v'] / $res['c'] < $data['val']) {
$data['id']=escapeshellarg(san($data['id']));
system("php propagate.php transaction '$data[id]' > /dev/null 2>&1 &");
}
api_echo("transaction-ok");
} elseif ($q == "submitBlock") {
// receive a new block from a peer
// if sanity sync, refuse all
if ($_config['sanity_sync'] == 1) {
_log('['.$ip."] Block rejected due to sanity sync");
//api_err("sanity-sync");
}
$sanity_lock=fopen(SANITY_LOCK_PATH,'w+');
if(!flock($sanity_lock, LOCK_EX | LOCK_NB)){
_log('['.$ip."] Block rejected due to sanity sync");
api_err("sanity-sync");
}
// make sure sanity doesn't start while we add a new block and no other blocks are added at the same time
flock($sanity_lock, LOCK_EX);
$data['id'] = san($data['id']);
$current = $block->current();
// block already in the blockchain
if ($current['id'] == $data['id']) {
api_echo("block-ok");
}
if ($data['date'] > time() + 30) {
api_err("block in the future");
}
if ($current['height'] == $data['height'] && $current['id'] != $data['id']) {
// different forks, same height
$accept_new = false;
// convert the first 12 characters from hex to decimal and the block with the largest number wins
$no1 = hexdec(substr(coin2hex($current['id']), 0, 12));
$no2 = hexdec(substr(coin2hex($data['id']), 0, 12));
if (gmp_cmp($no1, $no2) == 1) {
$accept_new = true;
}
if ($accept_new) {
// if the new block is accepted, run a microsanity to sync it
_log('['.$ip."] Starting microsanity - $data[height]");
$ip=escapeshellarg($ip);
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
api_echo("microsanity");
} else {
_log('['.$ip."] suggesting reverse-microsanity - $data[height]");
api_echo("reverse-microsanity"); // if it's not, suggest to the peer to get the block from us
}
}
// if it's not the next block
if ($current['height'] != $data['height'] - 1) {
// if the height of the block submitted is lower than our current height, send them our current block
if ($data['height'] < $current['height']) {
$pr = $db->row("SELECT * FROM peers WHERE ip=:ip", [":ip" => $ip]);
if (!$pr) {
api_err("block-too-old");
}
$peer_host = escapeshellcmd(base58_encode($pr['hostname']));
$pr['ip'] = escapeshellcmd(san_ip($pr['ip']));
system("php propagate.php block current '$peer_host' '$pr[ip]' > /dev/null 2>&1 &");
_log('['.$ip."] block too old, sending our current block - $data[height]");
api_err("block-too-old");
}
// if the block difference is bigger than 150, nothing should be done. They should sync via sanity
if ($data['height'] - $current['height'] > 150) {
_log('['.$ip."] block-out-of-sync - $data[height]");
api_err("block-out-of-sync");
}
// request them to send us a microsync with the latest blocks
_log('['.$ip."] requesting microsync - $current[height] - $data[height]");
api_echo(["request" => "microsync", "height" => $current['height'], "block" => $current['id']]);
}
// check block data
if (!$block->check($data)) {
_log('['.$ip."] invalid block - $data[height]");
api_err("invalid-block");
}
$b = $data;
// add the block to the blockchain
$res = $block->add(
$b['height'],
$b['public_key'],
$b['nonce'],
$b['data'],
$b['date'],
$b['signature'],
$b['difficulty'],
$b['reward_signature'],
$b['argon']
);
if (!$res) {
_log('['.$ip."] invalid block data - $data[height]");
api_err("invalid-block-data");
}
_log('['.$ip."] block ok, repropagating - $data[height]");
// send it to all our peers
$data['id']=escapeshellcmd(san($data['id']));
system("php propagate.php block '$data[id]' all all linear > /dev/null 2>&1 &");
api_echo("block-ok");
} // return the current block, used in syncing
elseif ($q == "currentBlock") {
$current = $block->current();
api_echo($current);
} // return a specific block, used in syncing
elseif ($q == "getBlock") {
$height = intval($data['height']);
$export = $block->export("", $height);
if (!$export) {
api_err("invalid-block");
}
api_echo($export);
} elseif ($q == "getBlocks") {
// returns X block starting at height, used in syncing
$height = intval($data['height']);
$r = $db->run(
"SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",
[":height" => $height]
);
foreach ($r as $x) {
$blocks[$x['height']] = $block->export($x['id']);
}
api_echo($blocks);
} // returns a full list of unblacklisted peers in a random order
elseif ($q == "getPeers") {
$peers = $db->run("SELECT ip,hostname FROM peers WHERE blacklisted<UNIX_TIMESTAMP() ORDER by RAND()");
api_echo($peers);
} else {
api_err("Invalid request");
}

View File

@@ -1,7 +1,7 @@
<?php
/*
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
Copyright (c) 2018 AroDev
www.arionum.com
@@ -24,117 +24,161 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
*/
set_time_limit(360);
require_once("include/init.inc.php");
$block= new Block();
require_once __DIR__.'/include/init.inc.php';
$block = new Block();
$type=san($argv[1]);
$id=san($argv[2]);
$debug=false;
$linear=false;
$type = san($argv[1]);
$id = san($argv[2]);
$debug = false;
$linear = false;
// if debug mode, all data is printed to console, no background processes
if(trim($argv[5])=='debug') $debug=true;
if(trim($argv[5])=='linear') $linear=true;
$peer=san(trim($argv[3]));
if (trim($argv[5]) == 'debug') {
$debug = true;
}
if (trim($argv[5]) == 'linear') {
$linear = true;
}
$peer = san(trim($argv[3]));
// broadcasting a block to all peers
if((empty($peer)||$peer=='all')&&$type=="block"){
$whr="";
if($id=="current") {
$current=$block->current();
$id=$current['id'];
}
$data=$block->export($id);
$id=san($id);
if($data===false||empty($data)) die("Could not export block");
$data=json_encode($data);
// cache it to reduce the load
$res=file_put_contents("tmp/$id",$data);
if($res===false) die("Could not write the cache file");
// broadcasting to all peers
$ewhr="";
// boradcasting to only certain peers
if($linear==true) $ewhr=" ORDER by RAND() LIMIT 5";
$r=$db->run("SELECT * FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 $ewhr");
foreach($r as $x) {
// encode the hostname in base58 and sanitize the IP to avoid any second order shell injections
$host=base58_encode($x['hostname']);
$ip=filter_var($x['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
// fork a new process to send the blocks async
if($debug) system("php propagate.php '$type' '$id' '$host' '$ip' debug");
elseif($linear) system("php propagate.php '$type' '$id' '$host' '$ip' linear");
else system("php propagate.php '$type' '$id' '$host' 'ip' > /dev/null 2>&1 &");
}
exit;
if ((empty($peer) || $peer == 'all') && $type == "block") {
$whr = "";
if ($id == "current") {
$current = $block->current();
$id = $current['id'];
}
$data = $block->export($id);
$id = san($id);
if ($data === false || empty($data)) {
die("Could not export block");
}
$data = json_encode($data);
// cache it to reduce the load
$res = file_put_contents("tmp/$id", $data);
if ($res === false) {
die("Could not write the cache file");
}
// broadcasting to all peers
$ewhr = "";
// boradcasting to only certain peers
if ($linear == true) {
$ewhr = " ORDER by RAND() LIMIT 5";
}
$r = $db->run("SELECT * FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 $ewhr");
foreach ($r as $x) {
if($x['hostname']==$_config['hostname']) continue;
// encode the hostname in base58 and sanitize the IP to avoid any second order shell injections
$host = escapeshellcmd(base58_encode($x['hostname']));
$ip = escapeshellcmd(filter_var($x['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE));
// fork a new process to send the blocks async
$type=escapeshellcmd(san($type));
$id=escapeshellcmd(san($id));
if ($debug) {
system("php propagate.php '$type' '$id' '$host' '$ip' debug");
} elseif ($linear) {
system("php propagate.php '$type' '$id' '$host' '$ip' linear");
} else {
system("php propagate.php '$type' '$id' '$host' 'ip' > /dev/null 2>&1 &");
}
}
exit;
}
// broadcast a block to a single peer (usually a forked process from above)
if($type=="block"){
// current block or read cache
if($id=="current"){
$current=$block->current();
$data=$block->export($current['id']);
if(!$data) { echo "Invalid Block data"; exit; }
} else {
$data=file_get_contents("tmp/$id");
if(empty($data)) { echo "Invalid Block data"; exit; }
$data=json_decode($data,true);
}
$hostname=base58_decode($peer);
// send the block as POST to the peer
echo "Block sent to $hostname:\n";
$response= peer_post($hostname."/peer.php?q=submitBlock",$data,60, $debug);
if($response=="block-ok") { echo "Block $i accepted. Exiting.\n"; exit;}
elseif($response['request']=="microsync"){
// the peer requested us to send more blocks, as it's behind
echo "Microsync request\n";
$height=intval($response['height']);
$bl=san($response['block']);
$current=$block->current();
// maximum microsync is 10 blocks, for more, the peer should sync by sanity
if($current['height']-$height>10) { echo "Height Differece too high\n"; exit; }
$last_block=$block->get($height);
// if their last block does not match our blockchain/fork, ignore the request
if ($last_block['id'] != $bl ) { echo "Last block does not match\n"; exit; }
echo "Sending the requested blocks\n";
//start sending the requested block
for($i=$height+1;$i<=$current['height'];$i++){
$data=$block->export("",$i);
$response = peer_post($hostname."/peer.php?q=submitBlock",$data,60,$debug);
if($response!="block-ok") { echo "Block $i not accepted. Exiting.\n"; exit;}
echo "Block\t$i\t accepted\n";
}
} elseif($response=="reverse-microsanity"){
// the peer informe us that we should run a microsanity
echo "Running microsanity\n";
$ip=trim($argv[4]);
$ip=filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
if(empty($ip)) die("Invalid IP");
// fork a microsanity in a new process
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
}
else echo "Block not accepted!\n";
// broadcast a block to a single peer (usually a forked process from above)
if ($type == "block") {
// current block or read cache
if ($id == "current") {
$current = $block->current();
$data = $block->export($current['id']);
if (!$data) {
echo "Invalid Block data";
exit;
}
} else {
$data = file_get_contents("tmp/$id");
if (empty($data)) {
echo "Invalid Block data";
exit;
}
$data = json_decode($data, true);
}
$hostname = base58_decode($peer);
// send the block as POST to the peer
echo "Block sent to $hostname:\n";
$response = peer_post($hostname."/peer.php?q=submitBlock", $data, 60, $debug);
_log("Propagating block to $hostname - [result: $response] $data[height] - $data[id]",2);
if ($response == "block-ok") {
echo "Block $i accepted. Exiting.\n";
exit;
} elseif ($response['request'] == "microsync") {
// the peer requested us to send more blocks, as it's behind
echo "Microsync request\n";
$height = intval($response['height']);
$bl = san($response['block']);
$current = $block->current();
// maximum microsync is 10 blocks, for more, the peer should sync by sanity
if ($current['height'] - $height > 10) {
echo "Height Differece too high\n";
exit;
}
$last_block = $block->get($height);
// if their last block does not match our blockchain/fork, ignore the request
if ($last_block['id'] != $bl) {
echo "Last block does not match\n";
exit;
}
echo "Sending the requested blocks\n";
//start sending the requested block
for ($i = $height + 1; $i <= $current['height']; $i++) {
$data = $block->export("", $i);
$response = peer_post($hostname."/peer.php?q=submitBlock", $data, 60, $debug);
if ($response != "block-ok") {
echo "Block $i not accepted. Exiting.\n";
exit;
}
echo "Block\t$i\t accepted\n";
}
} elseif ($response == "reverse-microsanity") {
// the peer informe us that we should run a microsanity
echo "Running microsanity\n";
$ip = trim($argv[4]);
$ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
if (empty($ip)) {
die("Invalid IP");
}
// fork a microsanity in a new process
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
} else {
echo "Block not accepted!\n";
}
}
// broadcast a transaction to some peers
if($type=="transaction"){
if ($type == "transaction") {
$trx = new Transaction();
// get the transaction data
$data = $trx->export($id);
$trx=new Transaction;
// get the transaction data
$data=$trx->export($id);
if (!$data) {
echo "Invalid transaction id\n";
exit;
}
// if the transaction was first sent locally, we will send it to all our peers, otherwise to just a few
if ($data['peer'] == "local") {
$r = $db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP()");
} else {
if(!$data){ echo "Invalid transaction id\n"; exit; }
// if the transaction was first sent locally, we will send it to all our peers, otherwise to just a few
if($data['peer']=="local") $r=$db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP()");
else $r=$db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 ORDER by RAND() LIMIT ".intval($_config['transaction_propagation_peers']));
foreach($r as $x){
$res= peer_post($x['hostname']."/peer.php?q=submitTransaction",$data);
if(!$res) echo "Transaction not accepted\n";
else echo "Transaction accepted\n";
}
$r = $db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 ORDER by RAND() LIMIT :limit",["limit"=>intval($_config['transaction_propagation_peers'])]);
}
foreach ($r as $x) {
$res = peer_post($x['hostname']."/peer.php?q=submitTransaction", $data);
if (!$res) {
echo "Transaction not accepted\n";
} else {
echo "Transaction accepted\n";
}
}
}
?>

1237
sanity.php

File diff suppressed because it is too large Load Diff

3
tmp/.gitignore vendored Executable file
View File

@@ -0,0 +1,3 @@
*
!.gitignore
!db-update

View File

493
util.php
View File

@@ -1,7 +1,7 @@
<?php
/*
/*
The MIT License (MIT)
Copyright (c) 2018 AroDev
Copyright (c) 2018 AroDev
www.arionum.com
@@ -24,17 +24,16 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
*/
// make sure it's not accessible in the browser
if(php_sapi_name() !== 'cli') die("This should only be run as cli");
require_once("include/init.inc.php");
$cmd=trim($argv[1]);
if (php_sapi_name() !== 'cli') {
die("This should only be run as cli");
}
require_once __DIR__.'/include/init.inc.php';
$cmd = trim($argv[1]);
/**
* @api {php util.php} clean Clean
* @api {php util.php} clean Clean
* @apiName clean
* @apiGroup UTIL
* @apiDescription Cleans the entire database
@@ -43,16 +42,21 @@ $cmd=trim($argv[1]);
* php util.php clean
*/
if($cmd=='clean'){
$tables=array("blocks","accounts","transactions","mempool");
foreach($tables as $table) $db->run("DELETE FROM {$table}");
if ($cmd == 'clean') {
if (file_exists("tmp/sanity-lock")) {
die("Sanity running. Wait for it to finish");
}
touch("tmp/sanity-lock");
$db->run("SET foreign_key_checks=0;");
$tables = ["accounts", "transactions", "mempool", "masternode","blocks"];
foreach ($tables as $table) {
$db->run("TRUNCATE TABLE {$table}");
}
$db->run("SET foreign_key_checks=1;");
echo "\n The database has been cleared\n";
}
/**
echo "\n The database has been cleared\n";
unlink("tmp/sanity-lock");
} /**
* @api {php util.php} pop Pop
* @apiName pop
* @apiGroup UTIL
@@ -64,13 +68,16 @@ echo "\n The database has been cleared\n";
* php util.php pop 1
*/
elseif($cmd=='pop'){
$no=intval($argv[2]);
$block=new Block;
$block->pop($no);
}
/**
elseif ($cmd == 'pop') {
if (file_exists("tmp/sanity-lock")) {
die("Sanity running. Wait for it to finish");
}
touch("tmp/sanity-lock");
$no = intval($argv[2]);
$block = new Block();
$block->pop($no);
unlink("tmp/sanity-lock");
} /**
* @api {php util.php} block-time Block-time
* @apiName block-time
* @apiGroup UTIL
@@ -86,29 +93,28 @@ elseif($cmd=='pop'){
* Average block time: 217 seconds
*/
elseif($cmd=='block-time'){
$t=time();
$r=$db->run("SELECT * FROM blocks ORDER by height DESC LIMIT 100");
$start=0;
foreach($r as $x){
if($start==0) $start=$x['date'];
$time=$t-$x['date'];
$t=$x['date'];
echo "$x[height] -> $time\n";
$end=$x['date'];
}
echo "Average block time: ".ceil(($start-$end)/100)." seconds\n";
}
/**
elseif ($cmd == 'block-time') {
$t = time();
$r = $db->run("SELECT * FROM blocks ORDER by height DESC LIMIT 100");
$start = 0;
foreach ($r as $x) {
if ($start == 0) {
$start = $x['date'];
}
$time = $t - $x['date'];
$t = $x['date'];
echo "$x[height]\t\t$time\t\t$x[difficulty]\n";
$end = $x['date'];
}
echo "Average block time: ".ceil(($start - $end) / 100)." seconds\n";
} /**
* @api {php util.php} peer Peer
* @apiName peer
* @apiGroup UTIL
* @apiDescription Creates a peering session with another node
*
* @apiParam {text} arg2 The Hostname of the other node
*
*
* @apiExample {cli} Example usage:
* php util.php peer http://peer1.arionum.com
*
@@ -117,12 +123,14 @@ echo "Average block time: ".ceil(($start-$end)/100)." seconds\n";
*/
elseif($cmd=="peer"){
$res=peer_post($argv[2]."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
if($res!==false) echo "Peering OK\n";
else echo "Peering FAIL\n";
}
/**
elseif ($cmd == "peer") {
$res = peer_post($argv[2]."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
if ($res !== false) {
echo "Peering OK\n";
} else {
echo "Peering FAIL\n";
}
} /**
* @api {php util.php} current Current
* @apiName current
* @apiGroup UTIL
@@ -155,15 +163,14 @@ elseif($cmd=="peer"){
*
*/
elseif ($cmd=="current") {
$block=new Block;
var_dump($block->current());
}
/**
elseif ($cmd == "current") {
$block = new Block();
var_dump($block->current());
} /**
* @api {php util.php} blocks Blocks
* @apiName blocks
* @apiGroup UTIL
* @apiDescription Prints the id and the height of the blocks >=arg2, max 100 or arg3
* @apiDescription Prints the id and the height of the blocks >=arg2, max 100 or arg3
*
* @apiParam {number} arg2 Starting height
*
@@ -180,16 +187,17 @@ elseif ($cmd=="current") {
* 10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m
*
*/
elseif($cmd=="blocks"){
$height=intval($argv[2]);
$limit=intval($argv[3]);
if($limit<1) $limit=100;
$r=$db->run("SELECT * FROM blocks WHERE height>:height ORDER by height ASC LIMIT $limit",array(":height"=>$height));
foreach($r as $x){
echo "$x[height]\t$x[id]\n";
}
}
/**
elseif ($cmd == "blocks") {
$height = intval($argv[2]);
$limit = intval($argv[3]);
if ($limit < 1) {
$limit = 100;
}
$r = $db->run("SELECT * FROM blocks WHERE height>:height ORDER by height ASC LIMIT :limit", [":height" => $height, ":limit"=>$limit]);
foreach ($r as $x) {
echo "$x[height]\t$x[id]\n";
}
} /**
* @api {php util.php} recheck-blocks Recheck-Blocks
* @apiName recheck-blocks
* @apiGroup UTIL
@@ -199,26 +207,33 @@ elseif ($cmd=="current") {
* php util.php recheck-blocks
*
*/
elseif($cmd=="recheck-blocks"){
$blocks=array();
$block=new Block();
$r=$db->run("SELECT * FROM blocks ORDER by height ASC");
foreach($r as $x){
$blocks[$x['height']]=$x;
$max_height=$x['height'];
}
for($i=2;$i<=$max_height;$i++){
$data=$blocks[$i];
$key=$db->single("SELECT public_key FROM accounts WHERE id=:id",array(":id"=>$data['generator']));
elseif ($cmd == "recheck-blocks") {
$blocks = [];
$block = new Block();
$r = $db->run("SELECT * FROM blocks ORDER by height ASC");
foreach ($r as $x) {
$blocks[$x['height']] = $x;
$max_height = $x['height'];
}
for ($i = 2; $i <= $max_height; $i++) {
$data = $blocks[$i];
if(!$block->mine($key,$data['nonce'], $data['argon'], $data['difficulty'], $blocks[$i-1]['id'],$blocks[$i-1]['height'])) {
_log("Invalid block detected. We should delete everything after $data[height] - $data[id]");
break;
}
}
}
/**
$key = $db->single("SELECT public_key FROM accounts WHERE id=:id", [":id" => $data['generator']]);
if (!$block->mine(
$key,
$data['nonce'],
$data['argon'],
$data['difficulty'],
$blocks[$i - 1]['id'],
$blocks[$i - 1]['height'],
$data['date']
)) {
_log("Invalid block detected. We should delete everything after $data[height] - $data[id]");
break;
}
}
} /**
* @api {php util.php} peers Peers
* @apiName peers
* @apiGroup UTIL
@@ -232,15 +247,16 @@ elseif($cmd=="recheck-blocks"){
* ...
* http://aro.master.hashpi.com active
*/
elseif($cmd=="peers"){
$r=$db->run("SELECT * FROM peers ORDER by reserve ASC");
$status="active";
if($x['reserve']==1) $status="reserve";
foreach($r as $x){
echo "$x[hostname]\t$status\n";
}
}
/**
elseif ($cmd == "peers") {
$r = $db->run("SELECT * FROM peers ORDER by reserve ASC");
$status = "active";
if ($x['reserve'] == 1) {
$status = "reserve";
}
foreach ($r as $x) {
echo "$x[hostname]\t$status\n";
}
} /**
* @api {php util.php} mempool Mempool
* @apiName mempool
* @apiGroup UTIL
@@ -252,12 +268,10 @@ elseif($cmd=="recheck-blocks"){
* @apiSuccessExample {text} Success-Response:
* Mempool size: 12
*/
elseif($cmd=="mempool"){
$res=$db->single("SELECT COUNT(1) from mempool");
echo "Mempool size: $res\n";
}
/**
elseif ($cmd == "mempool") {
$res = $db->single("SELECT COUNT(1) from mempool");
echo "Mempool size: $res\n";
} /**
* @api {php util.php} delete-peer Delete-peer
* @apiName delete-peer
* @apiGroup UTIL
@@ -271,24 +285,25 @@ echo "Mempool size: $res\n";
* @apiSuccessExample {text} Success-Response:
* Peer removed
*/
elseif($cmd=="delete-peer"){
$peer=trim($argv[2]);
if(empty($peer)) die("Invalid peer");
$db->run("DELETE FROM peers WHERE ip=:ip",array(":ip"=>$peer));
echo "Peer removed\n";
}elseif($cmd=="recheck-peers"){
$r=$db->run("SELECT * FROM peers");
foreach($r as $x){
$a=peer_post($x['hostname']."/peer.php?q=ping");
if($a!="pong"){
echo "$x[hostname] -> failed\n";
$db->run("DELETE FROM peers WHERE id=:id",array(":id"=>$x['id']));
} else echo "$x[hostname] ->ok \n";
}
}
/**
elseif ($cmd == "delete-peer") {
$peer = trim($argv[2]);
if (empty($peer)) {
die("Invalid peer");
}
$db->run("DELETE FROM peers WHERE ip=:ip", [":ip" => $peer]);
echo "Peer removed\n";
} elseif ($cmd == "recheck-peers") {
$r = $db->run("SELECT * FROM peers");
foreach ($r as $x) {
$a = peer_post($x['hostname']."/peer.php?q=ping");
if ($a != "pong") {
echo "$x[hostname] -> failed\n";
$db->run("DELETE FROM peers WHERE id=:id", [":id" => $x['id']]);
} else {
echo "$x[hostname] ->ok \n";
}
}
} /**
* @api {php util.php} peers-block Peers-Block
* @apiName peers-block
* @apiGroup UTIL
@@ -302,23 +317,24 @@ elseif($cmd=="delete-peer"){
* ...
* http://peer10.arionum.com 16849
*/
elseif($cmd=="peers-block"){
$only_diff=false;
if($argv[2]=="diff"){
$current=$db->single("SELECT height FROM blocks ORDER by height DESC LIMIT 1");
$only_diff=true;
}
$r=$db->run("SELECT * FROM peers WHERE blacklisted<UNIX_TIMESTAMP()");
foreach($r as $x){
$a=peer_post($x['hostname']."/peer.php?q=currentBlock",array(),5);
$enc=base58_encode($x['hostname']);
if($argv[2]=="debug") echo "$enc\t";
if($only_diff==false||$current!=$a['height']) echo "$x[hostname]\t$a[height]\n";
elseif ($cmd == "peers-block") {
$only_diff = false;
if ($argv[2] == "diff") {
$current = $db->single("SELECT height FROM blocks ORDER by height DESC LIMIT 1");
$only_diff = true;
}
$r = $db->run("SELECT * FROM peers WHERE blacklisted<UNIX_TIMESTAMP()");
foreach ($r as $x) {
$a = peer_post($x['hostname']."/peer.php?q=currentBlock", [], 5);
$enc = base58_encode($x['hostname']);
if ($argv[2] == "debug") {
echo "$enc\t";
}
}
/**
if ($only_diff == false || $current != $a['height']) {
echo "$x[hostname]\t$a[height]\n";
}
}
} /**
* @api {php util.php} balance Balance
* @apiName balance
* @apiGroup UTIL
@@ -333,14 +349,15 @@ elseif($cmd=="peers-block"){
* Balance: 2,487
*/
elseif($cmd=="balance"){
elseif ($cmd == "balance") {
$id = san($argv[2]);
$res = $db->single(
"SELECT balance FROM accounts WHERE id=:id OR public_key=:id2 LIMIT 1",
[":id" => $id, ":id2" => $id]
);
$id=san($argv[2]);
$res=$db->single("SELECT balance FROM accounts WHERE id=:id OR public_key=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id));
echo "Balance: ".number_format($res)."\n";
}
/**
echo "Balance: ".number_format($res)."\n";
} /**
* @api {php util.php} block Block
* @apiName block
* @apiGroup UTIL
@@ -373,14 +390,12 @@ elseif($cmd=="balance"){
* int(0)
* }
*/
elseif($cmd=="block"){
$id=san($argv[2]);
$res=$db->row("SELECT * FROM blocks WHERE id=:id OR height=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id));
var_dump($res);
elseif ($cmd == "block") {
$id = san($argv[2]);
$res = $db->row("SELECT * FROM blocks WHERE id=:id OR height=:id2 LIMIT 1", [":id" => $id, ":id2" => $id]);
}
/**
var_dump($res);
} /**
* @api {php util.php} check-address Check-Address
* @apiName check-address
* @apiGroup UTIL
@@ -394,14 +409,18 @@ elseif($cmd=="block"){
* @apiSuccessExample {text} Success-Response:
* The address is valid
*/
elseif($cmd=="check-address"){
$dst=trim($argv[2]);
$acc=new Account;
if(!$acc->valid($dst)) die("Invalid address");
$dst_b=base58_decode($dst);
if(strlen($dst_b)!=64) die("Invalid address - ".strlen($dst_b)." bytes");
echo "The address is valid\n";
elseif ($cmd == "check-address") {
$dst = trim($argv[2]);
$acc = new Account();
if (!$acc->valid($dst)) {
die("Invalid address");
}
$dst_b = base58_decode($dst);
if (strlen($dst_b) != 64) {
die("Invalid address - ".strlen($dst_b)." bytes");
}
echo "The address is valid\n";
}
/**
* @api {php util.php} get-address Get-Address
@@ -418,15 +437,165 @@ elseif($cmd=="check-address"){
* 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
*/
elseif($cmd=='get-address'){
$public_key=trim($argv2);
if(strlen($public_key)<32) die("Invalid public key");
elseif ($cmd == 'get-address') {
$public_key = trim($argv2);
if (strlen($public_key) < 32) {
die("Invalid public key");
}
print($acc->get_address($public_key));
/**
* @api {php util.php} clean-blacklist Clean-Blacklist
* @apiName clean-blacklist
* @apiGroup UTIL
* @apiDescription Removes all the peers from blacklist
*
* @apiExample {cli} Example usage:
* php util.php clean-blacklist
*
*/
} elseif ($cmd == 'clean-blacklist') {
$db->run("UPDATE peers SET blacklisted=0, fails=0, stuckfail=0");
echo "All the peers have been removed from the blacklist\n";
} elseif ($cmd == 'resync-accounts') {
die("Currently disabled due to asset implementation.");
// resyncs the balance on all accounts
if (file_exists("tmp/sanity-lock")) {
die("Sanity running. Wait for it to finish");
}
touch("tmp/sanity-lock");
// lock table to avoid race conditions on blocks
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE, masternode WRITE, peers write, config WRITE, assets WRITE, assets_balance WRITE, assets_market WRITE");
$r=$db->run("SELECT * FROM accounts");
foreach ($r as $x) {
$alias=$x['alias'];
if (empty($alias)) {
$alias="A";
}
$rec=$db->single("SELECT SUM(val) FROM transactions WHERE (dst=:id or dst=:alias) AND (height<80000 OR version!=100) and version<111", [":id"=>$x['id'], ":alias"=>$alias]);
$releases=$db->single("SELECT COUNT(1) FROM transactions WHERE dst=:id AND version=103", [":id"=>$x['id']]);
if ($releases>0) { //masternode releases
$rec+=$releases*100000;
}
$spent=$db->single("SELECT SUM(val+fee) FROM transactions WHERE public_key=:pub AND version>0", [":pub"=>$x['public_key']]);
if ($spent==false) {
$spent=0;
}
$balance=round(($rec-$spent), 8);
if ($x['balance']!=$balance) {
echo "rec: $rec, spent: $spent, bal: $x[balance], should be: $balance - $x[id] $x[public_key]\n";
if (trim($argv[2])!="check") {
$db->run("UPDATE accounts SET balance=:bal WHERE id=:id", [":id"=>$x['id'], ":bal"=>$balance]);
}
}
}
$db->exec("UNLOCK TABLES");
echo "All done";
unlink("tmp/sanity-lock");
} elseif ($cmd=="compare-blocks") {
$block=new Block();
$current=$block->current();
$peer=trim($argv[2]);
$limit=intval($argv[3]);
if ($limit==0) {
$limit=5000;
}
for ($i=$current['height']-$limit;$i<=$current['height'];$i++) {
$data=peer_post($peer."/peer.php?q=getBlock", ["height" => $i]);
if ($data==false) {
continue;
}
$our=$block->export(false, $i);
if ($data!=$our) {
echo "Failed block -> $i\n";
if ($argv[4]=="dump") {
echo "\n\n ---- Internal ----\n\n";
var_dump($our);
echo "\n\n ---- External ----\n\n";
var_dump($data);
}
}
}
} elseif ($cmd=='compare-accounts') {
$peer=trim($argv[2]);
$r=$db->run("SELECT id,balance FROM accounts");
foreach ($r as $x) {
$data=peer_post($peer."/api.php?q=getBalance", ["account" => $x['id']]);
if ($data==false) {
continue;
}
if ($data!=$x['balance']) {
echo "$x[id]\t\t$x[balance]\t$data\n";
}
}
} elseif ($cmd=='masternode-hash') {
$res=$db->run("SELECT * FROM masternode ORDER by public_key ASC");
$block=new Block();
$current=$block->current();
echo "Height:\t\t$current[height]\n";
echo "Hash:\t\t".md5(json_encode($res))."\n\n";
} elseif ($cmd=='accounts-hash') {
$res=$db->run("SELECT * FROM accounts ORDER by id ASC");
$block=new Block();
$current=$block->current();
echo "Height:\t\t$current[height]\n";
echo "Hash:\t\t".md5(json_encode($res))."\n\n";
} elseif ($cmd == "version") {
echo "\n\n".VERSION."\n\n";
} elseif ($cmd == "sendblock") {
$peer=trim($argv[3]);
if (!filter_var($peer, FILTER_VALIDATE_URL)) {
die("Invalid peer hostname");
}
$peer = filter_var($peer, FILTER_SANITIZE_URL);
$height=intval($argv[2]);
$block=new Block();
$data = $block->export("", $height);
if ($data===false) {
die("Could not find this block");
}
$response = peer_post($peer."/peer.php?q=submitBlock", $data, 60, true);
var_dump($response);
} elseif ($cmd == "recheck-external-blocks") {
$peer=trim($argv[2]);
if (!filter_var($peer, FILTER_VALIDATE_URL)) {
die("Invalid peer hostname");
}
$peer = filter_var($peer, FILTER_SANITIZE_URL);
$blocks = [];
$block = new Block();
$height=intval($argv[3]);
$last=peer_post($peer."/peer.php?q=currentBlock");
$b=peer_post($peer."/peer.php?q=getBlock", ["height"=>$height]);
for ($i = $height+1; $i <= $last['height']; $i++) {
$c=peer_post($peer."/peer.php?q=getBlock", ["height"=>$i]);
if (!$block->mine(
$c['public_key'],
$c['nonce'],
$c['argon'],
$c['difficulty'],
$b['id'],
$b['height'],
$c['date']
)) {
print("Invalid block detected. $c[height] - $c[id]\n");
break;
}
echo "Block $i -> ok\n";
$b=$c;
}
} else {
echo "Invalid command\n";
echo "Invalid command\n";
}
?>