documentation + base58 fix

This commit is contained in:
Arionum
2018-02-22 19:22:28 +02:00
parent e1971dd741
commit 8a648dcc8a
18 changed files with 1749 additions and 226 deletions

View File

@@ -40,6 +40,8 @@ OR OTHER DEALINGS IN THE SOFTWARE.
*
* @apiSuccess {String} status "ok"
* @apiSuccess {String} data The data provided by the api will be under this object.
*
*
*
* @apiSuccessExample {json} Success-Response:
*{

View File

@@ -999,6 +999,495 @@ define({ "api": [
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "php util.php",
"url": "balance",
"title": "Balance",
"name": "balance",
"group": "UTIL",
"description": "<p>Prints the balance of an address or a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>address or public_key</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php balance 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Balance: 2,487",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "block",
"title": "Block",
"name": "block",
"group": "UTIL",
"description": "<p>Returns a specific block</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>block id</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php block 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "array(9) {\n [\"id\"]=>\n string(88) \"4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR\"\n [\"generator\"]=>\n string(88) \"5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn\"\n [\"height\"]=>\n int(16833)\n [\"date\"]=>\n int(1519312385)\n [\"nonce\"]=>\n string(41) \"EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY\"\n [\"signature\"]=>\n string(97) \"AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu\"\n [\"difficulty\"]=>\n string(8) \"61982809\"\n [\"argon\"]=>\n string(68) \"$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg\"\n [\"transactions\"]=>\n int(0)\n}",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "block-time",
"title": "Block-time",
"name": "block_time",
"group": "UTIL",
"description": "<p>Shows the block time of the last 100 blocks</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php block-time",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "16830 -> 323\n...\n16731 -> 302\nAverage block time: 217 seconds",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "blocks",
"title": "Blocks",
"name": "blocks",
"group": "UTIL",
"description": "<p>Prints the id and the height of the blocks &gt;=arg2, max 100 or arg3</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "number",
"optional": false,
"field": "arg2",
"description": "<p>Starting height</p>"
},
{
"group": "Parameter",
"type": "number",
"optional": true,
"field": "arg3",
"description": "<p>Block Limit</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php blocks 10800 5",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "10801 2yAHaZ3ghNnThaNK6BJcup2zq7EXuFsruMb5qqXaHP9M6JfBfstAag1n1PX7SMKGcuYGZddMzU7hW87S5ZSayeKX\n10802 wNa4mRvRPCMHzsgLdseMdJCvmeBaCNibRJCDhsuTeznJh8C1aSpGuXRDPYMbqKiVtmGAaYYb9Ze2NJdmK1HY9zM\n10803 3eW3B8jCFBauw8EoKN4SXgrn33UBPw7n8kvDDpyQBw1uQcmJQEzecAvwBk5sVfQxUqgzv31JdNHK45JxUFcupVot\n10804 4mWK1f8ch2Ji3D6aw1BsCJavLNBhQgpUHBCHihnrLDuh8Bjwsou5bQDj7D7nV4RsEPmP2ZbjUUMZwqywpRc8r6dR\n10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "check-address",
"title": "Check-Address",
"name": "check_address",
"group": "UTIL",
"description": "<p>Checks a specific address for validity</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>block id</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php check-address 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "The address is valid",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "clean",
"title": "Clean",
"name": "clean",
"group": "UTIL",
"description": "<p>Cleans the entire database</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "current",
"title": "Current",
"name": "current",
"group": "UTIL",
"description": "<p>Prints the current block in var_dump</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php current",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "array(9) {\n [\"id\"]=>\n string(88) \"4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR\"\n [\"generator\"]=>\n string(88) \"5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn\"\n [\"height\"]=>\n int(16833)\n [\"date\"]=>\n int(1519312385)\n [\"nonce\"]=>\n string(41) \"EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY\"\n [\"signature\"]=>\n string(97) \"AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu\"\n [\"difficulty\"]=>\n string(8) \"61982809\"\n [\"argon\"]=>\n string(68) \"$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg\"\n [\"transactions\"]=>\n int(0)\n}",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "delete-peer",
"title": "Delete-peer",
"name": "delete_peer",
"group": "UTIL",
"description": "<p>Removes a peer from the peerlist</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>Peer's hostname</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php delete-peer http://peer1.arionum.com",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Peer removed",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "get-address",
"title": "Get-Address",
"name": "get_address",
"group": "UTIL",
"description": "<p>Converts a public key into an address</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>public key</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php get-address PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwQr8cE5s6APWAE1SWAmH6NM1nJTryBURULEsifA2hLVuW5GXFD1XU6s6REG1iPK7qGaRDkGpQwJjDhQKVoSVkSNp",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "mempool",
"title": "Mempool",
"name": "mempool",
"group": "UTIL",
"description": "<p>Prints the number of transactions in mempool</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php mempool",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Mempool size: 12",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peer",
"title": "Peer",
"name": "peer",
"group": "UTIL",
"description": "<p>Creates a peering session with another node</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>The Hostname of the other node</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php peer http://peer1.arionum.com",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Peering OK",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peers",
"title": "Peers",
"name": "peers",
"group": "UTIL",
"description": "<p>Prints all the peers and their status</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php peers",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "http://35.190.160.142 active\n...\nhttp://aro.master.hashpi.com active",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peers-block",
"title": "Peers-Block",
"name": "peers_block",
"group": "UTIL",
"description": "<p>Prints the current height of all the peers</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php peers-block",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "http://peer5.arionum.com 16849\n...\nhttp://peer10.arionum.com 16849",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "pop",
"title": "Pop",
"name": "pop",
"group": "UTIL",
"description": "<p>Cleans the entire database</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": false,
"field": "arg2",
"description": "<p>Number of blocks to delete</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php pop 1",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "recheck-blocks",
"title": "Recheck-Blocks",
"name": "recheck_blocks",
"group": "UTIL",
"description": "<p>Recheck all the blocks to make sure the blockchain is correct</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php recheck-blocks",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"success": {
"fields": {

View File

@@ -999,6 +999,495 @@
"filename": "./api.php",
"groupTitle": "API"
},
{
"type": "php util.php",
"url": "balance",
"title": "Balance",
"name": "balance",
"group": "UTIL",
"description": "<p>Prints the balance of an address or a public key</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>address or public_key</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php balance 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Balance: 2,487",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "block",
"title": "Block",
"name": "block",
"group": "UTIL",
"description": "<p>Returns a specific block</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>block id</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php block 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "array(9) {\n [\"id\"]=>\n string(88) \"4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR\"\n [\"generator\"]=>\n string(88) \"5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn\"\n [\"height\"]=>\n int(16833)\n [\"date\"]=>\n int(1519312385)\n [\"nonce\"]=>\n string(41) \"EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY\"\n [\"signature\"]=>\n string(97) \"AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu\"\n [\"difficulty\"]=>\n string(8) \"61982809\"\n [\"argon\"]=>\n string(68) \"$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg\"\n [\"transactions\"]=>\n int(0)\n}",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "block-time",
"title": "Block-time",
"name": "block_time",
"group": "UTIL",
"description": "<p>Shows the block time of the last 100 blocks</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php block-time",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "16830 -> 323\n...\n16731 -> 302\nAverage block time: 217 seconds",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "blocks",
"title": "Blocks",
"name": "blocks",
"group": "UTIL",
"description": "<p>Prints the id and the height of the blocks &gt;=arg2, max 100 or arg3</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "number",
"optional": false,
"field": "arg2",
"description": "<p>Starting height</p>"
},
{
"group": "Parameter",
"type": "number",
"optional": true,
"field": "arg3",
"description": "<p>Block Limit</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php blocks 10800 5",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "10801 2yAHaZ3ghNnThaNK6BJcup2zq7EXuFsruMb5qqXaHP9M6JfBfstAag1n1PX7SMKGcuYGZddMzU7hW87S5ZSayeKX\n10802 wNa4mRvRPCMHzsgLdseMdJCvmeBaCNibRJCDhsuTeznJh8C1aSpGuXRDPYMbqKiVtmGAaYYb9Ze2NJdmK1HY9zM\n10803 3eW3B8jCFBauw8EoKN4SXgrn33UBPw7n8kvDDpyQBw1uQcmJQEzecAvwBk5sVfQxUqgzv31JdNHK45JxUFcupVot\n10804 4mWK1f8ch2Ji3D6aw1BsCJavLNBhQgpUHBCHihnrLDuh8Bjwsou5bQDj7D7nV4RsEPmP2ZbjUUMZwqywpRc8r6dR\n10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "check-address",
"title": "Check-Address",
"name": "check_address",
"group": "UTIL",
"description": "<p>Checks a specific address for validity</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>block id</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php check-address 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "The address is valid",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "clean",
"title": "Clean",
"name": "clean",
"group": "UTIL",
"description": "<p>Cleans the entire database</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php clean",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "current",
"title": "Current",
"name": "current",
"group": "UTIL",
"description": "<p>Prints the current block in var_dump</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php current",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "array(9) {\n [\"id\"]=>\n string(88) \"4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR\"\n [\"generator\"]=>\n string(88) \"5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn\"\n [\"height\"]=>\n int(16833)\n [\"date\"]=>\n int(1519312385)\n [\"nonce\"]=>\n string(41) \"EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY\"\n [\"signature\"]=>\n string(97) \"AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu\"\n [\"difficulty\"]=>\n string(8) \"61982809\"\n [\"argon\"]=>\n string(68) \"$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg\"\n [\"transactions\"]=>\n int(0)\n}",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "delete-peer",
"title": "Delete-peer",
"name": "delete_peer",
"group": "UTIL",
"description": "<p>Removes a peer from the peerlist</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>Peer's hostname</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php delete-peer http://peer1.arionum.com",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Peer removed",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "get-address",
"title": "Get-Address",
"name": "get_address",
"group": "UTIL",
"description": "<p>Converts a public key into an address</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>public key</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php get-address PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwQr8cE5s6APWAE1SWAmH6NM1nJTryBURULEsifA2hLVuW5GXFD1XU6s6REG1iPK7qGaRDkGpQwJjDhQKVoSVkSNp",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "mempool",
"title": "Mempool",
"name": "mempool",
"group": "UTIL",
"description": "<p>Prints the number of transactions in mempool</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php mempool",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Mempool size: 12",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peer",
"title": "Peer",
"name": "peer",
"group": "UTIL",
"description": "<p>Creates a peering session with another node</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "text",
"optional": false,
"field": "arg2",
"description": "<p>The Hostname of the other node</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php peer http://peer1.arionum.com",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "Peering OK",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peers",
"title": "Peers",
"name": "peers",
"group": "UTIL",
"description": "<p>Prints all the peers and their status</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php peers",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "http://35.190.160.142 active\n...\nhttp://aro.master.hashpi.com active",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "peers-block",
"title": "Peers-Block",
"name": "peers_block",
"group": "UTIL",
"description": "<p>Prints the current height of all the peers</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php peers-block",
"type": "cli"
}
],
"success": {
"examples": [
{
"title": "Success-Response:",
"content": "http://peer5.arionum.com 16849\n...\nhttp://peer10.arionum.com 16849",
"type": "text"
}
]
},
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "pop",
"title": "Pop",
"name": "pop",
"group": "UTIL",
"description": "<p>Cleans the entire database</p>",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": false,
"field": "arg2",
"description": "<p>Number of blocks to delete</p>"
}
]
}
},
"examples": [
{
"title": "Example usage:",
"content": "php util.php pop 1",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"type": "php util.php",
"url": "recheck-blocks",
"title": "Recheck-Blocks",
"name": "recheck_blocks",
"group": "UTIL",
"description": "<p>Recheck all the blocks to make sure the blockchain is correct</p>",
"examples": [
{
"title": "Example usage:",
"content": "php util.php recheck-blocks",
"type": "cli"
}
],
"version": "0.0.0",
"filename": "./util.php",
"groupTitle": "UTIL"
},
{
"success": {
"fields": {

View File

@@ -7,7 +7,7 @@ define({
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-02-15T01:57:51.098Z",
"time": "2018-02-22T16:13:41.713Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@@ -7,7 +7,7 @@
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-02-15T01:57:51.098Z",
"time": "2018-02-22T16:13:41.713Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}

View File

@@ -3,7 +3,7 @@
class Account {
// inserts the account in the DB and updates the public key if empty
public function add($public_key, $block){
global $db;
$id=$this->get_address($public_key);
@@ -11,52 +11,64 @@ class Account {
$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){
for($i=0;$i<9;$i++) $hash=hash('sha512',$hash, true);
return base58_encode($hash);
//broken base58 addresses, which are block winners, missing the first 0 bytes from the address.
if($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwCpspGFGQSaF9yVGLamBgymdf8M7FafghmP3oPzQb3W4PZsZApVa41uQrrHRVBH5p9bdoz7c6XeRQHK2TkzWR45e') return '22SoB29oyq2JhMxtBbesL7JioEYytyC6VeFmzvBH6fRQrueSvyZfEXR5oR7ajSQ9mLERn6JKU85EAbVDNChke32';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCzbRyyz5oDNDKhk5jyjg4caRjkbqegMZMrUkuBjVMuYcVfPyc3aKuLmPHS4QEDjCrNGks7Z5oPxwv4yXSv7WJnkbL') return 'AoFnv3SLujrJSa2J7FDTADGD7Eb9kv3KtNAp7YVYQEUPcLE6cC6nLvvhVqcVnRLYF5BFF38C1DyunUtmfJBhyU';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyradtFFJoaYB4QdcXyBGSXjiASMMnofsT4f5ZNaxTnNDJt91ubemn3LzgKrfQh8CBpqaphkVNoRLub2ctdMnrzG1') return 'RncXQuc7S7aWkvTUJSHEFvYoV3ntAf7bfxEHjSiZNBvQV37MzZtg44L7GAV7szZ3uV8qWqikBewa3piZMqzBqm';
elseif($hash=='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyjKMBY4ihhJ2G25EVezg7KnoCBVbhdvWfqzNA4LC5R7wgu3VNfJgvqkCq9sKKZcCoCpX6Qr9cN882MoXsfGTvZoj') return 'Rq53oLzpCrb4BdJZ1jqQ2zsixV2ukxVdM4H9uvUhCGJCz1q2wagvuXV4hC6UVwK7HqAt1FenukzhVXgzyG1y32';
// hashes 9 times in sha512 (binary) and encodes in base58
for($i=0;$i<9;$i++) $hash=hash('sha512',$hash, true);
return base58_encode($hash);
}
// checks the ecdsa secp256k1 signature for a specific public key
public function check_signature($data, $signature, $public_key){
return ec_verify($data ,$signature, $public_key);
}
// generates a new account and a public/private key pair
public function generate_account(){
// using secp256k1 curve for ECDSA
$args = array(
"curve_name" => "secp256k1",
"private_key_type" => OPENSSL_KEYTYPE_EC,
);
// generates a new key pair
$key1 = openssl_pkey_new($args);
// exports the private key encoded as PEM
openssl_pkey_export($key1, $pvkey);
// converts the PEM to a base58 format
$private_key= pem2coin($pvkey);
// exports the private key encoded as PEM
$pub = openssl_pkey_get_details($key1);
// converts the PEM to a base58 format
$public_key= pem2coin($pub['key']);
// generates the account's address based on the public key
$address=$this->get_address($public_key);
return array("address"=>$address, "public_key"=>$public_key,"private_key"=>$private_key);
}
// check the validity of a base58 encoded key. At the moment, it checks only the characters to be base58.
public function valid_key($id){
$chars = str_split("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz");
for($i=0;$i<strlen($id);$i++) if(!in_array($id[$i],$chars)) return false;
@@ -64,6 +76,7 @@ class Account {
return true;
}
// 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");
@@ -72,26 +85,31 @@ class Account {
return true;
}
// returns the current account balance
public function balance($id){
global $db;
$res=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$id));
if($res===false) $res="0.00000000";
return number_format($res,8,".","");
}
// returns the account balance - any pending debits from the mempool
public function pending_balance($id){
global $db;
$res=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$id));
if($res===false) $res="0.00000000";
// 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();
$current=$block->current();
$public_key=$this->public_key($id);
$limit=intval($limit);
if($limit>100||$limit<1) $limit=100;
@@ -102,7 +120,8 @@ class Account {
$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";
@@ -115,6 +134,7 @@ class Account {
}
return $transactions;
}
// returns the transactions from the mempool
public function get_mempool_transactions($id){
global $db;
$transactions=array();
@@ -122,12 +142,14 @@ class Account {
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));

View File

@@ -8,28 +8,27 @@ public function add($height, $public_key, $nonce, $data, $date, $signature, $dif
global $db;
$acc=new Account;
$trx=new Transaction;
//try {
// } catch (Exception $e){
// }
$generator=$acc->get_address($public_key);
// the transactions are always sorted in the same way, on all nodes, as they are hashed as json
ksort($data);
// create the hash / block id
$hash=$this->hash($generator, $height, $date, $nonce, $data, $signature, $difficulty, $argon);
//fix for the broken base58 library used until block 17000, trimming the first 0 bytes.
if($height<=17000) $hash=ltrim($hash,'1');
$json=json_encode($data);
// create the block data and check it against the signature
$info="{$generator}-{$height}-{$date}-{$nonce}-{$json}-{$difficulty}-{$argon}";
if(!$acc->check_signature($info,$signature,$public_key)) return false;
if(!$acc->check_signature($info,$signature,$public_key)) { _log("Block signature check failed"); return false; }
if(!$this->parse_block($hash,$height,$data, true)) return false;
if(!$this->parse_block($hash,$height,$data, true)) { _log("Parse block failed"); return false; }
// lock table to avoid race conditions on blocks
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
$reward=$this->reward($height,$data);
@@ -37,34 +36,42 @@ public function add($height, $public_key, $nonce, $data, $date, $signature, $dif
$msg='';
// the reward transaction
$transaction=array("src"=>$generator, "dst"=>$generator, "val"=>$reward, "version"=>0, "date"=>$date, "message"=>$msg, "fee"=>"0.00000000","public_key"=>$public_key);
$transaction['signature']=$reward_signature;
// hash the transaction
$transaction['id']=$trx->hash($transaction);
$info=$transaction['val']."-".$transaction['fee']."-".$transaction['dst']."-".$transaction['message']."-".$transaction['version']."-".$transaction['public_key']."-".$transaction['date'];
if(!$acc->check_signature($info,$reward_signature,$public_key)) return false;
// check the signature
$info=$transaction['val']."-".$transaction['fee']."-".$transaction['dst']."-".$transaction['message']."-".$transaction['version']."-".$transaction['public_key']."-".$transaction['date'];
if(!$acc->check_signature($info,$reward_signature,$public_key)) {_log("Reward signature failed"); return false; }
// insert the block into the db
$db->beginTransaction();
$total=count($data);
$bind=array(":id"=>$hash,":generator"=>$generator, ":signature"=>$signature, ":height"=>$height, ":date"=>$date, ":nonce"=>$nonce, ":difficulty"=>$difficulty,":argon"=>$argon, ":transactions"=>$total);
$res=$db->run("INSERT into blocks SET id=:id, generator=:generator, height=:height,`date`=:date,nonce=:nonce, signature=:signature, difficulty=:difficulty, argon=:argon, transactions=:transactions",$bind);
if($res!=1) {
// rollback and exit if it fails
_log("Block DB insert failed");
$db->rollback();
$db->exec("UNLOCK TABLES");
return false;
}
// insert the reward transaction in the db
$trx->add($hash, $height,$transaction);
// parse the block's transactions and insert them to db
$res=$this->parse_block($hash,$height,$data, false);
// if any fails, rollback
if($res==false) $db->rollback();
else $db->commit();
// relese the locking as everything is finished
$db->exec("UNLOCK TABLES");
return true;
}
// returns the current block, without the transactions
public function current(){
global $db;
$current=$db->row("SELECT * FROM blocks ORDER by height DESC LIMIT 1");
@@ -75,7 +82,7 @@ public function current(){
return $current;
}
// returns the previous block
public function prev(){
global $db;
$current=$db->row("SELECT * FROM blocks ORDER by height DESC LIMIT 1,1");
@@ -83,9 +90,11 @@ public function prev(){
return $current;
}
// calculates the difficulty / base target for a specific block. The higher the difficulty number, the easier it is to win a block.
public function difficulty($height=0){
global $db;
// if no block height is specified, use the current block.
if($height==0){
$current=$this->current();
} else{
@@ -94,35 +103,46 @@ public function difficulty($height=0){
$height=$current['height'];
if($height==10801) return 5555555555; //hard fork 10900 resistance, force new difficulty
if($height==10801) return 5555555555; //hard fork 10900 resistance
// last 20 blocks used to check the block times
$limit=20;
if($height<20)
$limit=$height-1;
// for the first 10 blocks, use the genesis difficulty
if($height<10) return $current['difficulty'];
// elapsed time between the last 20 blocks
$first=$db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT $limit,1");
$time=$current['date']-$first['date'];
// avg block time
$result=ceil($time/$limit);
// if larger than 200 sec, increase by 5%
if($result>220){
$dif= bcmul($current['difficulty'], 1.05);
} elseif($result<260){
// if lower, decrease by 5%
$dif= bcmul($current['difficulty'], 0.95);
} else {
// keep current difficulty
$dif=$current['difficulty'];
}
if(strpos($dif,'.')!==false){
$dif=substr($dif,0,strpos($dif,'.'));
}
//minimum and maximum diff
if($dif<1000) $dif=1000;
if($dif>9223372036854775800) $dif=9223372036854775800;
return $dif;
}
// calculates the maximum block size and increase by 10% the number of transactions if > 100 on the last 100 blocks
public function max_transactions(){
global $db;
$current=$this->current();
@@ -132,15 +152,19 @@ public function max_transactions(){
return ceil($avg*1.1);
}
// calculate the reward for each block
public function reward($id,$data=array()){
// starting reward
$reward=1000;
// decrease by 1% each 10800 blocks (approx 1 month)
$factor=floor($id/10800)/100;
$reward-=$reward*$factor;
if($reward<0) $reward=0;
// calculate the transaction fees
$fees=0;
if(count($data)>0){
@@ -151,28 +175,39 @@ public function reward($id,$data=array()){
return number_format($reward+$fees,8,'.','');
}
// checks the validity of a block
public function check($data){
if(strlen($data['argon'])<20) return false;
// argon must have at least 20 chars
if(strlen($data['argon'])<20) { _log("Invalid block argon - $data[argon]"); return false; }
$acc=new Account;
if(!$acc->valid_key($data['public_key'])) return false;
if($data['difficulty']!=$this->difficulty()) return false;
if(!$this->mine($data['public_key'],$data['nonce'], $data['argon'])) return false;
// generator's public key must be valid
if(!$acc->valid_key($data['public_key'])) { _log("Invalid public key - $data[public_key]"); return false; }
//difficulty should be the same as our calculation
if($data['difficulty']!=$this->difficulty()) { _log("Invalid difficulty - $data[difficulty] - ".$this->difficulty()); return false; }
//check the argon hash and the nonce to produce a valid block
if(!$this->mine($data['public_key'],$data['nonce'], $data['argon'])) { _log("Mine check failed"); return false; }
return true;
}
// creates a new block on this node
public function forge($nonce, $argon, $public_key, $private_key){
//check the argon hash and the nonce to produce a valid block
if(!$this->mine($public_key,$nonce, $argon)) return false;
// the block's date timestamp must be bigger than the last block
$current=$this->current();
$height=$current['height']+=1;
$date=time();
if($date<=$current['date']) return 0;
// get the mempool transactions
$txn=new Transaction;
$data=$txn->mempool($this->max_transactions());
@@ -180,55 +215,72 @@ public function forge($nonce, $argon, $public_key, $private_key){
$difficulty=$this->difficulty();
$acc=new Account;
$generator=$acc->get_address($public_key);
// always sort the transactions in the same way
ksort($data);
// sign the block
$signature=$this->sign($generator, $height, $date, $nonce, $data, $private_key, $difficulty, $argon);
// reward signature
// reward transaction and signature
$reward=$this->reward($height,$data);
$msg='';
$transaction=array("src"=>$generator, "dst"=>$generator, "val"=>$reward, "version"=>0, "date"=>$date, "message"=>$msg, "fee"=>"0.00000000","public_key"=>$public_key);
ksort($transaction);
$reward_signature=$txn->sign($transaction, $private_key);
// add the block to the blockchain
$res=$this->add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon);
if(!$res) return false;
return true;
}
// check if the arguments are good for mining a specific block
public function mine($public_key, $nonce, $argon, $difficulty=0, $current_id=0, $current_height=0){
global $_config;
// if no id is specified, we use the current
if($current_id===0){
$current=$this->current();
$current_id=$current['id'];
$current_height=$current['height'];
}
// get the current difficulty if empty
if($difficulty===0) $difficulty=$this->difficulty();
if($current_height>10800) $argon='$argon2i$v=19$m=524288,t=1,p=1'.$argon; //10800
// the argon parameters are hardcoded to avoid any exploits
if($current_height>10800) $argon='$argon2i$v=19$m=524288,t=1,p=1'.$argon; //10800 block hard fork - resistance against gpu
else $argon='$argon2i$v=19$m=16384,t=4,p=4'.$argon;
// the hash base for agon
$base="$public_key-$nonce-".$current_id."-$difficulty";
// check argon's hash validity
if(!password_verify($base,$argon)) { return false; }
// all nonces are valid in testnet
if($_config['testnet']==true) return true;
// prepare the base for the hashing
$hash=$base.$argon;
// hash the base 6 times
for($i=0;$i<5;$i++) $hash=hash("sha512",$hash,true);
$hash=hash("sha512",$hash);
// split it in 2 char substrings, to be used as hex
$m=str_split($hash,2);
// calculate a number based on 8 hex numbers - no specific reason, we just needed an algoritm to generate the number from the hash
$duration=hexdec($m[10]).hexdec($m[15]).hexdec($m[20]).hexdec($m[23]).hexdec($m[31]).hexdec($m[40]).hexdec($m[45]).hexdec($m[55]);
$duration=ltrim($duration, '0');
$result=gmp_div($duration, $difficulty);
// the number must not start with 0
$duration=ltrim($duration, '0');
// divide the number by the difficulty and create the deadline
$result=gmp_div($duration, $difficulty);
// if the deadline >0 and <=240, the arguments are valid fora block win
if($result>0&&$result<=240) return true;
return false;
@@ -236,35 +288,43 @@ public function mine($public_key, $nonce, $argon, $difficulty=0, $current_id=0,
// parse the block transactions
public function parse_block($block, $height, $data, $test=true){
global $db;
// data must be array
if($data===false) return false;
$acc=new Account;
$trx=new Transaction;
// no transactions means all are valid
if(count($data)==0) return true;
// check if the number of transactions is not bigger than current block size
$max=$this->max_transactions();
if(count($data)>$max) return false;
$balance=array();
foreach($data as &$x){
// get the sender's account if empty
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']);
//validate the transaction
if(!$trx->check($x,$height)) return false;
$balance[$x['src']]+=$x['val']+$x['fee'];
if($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$x['id']))>0) return false; //duplicate transaction
// prepare total balance
$balance[$x['src']]+=$x['val']+$x['fee'];
// check if the transaction is already on the blockchain
if($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$x['id']))>0) return false;
}
// check if the account has enough balance to perform the transaction
foreach($balance as $id=>$bal){
$res=$db->single("SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",array(":id"=>$id, ":balance"=>$bal));
if($res==0) return false; // not enough balance for the transactions
}
// if the test argument is false, add the transactions to the blockchain
if($test==false){
foreach($data as $d){
@@ -277,6 +337,7 @@ public function parse_block($block, $height, $data, $test=true){
}
// initialize the blockchain, add the genesis block
private function genesis(){
global $db;
$signature='AN1rKvtLTWvZorbiiNk5TBYXLgxiLakra2byFef9qoz1bmRzhQheRtiWivfGSwP6r8qHJGrf8uBeKjNZP1GZvsdKUVVN2XQoL';
@@ -301,6 +362,7 @@ public function pop($no=1){
$this->delete($current['height']-$no+1);
}
// delete all blocks >= height
public function delete($height){
if($height<2) $height=2;
global $db;
@@ -331,6 +393,8 @@ public function delete($height){
return true;
}
// delete specific block
public function delete_id($id){
global $db;
@@ -339,27 +403,33 @@ public function delete_id($id){
$x=$db->row("SELECT * FROM blocks WHERE id=:id",array(":id"=>$id));
if($x===false) return false;
// avoid race conditions on blockchain manipulations
$db->beginTransaction();
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
// reverse all transactions of the block
$res=$trx->reverse($x['id']);
if($res===false) {
// rollback if you can't reverse the transactions
$db->rollback();
$db->exec("UNLOCK TABLES");
return false;
}
// remove the actual block
$res=$db->run("DELETE FROM blocks WHERE id=:id",array(":id"=>$x['id']));
if($res!=1){
//rollback if you can't delete the block
$db->rollback();
$db->exec("UNLOCK TABLES");
return false;
}
// commit and release if all good
$db->commit();
$db->exec("UNLOCK TABLES");
return true;
}
// sign a new block, used when mining
public function sign($generator, $height, $date, $nonce, $data, $key, $difficulty, $argon){
$json=json_encode($data);
@@ -370,6 +440,7 @@ public function sign($generator, $height, $date, $nonce, $data, $key, $difficult
}
// generate the sha512 hash of the block data and converts it to base58
public function hash($public_key, $height, $date, $nonce, $data, $signature, $difficulty, $argon){
$json=json_encode($data);
$hash= hash("sha512", "{$public_key}-{$height}-{$date}-{$nonce}-{$json}-{$signature}-{$difficulty}-{$argon}");
@@ -377,6 +448,7 @@ public function hash($public_key, $height, $date, $nonce, $data, $signature, $di
}
// exports the block data, to be used when submitting to other peers
public function export($id="",$height=""){
if(empty($id)&&empty($height)) return false;
@@ -396,13 +468,14 @@ public function export($id="",$height=""){
ksort($transactions);
$block['data']=$transactions;
// the reward transaction always has version 0
$gen=$db->row("SELECT public_key, signature FROM transactions WHERE version=0 AND block=:block",array(":block"=>$block['id']));
$block['public_key']=$gen['public_key'];
$block['reward_signature']=$gen['signature'];
return $block;
}
//return a specific block as array
public function get($height){
global $db;
if(empty($height)) return false;

View File

@@ -1,17 +1,34 @@
<?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;
?>

View File

@@ -1,4 +1,5 @@
<?php
// a simple wrapper for pdo
class db extends PDO {

View File

@@ -1,24 +1,25 @@
<?php
// simple santization function to accept only alphanumeric characters
function san($a,$b=""){
$a = preg_replace("/[^a-zA-Z0-9".$b."]/", "", $a);
return $a;
}
// api error and exit
function api_err($data){
global $_config;
echo json_encode(array("status"=>"error","data"=>$data, "coin"=>$_config['coin']));
exit;
}
// api print ok and exit
function api_echo($data){
global $_config;
echo json_encode(array("status"=>"ok","data"=>$data, "coin"=>$_config['coin']));
exit;
}
// log function, shows only in cli atm
function _log($data){
$date=date("[Y-m-d H:s:]");
$trace=debug_backtrace();
@@ -26,6 +27,7 @@ function _log($data){
if(php_sapi_name() === 'cli') echo "$date [$location] $data\n";
}
// converts PEM key to hex
function pem2hex ($data) {
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
$data=str_replace("-----END PUBLIC KEY-----","",$data);
@@ -37,6 +39,7 @@ function pem2hex ($data) {
return $data;
}
// converts hex key to PEM
function hex2pem ($data, $is_private_key=false) {
$data=hex2bin($data);
$data=base64_encode($data);
@@ -46,66 +49,94 @@ function hex2pem ($data, $is_private_key=false) {
//all credits for this base58 functions should go to tuupola / https://github.com/tuupola/base58/
function baseConvert(array $source, $source_base, $target_base)
// Base58 encoding/decoding functions - all credits go to https://github.com/stephen-hill/base58php
function base58_encode($string)
{
$result = [];
while ($count = count($source)) {
$quotient = [];
$remainder = 0;
for ($i = 0; $i !== $count; $i++) {
$accumulator = $source[$i] + $remainder * $source_base;
$digit = (integer) ($accumulator / $target_base);
$remainder = $accumulator % $target_base;
if (count($quotient) || $digit) {
array_push($quotient, $digit);
};
$alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base=strlen($alphabet);
// Type validation
if (is_string($string) === false) {
return false;
}
// If the string is empty, then the encoded string is obviously empty
if (strlen($string) === 0) {
return '';
}
// Now we need to convert the byte array into an arbitrary-precision decimal
// We basically do this by performing a base256 to base10 conversion
$hex = unpack('H*', $string);
$hex = reset($hex);
$decimal = gmp_init($hex, 16);
// This loop now performs base 10 to base 58 conversion
// The remainder or modulo on each loop becomes a base 58 character
$output = '';
while (gmp_cmp($decimal, $base) >= 0) {
list($decimal, $mod) = gmp_div_qr($decimal, $base);
$output .= $alphabet[gmp_intval($mod)];
}
// If there's still a remainder, append it
if (gmp_cmp($decimal, 0) > 0) {
$output .= $alphabet[gmp_intval($decimal)];
}
// Now we need to reverse the encoded data
$output = strrev($output);
// Now we need to add leading zeros
$bytes = str_split($string);
foreach ($bytes as $byte) {
if ($byte === "\x00") {
$output = $alphabet[0] . $output;
continue;
}
array_unshift($result, $remainder);
$source = $quotient;
break;
}
return $result;
return (string) $output;
}
function base58_encode($data)
function base58_decode($base58)
{
if (is_integer($data)) {
$data = [$data];
} else {
$data = str_split($data);
$data = array_map(function ($character) {
return ord($character);
}, $data);
$alphabet='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
$base=strlen($alphabet);
// Type Validation
if (is_string($base58) === false) {
return false;
}
$converted = baseConvert($data, 256, 58);
return implode("", array_map(function ($index) {
$chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
return $chars[$index];
}, $converted));
}
function base58_decode($data, $integer = false)
{
$data = str_split($data);
$data = array_map(function ($character) {
$chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
return strpos($chars, $character);
}, $data);
/* Return as integer when requested. */
if ($integer) {
$converted = baseConvert($data, 58, 10);
return (integer) implode("", $converted);
// If the string is empty, then the decoded string is obviously empty
if (strlen($base58) === 0) {
return '';
}
$converted = baseConvert($data, 58, 256);
return implode("", array_map(function ($ascii) {
return chr($ascii);
}, $converted));
$indexes = array_flip(str_split($alphabet));
$chars = str_split($base58);
// Check for invalid characters in the supplied base58 string
foreach ($chars as $char) {
if (isset($indexes[$char]) === false) {
return false;
}
}
// Convert from base58 to base10
$decimal = gmp_init($indexes[$chars[0]], 10);
for ($i = 1, $l = count($chars); $i < $l; $i++) {
$decimal = gmp_mul($decimal, $base);
$decimal = gmp_add($decimal, $indexes[$chars[$i]]);
}
// Convert from base10 to base256 (8-bit byte array)
$output = '';
while (gmp_cmp($decimal, 0) > 0) {
list($decimal, $byte) = gmp_div_qr($decimal, 256);
$output = pack('C', gmp_intval($byte)) . $output;
}
// Now we need to add leading zeros
foreach ($chars as $char) {
if ($indexes[$char] === 0) {
$output = "\x00" . $output;
continue;
}
break;
}
return $output;
}
// converts PEM key to the base58 version used by ARO
function pem2coin ($data) {
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
$data=str_replace("-----END PUBLIC KEY-----","",$data);
@@ -118,7 +149,7 @@ function pem2coin ($data) {
return base58_encode($data);
}
// converts the key in base58 to PEM
function coin2pem ($data, $is_private_key=false) {
@@ -133,9 +164,9 @@ function coin2pem ($data, $is_private_key=false) {
return "-----BEGIN PUBLIC KEY-----\n".$data."\n-----END PUBLIC KEY-----\n";
}
// sign data with private key
function ec_sign($data, $key){
// transform the base58 key format to PEM
$private_key=coin2pem($key,true);
@@ -145,9 +176,8 @@ function ec_sign($data, $key){
openssl_sign($data,$signature,$pkey,OPENSSL_ALGO_SHA256);
// the signature will be base58 encoded
return base58_encode($signature);
}
@@ -156,7 +186,7 @@ function ec_sign($data, $key){
function ec_verify($data, $signature, $key){
// transform the base58 key to PEM
$public_key=coin2pem($key);
$signature=base58_decode($signature);
@@ -170,7 +200,7 @@ function ec_verify($data, $signature, $key){
return false;
}
// 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){
global $_config;
if($debug) echo "\nPeer post: $url\n";
@@ -195,16 +225,19 @@ function peer_post($url, $data=array(),$timeout=60,$debug=false){
$result = file_get_contents($url, false, $context);
if($debug) echo "\nPeer response: $result\n";
$res=json_decode($result,true);
// the function will return false if something goes wrong
if($res['status']!="ok"||$res['coin']!=$_config['coin']) return false;
return $res['data'];
}
// convers hex to base58
function hex2coin($hex){
$data=hex2bin($hex);
return base58_encode($data);
}
// converts base58 to hex
function coin2hex($data){
$bin= base58_decode($data);

View File

@@ -1,15 +1,16 @@
<?php
// ARO version
define("VERSION", "0.2b");
// Amsterdam timezone by default, should probably be moved to config
date_default_timezone_set("Europe/Amsterdam");
//error_reporting(E_ALL & ~E_NOTICE);
error_reporting(0);
ini_set('display_errors',"off");
// not accessible directly
if(php_sapi_name() !== 'cli'&&substr_count($_SERVER['PHP_SELF'],"/")>1){
die("This application should only be run in the main directory /");
}
@@ -26,6 +27,8 @@ if($_config['db_pass']=="ENTER-DB-PASS") die("Please update your config file and
// initial DB connection
$db=new DB($_config['db_connect'],$_config['db_user'],$_config['db_pass'],0);
if(!$db) die("Could not connect to the DB backend.");
// 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");
@@ -47,10 +50,11 @@ foreach($query as $res){
// nothing is allowed while in maintenance
if($_config['maintenance']==1) api_err("under-maintenance");
// update the db schema, on every git pull or initial install
if(file_exists("tmp/db-update")){
$res=unlink("tmp/db-update");
@@ -62,19 +66,22 @@ if(file_exists("tmp/db-update")){
echo "Could not access the tmp/db-update file. Please give full permissions to this file\n";
}
// something went wront with the db schema
if($_config['dbversion']<2) exit;
// separate blockchain for testnet
if($_config['testnet']==true) $_config['coin'].="-testnet";
// current hostname
$hostname=(!empty($_SERVER['HTTPS'])?'https':'http')."://".$_SERVER['HTTP_HOST'];
if($hostname!=$_config['hostname']&&$_SERVER['HTTP_HOST']!="localhost"&&$_SERVER['HTTP_HOST']!="127.0.0.1"&&$_SERVER['hostname']!='::1'&&php_sapi_name() !== 'cli'){
// set the hostname to the current one
if($hostname!=$_config['hostname']&&$_SERVER['HTTP_HOST']!="localhost"&&$_SERVER['HTTP_HOST']!="127.0.0.1"&&$_SERVER['hostname']!='::1'&&php_sapi_name() !== 'cli' && ($_config['allow_hostname_change']!=false||empty($_config['hostname']))){
$db->run("UPDATE config SET val=:hostname WHERE cfg='hostname' LIMIT 1",array(":hostname"=>$hostname));
$_config['hostname']=$hostname;
}
if(empty($_config['hostname'])||$_config['hostname']=="http://"||$_config['hostname']=="https://") api_err("Invalid hostname");
// run sanity
$t=time();
if($t-$_config['sanity_last']>$_config['sanity_interval']&& php_sapi_name() !== 'cli') system("php sanity.php > /dev/null 2>&1 &");

View File

@@ -1,5 +1,6 @@
<?php
// when db schema modifications are done, this function is run.
$dbversion=intval($_config['dbversion']);
$db->beginTransaction();
@@ -135,8 +136,9 @@ if($dbversion==5){
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
$dbversion++;
}
// update the db version to the latest one
if($dbversion!=$_config['dbversion']) $db->run("UPDATE config SET val=:val WHERE cfg='dbversion'",array(":val"=>$dbversion));
$db->commit();
?>
?>

View File

@@ -1,8 +1,7 @@
<?php
class Transaction {
// reverse and remove all transactions from a block
public function reverse($block){
global $db;
$acc=new Account;
@@ -10,14 +9,17 @@ class Transaction {
foreach($r as $x){
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']);
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id",array(":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",array(":id"=>$x['src'], ":val"=>$x['val']+$x['fee']));
if($x['version']>0) $this->add_mempool($x);
// 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;
@@ -26,12 +28,14 @@ class Transaction {
$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){
@@ -72,11 +76,13 @@ class Transaction {
$transactions[$x['id']]=$trans;
}
}
// always sort the array
ksort($transactions);
return $transactions;
}
// add a new transaction to mempool and lock it with the current height
public function add_mempool($x, $peer=""){
global $db;
$block= new Block;
@@ -90,6 +96,7 @@ class Transaction {
}
// add a new transaction to the blockchain
public function add($block,$height, $x){
global $db;
$acc= new Account;
@@ -100,22 +107,24 @@ class Transaction {
$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;
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val']));
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']));
// no debit when the transaction is reward
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']));
$db->run("DELETE FROM mempool WHERE id=:id",array(":id"=>$x['id']));
return true;
}
// hash the transaction's most important fields and create the transaction ID
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);
return hex2coin($hash);
}
// check the transaction for validity
public function check($x, $height=0){
// if no specific block, use current
if($height===0){
$block=new Block;
$current=$block->current();
@@ -124,32 +133,54 @@ class Transaction {
$acc= new Account;
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
// the value must be >=0
if($x['val']<0){ _log("$x[id] - Value below 0"); return false; }
if($x['fee']<0) { _log("$x[id] - Fee below 0"); 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
$fee=$x['val']*0.0025;
$fee=number_format($fee,8,".","");
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
if(!$acc->valid($x['dst'])) { _log("$x[id] - Invalid destination address"); return false; }
// reward transactions are not added via this function
if($x['version']<1) { _log("$x[id] - Invalid version <1"); return false; }
//if($x['version']>1) { _log("$x[id] - Invalid version >1"); return false; }
if(strlen($x['public_key'])<15) { _log("$x[id] - Invalid public key size"); 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"); return false; }
// no transactions before the genesis
if($x['date']<1511725068) { _log("$x[id] - Date before genesis"); return false; }
// no future transactions
if($x['date']>time()+86400) { _log("$x[id] - Date in the future"); return false; }
// prevent the resending of broken base58 transactions
if($height>17000&&$x['date']<1519319340) return false;
$id=$this->hash($x);
if($x['id']!=$id) { _log("$x[id] - Invalid hash"); return false; }
// the hash does not match our regenerated hash
if($x['id']!=$id) {
// fix for broken base58 library which was used until block 17000, 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>17000){
_log("$x[id] - $id - Invalid hash");
return false;
}
}
if(!$acc->check_signature($info, $x['signature'], $x['public_key'])) { _log("$x[id] - Invalid signature"); return false; }
//verify the ecdsa signature
if(!$acc->check_signature($info, $x['signature'], $x['public_key'])) { _log("$x[id] - Invalid signature"); 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);
@@ -158,14 +189,14 @@ class Transaction {
}
//export a mempool transaction
public function export($id){
global $db;
$r=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id));
//unset($r['peer']);
return $r;
}
// get the transaction data as array
public function get_transaction($id){
global $db;
$acc=new Account;
@@ -188,6 +219,7 @@ class Transaction {
}
// return the transactions for a specific block id or height
public function get_transactions($height="", $id=""){
global $db;
$acc=new Account;
@@ -216,7 +248,7 @@ class Transaction {
}
// get a specific mempool transaction as array
public function get_mempool_transaction($id){
global $db;
$x=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id));

View File

@@ -30,24 +30,27 @@ set_time_limit(360);
$q=$_GET['q'];
$ip=$_SERVER['REMOTE_ADDR'];
// in case of testnet, all IPs are accepted for mining
if($_config['testnet']==false&&!in_array($ip,$_config['allowed_hosts'])) api_err("unauthorized");
if($q=="info"){
// provides the mining info to the miner
$diff=$block->difficulty();
$current=$block->current();
api_echo(array("difficulty"=>$diff, "block"=>$current['id'], "height"=>$current['height']));
exit;
} elseif($q=="submitNonce"){
// in case the blocks are syncing, reject all
if($_config['sanity_sync']==1) api_err("sanity-sync");
$nonce = san($_POST['nonce']);
$argon=$_POST['argon'];
$public_key=san($_POST['public_key']);
$private_key=san($_POST['private_key']);
// check if the miner won the block
$result=$block->mine($public_key, $nonce, $argon);
if($result) {
// generate the new block
$res=$block->forge($nonce,$argon, $public_key, $private_key);
@@ -55,6 +58,7 @@ if($q=="info"){
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");

View File

@@ -27,24 +27,26 @@ require_once("include/init.inc.php");
$trx = new Transaction;
$block=new Block;
$q=$_GET['q'];
// the data is sent as json, in $_POST['data']
if(!empty($_POST['data'])){
$data=json_decode(trim($_POST['data']),true);
$data=json_decode(trim($_POST['data']),true);
}
// make sure it's the same coin and not testnet
if($_POST['coin']!=$_config['coin']) api_err("Invalid coin");
$ip=$_SERVER['REMOTE_ADDR'];
$ip=filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
// peer with the current node
if($q=="peer"){
// sanitize the hostname
$hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL);
if (!filter_var($hostname, FILTER_VALIDATE_URL)) api_err("invalid-hostname");
// if it's already peered, only repeer on request
$res=$db->single("SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",array(":hostname"=>$hostname,":ip"=>$ip));
if($res==1){
if($data['repeer']==1){
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
@@ -53,11 +55,12 @@ if($q=="peer"){
}
api_echo("peer-ok-already");
}
// if we have enough peers, add it to DB as reserve
$res=$db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0");
$reserve=1;
if($res<$_config['max_peers']) $reserve=0;
$db->run("INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",array(":ip"=>$ip, ":hostname2"=>$hostname,":hostname"=>$hostname, ":reserve"=>$reserve));
// re-peer to make sure the peer is valid
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
if($res!==false) api_echo("re-peer-ok");
else{
@@ -66,60 +69,68 @@ if($q=="peer"){
}
}
elseif($q=="ping"){
// confirm peer is active
api_echo("pong");
}
elseif($q=="submitTransaction"){
} elseif($q=="submitTransaction"){
// receive a new transaction from a peer
$current=$block->current();
if($current['height']>10790&&$current['height']<10810) api_err("Hard fork in progress. Please retry the transaction later!");
// no transactions accepted if the sanity is syncing
if($_config['sanity_sync']==1) api_err("sanity-sync");
$data['id']=san($data['id']);
// validate transaction data
if(!$trx->check($data)) api_err("Invalid transaction");
$hash=$data['id'];
$hash=$data['id'];
// make sure it's not already in mempool
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash));
if($res!=0) api_err("The transaction is already in mempool");
// make sure the peer is not flooding us with transactions
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE src=:src",array(":src"=>$data['src']));
if($res>25) api_err("Too many transactions from this address in mempool. Please rebroadcast later.");
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer",array(":peer"=>$_SERVER['REMOTE_ADDR']));
if($res>$_config['peer_max_mempool']) api_error("Too many transactions broadcasted from this peer");
// make sure the transaction is not already on the blockchain
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$hash));
if($res!=0) api_err("The transaction is already in a block");
$acc=new Account;
$src=$acc->get_address($data['public_key']);
// make sure the sender has enough balance
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src));
if($balance<$val+$fee) api_err("Not enough funds");
// make sure the sender has enough pending balance
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src));
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)");
// add to mempool
$trx->add_mempool($data, $_SERVER['REMOTE_ADDR']);
// rebroadcast the transaction to some peers unless the transaction is smaller than the average size of transactions in mempool - protect against garbage data flooding
$res=$db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ",array(":src"=>$data['src']));
if($res['c']<$_config['max_mempool_rebroadcast']&&$res['v']/$res['c']<$data['val']) system("php propagate.php transaction '$data[id]' > /dev/null 2>&1 &");
api_echo("transaction-ok");
}
elseif($q=="submitBlock"){
// receive a new block from a peer
// if sanity sync, refuse all
if($_config['sanity_sync']==1) api_err("sanity-sync");
$data['id']=san($data['id']);
$current=$block->current();
// block already in the blockchain
if($current['id']==$data['id']) api_echo("block-ok");
if($current['height']==$data['height']&&$current['id']!=$data['id']){
// different forks, same height
$accept_new=false;
if($current['transactions']<$data['transactions']){
// accept the one with most transactions
$accept_new=true;
} elseif($current['transactions']==$data['transactions']) {
// convert the first 12 characters from hex to decimal and the block with the largest number wins
$no1=hexdec(substr(coin2hex($current['id']),0,12));
$no2=hexdec(substr(coin2hex($data['id']),0,12));
if(gmp_cmp($no1,$no2)==1){
@@ -127,12 +138,15 @@ elseif($q=="submitBlock"){
}
}
if($accept_new){
// if the new block is accepted, run a microsanity to sync it
system("php sanity.php microsanity '$ip' > /dev/null 2>&1 &");
api_echo("microsanity");
} else api_echo("reverse-microsanity");
} else api_echo("reverse-microsanity"); // if it's not, suggest to the peer to get the block from us
}
// if it's not the next block
if($current['height']!=$data['height']-1) {
// if the height of the block submitted is lower than our current height, send them our current block
if($data['height']<$current['height']){
$pr=$db->row("SELECT * FROM peers WHERE ip=:ip",array(":ip"=>$ip));
if(!$pr) api_err("block-too-old");
@@ -140,25 +154,30 @@ elseif($q=="submitBlock"){
system("php propagate.php block current '$peer_host' '$pr[ip]' > /dev/null 2>&1 &");
api_err("block-too-old");
}
// if the block difference is bigger than 150, nothing should be done. They should sync via sanity
if($data['height']-$current['height']>150) api_err("block-out-of-sync");
// request them to send us a microsync with the latest blocks
api_echo(array("request"=>"microsync","height"=>$current['height'], "block"=>$current['id']));
}
// check block data
if(!$block->check($data)) api_err("invalid-block");
$b=$data;
// add the block to the blockchain
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
if(!$res) api_err("invalid-block-data");
api_echo("block-ok");
// send it to all our peers
system("php propagate.php block '$data[id]' > /dev/null 2>&1 &");
}
// 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']);
@@ -167,6 +186,8 @@ elseif($q=="getBlock"){
api_echo($export);
}
elseif($q=="getBlocks"){
// returns X block starting at height, used in syncing
$height=intval($data['height']);
$r=$db->run("SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",array(":height"=>$height));
@@ -176,7 +197,7 @@ elseif($q=="getBlock"){
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);

View File

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

View File

@@ -25,26 +25,33 @@ OR OTHER DEALINGS IN THE SOFTWARE.
*/
set_time_limit(0);
error_reporting(0);
// make sure it's not accessible in the browser
if(php_sapi_name() !== 'cli') die("This should only be run as cli");
// make sure there's only a single sanity process running at the same time
if(file_exists("tmp/sanity-lock")){
$pid_time=filemtime("tmp/sanity-lock");
// if the process died, restart after 1day
if(time()-$pid_time>86400){
@unlink("tmp/sanity-lock");
}
die("Sanity lock in place");
}
// set the new sanity lock
$lock = fopen("tmp/sanity-lock", "w");
fclose($lock);
$arg=trim($argv[1]);
$arg2=trim($argv[2]);
// sleep for 10 seconds to make sure there's a delay between starting the sanity and other processes
if($arg!="microsanity") sleep(10);
require_once("include/init.inc.php");
// the sanity can't run without the schema being installed
if($_config['dbversion']<2){
die("DB schema not created");
@unlink("tmp/sanity-lock");
@@ -55,11 +62,12 @@ $block=new Block();
$acc=new Account();
$current=$block->current();
// the microsanity process is an anti-fork measure that will determine the best blockchain to choose for the last block
$microsanity=false;
if($arg=="microsanity"&&!empty($arg2)){
do {
// the microsanity runs only against 1 specific peer
$x=$db->row("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP() AND ip=:ip",array(":ip"=>$arg2));
if(!$x){ echo "Invalid node - $arg2\n"; break; }
@@ -67,12 +75,15 @@ do {
$data=peer_post($url."getBlock",array("height"=>$current['height']));
if(!$data) {echo "Invalid getBlock result\n"; break; }
// nothing to be done, same blockchain
if($data['id']==$current['id']) {echo "Same block\n"; break;}
// the blockchain with the most transactions wins the fork (to encourage the miners to include as many transactions as possible) / might backfire on garbage
if($current['transactions']>$data['transactions']){
echo "Block has less transactions\n";
break;
} elseif($current['transactions']==$data['transactions']) {
// transform the first 12 chars into an integer and choose the blockchain with the biggest value
$no1=hexdec(substr(coin2hex($current['id']),0,12));
$no2=hexdec(substr(coin2hex($data['id']),0,12));
@@ -81,15 +92,17 @@ do {
break;
}
}
// make sure the block is valid
$prev = $block->get($current['height']-1);
$public=$acc->public_key($data['generator']);
if(!$block->mine($public, $data['nonce'],$data['argon'],$block->difficulty($current['height']-1),$prev['id'], $prev['height'])) { echo "Invalid prev-block\n"; break;}
$block->pop(1);
if(!$block->check($data)) break;
// delete the last block
$block->pop(1);
// add the new block
echo "Starting to sync last block from $x[hostname]\n";
$b=$data;
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
@@ -114,6 +127,8 @@ exit;
$t=time();
//if($t-$_config['sanity_last']<300) {@unlink("tmp/sanity-lock"); die("The sanity cron was already run recently"); }
// update the last time sanity ran, to set the execution of the next run
$db->run("UPDATE config SET val=:time WHERE cfg='sanity_last'",array(":time"=>$t));
$block_peers=array();
$longest_size=0;
@@ -126,6 +141,7 @@ $total_active_peers=0;
// checking peers
// delete the dead peers
$db->run("DELETE from peers WHERE fails>100");
$r=$db->run("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP()");
@@ -133,18 +149,22 @@ $r=$db->run("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_
$total_peers=count($r);
$peered=array();
// if we have no peers, get the seed list from the official site
if($total_peers==0&&$_config['testnet']==false){
$i=0;
echo "No peers found. Attempting to get peers from arionum.com\n";
$f=file("https://www.arionum.com/peers.txt");
shuffle($f);
// we can't connect to arionum.com
if(count($f)<2){ @unlink("tmp/sanity-lock"); die("Could nto connect to arionum.com! Will try later!\n"); }
foreach($f as $peer){
//peer with all until max_peers, this will ask them to send a peering request to our peer.php where we add their peer to the db.
$peer=trim($peer);
$peer = filter_var($peer, FILTER_SANITIZE_URL);
if (!filter_var($peer, FILTER_VALIDATE_URL)) continue;
if (!filter_var($peer, FILTER_VALIDATE_URL)) continue;
// store the hostname as md5 hash, for easier checking
$pid=md5($peer);
// do not peer if we are already peered
if($peered[$pid]==1) continue;
$peered[$pid]=1;
$res=peer_post($peer."/peer.php?q=peer",array("hostname"=>$_config['hostname'], "repeer"=>1));
@@ -152,38 +172,45 @@ if($total_peers==0&&$_config['testnet']==false){
else echo "Peering FAIL - $peer\n";
if($i>$_config['max_peers']) break;
}
// count the total peers we have
$r=$db->run("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP()");
$total_peers=count($r);
if($total_peers==0){
// something went wrong, could nto add any peers -> exit
@unlink("tmp/sanity-lock");
die("Could not peer to any peers! Please check internet connectivity!\n");
}
}
// contact all the active peers
foreach($r as $x){
$url=$x['hostname']."/peer.php?q=";
// get their peers list
$data=peer_post($url."getPeers");
if($data===false) {
// if the peer is unresponsive, mark it as failed and blacklist it for a while
$db->run("UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*60) WHERE id=:id",array(":id"=>$x['id']));
continue;
}
$i=0;
foreach($data as $peer){
$pid=md5($peer);
// store the hostname as md5 hash, for easier checking
$pid=md5($peer['hostname']);
// do not peer if we are already peered
if($peered[$pid]==1) continue;
$peered[$pid]=1;
// if it's our hostname, ignore
if($peer['hostname']==$_config['hostname']) continue;
// if invalid hostname, ignore
if (!filter_var($peer['hostname'], FILTER_VALIDATE_URL)) continue;
// make sure there's no peer in db with this ip or hostname
if(!$db->single("SELECT COUNT(1) FROM peers WHERE ip=:ip or hostname=:hostname",array(":ip"=>$peer['ip'],":hostname"=>$peer['hostname']))){
$i++;
// check a max_test_peers number of peers from each peer
if($i>$_config['max_test_peers']) break;
$peer['hostname'] = filter_var($peer['hostname'], FILTER_SANITIZE_URL);
// peer with each one
$test=peer_post($peer['hostname']."/peer.php?q=peer",array("hostname"=>$_config['hostname']),20);
if($test!==false){
$total_peers++;
@@ -196,32 +223,42 @@ foreach($r as $x){
// get the current block and check it's blockchain
$data=peer_post($url."currentBlock");
if($data===false) continue;
// peer was responsive, mark it as good
$db->run("UPDATE peers SET fails=0 WHERE id=:id",array(":id"=>$x['id']));
$total_active_peers++;
// add the hostname and block relationship to an array
$block_peers[$data['id']][]=$x['hostname'];
// count the number of peers with this block id
$blocks_count[$data['id']]++;
// keep block data for this block id
$blocks[$data['id']]=$data;
// set the most common block on all peers
if($blocks_count[$data['id']]>$most_common_size){
$most_common=$data['id'];
$most_common_size=$blocks_count[$data['id']];
}
// set the largest height block
if($data['height']>$largest_height){
$largest_height=$data['height'];
$largest_height_block=$data['id'];
} elseif($data['height']==$largestblock&&$data['id']!=$largest_height_block){
} elseif($data['height']==$largest_height&&$data['id']!=$largest_height_block){
// if there are multiple blocks on the largest height, choose one with the smallest (hardest) difficulty
if($data['difficulty']==$blocks[$largest_height_block]['difficulty']){
// if they have the same difficulty, choose if it's most common
if($most_common==$data['id']){
$largest_height=$data['height'];
$largest_height_block=$data['id'];
} else {
// if this block has more transactions, declare it as winner
if($blocks[$largest_height_block]['transactions']<$data['transactions']){
$largest_height=$data['height'];
$largest_height_block=$data['id'];
} elseif($blocks[$largest_height_block]['transactions']==$data['transactions']) {
// if the blocks have the same number of transactions, choose the one with the highest derived integer from the first 12 hex characters
$no1=hexdec(substr(coin2hex($largest_height_block),0,12));
$no2=hexdec(substr(coin2hex($data['id']),0,12));
if(gmp_cmp($no1,$no2)==1){
@@ -231,6 +268,7 @@ foreach($r as $x){
}
}
} elseif($data['difficulty']<$blocks[$largest_height_block]['difficulty']){
// choose smallest (hardest) difficulty
$largest_height=$data['height'];
$largest_height_block=$data['id'];
}
@@ -244,18 +282,24 @@ echo "Most common: $most_common\n";
echo "Most common block: $most_common_size\n";
echo "Max height: $largest_height\n";
echo "Current block: $current[height]\n";
// if we're not on the largest height
if($current['height']<$largest_height&&$largest_height>1){
// start sanity sync / block all other transactions/blocks
$db->run("UPDATE config SET val=1 WHERE cfg='sanity_sync'");
sleep(10);
_log("Longest chain rule triggered - $largest_height - $largest_height_block");
_log("Longest chain rule triggered - $largest_height - $largest_height_block");
// choose the peers which have the larget height block
$peers=$block_peers[$largest_height_block];
shuffle($peers);
// sync from them
foreach($peers as $host){
_log("Starting to sync from $host");
$url=$host."/peer.php?q=";
$data=peer_post($url."getBlock",array("height"=>$current['height']));
// invalid data
if($data===false){ _log("Could not get block from $host - $current[height]"); continue; }
// if we're not on the same blockchain but the blockchain is most common with over 90% of the peers, delete the last 3 blocks and retry
if($data['id']!=$current['id']&&$data['id']==$most_common&&($most_common_size/$total_active_peers)>0.90){
$block->delete($current['height']-3);
$current=$block->current();
@@ -264,6 +308,7 @@ if($current['height']<$largest_height&&$largest_height>1){
if($data===false){_log("Could not get block from $host - $current[height]"); break; }
}elseif($data['id']!=$current['id']&&$data['id']!=$most_common){
//if we're not on the same blockchain and also it's not the most common, verify all the blocks on on this blockchain starting at current-10 until current
$invalid=false;
$last_good=$current['height'];
for($i=$current['height']-10;$i<$current['height'];$i++){
@@ -275,6 +320,7 @@ if($current['height']<$largest_height&&$largest_height>1){
if($ext['id']==$data['id']) $last_good=$i;
}
// if last 10 blocks are good, verify all the blocks
if($invalid==false) {
$cblock=array();
for($i=$last_good;$i<=$largest_height;$i++){
@@ -282,11 +328,12 @@ if($current['height']<$largest_height&&$largest_height>1){
if($data===false){ $invalid=true; break; }
$cblock[$i]=$data;
}
// check if the block mining data is correct
for($i=$last_good+1;$i<=$largest_height;$i++){
if(!$block->mine($cblock[$i]['public_key'], $cblock[$i]['nonce'], $cblock[$i]['argon'], $cblock[$i]['difficulty'], $cblock[$i-1]['id'],$cblock[$i-1]['height'])) {$invalid=true; break; }
}
}
// if the blockchain proves ok, delete until the last block
if($invalid==false){
$block->delete($last_good);
$current=$block->current();
@@ -294,7 +341,9 @@ if($current['height']<$largest_height&&$largest_height>1){
}
}
// if current still doesn't match the data, something went wrong
if($data['id']!=$current['id']) continue;
// start syncing all blocks
while($current['height']<$largest_height){
$data=peer_post($url."getBlocks",array("height"=>$current['height']+1));
@@ -364,7 +413,7 @@ foreach($f as $x){
}
//recheck blocks
//recheck the last blocks
if($_config['sanity_recheck_blocks']>0){
$blocks=array();
$all_blocks_ok=true;

294
util.php
View File

@@ -23,10 +23,25 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
*/
// make sure it's not accessible in the browser
if(php_sapi_name() !== 'cli') die("This should only be run as cli");
require_once("include/init.inc.php");
$cmd=trim($argv[1]);
/**
* @api {php util.php} clean Clean
* @apiName clean
* @apiGroup UTIL
* @apiDescription Cleans the entire database
*
* @apiExample {cli} Example usage:
* php util.php clean
*/
if($cmd=='clean'){
$tables=array("blocks","accounts","transactions","mempool");
@@ -37,12 +52,39 @@ echo "\n The database has been cleared\n";
}
/**
* @api {php util.php} pop Pop
* @apiName pop
* @apiGroup UTIL
* @apiDescription Cleans the entire database
*
* @apiParam {Number} arg2 Number of blocks to delete
*
* @apiExample {cli} Example usage:
* php util.php pop 1
*/
elseif($cmd=='pop'){
$no=intval($argv[2]);
$block=new Block;
$block->pop($no);
}
/**
* @api {php util.php} block-time Block-time
* @apiName block-time
* @apiGroup UTIL
* @apiDescription Shows the block time of the last 100 blocks
*
* @apiExample {cli} Example usage:
* php util.php block-time
*
* @apiSuccessExample {text} Success-Response:
* 16830 -> 323
* ...
* 16731 -> 302
* Average block time: 217 seconds
*/
elseif($cmd=='block-time'){
$t=time();
@@ -59,16 +101,86 @@ echo "Average block time: ".ceil(($start-$end)/100)." seconds\n";
}
/**
* @api {php util.php} peer Peer
* @apiName peer
* @apiGroup UTIL
* @apiDescription Creates a peering session with another node
*
* @apiParam {text} arg2 The Hostname of the other node
*
* @apiExample {cli} Example usage:
* php util.php peer http://peer1.arionum.com
*
* @apiSuccessExample {text} Success-Response:
* Peering OK
*/
elseif($cmd=="peer"){
$res=peer_post($argv[2]."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
if($res!==false) echo "Peering OK\n";
else echo "Peering FAIL\n";
}
/**
* @api {php util.php} current Current
* @apiName current
* @apiGroup UTIL
* @apiDescription Prints the current block in var_dump
*
* @apiExample {cli} Example usage:
* php util.php current
*
* @apiSuccessExample {text} Success-Response:
* array(9) {
* ["id"]=>
* string(88) "4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR"
* ["generator"]=>
* string(88) "5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn"
* ["height"]=>
* int(16833)
* ["date"]=>
* int(1519312385)
* ["nonce"]=>
* string(41) "EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY"
* ["signature"]=>
* string(97) "AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu"
* ["difficulty"]=>
* string(8) "61982809"
* ["argon"]=>
* string(68) "$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg"
* ["transactions"]=>
* int(0)
* }
*
*/
elseif ($cmd=="current") {
$block=new Block;
var_dump($block->current());
} elseif($cmd=="blocks"){
}
/**
* @api {php util.php} blocks Blocks
* @apiName blocks
* @apiGroup UTIL
* @apiDescription Prints the id and the height of the blocks >=arg2, max 100 or arg3
*
* @apiParam {number} arg2 Starting height
*
* @apiParam {number} [arg3] Block Limit
*
* @apiExample {cli} Example usage:
* php util.php blocks 10800 5
*
* @apiSuccessExample {text} Success-Response:
* 10801 2yAHaZ3ghNnThaNK6BJcup2zq7EXuFsruMb5qqXaHP9M6JfBfstAag1n1PX7SMKGcuYGZddMzU7hW87S5ZSayeKX
* 10802 wNa4mRvRPCMHzsgLdseMdJCvmeBaCNibRJCDhsuTeznJh8C1aSpGuXRDPYMbqKiVtmGAaYYb9Ze2NJdmK1HY9zM
* 10803 3eW3B8jCFBauw8EoKN4SXgrn33UBPw7n8kvDDpyQBw1uQcmJQEzecAvwBk5sVfQxUqgzv31JdNHK45JxUFcupVot
* 10804 4mWK1f8ch2Ji3D6aw1BsCJavLNBhQgpUHBCHihnrLDuh8Bjwsou5bQDj7D7nV4RsEPmP2ZbjUUMZwqywpRc8r6dR
* 10805 5RBeWXo2c9NZ7UF2ubztk53PZpiA4tsk3bhXNXbcBk89cNqorNj771Qu4kthQN5hXLtu1hzUnv7nkH33hDxBM34m
*
*/
elseif($cmd=="blocks"){
$height=intval($argv[2]);
$limit=intval($argv[3]);
if($limit<1) $limit=100;
@@ -77,7 +189,16 @@ elseif ($cmd=="current") {
echo "$x[height]\t$x[id]\n";
}
}
/**
* @api {php util.php} recheck-blocks Recheck-Blocks
* @apiName recheck-blocks
* @apiGroup UTIL
* @apiDescription Recheck all the blocks to make sure the blockchain is correct
*
* @apiExample {cli} Example usage:
* php util.php recheck-blocks
*
*/
elseif($cmd=="recheck-blocks"){
$blocks=array();
$block=new Block();
@@ -96,17 +217,61 @@ elseif($cmd=="recheck-blocks"){
break;
}
}
} elseif($cmd=="peers"){
$r=$db->run("SELECT * FROM peers ORDER by reserve ASC LIMIT 100");
}
/**
* @api {php util.php} peers Peers
* @apiName peers
* @apiGroup UTIL
* @apiDescription Prints all the peers and their status
*
* @apiExample {cli} Example usage:
* php util.php peers
*
* @apiSuccessExample {text} Success-Response:
* http://35.190.160.142 active
* ...
* http://aro.master.hashpi.com active
*/
elseif($cmd=="peers"){
$r=$db->run("SELECT * FROM peers ORDER by reserve ASC");
$status="active";
if($x['reserve']==1) $status="reserve";
foreach($r as $x){
echo "$x[hostname]\t$x[reserve]\n";
echo "$x[hostname]\t$status\n";
}
} elseif($cmd=="mempool"){
}
/**
* @api {php util.php} mempool Mempool
* @apiName mempool
* @apiGroup UTIL
* @apiDescription Prints the number of transactions in mempool
*
* @apiExample {cli} Example usage:
* php util.php mempool
*
* @apiSuccessExample {text} Success-Response:
* Mempool size: 12
*/
elseif($cmd=="mempool"){
$res=$db->single("SELECT COUNT(1) from mempool");
echo "Mempool size: $res\n";
} elseif($cmd=="delete-peer"){
}
/**
* @api {php util.php} delete-peer Delete-peer
* @apiName delete-peer
* @apiGroup UTIL
* @apiDescription Removes a peer from the peerlist
*
* @apiParam {text} arg2 Peer's hostname
*
* @apiExample {cli} Example usage:
* php util.php delete-peer http://peer1.arionum.com
*
* @apiSuccessExample {text} Success-Response:
* Peer removed
*/
elseif($cmd=="delete-peer"){
$peer=trim($argv[2]);
if(empty($peer)) die("Invalid peer");
$db->run("DELETE FROM peers WHERE ip=:ip",array(":ip"=>$peer));
@@ -121,7 +286,23 @@ echo "Mempool size: $res\n";
} else echo "$x[hostname] ->ok \n";
}
}elseif($cmd=="peers-block"){
}
/**
* @api {php util.php} peers-block Peers-Block
* @apiName peers-block
* @apiGroup UTIL
* @apiDescription Prints the current height of all the peers
*
* @apiExample {cli} Example usage:
* php util.php peers-block
*
* @apiSuccessExample {text} Success-Response:
* http://peer5.arionum.com 16849
* ...
* http://peer10.arionum.com 16849
*/
elseif($cmd=="peers-block"){
$r=$db->run("SELECT * FROM peers");
foreach($r as $x){
$a=peer_post($x['hostname']."/peer.php?q=currentBlock",array(),5);
@@ -130,17 +311,85 @@ echo "Mempool size: $res\n";
echo "$x[hostname]\t$a[height]\n";
}
}elseif($cmd=="balance"){
}
/**
* @api {php util.php} balance Balance
* @apiName balance
* @apiGroup UTIL
* @apiDescription Prints the balance of an address or a public key
*
* @apiParam {text} arg2 address or public_key
*
* @apiExample {cli} Example usage:
* php util.php balance 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
*
* @apiSuccessExample {text} Success-Response:
* Balance: 2,487
*/
elseif($cmd=="balance"){
$id=san($argv[2]);
$res=$db->single("SELECT balance FROM accounts WHERE id=:id OR public_key=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id));
echo "Balance: ".number_format($res)."\n";
}elseif($cmd=="block"){
}
/**
* @api {php util.php} block Block
* @apiName block
* @apiGroup UTIL
* @apiDescription Returns a specific block
*
* @apiParam {text} arg2 block id
*
* @apiExample {cli} Example usage:
* php util.php block 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR
*
* @apiSuccessExample {text} Success-Response:
* array(9) {
* ["id"]=>
* string(88) "4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR"
* ["generator"]=>
* string(88) "5ADfrJUnLefPsaYjMTR4KmvQ79eHo2rYWnKBRCXConYKYJVAw2adtzb38oUG5EnsXEbTct3p7GagT2VVZ9hfVTVn"
* ["height"]=>
* int(16833)
* ["date"]=>
* int(1519312385)
* ["nonce"]=>
* string(41) "EwtJ1EigKrLurlXROuuiozrR6ICervJDF2KFl4qEY"
* ["signature"]=>
* string(97) "AN1rKpqit8UYv6uvf79GnbjyihCPE1UZu4CGRx7saZ68g396yjHFmzkzuBV69Hcr7TF2egTsEwVsRA3CETiqXVqet58MCM6tu"
* ["difficulty"]=>
* string(8) "61982809"
* ["argon"]=>
* string(68) "$SfghIBNSHoOJDlMthVcUtg$WTJMrQWHHqDA6FowzaZJ+O9JC8DPZTjTxNE4Pj/ggwg"
* ["transactions"]=>
* int(0)
* }
*/
elseif($cmd=="block"){
$id=san($argv[2]);
$res=$db->row("SELECT * FROM blocks WHERE id=:id OR height=:id2 LIMIT 1",array(":id"=>$id, ":id2"=>$id));
var_dump($res);
}elseif($cmd=="check-address"){
}
/**
* @api {php util.php} check-address Check-Address
* @apiName check-address
* @apiGroup UTIL
* @apiDescription Checks a specific address for validity
*
* @apiParam {text} arg2 block id
*
* @apiExample {cli} Example usage:
* php util.php check-address 4khstc1AknzDXg8h2v12rX42vDrzBaai6Rz53mbaBsghYN4DnfPhfG7oLZS24Q92MuusdYmwvDuiZiuHHWgdELLR
*
* @apiSuccessExample {text} Success-Response:
* The address is valid
*/
elseif($cmd=="check-address"){
$dst=trim($argv[2]);
$acc=new Account;
if(!$acc->valid($dst)) die("Invalid address");
@@ -148,6 +397,27 @@ echo "Mempool size: $res\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
* @apiName get-address
* @apiGroup UTIL
* @apiDescription Converts a public key into an address
*
* @apiParam {text} arg2 public key
*
* @apiExample {cli} Example usage:
* php util.php get-address PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCwQr8cE5s6APWAE1SWAmH6NM1nJTryBURULEsifA2hLVuW5GXFD1XU6s6REG1iPK7qGaRDkGpQwJjDhQKVoSVkSNp
*
* @apiSuccessExample {text} Success-Response:
* 5WuRMXGM7Pf8NqEArVz1NxgSBptkimSpvuSaYC79g1yo3RDQc8TjVtGH5chQWQV7CHbJEuq9DmW5fbmCEW4AghQr
*/
elseif($cmd=='get-address'){
$public_key=trim($argv2);
if(strlen($public_key)<32) die("Invalid public key");
print($acc->get_address($public_key));
} else {
echo "Invalid command\n";