136 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
21 changed files with 2316 additions and 670 deletions

3
.gitignore vendored
View File

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

View File

@@ -2,24 +2,21 @@ dist: trusty
language: php
php:
- 7.2
- nightly
- 7.2
- 7.3
- nightly
matrix:
allow_failures:
- php: nightly
# This triggers builds to run on the new TravisCI infrastructure.
# See: https://docs.travis-ci.com/user/reference/trusty#Container-based-with-sudo%3A-false
sudo: false
- php: nightly
## Cache composer
cache:
directories:
- $HOME/.composer/cache
- $HOME/.composer/cache
before_script:
- travis_retry composer update --no-interaction --prefer-dist
- travis_retry composer update --no-interaction --prefer-dist
script:
- vendor/bin/phpcs -l --standard=psr2 . include
- vendor/bin/phpcs -l --standard=psr2 . include

150
api.php
View File

@@ -70,7 +70,7 @@ header('Content-Type: application/json');
use Arionum\Blacklist;
require_once __DIR__.'/include/init.inc.php';
error_reporting(0);
$ip = san_ip($_SERVER['REMOTE_ADDR']);
$ip = filter_var($ip, FILTER_VALIDATE_IP);
@@ -164,6 +164,7 @@ if ($q == "getAddress") {
*/
$account = $data['account'];
$public_key = san($data['public_key'] ?? '');
if (!empty($public_key) && strlen($public_key) < 32) {
api_err("Invalid public key");
}
@@ -184,7 +185,7 @@ if ($q == "getAddress") {
*
* @apiParam {string} [public_key] Public key
* @apiParam {string} [account] Account id / address
* @apiParam {numeric} [limit] Number of confirmed transactions, max 1000, min 1
* @apiParam {numeric} [limit] Number of confirmed transactions, max 100, min 1
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmation Number of confirmations
@@ -203,6 +204,7 @@ if ($q == "getAddress") {
*/
$account = san($data['account']);
$public_key = san($data['public_key'] ?? '');
if (!empty($public_key) && strlen($public_key) < 32) {
api_err("Invalid public key");
}
@@ -341,6 +343,7 @@ if ($q == "getAddress") {
*
* @apiParam {numeric} [height] Block Height
* @apiParam {string} [block] Block id
* @apiParam {boolean} [includeMiningRewards] Include mining rewards
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmations Number of confirmations
@@ -359,7 +362,13 @@ if ($q == "getAddress") {
*/
$height = san($data['height']);
$block = san($data['block']);
$ret = $trx->get_transactions($height, $block);
$includeMiningRewards = (
isset($data['includeMiningRewards']) &&
!($data['includeMiningRewards'] === '0' || $data['includeMiningRewards'] === 'false')
);
$ret = $trx->get_transactions($height, $block, $includeMiningRewards);
if ($ret === false) {
api_err("Invalid block");
} else {
@@ -410,6 +419,8 @@ if ($q == "getAddress") {
$version = 1;
}
if ($version==1) {
if (!$acc->valid($dst)) {
api_err("Invalid destination address");
@@ -426,7 +437,8 @@ if ($q == "getAddress") {
}
}
$public_key = san($data['public_key']);
if (!$acc->valid_key($public_key)) {
@@ -437,6 +449,9 @@ if ($q == "getAddress") {
api_err("Blacklisted account");
}
}
$private_key = san($data['private_key']);
if (!$acc->valid_key($private_key)) {
api_err("Invalid private key");
@@ -457,6 +472,7 @@ if ($q == "getAddress") {
api_err("Invalid Date");
}
$message=$data['message'];
if (strlen($message) > 128) {
api_err("The message must be less than 128 chars");
@@ -471,10 +487,11 @@ if ($q == "getAddress") {
if ($fee > 10 && $current['height'] > 10800) {
$fee = 10; //10800
}
if ($val < 0.00000001) {
if ($val < 0) {
api_err("Invalid value");
}
// set alias
if ($version==3) {
$fee=10;
@@ -510,6 +527,9 @@ if ($q == "getAddress") {
if (empty($private_key) && empty($signature)) {
api_err("Either the private_key or the signature must be sent");
}
if (empty($public_key)) {
$pk = coin2pem($private_key, true);
$pkey = openssl_pkey_get_private($pk);
@@ -537,11 +557,12 @@ if ($q == "getAddress") {
$transaction['id'] = $hash;
if (!$trx->check($transaction)) {
api_err("Transaction signature failed");
}
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE id=:id", [":id" => $hash]);
if ($res != 0) {
api_err("The transaction is already in mempool");
@@ -643,12 +664,18 @@ if ($q == "getAddress") {
* @apiGroup API
* @apiDescription Returns all the masternode data
*
* @apiParam {string} [public_key] Optional public key
*
*
* @apiSuccess {boolean} data masternode date
* @apiSuccess {json} data masternode date
*/
$res=$db->run("SELECT * FROM masternode ORDER by public_key ASC");
$bind=[];
$whr='';
$public_key=san($data['public_key']);
if(!empty($public_key)){
$whr="WHERE public_key=:public_key";
$bind[':public_key']=$public_key;
}
$res=$db->run("SELECT * FROM masternode $whr ORDER by public_key ASC",$bind);
api_echo(["masternodes"=>$res, "hash"=>md5(json_encode($res))]);
} elseif ($q == "getAlias") {
@@ -712,6 +739,7 @@ if ($q == "getAddress") {
* @apiSuccess {number} data.transactions The number of transactions known by the node.
* @apiSuccess {number} data.mempool The number of transactions in the mempool.
* @apiSuccess {number} data.masternodes The number of masternodes known by the node.
* @apiSuccess {number} data.peers The number of valid peers.
*/
$dbVersion = $db->single("SELECT val FROM config WHERE cfg='dbversion'");
$hostname = $db->single("SELECT val FROM config WHERE cfg='hostname'");
@@ -719,7 +747,7 @@ if ($q == "getAddress") {
$tr = $db->single("SELECT COUNT(1) FROM transactions");
$masternodes = $db->single("SELECT COUNT(1) FROM masternode");
$mempool = $db->single("SELECT COUNT(1) FROM mempool");
$peers = $db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP()");
api_echo([
'hostname' => $hostname,
'version' => VERSION,
@@ -728,7 +756,107 @@ if ($q == "getAddress") {
'transactions' => $tr,
'mempool' => $mempool,
'masternodes' => $masternodes,
'peers' => $peers
]);
} elseif ($q === 'checkAddress') {
/**
* @api {get} /api.php?q=checkAddress 22. checkAddress
* @apiName checkAddress
* @apiGroup API
* @apiDescription Checks the validity of an address.
*
* @apiParam {string} account Account id / address
* @apiParam {string} [public_key] Public key
*
* @apiSuccess {boolean} data True if the address is valid, false otherwise.
*/
$address=$data['account'];
$public_key=$data['public_key'];
$acc = new Account();
if (!$acc->valid($address)) {
api_err(false);
}
$dst_b = base58_decode($address);
if (strlen($dst_b) != 64) {
api_err(false);
}
if (!empty($public_key)) {
if($acc->get_address($public_key)!=$address){
api_err(false);
}
}
api_echo(true);
} elseif ($q === "assetBalance"){
$asset = san($data['asset']);
$public_key = san($data['public_key']);
$account = san($data['account']);
if (!empty($public_key) && strlen($public_key) < 32) {
api_err("Invalid public key");
}
if (!empty($public_key)) {
$account = $acc->get_address($public_key);
}
if(empty($asset)&&empty($account)){
api_err("An asset or an account are necessary");
}
if(!empty($asset)&&!empty($account)){
api_err("Choose either account or asset parameter");
}
$whr="WHERE assets_balance.";
$bind=[];
if(!empty($asset)){
$whr.="asset=:asset ";
$bind[':asset']=$asset;
}
elseif(!empty($account)){
$whr.="account=:account ";
$bind[':account']=$account;
}
$r=$db->run("SELECT asset, alias, account, assets_balance.balance FROM assets_balance LEFT JOIN accounts ON accounts.id=assets_balance.asset $whr LIMIT 1000",$bind);
if ($r)
api_echo($r);
else
api_err("An asset or an account not found");
} elseif ($q === "asset-orders"){
$asset = san($data['asset']);
$account = san($data['account']);
if(empty($asset)&&empty($account)){
api_err("An asset or an account are necessary");
}
$whr="status=0";
$bind=[];
if(!empty($asset)){
$whr.=" AND asset=:asset ";
$bind[':asset']=$asset;
}
if(!empty($account)){
$whr.=" AND account=:account ";
$bind[':account']=$account;
}
$r=$db->run("SELECT * FROM assets_market WHERE $whr", $bind);
api_echo($r);
} elseif ($q === "assets"){
$asset = san($data['asset']);
$whr="";
$bind=[];
if(!empty($asset)){
$whr.=" WHERE assets.id=:asset ";
$bind[':asset']=$asset;
}
$r=$db->run("SELECT assets.*, accounts.alias, accounts.balance FROM assets LEFT JOIN accounts ON accounts.id=assets.id $whr LIMIT 1000",$bind);
api_echo($r);
} else {
api_err("Invalid request");
}

View File

@@ -101,6 +101,50 @@ 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",
@@ -504,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>"
}
]
}
@@ -854,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>"
}
]
}
@@ -1080,6 +1131,13 @@ define({ "api": [
"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>"
}
]
}
@@ -1301,34 +1359,6 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"optional": false,
"field": "varname1",
"description": "<p>No type.</p>"
},
{
"group": "Success 200",
"type": "String",
"optional": false,
"field": "varname2",
"description": "<p>With type.</p>"
}
]
}
},
"type": "",
"url": "",
"version": "0.0.0",
"filename": "./doc/main.js",
"group": "C__Users_owen_voke_Documents_GitHub_php_arionum_node_doc_main_js",
"groupTitle": "C__Users_owen_voke_Documents_GitHub_php_arionum_node_doc_main_js",
"name": ""
},
{
"type": "php util.php",
"url": "balance",
@@ -1835,5 +1865,33 @@ define({ "api": [
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"optional": false,
"field": "varname1",
"description": "<p>No type.</p>"
},
{
"group": "Success 200",
"type": "String",
"optional": false,
"field": "varname2",
"description": "<p>With type.</p>"
}
]
}
},
"type": "",
"url": "",
"version": "0.0.0",
"filename": "./doc/main.js",
"group": "_github_node_doc_main_js",
"groupTitle": "_github_node_doc_main_js",
"name": ""
}
] });

View File

@@ -101,6 +101,50 @@
"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",
@@ -504,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>"
}
]
}
@@ -854,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>"
}
]
}
@@ -1080,6 +1131,13 @@
"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>"
}
]
}
@@ -1301,34 +1359,6 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"optional": false,
"field": "varname1",
"description": "<p>No type.</p>"
},
{
"group": "Success 200",
"type": "String",
"optional": false,
"field": "varname2",
"description": "<p>With type.</p>"
}
]
}
},
"type": "",
"url": "",
"version": "0.0.0",
"filename": "./doc/main.js",
"group": "C__Users_owen_voke_Documents_GitHub_php_arionum_node_doc_main_js",
"groupTitle": "C__Users_owen_voke_Documents_GitHub_php_arionum_node_doc_main_js",
"name": ""
},
{
"type": "php util.php",
"url": "balance",
@@ -1835,5 +1865,33 @@
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"optional": false,
"field": "varname1",
"description": "<p>No type.</p>"
},
{
"group": "Success 200",
"type": "String",
"optional": false,
"field": "varname2",
"description": "<p>With type.</p>"
}
]
}
},
"type": "",
"url": "",
"version": "0.0.0",
"filename": "./doc/main.js",
"group": "_github_node_doc_main_js",
"groupTitle": "_github_node_doc_main_js",
"name": ""
}
]

View File

