62 Commits

Author SHA1 Message Date
Arionum
984aedc940 version 2018-08-13 02:51:49 +03:00
Arionum
e59bb416b4 80460 hard fork 2018-08-13 02:36:59 +03:00
Arionum
57d2257c43 80460 hf 2018-08-13 02:36:20 +03:00
Arionum
8c32d1c71b 80500 hf mn 2018-08-12 21:39:38 +03:00
Arionum
b5110bf01f protection 2018-08-12 16:47:52 +03:00
Arionum
0567899edd temp fix 2018-08-12 05:44:03 +03:00
Arionum
66b1221e22 blacklist time 83000 2018-08-12 01:47:56 +03:00
Arionum
9e9c12fbbb accounts resync option 2018-08-11 23:00:33 +03:00
Arionum
c5002508d6 internal transaction 2018-08-10 22:19:22 +03:00
Arionum
fbc48921f3 mn fix 2018-08-10 21:49:34 +03:00
Arionum
07dcebd895 limit peers 2018-08-10 18:19:19 +03:00
arionum
6651ff54fc Update db-update 2018-08-09 15:32:09 +03:00
Arionum
38745111ef Merge branch 'master' of https://github.com/arionum/node 2018-08-07 22:36:19 +03:00
Arionum
c83895d384 homepage version 2018-08-07 22:36:04 +03:00
arionum
62cc290b50 Update README.md 2018-08-07 18:19:17 +03:00
Arionum
dab6648ae8 log fix 2018-08-07 18:09:26 +03:00
arionum
5c476be954 Merge pull request #23 from pxgamer/feature/config-formatting
Beautify the sample configuration with sections
2018-08-07 15:57:17 +03:00
pxgamer
0d5f79fc20 Beautify the sample configuration with sections 2018-08-07 11:38:47 +01:00
arionum
5fc167cfbf Update README.md 2018-08-07 13:12:32 +03:00
arionum
5f4b6d9d51 Rename config.inc.php to config-sample.inc.php 2018-08-07 13:11:09 +03:00
Arionum
e707f8daa4 external mansternode miner support 2018-08-07 09:32:57 +03:00
Arionum
a09279d091 upgrade info 2018-08-06 00:23:00 +03:00
Arionum
d9e5ad94de docs 2018-08-06 00:17:28 +03:00
Arionum
8b006fc26a hf alias and mining 2018-08-06 00:13:36 +03:00
Arionum
066211b2c0 transaction indexes 2018-06-28 17:52:19 +03:00
arionum
06960e64e7 Merge pull request #21 from pxgamer/feature/custom-index-page
Create a custom index page
2018-06-28 12:47:36 +03:00
pxgamer
b276fde6da Update the order of CSS properties 2018-06-25 09:00:51 +01:00
pxgamer
7ebf3e0e8a Update the styling of the index 2018-06-22 12:23:21 +01:00
arionum
3a4aaf973c Update sanity.php 2018-06-19 22:09:36 +03:00
arionum
81bd36d2c5 Merge pull request #6 from pxgamer/feature/psr-2
Update to follow PSR-2
2018-06-19 22:05:22 +03:00
Arionum
85cf950fca bugfix send 2018-06-01 18:18:00 +03:00
pxgamer
2f4e95f22d Update files to use class constructor parentheses 2018-05-29 15:50:30 +01:00
pxgamer
c6393d7e8d Update util file to use PSR-2 2018-05-29 15:49:35 +01:00
pxgamer
aae845548f Update sanity file to PSR-2 2018-05-29 15:47:59 +01:00
pxgamer
b68f316029 Update propagate file to PSR-2 2018-05-29 15:47:18 +01:00
pxgamer
ffd3a55144 Update peer file to PSR-2 2018-05-29 15:46:52 +01:00
pxgamer
aa3459181e Update mine file to PSR-2 2018-05-29 15:46:16 +01:00
pxgamer
28807ef9ca Update index to use PSR-2 2018-05-29 15:44:23 +01:00
pxgamer
2fded5d4eb Update api file to PSR-2 2018-05-29 15:43:26 +01:00
pxgamer
f95d94f205 Update Transaction class to use PSR-2 2018-05-29 15:41:58 +01:00
pxgamer
c82e68c9b2 Update schema to PSR-2 2018-05-29 15:40:28 +01:00
pxgamer
bff8025b15 Update init file to PSR-2 2018-05-29 15:39:30 +01:00
pxgamer
636f4013dc Update functions to PSR-2 2018-05-29 15:38:24 +01:00
pxgamer
d8f1f4ea57 Update DB class to PSR-2 2018-05-29 15:37:05 +01:00
pxgamer
44c9eb5ce6 Update Block class to PSR-2 2018-05-29 15:35:37 +01:00
pxgamer
425bdcb504 Update Account class to PSR-2 2018-05-29 15:35:14 +01:00
pxgamer
af98de2e86 Add the include directory for linting 2018-05-29 15:31:48 +01:00
pxgamer
5d5e4d1f3c Add Composer config and PHPCS dependency 2018-05-29 15:31:05 +01:00
Arionum
e1d52c0e31 testnet 2018-05-18 15:59:40 +03:00
Arionum
58a815e068 testnet mining info 2018-05-18 15:50:19 +03:00
arionum
6bf10c408c Merge pull request #5 from pxgamer/feature/readme-restructure
Update the README layout to match the other repos
2018-05-18 15:35:20 +03:00
pxgamer
128241a3d9 Update the README layout to match the other repos 2018-05-17 09:42:48 +01:00
arionum
0a5be52570 Merge pull request #4 from pxgamer/feature/readme-donation-table
Change donations to be in a neater table
2018-05-11 16:23:49 +03:00
pxgamer
4ee780578b Change donations to be in a neater table 2018-05-10 09:05:33 +01:00
Arionum
0e561e5407 random number 2018-05-10 02:06:32 +03:00
Arionum
82ef1e9d10 Merge branch 'master' of https://github.com/arionum/node 2018-05-03 21:56:32 +03:00
Arionum
f2dbc02e93 spelling mistake 2018-05-03 21:56:01 +03:00
arionum
a5a1ff7f7a Merge pull request #3 from nunomendes/patch-1
Update api_data.js
2018-05-03 21:54:25 +03:00
Arionum
1115e02e5d ip fix 2018-05-01 13:13:02 +03:00
Arionum
54aea30232 wildcard hosts 2018-04-26 02:53:41 +03:00
Arionum
3898b7c7e0 new functions 2018-04-26 02:45:09 +03:00
nunomendes
70966df297 Update api_data.js
field name correction on getTransactions api call
2018-04-25 12:06:26 +01:00
24 changed files with 5200 additions and 2633 deletions

View File

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

6
UPGRADE Normal file
View File

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

938
api.php

File diff suppressed because it is too large Load Diff

19
composer.json Normal file
View File

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

View File

@@ -101,6 +101,57 @@ define({ "api": [
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=checkSignature",
"title": "17. checkSignature",
"name": "checkSignature",
"group": "API",
"description": "<p>Checks a signature against a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "signature",
"description": "<p>signature</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "data",
"description": "<p>signed data</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>true or false</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=currentBlock", "url": "/api.php?q=currentBlock",
@@ -249,6 +300,50 @@ define({ "api": [
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=getAlias",
"title": "189. getAlias",
"name": "getAlias",
"group": "API",
"description": "<p>Returns the alias of an account</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data",
"description": "<p>alias</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=getBalance", "url": "/api.php?q=getBalance",
@@ -272,6 +367,13 @@ define({ "api": [
"optional": true, "optional": true,
"field": "account", "field": "account",
"description": "<p>Account id / address</p>" "description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "alias",
"description": "<p>alias</p>"
} }
] ]
} }
@@ -420,7 +522,7 @@ define({ "api": [
"group": "Success 200", "group": "Success 200",
"type": "numeric", "type": "numeric",
"optional": false, "optional": false,
"field": "confirmation", "field": "confirmations",
"description": "<p>Number of confirmations</p>" "description": "<p>Number of confirmations</p>"
}, },
{ {
@@ -865,6 +967,30 @@ define({ "api": [
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=masternodes",
"title": "18. masternodes",
"name": "masternodes",
"group": "API",
"description": "<p>Returns all the masternode data</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>masternode date</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=mempoolSize", "url": "/api.php?q=mempoolSize",
@@ -889,6 +1015,64 @@ define({ "api": [
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=randomNumber",
"title": "16. randomNumber",
"name": "randomNumber",
"group": "API",
"description": "<p>Returns a random number based on an ARO block id.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "height",
"description": "<p>The height of the block on which the random number will be based on (should be a future block when starting)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "min",
"description": "<p>Minimum number (default 1)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "max",
"description": "<p>Maximum number</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "seed",
"description": "<p>A seed to generate different numbers for each use cases.</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "data",
"description": "<p>The random number</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=send", "url": "/api.php?q=send",
@@ -1211,6 +1395,24 @@ define({ "api": [
"filename": "./util.php", "filename": "./util.php",
"groupTitle": "UTIL" "groupTitle": "UTIL"
}, },
{
"type": "php util.php",
"url": "clean-blacklist",
"title": "Clean-Blacklist",
"name": "clean_blacklist",
"group": "UTIL",
"description": "<p>Removes all the peers from blacklist</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean-blacklist",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{ {
"type": "php util.php", "type": "php util.php",
"url": "current", "url": "current",

View File

@@ -101,6 +101,57 @@
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=checkSignature",
"title": "17. checkSignature",
"name": "checkSignature",
"group": "API",
"description": "<p>Checks a signature against a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "signature",
"description": "<p>signature</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "data",
"description": "<p>signed data</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>true or false</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=currentBlock", "url": "/api.php?q=currentBlock",
@@ -249,6 +300,50 @@
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=getAlias",
"title": "189. getAlias",
"name": "getAlias",
"group": "API",
"description": "<p>Returns the alias of an account</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "public_key",
"description": "<p>Public key</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "account",
"description": "<p>Account id / address</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "string",
"optional": false,
"field": "data",
"description": "<p>alias</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=getBalance", "url": "/api.php?q=getBalance",
@@ -272,6 +367,13 @@
"optional": true, "optional": true,
"field": "account", "field": "account",
"description": "<p>Account id / address</p>" "description": "<p>Account id / address</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": true,
"field": "alias",
"description": "<p>alias</p>"
} }
] ]
} }
@@ -420,7 +522,7 @@
"group": "Success 200", "group": "Success 200",
"type": "numeric", "type": "numeric",
"optional": false, "optional": false,
"field": "confirmation", "field": "confirmations",
"description": "<p>Number of confirmations</p>" "description": "<p>Number of confirmations</p>"
}, },
{ {
@@ -865,6 +967,30 @@
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=masternodes",
"title": "18. masternodes",
"name": "masternodes",
"group": "API",
"description": "<p>Returns all the masternode data</p>",
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "boolean",
"optional": false,
"field": "data",
"description": "<p>masternode date</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=mempoolSize", "url": "/api.php?q=mempoolSize",
@@ -889,6 +1015,64 @@
"filename": "./api.php", "filename": "./api.php",
"groupTitle": "API" "groupTitle": "API"
}, },
{
"type": "get",
"url": "/api.php?q=randomNumber",
"title": "16. randomNumber",
"name": "randomNumber",
"group": "API",
"description": "<p>Returns a random number based on an ARO block id.</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "height",
"description": "<p>The height of the block on which the random number will be based on (should be a future block when starting)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "min",
"description": "<p>Minimum number (default 1)</p>"
},
{
"group": "Parameter",
"type": "numeric",
"optional": false,
"field": "max",
"description": "<p>Maximum number</p>"
},
{
"group": "Parameter",
"type": "string",
"optional": false,
"field": "seed",
"description": "<p>A seed to generate different numbers for each use cases.</p>"
}
]
}
},
"success": {
"fields": {
"Success 200": [
{
"group": "Success 200",
"type": "numeric",
"optional": false,
"field": "data",
"description": "<p>The random number</p>"
}
]
}
},
"version": "0.0.0",
"filename": "./api.php",
"groupTitle": "API"
},
{ {
"type": "get", "type": "get",
"url": "/api.php?q=send", "url": "/api.php?q=send",
@@ -1211,6 +1395,24 @@
"filename": "./util.php", "filename": "./util.php",
"groupTitle": "UTIL" "groupTitle": "UTIL"
}, },
{
"type": "php util.php",
"url": "clean-blacklist",
"title": "Clean-Blacklist",
"name": "clean_blacklist",
"group": "UTIL",
"description": "<p>Removes all the peers from blacklist</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean-blacklist",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{ {
"type": "php util.php", "type": "php util.php",
"url": "current", "url": "current",

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

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

@@ -0,0 +1,114 @@
<?php
/*
|--------------------------------------------------------------------------
| Database Configuration
|--------------------------------------------------------------------------
*/
// The database DSN
$_config['db_connect'] = 'mysql:host=localhost;dbname=ENTER-DB-NAME';
// The database username
$_config['db_user'] = 'ENTER-DB-USER';
// The database password
$_config['db_pass'] = 'ENTER-DB-PASS';
/*
|--------------------------------------------------------------------------
| General Configuration
|--------------------------------------------------------------------------
*/
// Maximum number of connected peers
$_config['max_peers'] = 30;
// Enable testnet mode for development
$_config['testnet'] = false;
// To avoid any problems if other clones are made
$_config['coin'] = 'arionum';
// Allow others to connect to the node api (if set to false, only the below 'allowed_hosts' are allowed)
$_config['public_api'] = true;
// Hosts that are allowed to mine on this node
$_config['allowed_hosts'] = [
'127.0.0.1',
];
/*
|--------------------------------------------------------------------------
| Peer Configuration
|--------------------------------------------------------------------------
*/
// The number of peers to send each new transaction to
$_config['transaction_propagation_peers'] = 5;
// How many new peers to check from each peer
$_config['max_test_peers'] = 5;
/*
|--------------------------------------------------------------------------
| Mempool Configuration
|--------------------------------------------------------------------------
*/
// The maximum transactions to accept from a single peer
$_config['peer_max_mempool'] = 100;
// The maximum number of mempool transactions to be rebroadcasted
$_config['max_mempool_rebroadcast'] = 5000;
// The number of blocks between rebroadcasting transactions
$_config['sanity_rebroadcast_height'] = 30;
/*
|--------------------------------------------------------------------------
| Sanity Configuration
|--------------------------------------------------------------------------
*/
// Recheck the last blocks on sanity
$_config['sanity_recheck_blocks'] = 10;
// The interval to run the sanity in seconds
$_config['sanity_interval'] = 900;
// Enable setting a new hostname (should be used only if you want to change the hostname)
$_config['allow_hostname_change'] = false;
// Rebroadcast local transactions when running sanity
$_config['sanity_rebroadcast_locals'] = true;
// Get more peers?
$_config['get_more_peers']=true;
/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
*/
// Enable log output to the specified file
$_config['enable_logging'] = false;
// The specified file to write to (this should not be publicly visible)
$_config['log_file'] = '/var/log/aro.log';
// Log verbosity (default 0, maximum 3)
$_config['log_verbosity'] = 0;
/*
|--------------------------------------------------------------------------
| Masternode Configuration
|--------------------------------------------------------------------------
*/
// Enable this node as a masternode
$_config['masternode'] = false;
// The public key for the masternode
$_config['masternode_public_key'] = '';

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,18 +1,17 @@
<?php <?php
// when db schema modifications are done, this function is run. // when db schema modifications are done, this function is run.
$dbversion = intval($_config['dbversion']);
$dbversion=intval($_config['dbversion']);
$db->beginTransaction(); $db->beginTransaction();
if($dbversion==0){ if ($dbversion == 0) {
$db->run(" $db->run("
CREATE TABLE `accounts` ( CREATE TABLE `accounts` (
`id` varbinary(128) NOT NULL, `id` varbinary(128) NOT NULL,
`public_key` varbinary(1024) NOT NULL, `public_key` varbinary(1024) NOT NULL,
`block` varbinary(128) NOT NULL, `block` varbinary(128) NOT NULL,
`balance` decimal(20,8) NOT NULL `balance` decimal(20,8) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;");
$db->run("CREATE TABLE `blocks` ( $db->run("CREATE TABLE `blocks` (
`id` varbinary(128) NOT NULL, `id` varbinary(128) NOT NULL,
`generator` varbinary(128) NOT NULL, `generator` varbinary(128) NOT NULL,
@@ -24,16 +23,16 @@ if($dbversion==0){
`argon` varbinary(128) NOT NULL, `argon` varbinary(128) NOT NULL,
`transactions` INT NOT NULL `transactions` INT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `config` ( $db->run("CREATE TABLE `config` (
`cfg` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `cfg` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`val` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL `val` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES $db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
('hostname', '');"); ('hostname', '');");
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES $db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
('dbversion', '1');"); ('dbversion', '1');");
@@ -51,7 +50,7 @@ if($dbversion==0){
`date` bigint(20) NOT NULL, `date` bigint(20) NOT NULL,
`peer` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL `peer` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `peers` ( $db->run("CREATE TABLE `peers` (
`id` int(11) NOT NULL, `id` int(11) NOT NULL,
`hostname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, `hostname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
@@ -60,8 +59,8 @@ if($dbversion==0){
`reserve` tinyint(4) NOT NULL DEFAULT 1, `reserve` tinyint(4) NOT NULL DEFAULT 1,
`ip` varchar(45) NOT NULL `ip` varchar(45) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("CREATE TABLE `transactions` ( $db->run("CREATE TABLE `transactions` (
`id` varbinary(128) NOT NULL, `id` varbinary(128) NOT NULL,
`block` varbinary(128) NOT NULL, `block` varbinary(128) NOT NULL,
@@ -75,75 +74,101 @@ if($dbversion==0){
`date` int(11) NOT NULL, `date` int(11) NOT NULL,
`public_key` varbinary(1024) NOT NULL `public_key` varbinary(1024) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
$db->run("ALTER TABLE `peers` $db->run("ALTER TABLE `peers`
ADD PRIMARY KEY (`id`);"); ADD PRIMARY KEY (`id`);");
$db->run("ALTER TABLE `peers` $db->run("ALTER TABLE `peers`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;"); MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;");
$db->run("ALTER TABLE `accounts` $db->run("ALTER TABLE `accounts`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD KEY `accounts` (`block`);"); ADD KEY `accounts` (`block`);");
$db->run("ALTER TABLE `blocks` $db->run("ALTER TABLE `blocks`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `height` (`height`);"); ADD UNIQUE KEY `height` (`height`);");
$db->run("ALTER TABLE `config` ADD PRIMARY KEY (`cfg`);"); $db->run("ALTER TABLE `config` ADD PRIMARY KEY (`cfg`);");
$db->run("ALTER TABLE `mempool` $db->run("ALTER TABLE `mempool`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD KEY `height` (`height`);"); ADD KEY `height` (`height`);");
$db->run("ALTER TABLE `peers` $db->run("ALTER TABLE `peers`
ADD UNIQUE KEY `hostname` (`hostname`), ADD UNIQUE KEY `hostname` (`hostname`),
ADD UNIQUE KEY `ip` (`ip`), ADD UNIQUE KEY `ip` (`ip`),
ADD KEY `blacklisted` (`blacklisted`), ADD KEY `blacklisted` (`blacklisted`),
ADD KEY `ping` (`ping`), ADD KEY `ping` (`ping`),
ADD KEY `reserve` (`reserve`);"); ADD KEY `reserve` (`reserve`);");
$db->run("ALTER TABLE `transactions` $db->run("ALTER TABLE `transactions`
ADD PRIMARY KEY (`id`), ADD PRIMARY KEY (`id`),
ADD KEY `block_id` (`block`);"); ADD KEY `block_id` (`block`);");
$db->run("ALTER TABLE `accounts` $db->run("ALTER TABLE `accounts`
ADD CONSTRAINT `accounts` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;"); ADD CONSTRAINT `accounts` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
$db->run("ALTER TABLE `transactions` $db->run("ALTER TABLE `transactions`
ADD CONSTRAINT `block_id` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;"); ADD CONSTRAINT `block_id` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
$dbversion++; $dbversion++;
} }
if($dbversion==1){ if ($dbversion == 1) {
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_last', '0');"); $db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_last', '0');");
$dbversion++; $dbversion++;
} }
if($dbversion==2){ if ($dbversion == 2) {
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_sync', '0');"); $db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_sync', '0');");
$dbversion++; $dbversion++;
} }
if($dbversion==3){ if ($dbversion == 3) {
$dbversion++;
}
if ($dbversion == 4) {
$db->run("ALTER TABLE `mempool` ADD INDEX(`src`);");
$db->run("ALTER TABLE `mempool` ADD INDEX(`peer`); ");
$db->run("ALTER TABLE `mempool` ADD INDEX(`val`); ");
$dbversion++;
}
if ($dbversion == 5) {
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
$dbversion++;
}
if ($dbversion == 6) {
$db->run("ALTER TABLE `peers` ADD `stuckfail` TINYINT(4) NOT NULL DEFAULT '0' AFTER `fails`, ADD INDEX (`stuckfail`); ");
$db->run("ALTER TABLE `accounts` ADD `alias` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL AFTER `balance`; ");
$dbversion++;
}
if ($dbversion == 7) {
$db->run("ALTER TABLE `accounts` ADD INDEX(`alias`); ");
$db->run("ALTER TABLE `transactions` ADD KEY `dst` (`dst`), ADD KEY `height` (`height`), ADD KEY `public_key` (`public_key`);");
$dbversion++;
}
if ($dbversion == 8) {
$db->run("CREATE TABLE `masternode` (
`public_key` varchar(128) COLLATE utf8mb4_bin NOT NULL,
`height` int(11) NOT NULL,
`ip` varchar(16) COLLATE utf8mb4_bin NOT NULL,
`last_won` int(11) NOT NULL DEFAULT '0',
`blacklist` int(11) NOT NULL DEFAULT '0',
`fails` int(11) NOT NULL DEFAULT '0',
`status` tinyint(4) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;");
$db->run("ALTER TABLE `masternode`
ADD PRIMARY KEY (`public_key`),
ADD KEY `last_won` (`last_won`),
ADD KEY `status` (`status`),
ADD KEY `blacklist` (`blacklist`),
ADD KEY `height` (`height`);");
$dbversion++; $dbversion++;
} }
if($dbversion==4){
$db->run("ALTER TABLE `mempool` ADD INDEX(`src`);");
$db->run("ALTER TABLE `mempool` ADD INDEX(`peer`); ");
$db->run("ALTER TABLE `mempool` ADD INDEX(`val`); ");
$dbversion++;
}
if($dbversion==5){
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
$dbversion++;
}
if($dbversion==6){
$db->run("ALTER TABLE `peers` ADD `stuckfail` TINYINT(4) NOT NULL DEFAULT '0' AFTER `fails`, ADD INDEX (`stuckfail`); ");
$db->run("ALTER TABLE `accounts` ADD `alias` VARCHAR(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL AFTER `balance`; ");
$dbversion++;
}
// update the db version to the latest one // update the db version to the latest one
if($dbversion!=$_config['dbversion']) $db->run("UPDATE config SET val=:val WHERE cfg='dbversion'",array(":val"=>$dbversion)); if ($dbversion != $_config['dbversion']) {
$db->run("UPDATE config SET val=:val WHERE cfg='dbversion'", [":val" => $dbversion]);
}
$db->commit(); $db->commit();
?>

View File

@@ -1,275 +1,636 @@
<?php <?php
class Transaction { class Transaction
{
// reverse and remove all transactions from a block // reverse and remove all transactions from a block
public function reverse($block){ public function reverse($block)
{
global $db; global $db;
$acc=new Account; $acc = new Account();
$r=$db->run("SELECT * FROM transactions WHERE block=:block",array(":block"=>$block)); $r = $db->run("SELECT * FROM transactions WHERE block=:block ORDER by `version` ASC", [":block" => $block]);
foreach($r as $x){ foreach ($r as $x) {
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']); if (empty($x['src'])) {
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val'])); $x['src'] = $acc->get_address($x['public_key']);
}
if ($x['version'] == 2) {
// payment sent to alias
$db->run(
"UPDATE accounts SET balance=balance-:val WHERE alias=:alias",
[":alias" => $x['dst'], ":val" => $x['val']]
);
} else {
// other type of transactions
if($x['version']!=100) { $db->run(
"UPDATE accounts SET balance=balance-:val WHERE id=:id",
[":id" => $x['dst'], ":val" => $x['val']]
);
}
}
// on version 0 / reward transaction, don't credit anyone
if ($x['version'] > 0) {
$db->run(
"UPDATE accounts SET balance=balance+:val WHERE id=:id",
[":id" => $x['src'], ":val" => $x['val'] + $x['fee']]
);
}
// removing the alias if the alias transaction is reversed
if ($x['version']==3) {
$db->run(
"UPDATE accounts SET alias=NULL WHERE id=:id",
[":id" => $x['src']]
);
}
// on version 0 / reward transaction, don't credit anyone
if($x['version']>0) $db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['src'], ":val"=>$x['val']+$x['fee']));
// add the transactions to mempool
if($x['version']>0) $this->add_mempool($x);
$res= $db->run("DELETE FROM transactions WHERE id=:id",array(":id"=>$x['id']));
if($res!=1) return false;
}
}
// clears the mempool
public function clean_mempool(){
global $db;
$block= new Block;
$current=$block->current();
$height=$current['height'];
$limit=$height-1000;
$db->run("DELETE FROM mempool WHERE height<:limit",array(":limit"=>$limit));
}
// returns X transactions from mempool
public function mempool($max){
global $db;
$block=new Block;
$current=$block->current();
$height=$current['height']+1;
// only get the transactions that are not locked with a future height
$r=$db->run("SELECT * FROM mempool WHERE height<=:height ORDER by val/fee DESC LIMIT :max",array(":height"=>$height, ":max"=>$max+50));
$transactions=array();
if(count($r)>0){
$i=0;
$balance=array();
foreach($r as $x){
$trans=array("id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
if($i>=$max) break;
if(empty($x['public_key'])){ if ($x['version']>=100&&$x['version']<110&&$x['height']>=80000) {
_log("$x[id] - Transaction has empty public_key"); if ($x['version']==100) {
continue; $db->run("DELETE FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} } elseif ($x['version']==101) {
if(empty($x['src'])){ $db->run(
_log("$x[id] - Transaction has empty src"); "UPDATE masternode SET status=1 WHERE public_key=:public_key",
continue; [':public_key'=>$x['public_key']]
} );
if(!$this->check($trans, $current['height'])){ } elseif ($x['version']==102) {
_log("$x[id] - Transaction Check Failed"); $db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
continue; } elseif ($x['version']==103) {
} $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(
$balance[$x['src']]+=$x['val']+$x['fee']; "SELECT `version` FROM transactions WHERE (version=101 or version=102) AND public_key=:public_key AND height>:height ORDER by height DESC LIMIT 1",
if($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$x['id']))>0) { [":public_key"=>$x['public_key'],":height"=>$mnt['height']]
_log("$x[id] - Duplicate transaction"); );
continue; //duplicate transaction
$status=1;
if ($vers==101) {
$status=0;
} }
$res=$db->single("SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",array(":id"=>$x['src'], ":balance"=>$balance[$x['src']])); $db->run(
"INSERT into masternode SET `public_key`=:public_key, `height`=:height, `ip`=:ip, `status`=:status",
if($res==0) { [":public_key"=>$x['public_key'], ":height"=>$mnt['height'], ":ip"=>$mnt['message'], ":status"=>$status]
_log("$x[id] - Not enough funds in balance"); );
continue; // not enough balance for the transactions $db->run("UPDATE accounts SET balance=balance-100000 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
}
$i++;
ksort($trans);
$transactions[$x['id']]=$trans;
} }
}
// internal masternode history
if ($x['version']==111) {
$m=explode(",", $x['message']);
$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]]
);
}
// add the transactions to mempool
if ($x['version'] > 0 && $x['version']<=110) {
$this->add_mempool($x);
}
$res = $db->run("DELETE FROM transactions WHERE id=:id", [":id" => $x['id']]);
if ($res != 1) {
return false;
}
} }
// always sort the array }
// clears the mempool
public function clean_mempool()
{
global $db;
$block = new Block();
$current = $block->current();
$height = $current['height'];
$limit = $height - 1000;
$db->run("DELETE FROM mempool WHERE height<:limit", [":limit" => $limit]);
}
// returns X transactions from mempool
public function mempool($max)
{
global $db;
$block = new Block();
$current = $block->current();
$height = $current['height'] + 1;
// only get the transactions that are not locked with a future height
$r = $db->run(
"SELECT * FROM mempool WHERE height<=:height ORDER by val/fee DESC LIMIT :max",
[":height" => $height, ":max" => $max + 50]
);
$transactions = [];
if (count($r) > 0) {
$i = 0;
$balance = [];
foreach ($r as $x) {
$trans = [
"id" => $x['id'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
if ($i >= $max) {
break;
}
if (empty($x['public_key'])) {
_log("$x[id] - Transaction has empty public_key");
continue;
}
if (empty($x['src'])) {
_log("$x[id] - Transaction has empty src");
continue;
}
if (!$this->check($trans, $current['height'])) {
_log("$x[id] - Transaction Check Failed");
continue;
}
$balance[$x['src']] += $x['val'] + $x['fee'];
if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) {
_log("$x[id] - Duplicate transaction");
continue; //duplicate transaction
}
$res = $db->single(
"SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",
[":id" => $x['src'], ":balance" => $balance[$x['src']]]
);
if ($res == 0) {
_log("$x[id] - Not enough funds in balance");
continue; // not enough balance for the transactions
}
$i++;
ksort($trans);
$transactions[$x['id']] = $trans;
}
}
// always sort the array
ksort($transactions); ksort($transactions);
return $transactions; return $transactions;
} }
// add a new transaction to mempool and lock it with the current height // add a new transaction to mempool and lock it with the current height
public function add_mempool($x, $peer=""){ public function add_mempool($x, $peer = "")
{
global $db; global $db;
$block= new Block; $block = new Block();
$current=$block->current(); if ($x['version']>110) {
$height=$current['height']; return true;
$x['id']=san($x['id']); }
$bind=array(":peer"=>$peer, ":id"=>$x['id'],"public_key"=>$x['public_key'], ":height"=>$height, ":src"=>$x['src'],":dst"=>$x['dst'],":val"=>$x['val'], ":fee"=>$x['fee'],":signature"=>$x['signature'], ":version"=>$x['version'],":date"=>$x['date'], ":message"=>$x['message']); $current = $block->current();
$db->run("INSERT into mempool SET peer=:peer, id=:id, public_key=:public_key, height=:height, src=:src, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",$bind); $height = $current['height'];
$x['id'] = san($x['id']);
$bind = [
":peer" => $peer,
":id" => $x['id'],
"public_key" => $x['public_key'],
":height" => $height,
":src" => $x['src'],
":dst" => $x['dst'],
":val" => $x['val'],
":fee" => $x['fee'],
":signature" => $x['signature'],
":version" => $x['version'],
":date" => $x['date'],
":message" => $x['message'],
];
//only a single masternode command of same type, per block
if ($x['version']>=100&&$x['version']<110) {
$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);
return false;
}
}
$db->run(
"INSERT into mempool SET peer=:peer, id=:id, public_key=:public_key, height=:height, src=:src, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
return true; return true;
} }
// add a new transaction to the blockchain // add a new transaction to the blockchain
public function add($block,$height, $x){ public function add($block, $height, $x)
{
global $db; global $db;
$acc= new Account; $acc = new Account();
$acc->add($x['public_key'], $block); $acc->add($x['public_key'], $block);
$acc->add_id($x['dst'],$block); if ($x['version']==1) {
$x['id']=san($x['id']); $acc->add_id($x['dst'], $block);
$bind=array(":id"=>$x['id'], ":public_key"=>$x['public_key'],":height"=>$height, ":block"=>$block, ":dst"=>$x['dst'],":val"=>$x['val'], ":fee"=>$x['fee'],":signature"=>$x['signature'], ":version"=>$x['version'],":date"=>$x['date'], ":message"=>$x['message']); }
$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); $x['id'] = san($x['id']);
if($res!=1) return false; $bind = [
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val'])); ":id" => $x['id'],
// no debit when the transaction is reward ":public_key" => $x['public_key'],
if($x['version']>0) $db->run("UPDATE accounts SET balance=(balance-:val)-:fee WHERE id=:id",array(":id"=>$x['src'], ":val"=>$x['val'], ":fee"=>$x['fee'])); ":height" => $height,
$db->run("DELETE FROM mempool WHERE id=:id",array(":id"=>$x['id'])); ":block" => $block,
":dst" => $x['dst'],
":val" => $x['val'],
":fee" => $x['fee'],
":signature" => $x['signature'],
":version" => $x['version'],
":date" => $x['date'],
":message" => $x['message'],
];
$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) {
return false;
}
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']==100&&$height>=80000) {
//master node deposit
} elseif ($x['version']==103&&$height>=80000) {
$blk=new Block();
$blk->masternode_log($x['public_key'],$height,$block);
//master node withdrawal
} else {
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']]);
}
// no debit when the transaction is reward
if ($x['version'] > 0) {
$db->run(
"UPDATE accounts SET balance=(balance-:val)-:fee WHERE id=:id",
[":id" => $x['src'], ":val" => $x['val'], ":fee" => $x['fee']]
);
}
// 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']]
);
}
if ($x['version']>=100&&$x['version']<110&&$height>=80000) {
$message=$x['message'];
$message=preg_replace("/[^0-9\.]/", "", $message);
if ($x['version']==100) {
$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) {
$db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} elseif ($x['version']==102) {
$db->run("UPDATE masternode SET status=1 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]);
} elseif ($x['version']==103) {
$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']]);
}
}
}
$db->run("DELETE FROM mempool WHERE id=:id", [":id" => $x['id']]);
return true; return true;
} }
// hash the transaction's most important fields and create the transaction ID // hash the transaction's most important fields and create the transaction ID
public function hash($x){ public function hash($x)
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature']; {
$hash= hash("sha512",$info); $info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature'];
return hex2coin($hash); $hash = hash("sha512", $info);
return hex2coin($hash);
} }
// check the transaction for validity // check the transaction for validity
public function check($x, $height=0){ public function check($x, $height = 0)
// if no specific block, use current {
if($height===0){ // if no specific block, use current
$block=new Block; if ($height === 0) {
$current=$block->current(); $block = new Block();
$height=$current['height']; $current = $block->current();
} $height = $current['height'];
$acc= new Account; }
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']; $acc = new Account();
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
// the value must be >=0 // hard fork at 80000 to implement alias, new mining system, assets
if($x['val']<0){ _log("$x[id] - Value below 0"); return false; } // if($x['version']>1 && $height<80000){
// return false;
// the fee must be >=0 // }
if($x['fee']<0) { _log("$x[id] - Fee below 0"); return false; }
// the fee is 0.25%, hardcoded // internal transactions
$fee=$x['val']*0.0025; if($x['version']>110){
$fee=number_format($fee,8,".",""); return false;
if($fee<0.00000001) $fee=0.00000001; }
// max fee after block 10800 is 10
if($height>10800&&$fee>10) $fee=10; //10800
// added fee does not match
if($fee!=$x['fee']) { _log("$x[id] - Fee not 0.25%"); return false; }
// invalid destination address // the value must be >=0
if(!$acc->valid($x['dst'])) { _log("$x[id] - Invalid destination address"); return false; } if ($x['val'] < 0) {
_log("$x[id] - Value below 0", 3);
// reward transactions are not added via this function return false;
if($x['version']<1) { _log("$x[id] - Invalid version <1"); return false; } }
//if($x['version']>1) { _log("$x[id] - Invalid version >1"); return false; }
// the fee must be >=0
if ($x['fee'] < 0) {
_log("$x[id] - Fee below 0", 3);
return false;
}
// the fee is 0.25%, hardcoded
$fee = $x['val'] * 0.0025;
$fee = number_format($fee, 8, ".", "");
if ($fee < 0.00000001) {
$fee = 0.00000001;
}
//alias fee
if ($x['version']==3&&$height>=80000) {
$fee=10;
if (!$acc->free_alias($x['message'])) {
_log("Alias not free", 3);
return false;
}
// alias can only be set once per account
if ($acc->has_alias($x['public_key'])) {
_log("The account already has an alias", 3);
return false;
}
}
//masternode transactions
// public key must be at least 15 chars / probably should be replaced with the validator function if ($x['version']>=100&&$x['version']<110&&$height>=80000) {
if(strlen($x['public_key'])<15) { _log("$x[id] - Invalid public key size"); return false; } if ($x['version']==100) {
// no transactions before the genesis $message=$x['message'];
if($x['date']<1511725068) { _log("$x[id] - Date before genesis"); return false; } $message=preg_replace("/[^0-9\.]/", "", $message);
// no future transactions if (!filter_var($message, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
if($x['date']>time()+86400) { _log("$x[id] - Date in the future"); return false; } _log("The Masternode IP is invalid", 3);
// prevent the resending of broken base58 transactions return false;
if($height>16900&&$x['date']<1519327780) return false; }
$id=$this->hash($x); }
// the hash does not match our regenerated hash
if($x['id']!=$id) {
// fix for broken base58 library which was used until block 16900, accepts hashes without the first 1 or 2 bytes if ($x['version']==100&&$x['val']!=100000) {
$xs=base58_decode($x['id']); _log("The masternode transaction is not 100k", 3);
if(((strlen($xs)!=63||substr($id,1)!=$x['id'])&&(strlen($xs)!=62||substr($id,2)!=$x['id']))||$height>16900){ return false;
_log("$x[id] - $id - Invalid hash"); } elseif ($x['version']!=100) {
return false; $mn=$acc->get_masternode($x['public_key']);
}
}
//verify the ecdsa signature if (!$mn) {
if(!$acc->check_signature($info, $x['signature'], $x['public_key'])) { _log("$x[id] - Invalid signature"); return false; } _log("The masternode does not exist", 3);
return false;
return true; }
} if ($x['version']==101&&$mn['status']!=1) {
// sign a transaction _log("The masternode does is not running", 3);
public function sign($x, $private_key){ return false;
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']; } elseif ($x['version']==102 && $mn['status']!=0) {
$signature=ec_sign($info,$private_key); _log("The masternode is not paused", 3);
return false;
} elseif ($x['version']==103) {
if ($mn['status']!=0) {
_log("The masternode is not paused", 3);
return false;
} elseif ($height-$mn['last_won']<10800) { //10800
_log("The masternode last won block is less than 10800 blocks", 3);
return false;
} elseif ($height-$mn['height']<32400) { //32400
_log("The masternode start height is less than 32400 blocks! $height - $mn[height]", 3);
return false;
}
}
}
}
return $signature;
// max fee after block 10800 is 10
if ($height > 10800 && $fee > 10) {
$fee = 10; //10800
}
// added fee does not match
if ($fee != $x['fee']) {
_log("$x[id] - Fee not 0.25%", 3);
_log(json_encode($x), 3);
return false;
}
if ($x['version']==1) {
// invalid destination address
if (!$acc->valid($x['dst'])) {
_log("$x[id] - Invalid destination address", 3);
return false;
}
} elseif ($x['version']==2&&$height>=80000) {
if (!$acc->valid_alias($x['dst'])) {
_log("$x[id] - Invalid destination alias", 3);
return false;
}
}
// reward transactions are not added via this function
if ($x['version'] < 1) {
_log("$x[id] - Invalid version <1", 3);
return false;
}
//if($x['version']>1) { _log("$x[id] - Invalid version >1"); return false; }
// public key must be at least 15 chars / probably should be replaced with the validator function
if (strlen($x['public_key']) < 15) {
_log("$x[id] - Invalid public key size", 3);
return false;
}
// no transactions before the genesis
if ($x['date'] < 1511725068) {
_log("$x[id] - Date before genesis", 3);
return false;
}
// no future transactions
if ($x['date'] > time() + 86400) {
_log("$x[id] - Date in the future", 3);
return false;
}
// prevent the resending of broken base58 transactions
if ($height > 16900 && $x['date'] < 1519327780) {
_log("$x[id] - Broken base58 transaction", 3);
return false;
}
$id = $this->hash($x);
// the hash does not match our regenerated hash
if ($x['id'] != $id) {
// fix for broken base58 library which was used until block 16900, accepts hashes without the first 1 or 2 bytes
$xs = base58_decode($x['id']);
if (((strlen($xs) != 63 || substr($id, 1) != $x['id']) && (strlen($xs) != 62 || substr(
$id,
2
) != $x['id'])) || $height > 16900) {
_log("$x[id] - $id - Invalid hash");
return false;
}
}
//verify the ecdsa signature
if (!$acc->check_signature($info, $x['signature'], $x['public_key'])) {
_log("$x[id] - Invalid signature - $info");
return false;
}
return true;
}
// sign a transaction
public function sign($x, $private_key)
{
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
$signature = ec_sign($info, $private_key);
return $signature;
} }
//export a mempool transaction //export a mempool transaction
public function export($id){ public function export($id)
{
global $db; global $db;
$r=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id)); $r = $db->row("SELECT * FROM mempool WHERE id=:id", [":id" => $id]);
return $r; return $r;
} }
// get the transaction data as array // get the transaction data as array
public function get_transaction($id){ public function get_transaction($id)
{
global $db; global $db;
$acc=new Account; $acc = new Account();
$block=new Block; $block = new Block();
$current=$block->current(); $current = $block->current();
$x=$db->row("SELECT * FROM transactions WHERE id=:id",array(":id"=>$id)); $x = $db->row("SELECT * FROM transactions WHERE id=:id", [":id" => $id]);
if(!$x) return false;
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
$trans['src']=$acc->get_address($x['public_key']);
$trans['confirmations']=$current['height']-$x['height'];
if($x['version']==0) $trans['type']="mining"; if (!$x) {
elseif($x['version']==1){ return false;
if($x['dst']==$id) $trans['type']="credit"; }
else $trans['type']="debit"; $trans = [
} else { "block" => $x['block'],
$trans['type']="other"; "height" => $x['height'],
} "id" => $x['id'],
ksort($trans); "dst" => $x['dst'],
return $trans; "val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $acc->get_address($x['public_key']);
$trans['confirmations'] = $current['height'] - $x['height'];
if ($x['version'] == 0) {
$trans['type'] = "mining";
} elseif ($x['version'] == 1 || $x['version'] == 2) {
if ($x['dst'] == $id) {
$trans['type'] = "credit";
} else {
$trans['type'] = "debit";
}
} else {
$trans['type'] = "other";
}
ksort($trans);
return $trans;
} }
// return the transactions for a specific block id or height // return the transactions for a specific block id or height
public function get_transactions($height="", $id=""){ public function get_transactions($height = "", $id = "")
{
global $db; global $db;
$block=new Block; $block = new Block();
$current=$block->current(); $current = $block->current();
$acc=new Account; $acc = new Account();
$height=san($height); $height = san($height);
$id=san($id); $id = san($id);
if(empty($id)&&empty($height)) return false; if (empty($id) && empty($height)) {
if(!empty($id)) $r=$db->run("SELECT * FROM transactions WHERE block=:id AND version>0",array(":id"=>$id)); return false;
else $r=$db->run("SELECT * FROM transactions WHERE height=:height AND version>0",array(":height"=>$height)); }
$res=array(); if (!empty($id)) {
foreach($r as $x){ $r = $db->run("SELECT * FROM transactions WHERE block=:id AND version>0", [":id" => $id]);
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']); } else {
$trans['src']=$acc->get_address($x['public_key']); $r = $db->run("SELECT * FROM transactions WHERE height=:height AND version>0", [":height" => $height]);
$trans['confirmations']=$current['height']-$x['height']; }
$res = [];
if($x['version']==0) $trans['type']="mining"; foreach ($r as $x) {
elseif($x['version']==1){ if ($x['version']>110) {
if($x['dst']==$id) $trans['type']="credit"; continue; //internal transactions
else $trans['type']="debit"; }
} else { $trans = [
$trans['type']="other"; "block" => $x['block'],
} "height" => $x['height'],
ksort($trans); "id" => $x['id'],
$res[]=$trans; "dst" => $x['dst'],
} "val" => $x['val'],
return $res; "fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $acc->get_address($x['public_key']);
$trans['confirmations'] = $current['height'] - $x['height'];
if ($x['version'] == 0) {
$trans['type'] = "mining";
} elseif ($x['version'] == 1||$x['version'] == 2) {
if ($x['dst'] == $id) {
$trans['type'] = "credit";
} else {
$trans['type'] = "debit";
}
} else {
$trans['type'] = "other";
}
ksort($trans);
$res[] = $trans;
}
return $res;
} }
// get a specific mempool transaction as array // get a specific mempool transaction as array
public function get_mempool_transaction($id){ public function get_mempool_transaction($id)
{
global $db; global $db;
$x=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id)); $x = $db->row("SELECT * FROM mempool WHERE id=:id", [":id" => $id]);
if(!$x) return false; if (!$x) {
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']); return false;
$trans['src']=$x['src']; }
$trans = [
"block" => $x['block'],
"height" => $x['height'],
"id" => $x['id'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $x['src'];
$trans['type']="mempool"; $trans['type'] = "mempool";
$trans['confirmations']=-1; $trans['confirmations'] = -1;
ksort($trans); ksort($trans);
return $trans; return $trans;
} }
} }
?>

310
index.php
View File

@@ -1,7 +1,7 @@
<?php <?php
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 AroDev Copyright (c) 2018 AroDev
www.arionum.com www.arionum.com
@@ -24,14 +24,300 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
require_once __DIR__.'/include/init.inc.php';
require_once("include/init.inc.php"); $block = new Block();
$block=new Block; $current = $block->current();
$current=$block->current();
echo "<h3>Arionum Node</h3>";
echo "System check complete.<br><br> Current block: $current[height]";
?> ?>
<!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>

233
mine.php
View File

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

507
peer.php
View File

@@ -1,228 +1,279 @@
<?php <?php
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 AroDev Copyright (c) 2018 AroDev
www.arionum.com www.arionum.com
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 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 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 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
require_once("include/init.inc.php"); require_once("include/init.inc.php");
$trx = new Transaction; $trx = new Transaction();
$block=new Block; $block = new Block();
$q=$_GET['q']; $q = $_GET['q'];
// the data is sent as json, in $_POST['data'] // the data is sent as json, in $_POST['data']
if(!empty($_POST['data'])){ if (!empty($_POST['data'])) {
$data=json_decode(trim($_POST['data']),true); $data = json_decode(trim($_POST['data']), true);
} }
// make sure it's the same coin and not testnet // make sure it's the same coin and not testnet
if($_POST['coin']!=$_config['coin']) api_err("Invalid coin"); if ($_POST['coin'] != $_config['coin']) {
$ip=$_SERVER['REMOTE_ADDR']; api_err("Invalid coin");
$ip=filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); }
$ip = san_ip($_SERVER['REMOTE_ADDR']);
// peer with the current node $ip = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
if($q=="peer"){
// sanitize the hostname // peer with the current node
$hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL); if ($q == "peer") {
// sanitize the hostname
if (!filter_var($hostname, FILTER_VALIDATE_URL)) api_err("invalid-hostname"); $hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL);
// if it's already peered, only repeer on request $bad_peers = ["127.", "localhost", "10.", "192.168.","172.16.","172.17.","172.18.","172.19.","172.20.","172.21.","172.22.","172.23.","172.24.","172.25.","172.26.","172.27.","172.28.","172.29.","172.30.","172.31."];
$res=$db->single("SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",array(":hostname"=>$hostname,":ip"=>$ip)); $tpeer=str_replace(["https://","http://","//"], "", $hostname);
if($res==1){ foreach ($bad_peers as $bp) {
if($data['repeer']==1){ if (strpos($tpeer, $bp)===0) {
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname'])); api_err("invalid-hostname");
if($res!==false) api_echo("re-peer-ok"); }
else api_err("re-peer failed - $result"); }
}
api_echo("peer-ok-already"); if (!filter_var($hostname, FILTER_VALIDATE_URL)) {
} api_err("invalid-hostname");
// if we have enough peers, add it to DB as reserve }
$res=$db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0"); $hostname = san_host($hostname);
$reserve=1; // if it's already peered, only repeer on request
if($res<$_config['max_peers']) $reserve=0; $res = $db->single(
$db->run("INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",array(":ip"=>$ip, ":hostname2"=>$hostname,":hostname"=>$hostname, ":reserve"=>$reserve)); "SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",
// re-peer to make sure the peer is valid [":hostname" => $hostname, ":ip" => $ip]
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname'])); );
if($res!==false) api_echo("re-peer-ok"); if ($res == 1) {
else{ if ($data['repeer'] == 1) {
$db->run("DELETE FROM peers WHERE ip=:ip",array(":ip"=>$ip)); $res = peer_post($hostname."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
api_err("re-peer failed - $result"); if ($res !== false) {
} api_echo("re-peer-ok");
} } else {
elseif($q=="ping"){ api_err("re-peer failed - $result");
// confirm peer is active }
api_echo("pong"); }
} elseif($q=="submitTransaction"){ api_echo("peer-ok-already");
// receive a new transaction from a peer }
$current=$block->current(); // if we have enough peers, add it to DB as reserve
$res = $db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0");
$reserve = 1;
// no transactions accepted if the sanity is syncing if ($res < $_config['max_peers']) {
if($_config['sanity_sync']==1) api_err("sanity-sync"); $reserve = 0;
}
$data['id']=san($data['id']); $db->run(
// validate transaction data "INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",
if(!$trx->check($data)) api_err("Invalid transaction"); [":ip" => $ip, ":hostname2" => $hostname, ":hostname" => $hostname, ":reserve" => $reserve]
$hash=$data['id']; );
// make sure it's not already in mempool // re-peer to make sure the peer is valid
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash)); $res = peer_post($hostname."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
if($res!=0) api_err("The transaction is already in mempool"); if ($res !== false) {
// make sure the peer is not flooding us with transactions api_echo("re-peer-ok");
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE src=:src",array(":src"=>$data['src'])); } else {
if($res>25) api_err("Too many transactions from this address in mempool. Please rebroadcast later."); $db->run("DELETE FROM peers WHERE ip=:ip", [":ip" => $ip]);
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer",array(":peer"=>$ip)); api_err("re-peer failed - $result");
if($res>$_config['peer_max_mempool']) api_error("Too many transactions broadcasted from this peer"); }
} elseif ($q == "ping") {
// confirm peer is active
// make sure the transaction is not already on the blockchain api_echo("pong");
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$hash)); } elseif ($q == "submitTransaction") {
if($res!=0) api_err("The transaction is already in a block"); // receive a new transaction from a peer
$acc=new Account; $current = $block->current();
$src=$acc->get_address($data['public_key']);
// make sure the sender has enough balance
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src)); // no transactions accepted if the sanity is syncing
if($balance<$val+$fee) api_err("Not enough funds"); if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
// make sure the sender has enough pending balance }
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src));
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)"); $data['id'] = san($data['id']);
// validate transaction data
// add to mempool if (!$trx->check($data)) {
$trx->add_mempool($data, $ip); api_err("Invalid transaction");
}
// rebroadcast the transaction to some peers unless the transaction is smaller than the average size of transactions in mempool - protect against garbage data flooding $hash = $data['id'];
$res=$db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ",array(":src"=>$data['src'])); // make sure it's not already in mempool
if($res['c']<$_config['max_mempool_rebroadcast']&&$res['v']/$res['c']<$data['val']) system("php propagate.php transaction '$data[id]' > /dev/null 2>&1 &"); $res = $db->single("SELECT COUNT(1) FROM mempool WHERE id=:id", [":id" => $hash]);
api_echo("transaction-ok"); if ($res != 0) {
} api_err("The transaction is already in mempool");
elseif($q=="submitBlock"){ }
// receive a new block from a peer // make sure the peer is not flooding us with transactions
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE src=:src", [":src" => $data['src']]);
// if sanity sync, refuse all if ($res > 25) {
if($_config['sanity_sync']==1){ _log('['.$ip."] Block rejected due to sanity sync"); api_err("sanity-sync"); } api_err("Too many transactions from this address in mempool. Please rebroadcast later.");
$data['id']=san($data['id']); }
$current=$block->current(); $res = $db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer", [":peer" => $ip]);
// block already in the blockchain if ($res > $_config['peer_max_mempool']) {
if($current['id']==$data['id']) api_echo("block-ok"); api_error("Too many transactions broadcasted from this peer");
if($data['date']>time()+30) api_err("block in the future"); }
if($current['height']==$data['height']&&$current['id']!=$data['id']){
// different forks, same height // make sure the transaction is not already on the blockchain
$accept_new=false; $res = $db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $hash]);
if($current['transactions']<$data['transactions']){ if ($res != 0) {
// accept the one with most transactions api_err("The transaction is already in a block");
$accept_new=true; }
} elseif($current['transactions']==$data['transactions']) { $acc = new Account();
// convert the first 12 characters from hex to decimal and the block with the largest number wins $src = $acc->get_address($data['public_key']);
$no1=hexdec(substr(coin2hex($current['id']),0,12)); // make sure the sender has enough balance
$no2=hexdec(substr(coin2hex($data['id']),0,12)); $balance = $db->single("SELECT balance FROM accounts WHERE id=:id", [":id" => $src]);
if(gmp_cmp($no1,$no2)==1){ if ($balance < $val + $fee) {
$accept_new=true; api_err("Not enough funds");
} }
}
if($accept_new){ // make sure the sender has enough pending balance
// if the new block is accepted, run a microsanity to sync it $memspent = $db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src", [":src" => $src]);
_log('['.$ip."] Starting microsanity - $data[height]"); if ($balance - $memspent < $val + $fee) {
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &"); api_err("Not enough funds (mempool)");
api_echo("microsanity"); }
} else { // add to mempool
_log('['.$ip."] suggesting reverse-microsanity - $data[height]"); $trx->add_mempool($data, $ip);
api_echo("reverse-microsanity"); // if it's not, suggest to the peer to get the block from us
} // rebroadcast the transaction to some peers unless the transaction is smaller than the average size of transactions in mempool - protect against garbage data flooding
} $res = $db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ", [":src" => $data['src']]);
// if it's not the next block if ($res['c'] < $_config['max_mempool_rebroadcast'] && $res['v'] / $res['c'] < $data['val']) {
if($current['height']!=$data['height']-1) { system("php propagate.php transaction '$data[id]' > /dev/null 2>&1 &");
// if the height of the block submitted is lower than our current height, send them our current block }
if($data['height']<$current['height']){ api_echo("transaction-ok");
$pr=$db->row("SELECT * FROM peers WHERE ip=:ip",array(":ip"=>$ip)); } elseif ($q == "submitBlock") {
if(!$pr) api_err("block-too-old"); // receive a new block from a peer
$peer_host=base58_encode($pr['hostname']);
$pr['ip']=escapeshellcmd($pr['ip']); // if sanity sync, refuse all
system("php propagate.php block current '$peer_host' '$pr[ip]' > /dev/null 2>&1 &"); if ($_config['sanity_sync'] == 1) {
_log('['.$ip."] block too old, sending our current block - $data[height]"); _log('['.$ip."] Block rejected due to sanity sync");
api_err("sanity-sync");
api_err("block-too-old"); }
} $data['id'] = san($data['id']);
// if the block difference is bigger than 150, nothing should be done. They should sync via sanity $current = $block->current();
if($data['height']-$current['height']>150) { // block already in the blockchain
_log('['.$ip."] block-out-of-sync - $data[height]"); if ($current['id'] == $data['id']) {
api_err("block-out-of-sync"); api_echo("block-ok");
} }
// request them to send us a microsync with the latest blocks if ($data['date'] > time() + 30) {
_log('['.$ip."] requesting microsync - $current[height] - $data[height]"); api_err("block in the future");
api_echo(array("request"=>"microsync","height"=>$current['height'], "block"=>$current['id'])); }
} if ($current['height'] == $data['height'] && $current['id'] != $data['id']) {
// check block data // different forks, same height
if(!$block->check($data)){ $accept_new = false;
_log('['.$ip."] invalid block - $data[height]");
api_err("invalid-block"); // convert the first 12 characters from hex to decimal and the block with the largest number wins
} $no1 = hexdec(substr(coin2hex($current['id']), 0, 12));
$b=$data; $no2 = hexdec(substr(coin2hex($data['id']), 0, 12));
// add the block to the blockchain if (gmp_cmp($no1, $no2) == 1) {
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']); $accept_new = true;
}
if(!$res) {
_log('['.$ip."] invalid block data - $data[height]"); if ($accept_new) {
api_err("invalid-block-data"); // if the new block is accepted, run a microsanity to sync it
} _log('['.$ip."] Starting microsanity - $data[height]");
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
_log('['.$ip."] block ok, repropagating - $data[height]"); api_echo("microsanity");
} else {
// send it to all our peers _log('['.$ip."] suggesting reverse-microsanity - $data[height]");
system("php propagate.php block '$data[id]' all all linear > /dev/null 2>&1 &"); api_echo("reverse-microsanity"); // if it's not, suggest to the peer to get the block from us
api_echo("block-ok"); }
} }
// return the current block, used in syncing // if it's not the next block
elseif($q=="currentBlock"){ if ($current['height'] != $data['height'] - 1) {
$current=$block->current(); // if the height of the block submitted is lower than our current height, send them our current block
api_echo($current); if ($data['height'] < $current['height']) {
} $pr = $db->row("SELECT * FROM peers WHERE ip=:ip", [":ip" => $ip]);
// return a specific block, used in syncing if (!$pr) {
elseif($q=="getBlock"){ api_err("block-too-old");
$height=intval($data['height']); }
$peer_host = base58_encode($pr['hostname']);
$export=$block->export("",$height); $pr['ip'] = escapeshellcmd(san_ip($pr['ip']));
if(!$export) api_err("invalid-block"); system("php propagate.php block current '$peer_host' '$pr[ip]' > /dev/null 2>&1 &");
api_echo($export); _log('['.$ip."] block too old, sending our current block - $data[height]");
}
elseif($q=="getBlocks"){ api_err("block-too-old");
// returns X block starting at height, used in syncing }
// if the block difference is bigger than 150, nothing should be done. They should sync via sanity
$height=intval($data['height']); if ($data['height'] - $current['height'] > 150) {
_log('['.$ip."] block-out-of-sync - $data[height]");
$r=$db->run("SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",array(":height"=>$height)); api_err("block-out-of-sync");
foreach($r as $x){ }
$blocks[$x['height']]=$block->export($x['id']); // request them to send us a microsync with the latest blocks
} _log('['.$ip."] requesting microsync - $current[height] - $data[height]");
api_echo($blocks); api_echo(["request" => "microsync", "height" => $current['height'], "block" => $current['id']]);
}
} // check block data
// returns a full list of unblacklisted peers in a random order if (!$block->check($data)) {
elseif($q=="getPeers"){ _log('['.$ip."] invalid block - $data[height]");
$peers=$db->run("SELECT ip,hostname FROM peers WHERE blacklisted<UNIX_TIMESTAMP() ORDER by RAND()"); api_err("invalid-block");
api_echo($peers); }
} else { $b = $data;
api_err("Invalid request"); // add the block to the blockchain
} $res = $block->add(
$b['height'],
?> $b['public_key'],
$b['nonce'],
$b['data'],
$b['date'],
$b['signature'],
$b['difficulty'],
$b['reward_signature'],
$b['argon']
);
if (!$res) {
_log('['.$ip."] invalid block data - $data[height]");
api_err("invalid-block-data");
}
_log('['.$ip."] block ok, repropagating - $data[height]");
// send it to all our peers
system("php propagate.php block '$data[id]' all all linear > /dev/null 2>&1 &");
api_echo("block-ok");
} // return the current block, used in syncing
elseif ($q == "currentBlock") {
$current = $block->current();
api_echo($current);
} // return a specific block, used in syncing
elseif ($q == "getBlock") {
$height = intval($data['height']);
$export = $block->export("", $height);
if (!$export) {
api_err("invalid-block");
}
api_echo($export);
} elseif ($q == "getBlocks") {
// returns X block starting at height, used in syncing
$height = intval($data['height']);
$r = $db->run(
"SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",
[":height" => $height]
);
foreach ($r as $x) {
$blocks[$x['height']] = $block->export($x['id']);
}
api_echo($blocks);
} // returns a full list of unblacklisted peers in a random order
elseif ($q == "getPeers") {
$peers = $db->run("SELECT ip,hostname FROM peers WHERE blacklisted<UNIX_TIMESTAMP() ORDER by RAND()");
api_echo($peers);
} else {
api_err("Invalid request");
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
4.0.1

367
util.php
View File

@@ -1,7 +1,7 @@
<?php <?php
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 AroDev Copyright (c) 2018 AroDev
www.arionum.com www.arionum.com
@@ -24,17 +24,16 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
// make sure it's not accessible in the browser // make sure it's not accessible in the browser
if(php_sapi_name() !== 'cli') die("This should only be run as cli"); if (php_sapi_name() !== 'cli') {
die("This should only be run as cli");
}
require_once("include/init.inc.php"); require_once("include/init.inc.php");
$cmd=trim($argv[1]); $cmd = trim($argv[1]);
/** /**
* @api {php util.php} clean Clean * @api {php util.php} clean Clean
* @apiName clean * @apiName clean
* @apiGroup UTIL * @apiGroup UTIL
* @apiDescription Cleans the entire database * @apiDescription Cleans the entire database
@@ -43,16 +42,16 @@ $cmd=trim($argv[1]);
* php util.php clean * php util.php clean
*/ */
if($cmd=='clean'){ if ($cmd == 'clean') {
$tables=array("blocks","accounts","transactions","mempool"); $db->run("SET foreign_key_checks=0;");
foreach($tables as $table) $db->run("DELETE FROM {$table}"); $tables = ["accounts", "transactions", "mempool", "masternode","blocks"];
foreach ($tables as $table) {
$db->run("TRUNCATE TABLE {$table}");
}
$db->run("SET foreign_key_checks=1;");
echo "\n The database has been cleared\n"; echo "\n The database has been cleared\n";
} /**
}
/**
* @api {php util.php} pop Pop * @api {php util.php} pop Pop
* @apiName pop * @apiName pop
* @apiGroup UTIL * @apiGroup UTIL
@@ -64,13 +63,11 @@ echo "\n The database has been cleared\n";
* php util.php pop 1 * php util.php pop 1
*/ */
elseif($cmd=='pop'){ elseif ($cmd == 'pop') {
$no=intval($argv[2]); $no = intval($argv[2]);
$block=new Block; $block = new Block();
$block->pop($no); $block->pop($no);
} } /**
/**
* @api {php util.php} block-time Block-time * @api {php util.php} block-time Block-time
* @apiName block-time * @apiName block-time
* @apiGroup UTIL * @apiGroup UTIL
@@ -86,29 +83,28 @@ elseif($cmd=='pop'){
* Average block time: 217 seconds * Average block time: 217 seconds
*/ */
elseif($cmd=='block-time'){ elseif ($cmd == 'block-time') {
$t=time(); $t = time();
$r=$db->run("SELECT * FROM blocks ORDER by height DESC LIMIT 100"); $r = $db->run("SELECT * FROM blocks ORDER by height DESC LIMIT 100");
$start=0; $start = 0;
foreach($r as $x){ foreach ($r as $x) {
if($start==0) $start=$x['date']; if ($start == 0) {
$time=$t-$x['date']; $start = $x['date'];
$t=$x['date']; }
echo "$x[height] -> $time\n"; $time = $t - $x['date'];
$end=$x['date']; $t = $x['date'];
} echo "$x[height] -> $time\n";
echo "Average block time: ".ceil(($start-$end)/100)." seconds\n"; $end = $x['date'];
}
echo "Average block time: ".ceil(($start - $end) / 100)." seconds\n";
} } /**
/**
* @api {php util.php} peer Peer * @api {php util.php} peer Peer
* @apiName peer * @apiName peer
* @apiGroup UTIL * @apiGroup UTIL
* @apiDescription Creates a peering session with another node * @apiDescription Creates a peering session with another node
* *
* @apiParam {text} arg2 The Hostname of the other node * @apiParam {text} arg2 The Hostname of the other node
* *
* @apiExample {cli} Example usage: * @apiExample {cli} Example usage:
* php util.php peer http://peer1.arionum.com * php util.php peer http://peer1.arionum.com
* *
@@ -117,12 +113,14 @@ echo "Average block time: ".ceil(($start-$end)/100)." seconds\n";
*/ */
elseif($cmd=="peer"){ elseif ($cmd == "peer") {
$res=peer_post($argv[2]."/peer.php?q=peer",array("hostname"=>$_config['hostname'])); $res = peer_post($argv[2]."/peer.php?q=peer", ["hostname" => $_config['hostname']]);
if($res!==false) echo "Peering OK\n"; if ($res !== false) {
else echo "Peering FAIL\n"; echo "Peering OK\n";
} } else {
/** echo "Peering FAIL\n";
}
} /**
* @api {php util.php} current Current * @api {php util.php} current Current
* @apiName current * @apiName current
* @apiGroup UTIL * @apiGroup UTIL
@@ -155,15 +153,14 @@ elseif($cmd=="peer"){
* *
*/ */
elseif ($cmd=="current") { elseif ($cmd == "current") {
$block=new Block; $block = new Block();
var_dump($block->current()); var_dump($block->current());
} } /**
/**
* @api {php util.php} blocks Blocks * @api {php util.php} blocks Blocks
* @apiName blocks * @apiName blocks
* @apiGroup UTIL * @apiGroup UTIL
* @apiDescription Prints the id and the height of the blocks >=arg2, max 100 or arg3 * @apiDescription Prints the id and the height of the blocks >=arg2, max 100 or arg3
* *
* @apiParam {number} arg2 Starting height * @apiParam {number} arg2 Starting height
* *
@@ -180,16 +177,17 @@ elseif ($cmd=="current") {
* 10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m * 10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m
* *
*/ */
elseif($cmd=="blocks"){ elseif ($cmd == "blocks") {
$height=intval($argv[2]); $height = intval($argv[2]);
$limit=intval($argv[3]); $limit = intval($argv[3]);
if($limit<1) $limit=100; if ($limit < 1) {
$r=$db->run("SELECT * FROM blocks WHERE height>:height ORDER by height ASC LIMIT $limit",array(":height"=>$height)); $limit = 100;
foreach($r as $x){ }
echo "$x[height]\t$x[id]\n"; $r = $db->run("SELECT * FROM blocks WHERE height>:height ORDER by height ASC LIMIT $limit", [":height" => $height]);
} foreach ($r as $x) {
} echo "$x[height]\t$x[id]\n";
/** }
} /**
* @api {php util.php} recheck-blocks Recheck-Blocks * @api {php util.php} recheck-blocks Recheck-Blocks
* @apiName recheck-blocks * @apiName recheck-blocks
* @apiGroup UTIL * @apiGroup UTIL
@@ -199,26 +197,33 @@ elseif ($cmd=="current") {
* php util.php recheck-blocks * php util.php recheck-blocks
* *
*/ */
elseif($cmd=="recheck-blocks"){ elseif ($cmd == "recheck-blocks") {
$blocks=array(); $blocks = [];
$block=new Block(); $block = new Block();
$r=$db->run("SELECT * FROM blocks ORDER by height ASC"); $r = $db->run("SELECT * FROM blocks ORDER by height ASC");
foreach($r as $x){ foreach ($r as $x) {
$blocks[$x['height']]=$x; $blocks[$x['height']] = $x;
$max_height=$x['height']; $max_height = $x['height'];
} }
for($i=2;$i<=$max_height;$i++){ for ($i = 2; $i <= $max_height; $i++) {
$data=$blocks[$i]; $data = $blocks[$i];
$key=$db->single("SELECT public_key FROM accounts WHERE id=:id",array(":id"=>$data['generator']));
if(!$block->mine($key,$data['nonce'], $data['argon'], $data['difficulty'], $blocks[$i-1]['id'],$blocks[$i-1]['height'])) { $key = $db->single("SELECT public_key FROM accounts WHERE id=:id", [":id" => $data['generator']]);
_log("Invalid block detected. We should delete everything after $data[height] - $data[id]");
break; if (!$block->mine(
} $key,
} $data['nonce'],
} $data['argon'],
/** $data['difficulty'],
$blocks[$i - 1]['id'],
$blocks[$i - 1]['height'],
$data['date']
)) {
_log("Invalid block detected. We should delete everything after $data[height] - $data[id]");
break;
}
}
} /**
* @api {php util.php} peers Peers * @api {php util.php} peers Peers
* @apiName peers * @apiName peers
* @apiGroup UTIL * @apiGroup UTIL
@@ -232,15 +237,16 @@ elseif($cmd=="recheck-blocks"){
* ... * ...
* http://aro.master.hashpi.com active * http://aro.master.hashpi.com active
*/ */
elseif($cmd=="peers"){ elseif ($cmd == "peers") {
$r=$db->run("SELECT * FROM peers ORDER by reserve ASC"); $r = $db->run("SELECT * FROM peers ORDER by reserve ASC");
$status="active"; $status = "active";
if($x['reserve']==1) $status="reserve"; if ($x['reserve'] == 1) {
foreach($r as $x){ $status = "reserve";
echo "$x[hostname]\t$status\n"; }
} foreach ($r as $x) {
} echo "$x[hostname]\t$status\n";
/** }
} /**
* @api {php util.php} mempool Mempool * @api {php util.php} mempool Mempool
* @apiName mempool * @apiName mempool
* @apiGroup UTIL * @apiGroup UTIL
@@ -252,12 +258,10 @@ elseif($cmd=="recheck-blocks"){
* @apiSuccessExample {text} Success-Response: * @apiSuccessExample {text} Success-Response:
* Mempool size: 12 * Mempool size: 12
*/ */
elseif($cmd=="mempool"){ elseif ($cmd == "mempool") {
$res=$db->single("SELECT COUNT(1) from mempool"); $res = $db->single("SELECT COUNT(1) from mempool");
echo "Mempool size: $res\n"; echo "Mempool size: $res\n";
} /**
}
/**
* @api {php util.php} delete-peer Delete-peer * @api {php util.php} delete-peer Delete-peer
* @apiName delete-peer * @apiName delete-peer
* @apiGroup UTIL * @apiGroup UTIL
@@ -271,24 +275,25 @@ echo "Mempool size: $res\n";
* @apiSuccessExample {text} Success-Response: * @apiSuccessExample {text} Success-Response:
* Peer removed * Peer removed
*/ */
elseif($cmd=="delete-peer"){ elseif ($cmd == "delete-peer") {
$peer=trim($argv[2]); $peer = trim($argv[2]);
if(empty($peer)) die("Invalid peer"); if (empty($peer)) {
$db->run("DELETE FROM peers WHERE ip=:ip",array(":ip"=>$peer)); die("Invalid peer");
echo "Peer removed\n"; }
}elseif($cmd=="recheck-peers"){ $db->run("DELETE FROM peers WHERE ip=:ip", [":ip" => $peer]);
$r=$db->run("SELECT * FROM peers"); echo "Peer removed\n";
foreach($r as $x){ } elseif ($cmd == "recheck-peers") {
$a=peer_post($x['hostname']."/peer.php?q=ping"); $r = $db->run("SELECT * FROM peers");
if($a!="pong"){ foreach ($r as $x) {
echo "$x[hostname] -> failed\n"; $a = peer_post($x['hostname']."/peer.php?q=ping");
$db->run("DELETE FROM peers WHERE id=:id",array(":id"=>$x['id'])); if ($a != "pong") {
} else echo "$x[hostname] ->ok \n"; echo "$x[hostname] -> failed\n";
} $db->run("DELETE FROM peers WHERE id=:id", [":id" => $x['id']]);
} else {
} echo "$x[hostname] ->ok \n";
}
/** }
} /**
* @api {php util.php} peers-block Peers-Block * @api {php util.php} peers-block Peers-Block
* @apiName peers-block * @apiName peers-block
* @apiGroup UTIL * @apiGroup UTIL
@@ -302,23 +307,24 @@ elseif($cmd=="delete-peer"){
* ... * ...
* http://peer10.arionum.com 16849 * http://peer10.arionum.com 16849
*/ */
elseif($cmd=="peers-block"){ elseif ($cmd == "peers-block") {
$only_diff=false; $only_diff = false;
if($argv[2]=="diff"){ if ($argv[2] == "diff") {
$current=$db->single("SELECT height FROM blocks ORDER by height DESC LIMIT 1"); $current = $db->single("SELECT height FROM blocks ORDER by height DESC LIMIT 1");
$only_diff=true; $only_diff = true;
} }
$r=$db->run("SELECT * FROM peers WHERE blacklisted<UNIX_TIMESTAMP()"); $r = $db->run("SELECT * FROM peers WHERE blacklisted<UNIX_TIMESTAMP()");
foreach($r as $x){ foreach ($r as $x) {
$a=peer_post($x['hostname']."/peer.php?q=currentBlock",array(),5); $a = peer_post($x['hostname']."/peer.php?q=currentBlock", [], 5);
$enc=base58_encode($x['hostname']); $enc = base58_encode($x['hostname']);
if($argv[2]=="debug") echo "$enc\t"; if ($argv[2] == "debug") {
if($only_diff==false||$current!=$a['height']) echo "$x[hostname]\t$a[height]\n"; echo "$enc\t";
} }
} if ($only_diff == false || $current != $a['height']) {
echo "$x[hostname]\t$a[height]\n";
/** }
}
} /**
* @api {php util.php} balance Balance * @api {php util.php} balance Balance
* @apiName balance * @apiName balance
* @apiGroup UTIL * @apiGroup UTIL
@@ -333,14 +339,15 @@ elseif($cmd=="peers-block"){
* Balance: 2,487 * Balance: 2,487
*/ */
elseif($cmd=="balance"){ elseif ($cmd == "balance") {
$id = san($argv[2]);
$res = $db->single(
"SELECT balance FROM accounts WHERE id=:id OR public_key=:id2 LIMIT 1",
[":id" => $id, ":id2" => $id]
);
$id=san($argv[2]); echo "Balance: ".number_format($res)."\n";
$res=$db->single("SELECT balance FROM accounts WHERE id=:id OR public_key=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id)); } /**
echo "Balance: ".number_format($res)."\n";
}
/**
* @api {php util.php} block Block * @api {php util.php} block Block
* @apiName block * @apiName block
* @apiGroup UTIL * @apiGroup UTIL
@@ -373,14 +380,12 @@ elseif($cmd=="balance"){
* int(0) * int(0)
* } * }
*/ */
elseif($cmd=="block"){ elseif ($cmd == "block") {
$id=san($argv[2]); $id = san($argv[2]);
$res=$db->row("SELECT * FROM blocks WHERE id=:id OR height=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id)); $res = $db->row("SELECT * FROM blocks WHERE id=:id OR height=:id2 LIMIT 1", [":id" => $id, ":id2" => $id]);
var_dump($res);
} var_dump($res);
/** } /**
* @api {php util.php} check-address Check-Address * @api {php util.php} check-address Check-Address
* @apiName check-address * @apiName check-address
* @apiGroup UTIL * @apiGroup UTIL
@@ -394,15 +399,19 @@ elseif($cmd=="block"){
* @apiSuccessExample {text} Success-Response: * @apiSuccessExample {text} Success-Response:
* The address is valid * The address is valid
*/ */
elseif($cmd=="check-address"){ elseif ($cmd == "check-address") {
$dst=trim($argv[2]); $dst = trim($argv[2]);
$acc=new Account; $acc = new Account();
if(!$acc->valid($dst)) die("Invalid address"); if (!$acc->valid($dst)) {
$dst_b=base58_decode($dst); die("Invalid address");
if(strlen($dst_b)!=64) die("Invalid address - ".strlen($dst_b)." bytes"); }
$dst_b = base58_decode($dst);
echo "The address is valid\n"; if (strlen($dst_b) != 64) {
} die("Invalid address - ".strlen($dst_b)." bytes");
}
echo "The address is valid\n";
}
/** /**
* @api {php util.php} get-address Get-Address * @api {php util.php} get-address Get-Address
* @apiName get-address * @apiName get-address
@@ -418,15 +427,49 @@ elseif($cmd=="check-address"){
* 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr * 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
*/ */
elseif($cmd=='get-address'){ elseif ($cmd == 'get-address') {
$public_key = trim($argv2);
$public_key=trim($argv2); if (strlen($public_key) < 32) {
if(strlen($public_key)<32) die("Invalid public key"); die("Invalid public key");
}
print($acc->get_address($public_key)); print($acc->get_address($public_key));
/**
* @api {php util.php} clean-blacklist Clean-Blacklist
* @apiName clean-blacklist
* @apiGroup UTIL
* @apiDescription Removes all the peers from blacklist
*
* @apiExample {cli} Example usage:
* php util.php clean-blacklist
*
*/
} elseif ($cmd == 'clean-blacklist') {
$db->run("UPDATE peers SET blacklisted=0, fails=0, stuckfail=0");
echo "All the peers have been removed from the blacklist\n";
}elseif($cmd == 'resync-accounts'){
// resyncs the balance on all accounts
// lock table to avoid race conditions on blocks
$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";
$db->run("UPDATE accounts SET balance=:bal WHERE id=:id",[":id"=>$x['id'], ":bal"=>$balance]);
}
}
$db->exec("UNLOCK TABLES");
echo "All done";
} else { } else {
echo "Invalid command\n"; echo "Invalid command\n";
} }
?>