@@ -1,14 +1,13 @@
define({
"name": "Arionum Node",
"name": "",
"version": "0.0.0",
"description": "The Arionum Node API and utility documentation.",
"title": "Arionum Node",
"description": "",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-10-12T11:08:47.065Z",
"time": "2018-11-05T10:13:13.657Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@@ -1,14 +1,13 @@
{
"name": "Arionum Node",
"name": "",
"version": "0.0.0",
"description": "The Arionum Node API and utility documentation.",
"title": "Arionum Node",
"description": "",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-10-12T11:08:47.065Z",
"time": "2018-11-05T10:13:13.657Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@@ -35,8 +35,40 @@ class Account
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';
}
// hashes 9 times in sha512 (binary) and encodes in base58
for ($i = 0; $i < 9;
$i++) {
@@ -106,7 +138,7 @@ class Account
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 {

View File

@@ -2,6 +2,33 @@
class Block
{
public function add_log($hash, $log)
{
global $db;
$hash=san($hash);
//$json=["table"=>"masternode", "key"=>"public_key","id"=>$x['public_key'], "vals"=>['ip'=>$current_ip] ];
$db->run("INSERT into logs SET block=:id, json=:json", [':id'=>$hash, ":json"=>json_encode($log)]);
}
public function reverse_log($hash)
{
global $db;
$r=$db->run("SELECT json, id FROM logs WHERE block=:id ORDER by id DESC", [":id"=>$hash]);
foreach ($r as $json) {
$old=json_decode($json['json'], true);
if ($old!==false&&is_array($old)) {
//making sure there's no sql injection here, as the table name and keys are sanitized to A-Za-z0-9_
$table=san($old['table']);
$key=san($old['key'], '_');
$id=san($old['id'], '_');
foreach ($old['vals'] as $v=>$l) {
$v=san($v, '_');
$db->run("UPDATE `$table` SET `$v`=:val WHERE `$key`=:keyid", [":keyid"=>$id, ":val"=>$l]);
}
}
$db->run("DELETE FROM logs WHERE id=:id", [":id"=>$json['id']]);
}
}
public function add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon, $bootstrapping=false)
{
global $db;
@@ -37,31 +64,76 @@ class Block
}
}
// 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");
$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, votes WRITE, logs WRITE");
$reward = $this->reward($height, $data);
$msg = '';
$mn_reward_rate=0.33;
// hf
if ($height>216000) {
$votes=[];
$r=$db->run("SELECT id,val FROM votes");
foreach ($r as $vote) {
$votes[$vote['id']]=$vote['val'];
}
// emission cut by 30%
if ($votes['emission30']==1) {
$reward=round($reward*0.7);
}
// 50% to masternodes
if ($votes['masternodereward50']==1) {
$mn_reward_rate=0.5;
}
// minimum reward to always be 10 aro
if ($votes['endless10reward']==1&&$reward<10) {
$reward=10;
}
}
if ($height>=80458) {
//reward the masternode
// do not reward blacklisted mns after 320000
$check_mn_votes="";
if ($height>320000) {
$check_mn_votes="and voted=0";
}
$mn_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",
"SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start $check_mn_votes ORDER by last_won ASC, public_key ASC LIMIT 1",
[":current"=>$height, ":start"=>$height-360]
);
_log("MN Winner: $mn_winner", 2);
if ($mn_winner!==false) {
$mn_reward=round(0.33*$reward, 8);
$mn_reward=round($mn_reward_rate*$reward, 8);
$reward=round($reward-$mn_reward, 8);
$reward=number_format($reward, 8, ".", "");
$mn_reward=number_format($mn_reward, 8, ".", "");
_log("MN Reward: $mn_reward", 2);
}
}
$cold_winner=false;
$cold_reward=0;
$cold_last_won=0;
if ($height>216000) {
if ($votes['coldstacking']==1) {
$cold_reward=round($mn_reward*0.2, 8);
$mn_reward=$mn_reward-$cold_reward;
$mn_reward=number_format($mn_reward, 8, ".", "");
$cold_reward=number_format($cold_reward, 8, ".", "");
$cw=$db->row(
"SELECT public_key, cold_last_won FROM masternode WHERE height<:start ORDER by cold_last_won ASC, public_key ASC LIMIT 1",
[":start"=>$height-360]
);
$cold_winner=$cw['public_key'];
$cold_last_won=$cw['cold_last_won'];
_log("Cold MN Winner: $cold_winner [$cold_last_won]", 2);
}
}
// the reward transaction
$transaction = [
@@ -122,7 +194,47 @@ class Block
$db->exec("UNLOCK TABLES");
return false;
}
//masternode rewards
if ($mn_winner!==false&&$height>=80458&&$mn_reward>0) {
//cold stacking rewards
if ($cold_winner!==false&&$height>216000&&$cold_reward>0) {
$db->run("UPDATE accounts SET balance=balance+:bal WHERE public_key=:pub", [":pub"=>$cold_winner, ":bal"=>$cold_reward]);
$bind = [
":id" => hex2coin(hash("sha512", "cold".$hash.$height.$cold_winner)),
":public_key" => $public_key,
":height" => $height,
":block" => $hash,
":dst" => $acc->get_address($cold_winner),
":val" => $cold_reward,
":fee" => 0,
":signature" => $reward_signature,
":version" => 0,
":date" => $date,
":message" => 'masternode-cold',
];
$res = $db->run(
"INSERT into transactions SET id=:id, public_key=:public_key, block=:block, height=:height, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
if ($res != 1) {
// rollback and exit if it fails
_log("Masternode Cold reward DB insert failed");
$db->rollback();
$db->exec("UNLOCK TABLES");
return false;
}
if ($height>216070) {
$db->run("UPDATE masternode SET cold_last_won=:height WHERE public_key=:pub", [':pub'=>$cold_winner, ":height"=>$height]);
$this->add_log($hash, ["table"=>"masternode", "key"=>"public_key","id"=>$cold_winner, "vals"=>['cold_last_won'=>$cold_last_won]]);
}
}
$db->run("UPDATE accounts SET balance=balance+:bal WHERE public_key=:pub", [":pub"=>$mn_winner, ":bal"=>$mn_reward]);
$bind = [
":id" => hex2coin(hash("sha512", "mn".$hash.$height.$mn_winner)),
@@ -138,9 +250,9 @@ class Block
":message" => 'masternode',
];
$res = $db->run(
"INSERT into transactions SET id=:id, public_key=:public_key, block=:block, height=:height, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
"INSERT into transactions SET id=:id, public_key=:public_key, block=:block, height=:height, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
if ($res != 1) {
// rollback and exit if it fails
_log("Masternode reward DB insert failed");
@@ -157,6 +269,9 @@ class Block
$db->exec("UNLOCK TABLES");
return false;
}
$this->do_hard_forks($height, $hash);
}
// parse the block's transactions and insert them to db
@@ -166,17 +281,409 @@ class Block
$this->blacklist_masternodes();
$this->reset_fails_masternodes($public_key, $height, $hash);
}
// automated asset distribution, checked only every 1000 blocks to reduce load. Payouts every 10000 blocks.
if ($height>216000 && $height%50==1 && $res==true) { // every 50 for testing. No initial height set yet.
$res=$this->asset_distribute_dividends($height, $hash, $public_key, $date, $signature);
}
if ($height>216000 && $res==true) {
$res=$this->asset_market_orders($height, $hash, $public_key, $date, $signature);
}
if ($height>216000 && $height%43200==0) {
$res=$this->masternode_votes($public_key, $height, $hash);
}
// if any fails, rollback
if ($res == false) {
_log("Rollback block", 3);
$db->rollback();
} else {
_log("Commiting block", 3);
$db->commit();
}
// relese the locking as everything is finished
$db->exec("UNLOCK TABLES");
/***
*
*
*
*
* */
$continue = false;
try {
$res = $db->single(
"SHOW TABLES LIKE 'networkstats'"
);
if ($res !== false) $continue = true;
} catch (Exception $e) {
$continue = false;
}
//_log($continue,3);
if($continue) {
$hostname = 'https://www.ariochain.info'; // hardcoded
_log("Calculate supply",3);
$apiUrl = $hostname.'/api.php?q=node-info';
$aroUrl=file_get_contents($apiUrl);
$json=json_decode($aroUrl,true);
$apiUrlm = $hostname.'/stats/masternodes';
$aroUrlm=file_get_contents($apiUrlm);
$jsonm=json_decode($aroUrlm,true);
$cut = false;
_log("info from $hostname",3);
$last = $height;
//$last = 216000 + ( 43200 * 0);
$i = 1;
$supply = 0;
$max = 500904660;
$mn_reward_rate=0.33;
if ($last <= 80000) $mn_reward_rate = 0;
if ($last>216000) {
$votes=[];
$r=$db->run("SELECT id,val FROM votes");
foreach ($r as $vote) {
$votes[$vote['id']]=$vote['val'];
}
// 50% to masternodes
if ($votes['masternodereward50']==1) {
$mn_reward_rate=0.5;
}
// minimum reward to always be 10 aro
if ($votes['endless10reward']==1&&$reward<10) {
$reward=10;
}
}
while ($i <= ($last) ) {
$reward = $this->reward($i);
if ($i>216000) {
// emission cut by 30%
if ($votes['emission30']==1) {
$reward=round($reward*0.7);
$cut = true;
}
}
$supply += $reward;
$i++;
}
_log($reward,3);
_log($supply,3);
$data = $json['data'];
$mndata = $jsonm;
$bind = [
":id" => 0,
":height" => $height,
":difficulty" => $difficulty,
":accounts" => $data['accounts'],
":transactions" => $data['transactions'],
":masternodes" => $data['masternodes'],
":mn_paused" => $mndata['paused'],
":blacklisted" => $mndata['blacklisted'],
":supply" => $supply,
":reward" => $reward,
":mn_reward" => $mn_reward_rate,
":mn_daily_reward" => $mndata['daily_active_reward'],
":mn_daily_cold_reward" => $mndata['daily_cold_reward'],
];
$res = $db->run(
"INSERT into networkstats SET id=:id, height=:height, difficulty=:difficulty, accounts=:accounts, transactions=:transactions, masternodes=:masternodes, mn_paused=:mn_paused, blacklisted=:blacklisted,
supply=:supply, reward=:reward, mn_reward=:mn_reward, mn_daily_reward=:mn_daily_reward, mn_daily_cold_reward=:mn_daily_cold_reward",
$bind
);
}
return true;
}
public function masternode_votes($public_key, $height, $hash)
{
global $db;
$arodev='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCvcUb8x4p38GFbZWaJKcncEWqUbe7YJtrDXomwn7DtDYuyYnN2j6s4nQxP1u9BiwCA8U4TjtC9Z21j3R3STLJSFyL';
// masternode votes
if ($height%43200==0) {
_log("Checking masternode votes", 3);
$blacklist=[];
$total_mns=$db->single("SELECT COUNT(1) FROM masternode");
$total_mns_with_key=$db->single("SELECT COUNT(1) FROM masternode WHERE vote_key IS NOT NULL");
// only if at least 50% of the masternodes have voting keys
if ($total_mns_with_key/$total_mns>0.50) {
_log("Counting the votes from other masternodes", 3);
$r=$db->run("SELECT message, count(message) as c FROM transactions WHERE version=106 AND height>:height group by message", [':height'=>$height-43200]);
foreach ($r as $x) {
if ($x['c']>$total_mns_with_key/1.5) {
$blacklist[]=san($x['message']);
}
}
} else {
// If less than 50% of the mns have voting key, AroDev's votes are used
_log("Counting AroDev votes", 3);
$r=$db->run("SELECT message FROM transactions WHERE version=106 AND height>:height AND public_key=:pub", [':height'=>$height-43200, ":pub"=>$arodev]);
foreach ($r as $x) {
$blacklist[]=san($x['message']);
}
}
if($height>=1641600){
$r=$db->run("SELECT public_key FROM masternode WHERE voted=1");
foreach ($r as $masternode) {
if (!in_array($masternode['public_key'], $blacklist)) {
_log("Masternode removed from voting blacklist - $masternode[public_key]", 3);
$this->add_log($hash, ["table"=>"masternode", "key"=>"public_key","id"=>$masternode['public_key'], "vals"=>['voted'=>1]]);
$db->run("UPDATE masternode SET voted=0 WHERE public_key=:pub", [":pub"=>$masternode['public_key']]);
}
}
}
foreach ($blacklist as $masternode) {
$res=$db->single("SELECT voted FROM masternode WHERE public_key=:pub", [":pub"=>$masternode]);
if ($res==0) {
_log("Masternode blacklist voted - $masternode", 3);
$db->run("UPDATE masternode SET voted=1 WHERE public_key=:pub", [":pub"=>$masternode]);
$this->add_log($hash, ["table"=>"masternode", "key"=>"public_key","id"=>$masternode, "vals"=>['voted'=>0]]);
}
}
}
// blockchain votes
$voted=[];
if ($height%129600==0) {
// only if at least 50% of the masternodes have voting keys
if ($total_mns_with_key/$total_mns>0.50) {
_log("Counting masternode blockchain votes", 3);
$r=$db->run("SELECT message, count(message) as c FROM transactions WHERE version=107 AND height>:height group by message", [':height'=>$height-129600]);
foreach ($r as $x) {
if ($x['c']>$total_mns_with_key/1.5) {
$voted[]=san($x['message']);
}
}
} else {
_log("Counting AroDev blockchain votes", 3);
// If less than 50% of the mns have voting key, AroDev's votes are used
$r=$db->run("SELECT message FROM transactions WHERE version=107 AND height>:height AND public_key=:pub", [':height'=>$height-129600, ":pub"=>$arodev]);
foreach ($r as $x) {
$voted[]=san($x['message']);
}
}
foreach ($voted as $vote) {
$v=$db->row("SELECT id, val FROM votes WHERE id=:id", [":id"=>$vote]);
if ($v) {
if ($v['val']==0) {
_log("Blockchain vote - $v[id] = 1", 3);
$db->run("UPDATE votes SET val=1 WHERE id=:id", [":id"=>$v['id']]);
$this->add_log($hash, ["table"=>"votes", "key"=>"id","id"=>$v['id'], "vals"=>['val'=>0]]);
} else {
_log("Blockchain vote - $v[id] = 0", 3);
$db->run("UPDATE votes SET val=0 WHERE id=:id", [":id"=>$v['id']]);
$this->add_log($hash, ["table"=>"votes", "key"=>"id","id"=>$v['id'], "vals"=>['val'=>1]]);
}
}
}
}
return true;
}
public function asset_market_orders($height, $hash, $public_key, $date, $signature)
{
global $db;
$trx=new Transaction;
// checks all bid market orders ordered in the same way on all nodes
$r=$db->run("SELECT * FROM assets_market WHERE status=0 and val_done<val AND type='bid' ORDER by asset ASC, id ASC");
foreach ($r as $x) {
$finished=0;
//remaining part of the order
$val=$x['val']-$x['val_done'];
// starts checking all ask orders that are still valid and are on the same price. should probably adapt this to allow lower price as well in the future.
$asks=$db->run("SELECT * FROM assets_market WHERE status=0 and val_done<val AND asset=:asset AND price=:price AND type='ask' ORDER by price ASC, id ASC", [":asset"=>$x['asset'], ":price"=>$x['price']]);
foreach ($asks as $ask) {
//remaining part of the order
$remaining=$ask['val']-$ask['val_done'];
// how much of the ask should we use to fill the bid order
$use=0;
if ($remaining>$val) {
$use=$val;
} else {
$use=$remaining;
}
$val-=$use;
$db->run("UPDATE assets_market SET val_done=val_done+:done WHERE id=:id", [":id"=>$ask['id'], ":done"=>$use]);
$db->run("UPDATE assets_market SET val_done=val_done+:done WHERE id=:id", [":id"=>$x['id'], ":done"=>$use]);
// if we filled the order, we should exit the loop
$db->run("INSERT into assets_balance SET account=:account, asset=:asset, balance=:balance ON DUPLICATE KEY UPDATE balance=balance+:balance2", [":account"=>$x['account'], ":asset"=>$x['asset'], ":balance"=>$use, ":balance2"=>$use]);
$aro=$use*$x['price'];
$db->run("UPDATE accounts SET balance=balance+:balance WHERE id=:id", [":balance"=>$aro, ":id"=>$ask['account']]);
$random = hex2coin(hash("sha512", $x['id'].$ask['id'].$val.$hash));
$new = [
"id" => $random,
"public_key" => $x['id'],
"dst" => $ask['id'],
"val" => $aro,
"fee" => 0,
"signature" => $signature,
"version" => 58,
"date" => $date,
"message" => $use
];
$res=$trx->add($hash, $height, $new);
if (!$res) {
return false;
}
if ($val<=0) {
break;
}
}
}
return true;
}
public function asset_distribute_dividends($height, $hash, $public_key, $date, $signature)
{
global $db;
$trx=new Transaction;
_log("Starting automated dividend distribution", 3);
// just the assets with autodividend
$r=$db->run("SELECT * FROM assets WHERE auto_dividend=1");
if ($r===false) {
return true;
}
foreach ($r as $x) {
$asset=$db->row("SELECT id, public_key, balance FROM accounts WHERE id=:id", [":id"=>$x['id']]);
// minimum balance 1 aro
if ($asset['balance']<1) {
_log("Asset $asset[id] not enough balance", 3);
continue;
}
_log("Autodividend $asset[id] - $asset[balance] ARO", 3);
// every 10000 blocks and at minimum 10000 of asset creation or last distribution, manual or automated
$last=$db->single("SELECT height FROM transactions WHERE (version=54 OR version=50 or version=57) AND public_key=:pub ORDER by height DESC LIMIT 1", [":pub"=>$asset['public_key']]);
if ($height<$last+100) { // 100 for testnet
continue;
}
// generate a pseudorandom id and version 54 transaction for automated dividend distribution. No fees for such automated distributions to encourage the system
$random = hex2coin(hash("sha512", $x['id'].$hash.$height));
$new = [
"id" => $random,
"public_key" => $asset['public_key'],
"dst" => $asset['id'],
"val" => $asset['balance'],
"fee" => 0,
"signature" => $signature,
"version" => 57,
"date" => $date,
"src" => $asset['id'],
"message" => '',
];
$res=$trx->add($hash, $height, $new);
if (!$res) {
return false;
}
}
return true;
}
public function do_hard_forks($height, $block)
{
global $db;
if ($height==126215) {
// compromised masternodes are being removed
$mns=['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',
];
foreach ($mns as $mn) {
$db->run("DELETE FROM masternode WHERE public_key=:p", [":p"=>$mn]);
}
// their locked coins are added to dev's safewallet
$id=hex2coin(hash("sha512", "hf".$block.$height.'compromised-masternodes'));
$res=$db->run(
"INSERT into transactions SET id=:id, block=:block, height=:height, dst=:dst, val=4700000, fee=0, signature=:sig, version=0, message=:msg, date=:date, public_key=:public_key",
[":id"=>$id, ":block"=>$block, ":height"=>$height, ":dst"=>'4kWXV4HMuogUcjZBEzmmQdtc1dHzta6VykhCV1HWyEXK7kRWEMJLNoMWbuDwFMTfBrq5a9VthkZfmkMkamTfwRBP', ":sig"=>$id, ":msg"=>'compromised-masternodes-hf', ":date"=>time(), ":public_key"=>'4kWXV4HMuogUcjZBEzmmQdtc1dHzta6VykhCV1HWyEXK7kRWEMJLNoMWbuDwFMTfBrq5a9VthkZfmkMkamTfwRBP']
);
$db->run("UPDATE accounts SET balance=balance+4700000 where id='4kWXV4HMuogUcjZBEzmmQdtc1dHzta6VykhCV1HWyEXK7kRWEMJLNoMWbuDwFMTfBrq5a9VthkZfmkMkamTfwRBP' LIMIT 1");
}
}
// resets the number of fails when winning a block and marks it with a transaction
public function reset_fails_masternodes($public_key, $height, $hash)
@@ -211,10 +718,8 @@ class Block
$msg="$mn[blacklist],$mn[last_won],$mn[fails]";
$res=$db->run(
"INSERT into transactions SET id=:id, block=:block, height=:height, dst=:dst, val=0, fee=0, signature=:sig, version=111, message=:msg, date=:date, public_key=:public_key",
[":id"=>$id, ":block"=>$hash, ":height"=>$height, ":dst"=>$hash, ":sig"=>$hash, ":msg"=>$msg, ":date"=>time(), ":public_key"=>$public_key]
[":id"=>$id, ":block"=>$hash, ":height"=>$height, ":dst"=>$hash, ":sig"=>$hash, ":msg"=>$msg, ":date"=>time(), ":public_key"=>$public_key]
);
if ($res!=1) {
return 5;
@@ -276,7 +781,7 @@ class Block
// before mnn hf
if ($height<80000) {
// elapsed time between the last 20 blocks
$first = $db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT :limit,1",[":limit"=>$limit]);
$first = $db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT :limit,1", [":limit"=>$limit]);
$time = $current['date'] - $first['date'];
// avg block time
@@ -311,14 +816,28 @@ class Block
}
$result=ceil($total_time/$blks);
_log("Block time: $result", 3);
if ($result > 260) {
$dif = bcmul($current['difficulty'], 1.05);
} elseif ($result < 220) {
// if lower, decrease by 5%
$dif = bcmul($current['difficulty'], 0.95);
// 1 minute blocktime
if ($height>216000) {
if ($result > 65) {
$dif = bcmul($current['difficulty'], 1.05);
} elseif ($result < 55) {
// if lower, decrease by 5%
$dif = bcmul($current['difficulty'], 0.95);
} else {
// keep current difficulty
$dif = $current['difficulty'];
}
} else {
// keep current difficulty
$dif = $current['difficulty'];
// 4 minutes blocktime
if ($result > 260) {
$dif = bcmul($current['difficulty'], 1.05);
} elseif ($result < 220) {
// if lower, decrease by 5%
$dif = bcmul($current['difficulty'], 0.95);
} else {
// keep current difficulty
$dif = $current['difficulty'];
}
}
} else {
// hardfork 80000, fix difficulty targetting
@@ -394,17 +913,22 @@ class Block
// calculate the reward for each block
public function reward($id, $data = [])
{
// starting reward
$reward = 1000;
if ($id>216000) {
// 1min block time
$reward=200;
$factor = floor(($id-216000) / 43200) / 100;
$reward -= $reward * $factor;
} else {
// starting reward
$reward = 1000;
// decrease by 1% each 10800 blocks (approx 1 month)
$factor = floor($id / 10800) / 100;
$reward -= $reward * $factor;
}
// decrease by 1% each 10800 blocks (approx 1 month)
$factor = floor($id / 10800) / 100;
$reward -= $reward * $factor;
if ($reward < 0) {
$reward = 0;
}
// calculate the transaction fees
$fees = 0;
if (count($data) > 0) {
@@ -486,17 +1010,46 @@ class Block
// reward transaction and signature
$reward = $this->reward($height, $data);
$mn_reward_rate=0.33;
global $db;
// hf
if ($height>216000) {
$votes=[];
$r=$db->run("SELECT id,val FROM votes");
foreach ($r as $vote) {
$votes[$vote['id']]=$vote['val'];
}
// emission cut by 30%
if ($votes['emission30']==1) {
$reward=round($reward*0.7);
}
// 50% to masternodes
if ($votes['masternodereward50']==1) {
$mn_reward_rate=0.5;
}
// minimum reward to always be 10 aro
if ($votes['endless10reward']==1&&$reward<10) {
$reward=10;
}
}
if ($height>=80458) {
//reward the masternode
global $db;
// do not reward blacklisted mns after 320000
$check_mn_votes="";
if ($height>320000) {
$check_mn_votes="and voted=0";
}
$mn_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",
"SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start $check_mn_votes ORDER by last_won ASC, public_key ASC LIMIT 1",
[":current"=>$height, ":start"=>$height-360]
);
_log("MN Winner: $mn_winner", 2);
if ($mn_winner!==false) {
$mn_reward=round(0.33*$reward, 8);
$mn_reward=round($mn_reward_rate*$reward, 8);
$reward=round($reward-$mn_reward, 8);
$reward=number_format($reward, 8, ".", "");
$mn_reward=number_format($mn_reward, 8, ".", "");
@@ -792,10 +1345,12 @@ class Block
_log("Transaction check failed - $x[id]", 3);
return false;
}
if ($x['version']>=100&&$x['version']<110) {
if ($x['version']>=100&&$x['version']<110&&$x['version']!=106&&$x['version']!=107) {
$mns[] = $x['public_key'];
}
if ($x['version']==106||$x['version']==107) {
$mns[]=$x['public_key'].$x['message'];
}
// prepare total balance
$balance[$x['src']] += $x['val'] + $x['fee'];
@@ -817,9 +1372,9 @@ class Block
// check if the account has enough balance to perform the transaction
foreach ($balance as $id => $bal) {
$res = $db->single(
"SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",
[":id" => $id, ":balance" => $bal]
);
"SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",
[":id" => $id, ":balance" => $bal]
);
if ($res == 0) {
_log("Not enough balance for transaction - $id", 3);
return false; // not enough balance for the transactions
@@ -877,12 +1432,13 @@ class Block
public function pop($no = 1)
{
$current = $this->current();
$this->delete($current['height'] - $no + 1);
return $this->delete($current['height'] - $no + 1);
}
// delete all blocks >= height
public function delete($height)
{
global $_config;
if ($height < 2) {
$height = 2;
}
@@ -892,15 +1448,35 @@ class Block
$r = $db->run("SELECT * FROM blocks WHERE height>=:height ORDER by height DESC", [":height" => $height]);
if (count($r) == 0) {
return;
return true;
}
$db->beginTransaction();
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE, masternode WRITE");
$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, votes WRITE,logs WRITE");
foreach ($r as $x) {
$res = $trx->reverse($x['id']);
if ($res === false) {
_log("A transaction could not be reversed. Delete block failed.");
$db->rollback();
// the blockchain has some flaw, we should resync from scratch
$current = $this->current();
if (($current['date']<time()-(3600*48)) && $_config['auto_resync']!==false) {
_log("Blockchain corrupted. Resyncing from scratch.");
$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;");
$db->exec("UNLOCK TABLES");
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'");
@unlink(SANITY_LOCK_PATH);
system("php sanity.php > /dev/null 2>&1 &");
exit;
}
$db->exec("UNLOCK TABLES");
return false;
}
@@ -911,8 +1487,11 @@ class Block
$db->exec("UNLOCK TABLES");
return false;
}
$this->reverse_log($x['id']);
}
$db->commit();
$db->exec("UNLOCK TABLES");
return true;
@@ -932,7 +1511,8 @@ class Block
}
// avoid race conditions on blockchain manipulations
$db->beginTransaction();
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
$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, votes WRITE, logs WRITE");
// reverse all transactions of the block
$res = $trx->reverse($x['id']);
if ($res === false) {
@@ -996,7 +1576,7 @@ class Block
$r = $db->run("SELECT * FROM transactions WHERE version>0 AND block=:block", [":block" => $block['id']]);
$transactions = [];
foreach ($r as $x) {
if ($x['version']>110) {
if ($x['version']>110||$x['version']==57||$x['version']==58||$x['version']==59) {
//internal transactions
continue;
}

View File

@@ -114,7 +114,7 @@ $_config['use_official_blacklist'] = true;
*/
// Recheck the last blocks on sanity
$_config['sanity_recheck_blocks'] = 10;
$_config['sanity_recheck_blocks'] = 30;
// The interval to run the sanity in seconds
$_config['sanity_interval'] = 900;
@@ -128,6 +128,10 @@ $_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
@@ -154,3 +158,5 @@ $_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

@@ -250,6 +250,11 @@ function ec_verify($data, $signature, $key)
}
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 = [], $timeout = 60, $debug = false)
@@ -258,6 +263,9 @@ function peer_post($url, $data = [], $timeout = 60, $debug = false)
if ($debug) {
echo "\nPeer post: $url\n";
}
if (!isValidURL($url)) {
return false;
}
$postdata = http_build_query(
[
'data' => json_encode($data),

View File

@@ -1,13 +1,13 @@
<?php
// ARO version
define("VERSION", "0.4.3");
// Amsterdam timezone by default, should probably be moved to config
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 /");
@@ -59,6 +59,20 @@ 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");
@@ -93,13 +107,7 @@ if ($_config['testnet'] == true) {
$_config['coin'] .= "-testnet";
}
// 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;
}
if (empty($_config['hostname']) || $_config['hostname'] == "http://" || $_config['hostname'] == "https://") {
api_err("Invalid hostname");
}

View File

@@ -2,6 +2,7 @@
// when db schema modifications are done, this function is run.
$dbversion = intval($_config['dbversion']);
$db->beginTransaction();
if ($dbversion == 0) {
$db->run("
@@ -146,7 +147,7 @@ if ($dbversion == 7) {
$dbversion++;
}
if ($dbversion == 8) {
$db->run("CREATE TABLE `masternode` (
$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,
@@ -156,13 +157,108 @@ if ($dbversion == 8) {
`status` tinyint(4) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;");
$db->run("ALTER TABLE `masternode`
$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++;
$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++;
}

View File

@@ -4,13 +4,38 @@ use Arionum\Blacklist;
class Transaction
{
public function add_log($x, $log)
{
global $db;
//$json=["table"=>"masternode", "key"=>"public_key","id"=>$x['public_key'], "vals"=>['ip'=>$current_ip] ];
$db->run("INSERT into logs SET transaction=:id, json=:json", [':id'=>$x['id'], ":json"=>json_encode($log)]);
}
public function reverse_log($x)
{
global $db;
$r=$db->run("SELECT json, id FROM logs WHERE transaction=:id ORDER by id DESC", [":id"=>$x['id']]);
foreach ($r as $json) {
$old=json_decode($json['json'], true);
if ($old!==false&&is_array($old)) {
//making sure there's no sql injection here, as the table name and keys are sanitized to A-Za-z0-9_
$table=san($old['table']);
$key=san($old['key'], '_');
$id=san($old['id'], '_');
foreach ($old['vals'] as $v=>$l) {
$v=san($v, '_');
$db->run("UPDATE `$table` SET `$v`=:val WHERE `$key`=:keyid", [":keyid"=>$id, ":val"=>$l]);
}
}
$db->run("DELETE FROM logs WHERE id=:id",[":id"=>$json['id']]);
}
}
// reverse and remove all transactions from a block
public function reverse($block)
{
global $db;
$acc = new Account();
$r = $db->run("SELECT * FROM transactions WHERE block=:block ORDER by `version` ASC", [":block" => $block]);
$r = $db->run("SELECT * FROM transactions WHERE block=:block ORDER by `version` DESC", [":block" => $block]);
foreach ($r as $x) {
_log("Reversing transaction $x[id]", 4);
if (empty($x['src'])) {
@@ -29,19 +54,19 @@ class Transaction
} else {
// other type of transactions
if ($x['version']!=100&&$x['version']<111) {
if ($x['version']!=100 && $x['version']<111 && $x['version'] != 54 && $x['version'] != 57 && $x['version'] != 58 && $x['val']>0) {
$rez=$db->run(
"UPDATE accounts SET balance=balance-:val WHERE id=:id",
[":id" => $x['dst'], ":val" => $x['val']]
"UPDATE accounts SET balance=balance-:val WHERE id=:id",
[":id" => $x['dst'], ":val" => $x['val']]
);
if ($rez!=1) {
_log("Update accounts balance minus failed", 3);
_log("Update accounts balance minus failed - $x[id]", 3);
return false;
}
}
}
// on version 0 / reward transaction, don't credit anyone
if ($x['version'] > 0 && $x['version']<111) {
if ($x['version'] > 0 && $x['version']<111 && $x['version'] != 54 && $x['version'] != 57 && $x['version'] != 58 && ($x['val']+$x['fee'])>0) {
$rez=$db->run(
"UPDATE accounts SET balance=balance+:val WHERE id=:id",
[":id" => $x['src'], ":val" => $x['val'] + $x['fee']]
@@ -74,7 +99,7 @@ class Transaction
} elseif ($x['version']==101) {
$rez=$db->run(
"UPDATE masternode SET status=1 WHERE public_key=:public_key",
[':public_key'=>$x['public_key']]
[':public_key'=>$x['public_key']]
);
} elseif ($x['version']==102) {
$rez=$db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
@@ -82,7 +107,7 @@ class Transaction
$mnt=$db->row("SELECT height, `message` FROM transactions WHERE version=100 AND public_key=:public_key ORDER by height DESC LIMIT 1", [":public_key"=>$x['public_key']]);
$vers=$db->single(
"SELECT `version` FROM transactions WHERE (version=101 or version=102) AND public_key=:public_key AND height>:height ORDER by height DESC LIMIT 1",
[":public_key"=>$x['public_key'],":height"=>$mnt['height']]
[":public_key"=>$x['public_key'],":height"=>$mnt['height']]
);
$status=1;
@@ -93,7 +118,7 @@ class Transaction
$rez=$db->run(
"INSERT into masternode SET `public_key`=:public_key, `height`=:height, `ip`=:ip, `status`=:status",
[":public_key"=>$x['public_key'], ":height"=>$mnt['height'], ":ip"=>$mnt['message'], ":status"=>$status]
[":public_key"=>$x['public_key'], ":height"=>$mnt['height'], ":ip"=>$mnt['message'], ":status"=>$status]
);
if ($rez!=1) {
_log("Insert into masternode failed", 3);
@@ -104,6 +129,10 @@ class Transaction
_log("Update masternode balance failed", 3);
return false;
}
} elseif ($x['version']==104) {
$this->reverse_log($x);
} elseif ($x['version']==105) {
$db->run("UPDATE masternode SET vote_key=NULL WHERE public_key=:public_key", [":public_key"=>$x['public_key']]);
}
}
// internal masternode history
@@ -113,7 +142,7 @@ class Transaction
$rez=$db->run(
"UPDATE masternode SET fails=:fails, blacklist=:blacklist, last_won=:last_won WHERE public_key=:public_key",
[":public_key"=>$x['public_key'], ":blacklist"=> $m[0], ":fails"=>$m[2], ":last_won"=>$m[1]]
[":public_key"=>$x['public_key'], ":blacklist"=> $m[0], ":fails"=>$m[2], ":last_won"=>$m[1]]
);
if ($rez!=1) {
_log("Update masternode log failed", 3);
@@ -121,8 +150,78 @@ class Transaction
}
}
// asset transactions
if ($x['version']==50) {
$db->run("DELETE FROM assets WHERE id=:id", [":id"=>$x['src']]);
$db->run("DELETE FROM assets_balance WHERE asset=:id", [":id"=>$x['src']]);
} elseif ($x['version']==51) {
$t=json_decode($x['message'], true);
$db->run(
"UPDATE assets_balance SET balance=balance-:balance WHERE account=:account and asset=:asset",
[":account"=>$x['dst'], ":asset"=>san($t[0]), ":balance"=>intval($t[1])]
);
$db->run("UPDATE assets_balance SET balance=balance+:balance WHERE account=:account and asset=:asset", [":account"=>$x['src'], ":asset"=>san($t[0]), ":balance"=>intval($t[1])]);
} elseif ($x['version']==52) {
$t=json_decode($x['message'], true);
if ($t[4]=="ask") {
$type="ask";
} else {
$type="bid";
}
if ($type=="ask") {
$db->run("UPDATE assets_balance SET balance=balance+:val WHERE account=:account AND asset=:asset", [
":account"=>$x['src'],
":asset"=>san($t[0]),
":val"=>intval($t[2])
]);
} else {
$val=number_format($t[2]*$t[1], 8, '.', '');
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id"=>$x['src'], ":val"=>$val]);
}
$db->run("DELETE FROM assets_market WHERE id=:id", [':id'=>$x['id']]);
} elseif ($x['version']==53) {
$order=$db->row("SELECT * FROM assets_market WHERE id=:id AND account=:account AND status=2", [":id"=>san($x['message']), ":account"=>$x['src']]);
if ($order) {
$remaining=$order['val']-$order['val_done'];
if ($remaining>0) {
if ($order['type']=="ask") {
$db->run("UPDATE assets_balance SET balance=balance-:val WHERE account=:account AND asset=:asset", [
":account"=>$x['src'],
":asset"=>san($order['asset']),
":val"=>intval($remaining)
]);
} else {
$val=number_format($order['price']*$remaining, 8, '.', '');
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id", [":id"=>$x['src'], ":val"=>$val]);
}
$db->run("UPDATE assets_market SET status=0 WHERE id=:id", [":id"=>san($x['message'])]);
}
}
} elseif ($x['version']==54||$x['version']==57) {
//nothing to be done
} elseif ($x['version']==55) {
// the message stores the increment value
$plus=intval($x['message']);
$db->run("UPDATE assets_balance SET balance=balance-:plus WHERE account=:account AND asset=:asset", [":account"=>$x['src'], ":asset"=>$x['src'], ":plus"=>$plus]);
} elseif ($x['version']==58) {
// the message stores the number of units
$use=intval($x['message']);
// we stored the bid order id in the public key field and the ask in the dst field
$db->run("UPDATE assets_market SET val_done=val_done-:done WHERE id=:id", [":id"=>$x['public_key'], ":done"=>$use]);
$db->run("UPDATE assets_market SET val_done=val_done-:done WHERE id=:id", [":id"=>$x['dst'], ":done"=>$use]);
$bid=$db->row("SELECT * FROM assets_market WHERE id=:id", [':id'=>$x['public_key']]);
$ask=$db->row("SELECT * FROM assets_market WHERE id=:id", [':id'=>$x['dst']]);
$db->run("UPDATE assets_balance SET balance=balance-:balance WHERE account=:account AND asset=:asset", [":account"=>$bid['account'], ":asset"=>$bid['asset'], ":balance"=>$use]);
$aro=$x['val'];
$db->run("UPDATE accounts SET balance=balance-:balance WHERE id=:id", [":balance"=>$aro, ":id"=>$ask['account']]);
}
// add the transactions to mempool
if ($x['version'] > 0 && $x['version']<=110) {
if ($x['version'] > 0 && $x['version']<=110 && $x['version'] != 59 && $x['version'] != 58 && $x['version'] != 57) {
$this->add_mempool($x);
}
$res = $db->run("DELETE FROM transactions WHERE id=:id", [":id" => $x['id']]);
@@ -160,6 +259,7 @@ class Transaction
if (count($r) > 0) {
$i = 0;
$balance = [];
$assets=0;
foreach ($r as $x) {
$trans = [
"id" => $x['id'],
@@ -177,6 +277,24 @@ class Transaction
break;
}
//only a single asset creation per block
if ($x['version']==50) {
$assets++;
if ($assets>1) {
continue;
}
}
// single blockchain vote per block
if ($x['version']==106||$x['version']==107) {
$tid=$x['public_key'].$x['message'];
if ($exists[$tid]==1) {
continue;
}
$exists[$tid]=1;
}
if (empty($x['public_key'])) {
_log("$x[id] - Transaction has empty public_key");
continue;
@@ -250,7 +368,7 @@ class Transaction
];
//only a single masternode command of same type, per block
if ($x['version']>=100&&$x['version']<110) {
if ($x['version']>=100&&$x['version']<110&&$x['version']!=106&&$x['version']!=107) {
$check=$db->single("SELECT COUNT(1) FROM mempool WHERE public_key=:public_key", [":public_key"=>$x['public_key']]);
if ($check!=0) {
_log("Masternode transaction already in mempool", 3);
@@ -270,9 +388,14 @@ class Transaction
{
global $db;
$acc = new Account();
$acc->add($x['public_key'], $block);
if ($x['version']==1) {
$acc->add_id($x['dst'], $block);
// not a valid or useful public key for internal transactions
if ($x['version']!=58 && $x['version']!=59) {
// add the public key to the accounts table
$acc->add($x['public_key'], $block);
if ($x['version']==1 || $x['version'] == 51) {
// make sure the destination address in on the accounts table as well
$acc->add_id($x['dst'], $block);
}
}
$x['id'] = san($x['id']);
$bind = [
@@ -295,8 +418,16 @@ class Transaction
if ($res != 1) {
return false;
}
// market order side chain
if ($x['version']==58) {
return true;
}
if ($x['version'] == 2&&$height>=80000) {
$db->run("UPDATE accounts SET balance=balance+:val WHERE alias=:alias", [":alias" => $x['dst'], ":val" => $x['val']]);
} elseif ($x['version']==50||$x['version']==54||$x['version']==57) {
// asset creation and dividend distribution
} elseif ($x['version']==100&&$height>=80000) {
//master node deposit
} elseif ($x['version']==103&&$height>=80000) {
@@ -310,8 +441,8 @@ class Transaction
// no debit when the transaction is reward
if ($x['version'] > 0) {
// no debit when the transaction is reward or dividend distribution
if ($x['version'] > 0 && $x['version'] != 54 && $x['version'] != 57) {
$db->run(
"UPDATE accounts SET balance=(balance-:val)-:fee WHERE id=:id",
[":id" => $x['src'], ":val" => $x['val'], ":fee" => $x['fee']]
@@ -322,8 +453,8 @@ class Transaction
// set the alias
if ($x['version']==3&&$height>=80000) {
$db->run(
"UPDATE accounts SET alias=:alias WHERE id=:id",
[":id" => $x['src'], ":alias"=>$x['message']]
"UPDATE accounts SET alias=:alias WHERE id=:id",
[":id" => $x['src'], ":alias"=>$x['message']]
);
}
@@ -335,16 +466,142 @@ class Transaction
$db->run("INSERT into masternode SET `public_key`=:public_key, `height`=:height, `ip`=:ip, `status`=1", [":public_key"=>$x['public_key'], ":height"=>$height, ":ip"=>$message]);
} else {
if ($x['version']==101) {
// pause masternode
$db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} elseif ($x['version']==102) {
// reactivate pasternode
$db->run("UPDATE masternode SET status=1 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} elseif ($x['version']==103) {
// release and cancel the masternode
$db->run("DELETE FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
$db->run("UPDATE accounts SET balance=balance+100000 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} elseif ($x['version']==104) {
// update ip
$current_ip=$db->single("SELECT ip FROM masternode WHERE public_key=:public_key", [":public_key"=>$x['public_key']]);
$json=["table"=>"masternode", "key"=>"public_key","id"=>$x['public_key'], "vals"=>['ip'=>$current_ip] ];
$this->add_log($x, $json);
$db->run("UPDATE masternode SET ip=:ip WHERE public_key=:public_key", [':ip'=>$message, ":public_key"=>$x['public_key']]);
} elseif ($x['version']==105) {
// add vote key
$db->run("UPDATE masternode SET vote_key=:vote_key WHERE public_key=:public_key AND vote_key is NULL", [':vote_key'=>san($x['message']), ":public_key"=>$x['public_key']]);
}
}
}
// asset system
if ($x['version']==50) {
// asset creation
$bind=[];
$asset=json_decode($x['message'], true);
$bind[':max_supply']=intval($asset[0]);
$bind[':tradable']=intval($asset[1]);
$bind[':price']=number_format($asset[2], 8, '.', '');
$bind[':dividend_only']=intval($asset[3]);
$bind[':auto_divident']=intval($asset[4]);
$bind[':allow_bid']=intval($asset[5]);
$bind[':height']=$height;
$bind[':id']=$x['src'];
$db->run("INSERT into assets SET id=:id, max_supply=:max_supply, tradable=:tradable, price=:price, dividend_only=:dividend_only, auto_dividend=:auto_divident, height=:height, allow_bid=:allow_bid", $bind);
if ($bind[':max_supply']>0) {
$db->run("INSERT into assets_balance SET account=:account, asset=:asset, balance=:balance", [":account"=>$x['src'], ":asset"=>$x['src'], ":balance"=>$bind[':max_supply']]);
}
} elseif ($x['version']==51) {
// send asset
$t=json_decode($x['message'], true);
$db->run(
"INSERT into assets_balance SET account=:account, asset=:asset, balance=:balance ON DUPLICATE KEY UPDATE balance=balance+:balance2",
[":account"=>$x['dst'], ":asset"=>san($t[0]), ":balance"=>intval($t[1]), ":balance2"=>intval($t[1])]
);
$db->run("UPDATE assets_balance SET balance=balance-:balance WHERE account=:account and asset=:asset", [":account"=>$x['src'], ":asset"=>san($t[0]), ":balance"=>intval($t[1])]);
} elseif ($x['version']==52) {
// market order
$t=json_decode($x['message'], true);
if ($t[4]=="ask") {
$type="ask";
} else {
$type="bid";
}
$bind=[":id" => san($x['id']),
":account" => $x['src'],
":asset" => san($t[0]),
":price" => number_format($t[1], 8, '.', ''),
":date" => $x['date'],
":val"=>intval($t[2]),
":type" => $type,
":cancel" => intval($t[3])
];
$db->run("INSERT into assets_market SET id=:id, account=:account, asset=:asset, price=:price, `date`=:date, status=0, `type`=:type, val=:val, val_done=0, cancelable=:cancel", $bind);
if ($type=="ask") {
$db->run("UPDATE assets_balance SET balance=balance-:val WHERE account=:account AND asset=:asset", [
":account"=>$x['src'],
":asset"=>san($t[0]),
":val"=>intval($t[2])
]);
} else {
$val=number_format($t[2]*$t[1], 8, '.', '');
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id", [":id"=>$x['src'], ":val"=>$val]);
}
} elseif ($x['version']==53) {
// cancel order
$order=$db->row("SELECT * FROM assets_market WHERE id=:id AND account=:account AND status=0", [":id"=>san($x['message']), ":account"=>$x['src']]);
if ($order) {
$remaining=$order['val']-$order['val_done'];
if ($remaining>0) {
if ($order['type']=="ask") {
$db->run("UPDATE assets_balance SET balance=balance+:val WHERE account=:account AND asset=:asset", [
":account"=>$x['src'],
":asset"=>san($order['asset']),
":val"=>intval($remaining)
]);
} else {
$val=number_format($order['price']*$remaining, 8, '.', '');
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id"=>$x['src'], ":val"=>$val]);
}
$db->run("UPDATE assets_market SET status=2 WHERE id=:id", [":id"=>san($x['message'])]);
}
}
} elseif ($x['version']==54||$x['version']==57) {
//distribute dividends - only from asset wallet and only to other holders
$r=$db->run("SELECT * FROM assets_balance WHERE asset=:asset AND balance>0 AND account!=:acc", [":asset"=>$x['src'], ":acc"=>$x['src']]);
$total=0;
foreach ($r as $g) {
$total+=$g['balance'];
}
_log("Asset dividend distribution: $total units", 3);
foreach ($r as $g) {
$coins=number_format(($g['balance']/$total)*$x['val'], 8, '.', '');
$fee=number_format(($g['balance']/$total)*$x['fee'], 8, '.', '');
$hash = hex2coin(hash("sha512", $x['id'].$g['account']));
_log("Distributing to $g[account] for $g[balance] units - $coins ARO", 3);
$new = [
"id" => $hash,
"public_key" => $x['public_key'],
"dst" => $g['account'],
"val" => $coins,
"fee" => $fee,
"signature" => $x['signature'],
"version" => 59,
"date" => $x['date'],
"src" => $x['src'],
"message" => '',
];
$res=$this->add($block, $height, $new);
if (!$res) {
return false;
}
}
} elseif ($x['version']==55) {
// increase max supply
$plus=intval($x['message']);
$db->run("INSERT into assets_balance SET balance=:plus, account=:account, asset=:asset ON DUPLICATE KEY UPDATE balance=balance+:plus2", [":account"=>$x['src'], ":asset"=>$x['src'], ":plus"=>$plus, ":plus2"=>$plus]);
}
$db->run("DELETE FROM mempool WHERE id=:id", [":id" => $x['id']]);
@@ -356,12 +613,20 @@ class Transaction
{
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature'];
$hash = hash("sha512", $info);
//_log("Hashing: ".$info,3);
//_log("Hash: $hash",3);
return hex2coin($hash);
}
// check the transaction for validity
public function check($x, $height = 0)
{
global $db;
// blocktime lowered by 1 minute after 216000
$blocktime_factor=1;
if($height>216000){
$blocktime_factor=4;
}
// if no specific block, use current
if ($height === 0) {
$block = new Block();
@@ -371,13 +636,15 @@ class Transaction
$acc = new Account();
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
$src = $acc->get_address($x['public_key']);
// hard fork at 80000 to implement alias, new mining system, assets
// if($x['version']>1 && $height<80000){
// return false;
// }
// internal transactions
if ($x['version']>110) {
if ($x['version']>110 || $x['version'] == 57 || $x['version'] == 58 || $x['version'] == 59) {
return false;
}
@@ -411,8 +678,15 @@ class Transaction
_log("The account already has an alias", 3);
return false;
}
if ($x['dst']!=$src) {
// just to prevent some bypasses in the future
_log("DST must be SRC for this transaction", 3);
return false;
}
}
//masternode transactions
if ($x['version']>=100&&$x['version']<110&&$height>=80000) {
@@ -436,7 +710,12 @@ class Transaction
return false;
} elseif ($x['version']!=100) {
$mn=$acc->get_masternode($x['public_key']);
if ($x['dst']!=$src&&$x['version']!=106) {
// just to prevent some bypasses in the future
_log("DST must be SRC for this transaction", 3);
return false;
}
if (!$mn) {
_log("The masternode does not exist", 3);
return false;
@@ -451,17 +730,283 @@ class Transaction
if ($mn['status']!=0) {
_log("The masternode is not paused", 3);
return false;
} elseif ($height-$mn['last_won']<10800) { //10800
} elseif ($height-$mn['last_won']<10800*$blocktime_factor) { //10800
_log("The masternode last won block is less than 10800 blocks", 3);
return false;
} elseif ($height-$mn['height']<32400) { //32400
} elseif ($height-$mn['height']<32400*$blocktime_factor) { //32400
_log("The masternode start height is less than 32400 blocks! $height - $mn[height]", 3);
return false;
}
} elseif ($x['version']==104) {
//only once per month (every 43200 blocks)
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE public_key=:public_key AND version=104 AND height>:height", [':public_key'=>$x['public_key'], ":height"=>$height-43200]);
if ($res!=0) {
return false;
}
// already using this ip
if ($message==$mn['ip']) {
return false;
}
// valid ips
$message=$x['message'];
$message=preg_replace("/[^0-9\.]/", "", $message);
if (!filter_var($message, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
_log("The Masternode IP is invalid", 3);
return false;
}
// making sure the ip is not already in use
global $db;
$existing=$db->single("SELECT COUNT(1) FROM masternode WHERE ip=:ip", [":ip"=>$message]);
if ($existing!=0) {
return false;
}
} elseif ($x['version']==105) {
// masternode voting key can only be set once
if(!empty($mn['vote_key'])){
return false;
}
}
// masternode votes
elseif ($x['version']==106) {
// value always 0
if ($x['val']!=0) {
return false;
}
// one vote to each mn per 43200 blocks
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE dst=:dst AND version=106 AND public_key=:id AND height>:height", [':dst'=>$x['dst'], ":id"=>$x['public_key'], ":height"=>$height-43200]);
if ($res>0) {
return false;
}
}
// masternode blockchain votes
elseif ($x['version']==107) {
// value always 0
if ($x['val']!=0) {
_log("The value should be 0 for this transaction type - $x[val]",3);
return false;
}
// one vote to each mn per 129600 blocks
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE message=:message AND version=107 AND public_key=:id AND height>:height", [':message'=>$x['message'], ":id"=>$x['public_key'], ":height"=>$height-129600]);
if ($res>0) {
_log("There is already a vote in the last 129600 blocks",3);
return false;
}
}
}
}
// no asset transactions prior to 216000
if($x['version']>=50&&$x['version']<=55&&$height<=216000){
return false;
}
// no masternode voting prior to 216000
if(($x['version']==106||$x['version']==107||$x['version']==105||$x['version']==104)&&$height<=216000){
return false;
}
// assets
if ($x['version']==50) {
// asset creation
// fixed asset price 100 +. The 100 are burned and not distributed to miners.
if ($x['val']!=100) {
_log("The asset creation transaction is not 100", 3);
return false;
}
// stored in message in json format - [max supply, tradable, fixed price, dividend only, autodividend]
$asset=json_decode($x['message'], true);
if ($asset==false) {
_log("Invalid asset creation json", 3);
return false;
}
// minimum 0 (for inflatable assets) and maximum 1.000.000.000
if ($asset[0]>1000000000||$asset[0]<0||intval($asset[0])!=$asset[0]) {
_log("Invalid asset max supply", 3);
return false;
}
// 0 for non-tradable, 1 for tradable on the blockchain market
if ($asset[1]!==1&&$asset[1]!==0) {
_log("Invalid asset tradable", 3);
return false;
}
// If the price is set, it cannot be sold by the asset wallet at a dfferent price. Max price 1.000.000 aro
if (number_format($asset[2], 8, '.', '')!=$asset[2]||$asset[2]<0||$asset[2]>1000000) {
_log("Invalid asset price", 3);
return false;
}
// 1 to allow only dividend distribution, 0 to allow all transfers
if ($asset[3]!==1&&$asset[3]!==0) {
_log("Invalid asset dividend setting", 3);
return false;
}
// automatic dividend distribution every 10000 blocks
if ($asset[4]!==1&&$asset[4]!==0) {
_log("Invalid asset autodividend setting", 3);
return false;
}
// do not allow this asset to buy other assets via the market
if ($asset[5]!==1&&$asset[5]!==0) {
_log("Invalid asset bid_only setting", 3);
return false;
}
// make sure there is no similar asset with the same alias
$chk=$db->single("SELECT COUNT(1) FROM assets WHERE id=:id", [":id"=>$src]);
if ($chk!==0) {
_log("The asset already exists", 3);
return false;
}
}
// asset transfer
if ($x['version']==51) {
// Transfer details in json format, stored in the message. format: [asset id, units]
// The transfer is done to the dst address of the transactions
$asset=json_decode($x['message'], true);
if ($asset==false) {
_log("Invalid asset creation json", 3);
return false;
}
// check if the asset exists
$blockasset=$db->row("SELECT id, price FROM assets WHERE id=:id", [":id"=>san($asset[0])]);
if (!$blockasset) {
_log("Invalid asset", 3);
return false;
}
// minimum 1 unit is transfered
if (intval($asset[1])!=$asset[1]||$asset[1]<1) {
_log("Invalid amount", 3);
return false;
}
//make sure the wallet has enough asset units
$balance=$db->single("SELECT balance FROM assets_balance WHERE account=:account AND asset=:asset", [":account"=>$src, ":asset"=>san($asset[0])]);
if ($balance<=$asset[1]) {
_log("Not enough balance", 3);
return false;
}
if ($blockasset['price']>0 && $src == $blockasset['id'] && $blockasset['price']!=$asset[1] && $blockasset['tradable'] ==1) {
// if the asset has a price defined, check if the asset wallet owns all the asset units and only in this case allow transfers. In such cases, the asset should be sold on market
// on a fixed price always
$chk=$db->single("SELECT COUNT(1) FROM assets_balance WHERE asset=:asset AND account!=:account", [":account"=>$src, ":asset"=>$src]);
if ($chk!=0) {
_log("Initial asset distribution already done. Market orders only on fixed price.", 3);
return false;
}
}
}
// make sure the dividend only function is not bypassed after height X
if (($x['version']==1||$x['version']==2)&&$height>216000) {
$check=$db->single("SELECT COUNT(1) FROM assets WHERE id=:id AND dividend_only=1", [":id"=>$src]);
if ($check==1) {
_log("This asset wallet cannot send funds directly", 3);
return false;
}
}
// asset market orders
if ($x['version']==52) {
// we store the order details in a json array on the format [asset_id, price, amount of asset units, cancelable, order type ]
$asset=json_decode($x['message'], true);
if ($asset==false) {
_log("Invalid asset creation json", 3);
return false;
}
// only ask and bid allowed
if ($asset[4]!="ask"&&$asset[4]!="bid") {
_log("Invalid asset order type", 3);
return false;
}
$type=san($asset[4]);
$blockasset=$db->row("SELECT * FROM assets WHERE id=:id", [":id"=>san($asset[0])]);
if (!$blockasset||$blockasset['tradable']!=1) {
_log("Invalid asset", 3);
return false;
}
// the sale price per unit has to be at least 0.00000001 or max 1000000 aro
if (number_format($asset[1], 8, '.', '')!=$asset[1]||$asset[1]<=0||$asset[1]>1000000) {
_log("Invalid asset price", 3);
return false;
}
// integer min 1 and max 1000000
if (intval($asset[2])!=$asset[2]||$asset[2]<1||$asset[2]>1000000) {
_log("Invalid asset value", 3);
return false;
}
// if the order should be cancelable or not
if ($asset[3]!=1&&$asset[3]!=0) {
_log("Invalid asset cancel setting", 3);
return false;
}
// the type of order, ask or bid
if ($type=="ask") {
$balance=$db->single("SELECT balance FROM assets_balance WHERE asset=:asset AND account=:account", [":account"=>$src, ":asset"=>$asset[0]]);
if ($balance<$asset[2]) {
_log("Not enough asset balance", 3);
return false;
}
} else {
$balance=$acc->balance($src);
if ($balance<$asset[2]*$asset[1]) {
_log("Not enough aro balance", 3);
return false;
}
if ($blockasset['id']!=$src) {
$asset_bids_allowed=$db->single("SELECT COUNT(1) FROM assets WHERE id=:id AND allow_bid=0", [":id"=>$src]);
if ($asset_bids_allowed==1) {
_log("This wallet asset is not allowed to buy other assets", 3);
return false;
}
}
}
if ($blockasset['id']==$src && $blockasset['price']>0 && $blockasset['price']!=$asset[1]) {
// In case the asset has fixed price, the asset wallet cannot sell on a different price (to prevent abuse by the owner)
_log("This asset has fixed market price when sold by it's wallet", 3);
return false;
}
}
if ($x['version']==53) {
if (san($x['message'])!=$x['message']) {
_log("Invalid order id - $x[message]", 3);
return false;
}
$chk=$db->single("SELECT COUNT(1) FROM assets_market WHERE id=:id AND account=:src AND val_done<val AND status=0 AND cancelable=1", [":id"=>san($x['message']), ":src"=>$src]);
if ($chk!=1) {
_log("Invalid order - $x[message]", 3);
return false;
}
}
if ($x['version']==54) {
$balance=$acc->balance($src);
if ($balance<$x['val']||$x['val']<0.00000001) {
_log("Not enough aro balance", 3);
return false;
}
}
if ($x['version']==55) {
$plus=intval($x['message']);
if ($x['message']!=$plus) {
_log("Invalid asset value", 3);
return false;
}
$test=$db->single("SELECT COUNT(1) FROM assets WHERE id=:id AND max_supply=0", [":id"=>$src]);
if ($test!=1) {
_log("Asset not inflatable", 3);
return false;
}
$total=$db->single("SELECT SUM(balance) FROM assets_balance WHERE asset=:id", [":id"=>$src]);
$total+=$db->single("SELECT SUM(val-val_done) FROM assets_market WHERE status=0 AND type='ask' AND asset=:id", [":id"=>$src]);
if ($total+$plus>1000000000) {
_log("Maximum asset unit limits reached", 3);
return false;
}
}
// max fee after block 10800 is 10
if ($height > 10800 && $fee > 10) {
@@ -529,10 +1074,26 @@ class Transaction
}
}
//verify the ecdsa signature
if (!$acc->check_signature($info, $x['signature'], $x['public_key'])) {
_log("$x[id] - Invalid signature - $info");
return false;
if ($x['version']==106) {
// the masternode votes are using a different signature
$vote_key=$db->single("SELECT vote_key FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
if (empty($vote_key)) {
return false;
}
if (!$acc->check_signature($info, $x['signature'], $vote_key)) {
_log("$x[id] - Invalid vote key signature - $info");
return false;
}
} else {
//verify the ecdsa signature
if (!$acc->check_signature($info, $x['signature'], $x['public_key'])) {
_log("$x[id] - Invalid signature - $info");
return false;
}
}
return true;
@@ -601,7 +1162,7 @@ class Transaction
}
// return the transactions for a specific block id or height
public function get_transactions($height = "", $id = "")
public function get_transactions($height = "", $id = "", $includeMiningRewards = false)
{
global $db;
$block = new Block();
@@ -612,10 +1173,11 @@ class Transaction
if (empty($id) && empty($height)) {
return false;
}
$versionLimit = $includeMiningRewards ? 0 : 1;
if (!empty($id)) {
$r = $db->run("SELECT * FROM transactions WHERE block=:id AND version>0", [":id" => $id]);
$r = $db->run("SELECT * FROM transactions WHERE block=:id AND version >= :version", [":id" => $id, ":version" => $versionLimit]);
} else {
$r = $db->run("SELECT * FROM transactions WHERE height=:height AND version>0", [":height" => $height]);
$r = $db->run("SELECT * FROM transactions WHERE height=:height AND version >= :version", [":height" => $height, ":version" => $versionLimit]);
}
$res = [];
foreach ($r as $x) {

327
index.php
View File

@@ -1,323 +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 __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>
} else {
require_once __DIR__.'/node-index.php';
}

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>

View File

@@ -23,6 +23,7 @@ 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');
@@ -41,6 +42,12 @@ if ($_POST['coin'] != $_config['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
@@ -145,7 +152,7 @@ if ($q == "peer") {
if ($balance - $memspent < $val + $fee) {
api_err("Not enough funds (mempool)");
}
// add to mempool
$trx->add_mempool($data, $ip);
@@ -161,9 +168,17 @@ if ($q == "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

View File

@@ -23,6 +23,9 @@ 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';
set_time_limit(0);
error_reporting(0);
@@ -31,27 +34,16 @@ if (php_sapi_name() !== 'cli') {
die("This should only be run as cli");
}
require_once __DIR__.'/include/init.inc.php';
// make sure there's only a single sanity process running at the same time
if (file_exists("tmp/sanity-lock")) {
$ignore_lock = false;
if ($argv[1] == "force") {
$res = intval(shell_exec("ps aux|grep sanity.php|grep -v grep|wc -l"));
if ($res == 1) {
$ignore_lock = true;
}
}
$pid_time = filemtime("tmp/sanity-lock");
// if the process died, restart after 1day
if (time() - $pid_time > 86400) {
@unlink("tmp/sanity-lock");
}
if (!$ignore_lock) {
die("Sanity lock in place");
}
$sanity_lock=fopen(SANITY_LOCK_PATH,'w+');
if(!flock($sanity_lock, LOCK_EX | LOCK_NB)){
die("Sanity lock in place".PHP_EOL);
}
// set the new sanity lock
$lock = fopen("tmp/sanity-lock", "w");
fclose($lock);
flock($sanity_lock, LOCK_EX);
$arg = trim($argv[1]);
$arg2 = trim($argv[2]);
echo "Sleeping for 3 seconds\n";
@@ -60,8 +52,6 @@ if ($arg != "microsanity") {
sleep(3);
}
require_once __DIR__.'/include/init.inc.php';
if ($argv[1]=="dev") {
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE);
ini_set("display_errors", "on");
@@ -70,7 +60,7 @@ if ($argv[1]=="dev") {
// the sanity can't run without the schema being installed
if ($_config['dbversion'] < 2) {
die("DB schema not created");
@unlink("tmp/sanity-lock");
@unlink(SANITY_LOCK_PATH);
exit;
}
@@ -78,8 +68,9 @@ ini_set('memory_limit', '2G');
$block = new Block();
$acc = new Account();
$current = $block->current();
$trx= new Transaction();
$current = $block->current();
@@ -88,61 +79,39 @@ $current = $block->current();
// bootstrapping the initial sync
if ($current['height']==1) {
echo "Bootstrapping!\n";
$last=file_get_contents("http://dumps.arionum.com/last");
$last=intval($last);
$failed_sync=false;
for ($i=1000;$i<=$last;$i=$i+1000) {
echo "Download file $i\n";
$res=trim(file_get_contents("http://dumps.arionum.com/aro.db.$i"));
if ($res===false) {
echo "Could not download the bootstrap file $i. Syncing the old fashioned way.\n";
break;
}
$data=json_decode($res, true);
if ($data===false||is_null($data)) {
echo "Could not parse the bootstrap file $i. Syncing the old fashioned way.\n";
echo json_last_error_msg();
break;
}
foreach ($data as $x) {
if (count($x['data'])>0) {
$transactions=[];
foreach ($x['data'] as $d) {
$trans = [
"id" => $d[0],
"dst" => $d[1],
"val" => $d[2],
"fee" => $d[3],
"signature" => $d[4],
"message" => $d[5],
"version" => $d[6],
"date" => $d[7],
"public_key" => $d[8],
];
ksort($trans);
$transactions[$d[0]] = $trans;
}
ksort($transactions);
$x['data']=$transactions;
}
echo "-> Adding block $x[height]\n";
$db_name=substr($_config['db_connect'], strrpos($_config['db_connect'], "dbname=")+7);
$db_host=substr($_config['db_connect'], strpos($_config['db_connect'], ":host=")+6);
$db_host=substr($db_host, 0, strpos($db_host, ";"));
$res=$block->add($x['height'], $x['public_key'], $x['nonce'], $x['data'], $x['date'], $x['signature'], $x['difficulty'], $x['reward_signature'], $x['argon'], true);
if (!$res) {
echo "Error: Adding the block failed. Syncing the old way.\n";
$failed_sync=true;
break;
}
}
if ($failed_sync) {
break;
}
echo "DB name: $db_name\n";
echo "DB host: $db_host\n";
echo "Downloading the blockchain dump from arionum.info\n";
$arofile=__DIR__ . '/tmp/aro.sql';
if (file_exists("/usr/bin/curl")) {
// system("/usr/bin/curl -o $arofile 'https://arionum.info/dump/aro.sql'", $ret);
} elseif (file_exists("/usr/bin/wget")) {
// system("/usr/bin/wget -O $arofile 'https://arionum.info/dump/aro.sql'", $ret);
} else {
die("/usr/bin/curl and /usr/bin/wget not installed or inaccessible. Please install either of them.");
}
echo "Importing the blockchain dump\n";
// system("mysql -h ".escapeshellarg($db_host)." -u ".escapeshellarg($_config['db_user'])." -p".escapeshellarg($_config['db_pass'])." ".escapeshellarg($db_name). " < ".$arofile);
echo "Bootstrapping completed. Waiting 2mins for the tables to be unlocked.\n";
while (1) {
sleep(120);
$res=$db->run("SHOW OPEN TABLES WHERE In_use > 0");
if (count($res==0)) {
break;
}
echo "Tables still locked. Sleeping for another 2 min. \n";
}
$current = $block->current();
}
// the microsanity process is an anti-fork measure that will determine the best blockchain to choose for the last block
@@ -233,7 +202,7 @@ if ($arg == "microsanity" && !empty($arg2)) {
_log("Synced block from $host - $b[height] $b[difficulty]");
} while (0);
@unlink("tmp/sanity-lock");
@unlink(SANITY_LOCK_PATH);
exit;
}
@@ -252,7 +221,12 @@ $blocks = [];
$blocks_count = [];
$most_common = "";
$most_common_size = 0;
$most_common_height = 0;
$total_active_peers = 0;
$largest_most_common = "";
$largest_most_common_size = 0;
$largest_most_common_height = 0;
// checking peers
@@ -273,7 +247,7 @@ if ($total_peers == 0 && $_config['testnet'] == false) {
try {
$peers = $initialPeers->getAll();
} catch (\Arionum\Node\Exception $e) {
@unlink('tmp/sanity-lock');
@unlink(SANITY_LOCK_PATH);
die($e->getMessage().PHP_EOL);
}
@@ -302,9 +276,9 @@ if ($total_peers == 0 && $_config['testnet'] == false) {
}
$peered[$pid] = 1;
if($_config['passive_peering'] == true){
if ($_config['passive_peering'] == true) {
// does not peer, just add it to DB in passive mode
$db->run("INSERT into peers set hostname=:hostname, ping=0, reserve=0,ip=:ip",[":hostname"=>$peer, ":ip"=>md5($peer)]);
$db->run("INSERT into peers set hostname=:hostname, ping=0, reserve=0,ip=:ip", [":hostname"=>$peer, ":ip"=>md5($peer)]);
$res=true;
} else {
// forces the other node to peer with us.
@@ -325,7 +299,7 @@ if ($total_peers == 0 && $_config['testnet'] == false) {
$total_peers = count($r);
if ($total_peers == 0) {
// something went wrong, could not add any peers -> exit
@unlink("tmp/sanity-lock");
@unlink(SANITY_LOCK_PATH);
die("Could not peer to any peers! Please check internet connectivity!\n");
}
}
@@ -342,9 +316,9 @@ foreach ($r as $x) {
_log("Peer $x[hostname] unresponsive");
// if the peer is unresponsive, mark it as failed and blacklist it for a while
$db->run(
"UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id",
[":id" => $x['id']]
);
"UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id",
[":id" => $x['id']]
);
continue;
}
foreach ($data as $peer) {
@@ -374,9 +348,9 @@ foreach ($r as $x) {
}
// make sure there's no peer in db with this ip or hostname
if (!$db->single(
"SELECT COUNT(1) FROM peers WHERE ip=:ip or hostname=:hostname",
[":ip" => $peer['ip'], ":hostname" => $peer['hostname']]
)) {
"SELECT COUNT(1) FROM peers WHERE ip=:ip or hostname=:hostname",
[":ip" => $peer['ip'], ":hostname" => $peer['hostname']]
)) {
$i++;
// check a max_test_peers number of peers from each peer
if ($i > $_config['max_test_peers']) {
@@ -389,6 +363,8 @@ foreach ($r as $x) {
if ($test !== false) {
$total_peers++;
echo "Peered with: $peer[hostname]\n";
// a single new peer per sanity
$_config['get_more_peers']=false;
}
}
}
@@ -397,6 +373,13 @@ foreach ($r as $x) {
// get the current block and check it's blockchain
$data = peer_post($url."currentBlock", [], 5);
if ($data === false) {
_log("Peer $x[hostname] unresponsive");
// if the peer is unresponsive, mark it as failed and blacklist it for a while
$db->run(
"UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id",
[":id" => $x['id']]
);
continue;
}
// peer was responsive, mark it as good
@@ -428,6 +411,12 @@ foreach ($r as $x) {
if ($blocks_count[$data['id']] > $most_common_size) {
$most_common = $data['id'];
$most_common_size = $blocks_count[$data['id']];
$most_common_height = $data['height'];
}
if ($blocks_count[$data['id']] > $largest_most_common_size && $data['height'] > $current['height']) {
$largest_most_common = $data['id'];
$largest_most_common_size = $blocks_count[$data['id']];
$largest_most_common_height = $data['height'];
}
// set the largest height block
if ($data['height'] > $largest_height) {
@@ -458,11 +447,54 @@ foreach ($r as $x) {
}
}
}
$largest_size=$blocks_count[$largest_height_block];
echo "Most common: $most_common\n";
echo "Most common block: $most_common_size\n";
echo "Max height: $largest_height\n";
echo "Most common block size: $most_common_size\n";
echo "Most common height: $most_common_height\n\n";
echo "Longest chain height: $largest_height\n";
echo "Longest chain size: $largest_size\n\n";
echo "Larger Most common: $largest_most_common\n";
echo "Larger Most common block size: $largest_most_common_size\n";
echo "Larger Most common height: $largest_most_common_height\n\n";
echo "Total size: $total_active_peers\n\n";
echo "Current block: $current[height]\n";
// if this is the node that's ahead, and other nodes are not catching up, pop 200
if($largest_height-$most_common_height>100&&$largest_size==1&&$current['id']==$largest_height_block){
_log("Current node is alone on the chain and over 100 blocks ahead. Poping 200 blocks.");
$db->run("UPDATE config SET val=1 WHERE cfg='sanity_sync'");
$block->pop(200);
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'");
_log("Exiting sanity, next sanity will sync from 200 blocks ago.");
@unlink(SANITY_LOCK_PATH);
exit;
}
// if there's a single node with over 100 blocks ahead on a single peer, use the most common block
if($largest_height-$most_common_height>100 && $largest_size==1){
if($current['id']==$most_common && $largest_most_common_size>3){
_log("Longest chain is way ahead, using largest most common block");
$largest_height=$largest_most_common_height;
$largest_size=$largest_most_common_size;
$largest_height_block=$largest_most_common;
} else {
_log("Longest chain is way ahead, using most common block");
$largest_height=$most_common_height;
$largest_size=$most_common_size;
$largest_height_block=$most_common;
}
}
$block_parse_failed=false;
$failed_syncs=0;
// if we're not on the largest height
if ($current['height'] < $largest_height && $largest_height > 1) {
// start sanity sync / block all other transactions/blocks
@@ -499,14 +531,16 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
//if we're not on the same blockchain and also it's not the most common, verify all the blocks on on this blockchain starting at current-30 until current
$invalid = false;
$last_good = $current['height'];
for ($i = $current['height'] - 30; $i < $current['height']; $i++) {
for ($i = $current['height'] - 100; $i < $current['height']; $i++) {
$data = peer_post($url."getBlock", ["height" => $i]);
if ($data === false) {
$invalid = true;
_log("Could not get block from $host - $i");
break;
}
$ext = $block->get($i);
if ($i == $current['height'] - 30 && $ext['id'] != $data['id']) {
if ($i == $current['height'] - 100 && $ext['id'] != $data['id']) {
_log("100 blocks ago was still on a different chain. Ignoring.");
$invalid = true;
break;
}
@@ -516,7 +550,11 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
}
}
if ($last_good==$current['height']-1) {
$block->pop(1);
$try_pop=$block->pop(1);
if($try_pop==false){
// we can't pop the last block, we should resync
$block_parse_failed=true;
}
}
// if last 10 blocks are good, verify all the blocks
if ($invalid == false) {
@@ -524,6 +562,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
for ($i = $last_good; $i <= $largest_height; $i++) {
$data = peer_post($url."getBlock", ["height" => $i]);
if ($data === false) {
_log("Could not get block from $host - $i");
$invalid = true;
break;
}
@@ -551,13 +590,21 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
// if the blockchain proves ok, delete until the last block
if ($invalid == false) {
_log("Changing fork, deleting $last_good", 1);
$block->delete($last_good);
$res=$block->delete($last_good);
if($res==false){
$block_parse_failed=true;
break;
}
$current = $block->current();
$data = $current;
}
}
// if current still doesn't match the data, something went wrong
if ($data['id'] != $current['id']) {
if($largest_size==1){
//blacklisting the peer if it's the largest height on a broken blockchain
$db->run("UPDATE peers SET blacklisted=UNIX_TIMESTAMP()+1800 WHERE hostname=:host LIMIT 1",[':host'=>$host]);
}
continue;
}
// start syncing all blocks
@@ -577,6 +624,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
$block_parse_failed=true;
_log("Block check: could not add block - $b[id] - $b[height]");
$good_peer = false;
$failed_syncs++;
break;
}
$res = $block->add(
@@ -594,6 +642,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
$block_parse_failed=true;
_log("Block add: could not add block - $b[id] - $b[height]");
$good_peer = false;
$failed_syncs++;
break;
}
@@ -607,8 +656,47 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
if ($good_peer) {
break;
}
if ($failed_syncs>5) {
break;
}
}
if ($block_parse_failed==true||$argv[1]=="resync") {
$last_resync=$db->single("SELECT val FROM config WHERE cfg='last_resync'");
if ($last_resync<time()-(3600*24)||$argv[1]=="resync") {
if ((($current['date']<time()-(3600*72)) && $_config['auto_resync']!==false) || $argv[1]=="resync") {
$db->run("SET foreign_key_checks=0;");
$tables = ["accounts", "transactions", "mempool", "masternode","blocks"];
foreach ($tables as $table) {
$db->run("TRUNCATE TABLE {$table}");
}
_log("Removing $to_remove blocks, the blockchain is stale.");
//$block->pop($to_remove);
$db->run("SET foreign_key_checks=1;");
$resyncing=true;
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'", [":time" => $t]);
_log("Exiting sanity, next sanity will resync from scratch.");
@unlink(SANITY_LOCK_PATH);
exit;
} elseif ($current['date']<time()-(3600*24)) {
_log("Removing 200 blocks, the blockchain is stale.");
//$block->pop(200);
$resyncing=true;
}
}
}
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'", [":time" => $t]);
}
$resyncing=false;
if ($block_parse_failed==true&&$current['date']<time()-(3600*24)) {
_log("Rechecking reward transactions");
@@ -623,58 +711,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) {
}
}
}
if ($block_parse_failed==true||$argv[1]=="resync") {
$last_resync=$db->single("SELECT val FROM config WHERE cfg='last_resync'");
if ($last_resync<time()-(3600*24)||$argv[1]=="resync") {
if ($current['date']<time()-(3600*72)||$argv[1]=="resync") {
$to_remove=3000;
if (intval($argv[2])>0) {
$to_remove=intval($argv[2]);
}
_log("Removing $to_remove blocks, the blockchain is stale.");
$block->pop(to_remove);
$resyncing=true;
} elseif ($current['date']<time()-(3600*24)) {
_log("Removing 200 blocks, the blockchain is stale.");
$block->pop(200);
$resyncing=true;
}
if ($resyncing==true) {
_log("Resyncing accounts");
$db->run("INSERT into config SET val=UNIX_TIMESTAMP(), cfg='last_resync' ON DUPLICATE KEY UPDATE val=UNIX_TIMESTAMP()");
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool 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!=103)) and version<111", [":id"=>$x['id'], ":alias"=>$alias]);
$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]);
}
}
}
$current = $block->current();
$db->run("DELETE FROM masternode WHERE height>:h", [":h"=>$current['height']]);
$db->exec("UNLOCK TABLES");
}
}
}
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'", [":time" => $t]);
}
// deleting mempool transactions older than 14 days
$db->run("DELETE FROM `mempool` WHERE `date` < UNIX_TIMESTAMP()-(3600*24*14)");
@@ -683,7 +720,7 @@ $db->run("DELETE FROM `mempool` WHERE `date` < UNIX_TIMESTAMP()-(3600*24*14)");
//rebroadcasting local transactions
if ($_config['sanity_rebroadcast_locals'] == true && $_config['disable_repropagation'] == false) {
$r = $db->run(
"SELECT id FROM mempool WHERE height>=:current and peer='local' order by `height` asc LIMIT 20",
"SELECT id FROM mempool WHERE height<:current and peer='local' order by `height` asc LIMIT 20",
[":current" => $current['height']]
);
_log("Rebroadcasting local transactions - ".count($r));
@@ -700,10 +737,17 @@ if ($_config['sanity_rebroadcast_locals'] == true && $_config['disable_repropaga
//rebroadcasting transactions
if ($_config['disable_repropagation'] == false) {
$forgotten = $current['height'] - $_config['sanity_rebroadcast_height'];
$r = $db->run(
"SELECT id FROM mempool WHERE height<:forgotten ORDER by val DESC LIMIT 10",
[":forgotten" => $forgotten]
);
$r1 = $db->run(
"SELECT id FROM mempool WHERE height<:forgotten ORDER by val DESC LIMIT 10",
[":forgotten" => $forgotten]
);
// getting some random transactions as well
$r2 = $db->run(
"SELECT id FROM mempool WHERE height<:forgotten ORDER by RAND() LIMIT 10",
[":forgotten" => $forgotten]
);
$r=array_merge($r1, $r2);
_log("Rebroadcasting external transactions - ".count($r));
@@ -737,19 +781,6 @@ foreach ($r as $x) {
}
}
//clean tmp files
_log("Cleaning tmp files");
$f = scandir("tmp/");
$time = time();
foreach ($f as $x) {
if (strlen($x) < 5 && substr($x, 0, 1) == ".") {
continue;
}
$pid_time = filemtime("tmp/$x");
if ($time - $pid_time > 7200) {
@unlink("tmp/$x");
}
}
//recheck the last blocks
@@ -796,6 +827,102 @@ if ($_config['sanity_recheck_blocks'] > 0 && $_config['testnet'] == false) {
}
}
// not too often to not cause load
if (rand(0, 10)==1) {
// after 10000 blocks, clear asset internal transactions
$db->run("DELETE FROM transactions WHERE (version=57 or version=58 or version=59) AND height<:height", [":height"=>$current['height']-10000]);
// remove market orders that have been filled, after 10000 blocks
$r=$db->run("SELECT id FROM assets_market WHERE val_done=val or status=2");
foreach ($r as $x) {
$last=$db->single("SELECT height FROM transactions WHERE (public_key=:id or dst=:id2) ORDER by height DESC LIMIT 1", [":id"=>$x['id'], ":id2"=>$x['id']]);
if ($current['height']-$last>10000) {
$db->run("DELETE FROM assets_market WHERE id=:id", [":id"=>$x['id']]);
}
}
}
if ($_config['masternode']==true&&!empty($_config['masternode_public_key'])&&!empty($_config['masternode_voting_public_key'])&&!empty($_config['masternode_voting_private_key'])) {
echo "Masternode votes\n";
$r=$db->run("SELECT * FROM masternode WHERE status=1 ORDER by RAND() LIMIT 3");
foreach ($r as $x) {
$blacklist=0;
$x['ip']=san_ip($x['ip']);
echo "Testing masternode: $x[ip]\n";
$f=file_get_contents("http://$x[ip]/api.php?q=currentBlock");
if ($f) {
$res=json_decode($f, true);
$res=$res['data'];
if ($res['height']<$current['height']-10080) {
$blacklist=1;
}
echo "Masternode Height: ".$res['height']."\n";
} else {
echo "---> Unresponsive\n";
$blacklist=1;
}
if ($blacklist) {
echo "Blacklisting masternode $x[public_key]\n";
$val='0.00000000';
$fee='0.00000001';
$date=time();
$version=106;
$msg=san($x['public_key']);
$address=$acc->get_address($x['public_key']);
$public_key=$_config['masternode_public_key'];
$private_key=$_config['masternode_voting_private_key'];
$info=$val."-".$fee."-".$address."-".$msg."-$version-".$public_key."-".$date;
$signature=ec_sign($info, $private_key);
$transaction = [
"src" => $acc->get_address($_config['masternode_public_key']),
"val" => $val,
"fee" => $fee,
"dst" => $address,
"public_key" => $public_key,
"date" => $date,
"version" => $version,
"message" => $msg,
"signature" => $signature,
];
$hash = $trx->hash($transaction);
$transaction['id'] = $hash;
if (!$trx->check($transaction)) {
print("Blacklist transaction signature failed\n");
}
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE id=:id", [":id" => $hash]);
if ($res != 0) {
print("Blacklist transaction already in mempool\n");
}
$trx->add_mempool($transaction, "local");
$hash=escapeshellarg(san($hash));
system("php propagate.php transaction $hash > /dev/null 2>&1 &");
echo "Blacklist Hash: $hash\n";
}
}
}
//clean tmp files
_log("Cleaning tmp files");
$f = scandir("tmp/");
$time = time();
foreach ($f as $x) {
if (strlen($x) < 5 && substr($x, 0, 1) == ".") {
continue;
}
$pid_time = filemtime("tmp/$x");
if ($time - $pid_time > 7200) {
@unlink("tmp/$x");
}
}
_log("Finishing sanity");
@unlink("tmp/sanity-lock");
@unlink(SANITY_LOCK_PATH);

3
tmp/.gitignore vendored Executable file
View File

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

View File

@@ -1 +0,0 @@
4.0.2

View File

@@ -457,13 +457,15 @@ elseif ($cmd == 'get-address') {
$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");
$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) {
@@ -471,7 +473,13 @@ elseif ($cmd == 'get-address') {
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!=103)) and version<111", [":id"=>$x['id'], ":alias"=>$alias]);
$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;
@@ -538,7 +546,7 @@ elseif ($cmd == 'get-address') {
echo "Hash:\t\t".md5(json_encode($res))."\n\n";
} elseif ($cmd == "version") {
echo "\n\n".VERSION."\n\n";
} elseif ($cmd == "sendblock"){
} elseif ($cmd == "sendblock") {
$peer=trim($argv[3]);
if (!filter_var($peer, FILTER_VALIDATE_URL)) {
die("Invalid peer hostname");
@@ -549,13 +557,12 @@ elseif ($cmd == 'get-address') {
$data = $block->export("", $height);
if($data===false){
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") {
} elseif ($cmd == "recheck-external-blocks") {
$peer=trim($argv[2]);
if (!filter_var($peer, FILTER_VALIDATE_URL)) {
die("Invalid peer hostname");
@@ -569,10 +576,10 @@ elseif ($cmd == 'get-address') {
$last=peer_post($peer."/peer.php?q=currentBlock");
$b=peer_post($peer."/peer.php?q=getBlock",["height"=>$height]);
$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]);
$c=peer_post($peer."/peer.php?q=getBlock", ["height"=>$i]);
if (!$block->mine(
$c['public_key'],
@@ -585,12 +592,10 @@ elseif ($cmd == 'get-address') {
)) {
print("Invalid block detected. $c[height] - $c[id]\n");
break;
}
}
echo "Block $i -> ok\n";
$b=$c;
}
} else {
echo "Invalid command\n";
}