Merge pull request #6 from pxgamer/feature/psr-2

Update to follow PSR-2
This commit is contained in:
arionum
2018-06-19 22:05:22 +03:00
committed by GitHub
16 changed files with 3343 additions and 2602 deletions

858
api.php
View File

@@ -1,7 +1,7 @@
<?php <?php
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 AroDev Copyright (c) 2018 AroDev
www.arionum.com www.arionum.com
@@ -24,7 +24,6 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
/** /**
* @api {get} /api.php 01. Basic Information * @api {get} /api.php 01. Basic Information
* @apiName Info * @apiName Info
@@ -36,11 +35,11 @@ OR OTHER DEALINGS IN THE SOFTWARE.
* *
* The "data" object returns the requested data, as sub-objects. * The "data" object returns the requested data, as sub-objects.
* *
* The parameters must be sent either as POST['data'], json encoded array or independently as GET. * The parameters must be sent either as POST['data'], json encoded array or independently as GET.
* *
* @apiSuccess {String} status "ok" * @apiSuccess {String} status "ok"
* @apiSuccess {String} data The data provided by the api will be under this object. * @apiSuccess {String} data The data provided by the api will be under this object.
* *
* *
* *
* @apiSuccessExample {json} Success-Response: * @apiSuccessExample {json} Success-Response:
@@ -66,30 +65,26 @@ OR OTHER DEALINGS IN THE SOFTWARE.
* } * }
*/ */
require_once("include/init.inc.php"); require_once("include/init.inc.php");
error_reporting(0); error_reporting(0);
$ip=san_ip($_SERVER['REMOTE_ADDR']); $ip = san_ip($_SERVER['REMOTE_ADDR']);
$ip=filter_var($ip, FILTER_VALIDATE_IP); $ip = filter_var($ip, FILTER_VALIDATE_IP);
if($_config['public_api']==false&&!in_array($ip,$_config['allowed_hosts'])){ if ($_config['public_api'] == false && !in_array($ip, $_config['allowed_hosts'])) {
api_err("private-api"); api_err("private-api");
} }
$acc = new Account; $acc = new Account();
$block = new Block; $block = new Block();
$trx = new Transaction; $trx = new Transaction();
$q=$_GET['q']; $q = $_GET['q'];
if(!empty($_POST['data'])){ if (!empty($_POST['data'])) {
$data=json_decode($_POST['data'],true); $data = json_decode($_POST['data'], true);
} else { } else {
$data=$_GET; $data = $_GET;
} }
/** /**
* @api {get} /api.php?q=getAddress 02. getAddress * @api {get} /api.php?q=getAddress 02. getAddress
* @apiName getAddress * @apiName getAddress
@@ -101,419 +96,480 @@ if(!empty($_POST['data'])){
* @apiSuccess {string} data Contains the address * @apiSuccess {string} data Contains the address
*/ */
if($q=="getAddress"){ if ($q == "getAddress") {
$public_key=$data['public_key']; $public_key = $data['public_key'];
if(strlen($public_key)<32) api_err("Invalid public key"); if (strlen($public_key) < 32) {
api_err("Invalid public key");
}
api_echo($acc->get_address($public_key)); api_echo($acc->get_address($public_key));
} } elseif ($q == "base58") {
elseif($q=="base58"){ /**
/** * @api {get} /api.php?q=base58 03. base58
* @api {get} /api.php?q=base58 03. base58 * @apiName base58
* @apiName base58 * @apiGroup API
* @apiGroup API * @apiDescription Converts a string to base58.
* @apiDescription Converts a string to base58. *
* * @apiParam {string} data Input string
* @apiParam {string} data Input string *
* * @apiSuccess {string} data Output string
* @apiSuccess {string} data Output string */
*/
api_echo(base58_encode($data['data'])); api_echo(base58_encode($data['data']));
} } elseif ($q == "getBalance") {
elseif($q=="getBalance"){ /**
/** * @api {get} /api.php?q=getBalance 04. getBalance
* @api {get} /api.php?q=getBalance 04. getBalance * @apiName getBalance
* @apiName getBalance * @apiGroup API
* @apiGroup API * @apiDescription Returns the balance of a specific account or public key.
* @apiDescription Returns the balance of a specific account or public key. *
* * @apiParam {string} [public_key] Public key
* @apiParam {string} [public_key] Public key * @apiParam {string} [account] Account id / address
* @apiParam {string} [account] Account id / address *
* * @apiSuccess {string} data The ARO balance
* @apiSuccess {string} data The ARO balance */
*/
$public_key=$data['public_key']; $public_key = $data['public_key'];
$account=$data['account']; $account = $data['account'];
if(!empty($public_key)&&strlen($public_key)<32) api_err("Invalid public key"); if (!empty($public_key) && strlen($public_key) < 32) {
if(!empty($public_key)) $account=$acc->get_address($public_key); api_err("Invalid public key");
if(empty($account)) api_err("Invalid account id"); }
$account=san($account); if (!empty($public_key)) {
$account = $acc->get_address($public_key);
}
if (empty($account)) {
api_err("Invalid account id");
}
$account = san($account);
api_echo($acc->balance($account)); api_echo($acc->balance($account));
} } elseif ($q == "getPendingBalance") {
elseif($q=="getPendingBalance"){ /**
/** * @api {get} /api.php?q=getPendingBalance 05. getPendingBalance
* @api {get} /api.php?q=getPendingBalance 05. getPendingBalance * @apiName getPendingBalance
* @apiName getPendingBalance * @apiGroup API
* @apiGroup API * @apiDescription Returns the pending balance, which includes pending transactions, of a specific account or public key.
* @apiDescription Returns the pending balance, which includes pending transactions, of a specific account or public key. *
* * @apiParam {string} [public_key] Public key
* @apiParam {string} [public_key] Public key * @apiParam {string} [account] Account id / address
* @apiParam {string} [account] Account id / address *
* * @apiSuccess {string} data The ARO balance
* @apiSuccess {string} data The ARO balance */
*/
$account = $data['account'];
$account=$data['account']; if (!empty($public_key) && strlen($public_key) < 32) {
if(!empty($public_key)&&strlen($public_key)<32) api_err("Invalid public key"); api_err("Invalid public key");
if(!empty($public_key)) $account=$acc->get_address($public_key); }
if(empty($account)) api_err("Invalid account id"); if (!empty($public_key)) {
$account=san($account); $account = $acc->get_address($public_key);
}
if (empty($account)) {
api_err("Invalid account id");
}
$account = san($account);
api_echo($acc->pending_balance($account)); api_echo($acc->pending_balance($account));
} } elseif ($q == "getTransactions") {
elseif($q=="getTransactions"){ /**
/** * @api {get} /api.php?q=getTransactions 06. getTransactions
* @api {get} /api.php?q=getTransactions 06. getTransactions * @apiName getTransactions
* @apiName getTransactions * @apiGroup API
* @apiGroup API * @apiDescription Returns the latest transactions of an account.
* @apiDescription Returns the latest transactions of an account. *
* * @apiParam {string} [public_key] Public key
* @apiParam {string} [public_key] Public key * @apiParam {string} [account] Account id / address
* @apiParam {string} [account] Account id / address * @apiParam {numeric} [limit] Number of confirmed transactions, max 1000, min 1
* @apiParam {numeric} [limit] Number of confirmed transactions, max 1000, min 1 *
* * @apiSuccess {string} block Block ID
* @apiSuccess {string} block Block ID * @apiSuccess {numeric} confirmation Number of confirmations
* @apiSuccess {numeric} confirmation Number of confirmations * @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format
* @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format * @apiSuccess {string} dst Transaction destination
* @apiSuccess {string} dst Transaction destination * @apiSuccess {numeric} fee The transaction's fee
* @apiSuccess {numeric} fee The transaction's fee * @apiSuccess {numeric} height Block height
* @apiSuccess {numeric} height Block height * @apiSuccess {string} id Transaction ID/HASH
* @apiSuccess {string} id Transaction ID/HASH * @apiSuccess {string} message Transaction's message
* @apiSuccess {string} message Transaction's message * @apiSuccess {string} signature Transaction's signature
* @apiSuccess {string} signature Transaction's signature * @apiSuccess {string} public_key Account's public_key
* @apiSuccess {string} public_key Account's public_key * @apiSuccess {string} src Sender's address
* @apiSuccess {string} src Sender's address * @apiSuccess {string} type "debit", "credit" or "mempool"
* @apiSuccess {string} type "debit", "credit" or "mempool" * @apiSuccess {numeric} val Transaction value
* @apiSuccess {numeric} val Transaction value * @apiSuccess {numeric} version Transaction version
* @apiSuccess {numeric} version Transaction version */
*/
$account=san($data['account']); $account = san($data['account']);
if(!empty($public_key)&&strlen($public_key)<32) api_err("Invalid public key"); if (!empty($public_key) && strlen($public_key) < 32) {
if(!empty($public_key)) $account=$acc->get_address($public_key); api_err("Invalid public key");
if(empty($account)) api_err("Invalid account id"); }
if (!empty($public_key)) {
$account = $acc->get_address($public_key);
}
if (empty($account)) {
api_err("Invalid account id");
}
$limit=intval($data['limit']); $limit = intval($data['limit']);
$transactions=$acc->get_mempool_transactions($account); $transactions = $acc->get_mempool_transactions($account);
$transactions=array_merge($transactions, $acc->get_transactions($account,$limit)); $transactions = array_merge($transactions, $acc->get_transactions($account, $limit));
api_echo($transactions); api_echo($transactions);
} elseif ($q == "getTransaction") {
/**
* @api {get} /api.php?q=getTransaction 07. getTransaction
* @apiName getTransaction
* @apiGroup API
* @apiDescription Returns one transaction.
*
* @apiParam {string} transaction Transaction ID
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmation Number of confirmations
* @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format
* @apiSuccess {string} dst Transaction destination
* @apiSuccess {numeric} fee The transaction's fee
* @apiSuccess {numeric} height Block height
* @apiSuccess {string} id Transaction ID/HASH
* @apiSuccess {string} message Transaction's message
* @apiSuccess {string} signature Transaction's signature
* @apiSuccess {string} public_key Account's public_key
* @apiSuccess {string} src Sender's address
* @apiSuccess {string} type "debit", "credit" or "mempool"
* @apiSuccess {numeric} val Transaction value
* @apiSuccess {numeric} version Transaction version
*/
} elseif($q=="getTransaction"){ $id = san($data['transaction']);
/** $res = $trx->get_transaction($id);
* @api {get} /api.php?q=getTransaction 07. getTransaction if ($res === false) {
* @apiName getTransaction $res = $trx->get_mempool_transaction($id);
* @apiGroup API if ($res === false) {
* @apiDescription Returns one transaction. api_err("invalid transaction");
* }
* @apiParam {string} transaction Transaction ID
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmation Number of confirmations
* @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format
* @apiSuccess {string} dst Transaction destination
* @apiSuccess {numeric} fee The transaction's fee
* @apiSuccess {numeric} height Block height
* @apiSuccess {string} id Transaction ID/HASH
* @apiSuccess {string} message Transaction's message
* @apiSuccess {string} signature Transaction's signature
* @apiSuccess {string} public_key Account's public_key
* @apiSuccess {string} src Sender's address
* @apiSuccess {string} type "debit", "credit" or "mempool"
* @apiSuccess {numeric} val Transaction value
* @apiSuccess {numeric} version Transaction version
*/
$id=san($data['transaction']);
$res=$trx->get_transaction($id);
if($res===false) {
$res=$trx->get_mempool_transaction($id);
if($res===false) api_err("invalid transaction");
} }
api_Echo($res); api_Echo($res);
} elseif($q=="getPublicKey"){ } elseif ($q == "getPublicKey") {
/** /**
* @api {get} /api.php?q=getPublicKey 08. getPublicKey * @api {get} /api.php?q=getPublicKey 08. getPublicKey
* @apiName getPublicKey * @apiName getPublicKey
* @apiGroup API * @apiGroup API
* @apiDescription Returns the public key of a specific account. * @apiDescription Returns the public key of a specific account.
* *
* @apiParam {string} account Account id / address * @apiParam {string} account Account id / address
* *
* @apiSuccess {string} data The public key * @apiSuccess {string} data The public key
*/ */
$account=san($data['account']); $account = san($data['account']);
if(empty($account)) api_err("Invalid account id"); if (empty($account)) {
$public_key=$acc->public_key($account); api_err("Invalid account id");
if($public_key===false) api_err("No public key found for this account"); }
else api_echo($public_key); $public_key = $acc->public_key($account);
if ($public_key === false) {
api_err("No public key found for this account");
} else {
api_echo($public_key);
}
} elseif ($q == "generateAccount") {
/**
* @api {get} /api.php?q=generateAccount 09. generateAccount
* @apiName generateAccount
* @apiGroup API
* @apiDescription Generates a new account. This function should only be used when the node is on the same host or over a really secure network.
*
* @apiSuccess {string} address Account address
* @apiSuccess {string} public_key Public key
* @apiSuccess {string} private_key Private key
*/
$acc = new Account();
} elseif($q=="generateAccount"){ $res = $acc->generate_account();
/** api_echo($res);
* @api {get} /api.php?q=generateAccount 09. generateAccount } elseif ($q == "currentBlock") {
* @apiName generateAccount /**
* @apiGroup API * @api {get} /api.php?q=currentBlock 10. currentBlock
* @apiDescription Generates a new account. This function should only be used when the node is on the same host or over a really secure network. * @apiName currentBlock
* * @apiGroup API
* @apiSuccess {string} address Account address * @apiDescription Returns the current block.
* @apiSuccess {string} public_key Public key *
* @apiSuccess {string} private_key Private key * @apiSuccess {string} id Blocks id
*/ * @apiSuccess {string} generator Block Generator
* @apiSuccess {numeric} height Height
* @apiSuccess {numeric} date Block's date in UNIX TIMESTAMP format
* @apiSuccess {string} nonce Mining nonce
* @apiSuccess {string} signature Signature signed by the generator
* @apiSuccess {numeric} difficulty The base target / difficulty
* @apiSuccess {string} argon Mining argon hash
*/
$acc=new Account; $current = $block->current();
$res=$acc->generate_account(); api_echo($current);
api_echo($res); } elseif ($q == "getBlock") {
} elseif($q=="currentBlock"){ /**
/** * @api {get} /api.php?q=getBlock 11. getBlock
* @api {get} /api.php?q=currentBlock 10. currentBlock * @apiName getBlock
* @apiName currentBlock * @apiGroup API
* @apiGroup API * @apiDescription Returns the block.
* @apiDescription Returns the current block. *
* * @apiParam {numeric} height Block Height
* @apiSuccess {string} id Blocks id *
* @apiSuccess {string} generator Block Generator * @apiSuccess {string} id Block id
* @apiSuccess {numeric} height Height * @apiSuccess {string} generator Block Generator
* @apiSuccess {numeric} date Block's date in UNIX TIMESTAMP format * @apiSuccess {numeric} height Height
* @apiSuccess {string} nonce Mining nonce * @apiSuccess {numeric} date Block's date in UNIX TIMESTAMP format
* @apiSuccess {string} signature Signature signed by the generator * @apiSuccess {string} nonce Mining nonce
* @apiSuccess {numeric} difficulty The base target / difficulty * @apiSuccess {string} signature Signature signed by the generator
* @apiSuccess {string} argon Mining argon hash * @apiSuccess {numeric} difficulty The base target / difficulty
* @apiSuccess {string} argon Mining argon hash
*/
$height = san($data['height']);
$ret = $block->get($height);
if ($ret == false) {
api_err("Invalid block");
} else {
api_echo($ret);
}
} elseif ($q == "getBlockTransactions") {
/**
* @api {get} /api.php?q=getBlockTransactions 12. getBlockTransactions
* @apiName getBlockTransactions
* @apiGroup API
* @apiDescription Returns the transactions of a specific block.
*
* @apiParam {numeric} [height] Block Height
* @apiParam {string} [block] Block id
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmations Number of confirmations
* @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format
* @apiSuccess {string} dst Transaction destination
* @apiSuccess {numeric} fee The transaction's fee
* @apiSuccess {numeric} height Block height
* @apiSuccess {string} id Transaction ID/HASH
* @apiSuccess {string} message Transaction's message
* @apiSuccess {string} signature Transaction's signature
* @apiSuccess {string} public_key Account's public_key
* @apiSuccess {string} src Sender's address
* @apiSuccess {string} type "debit", "credit" or "mempool"
* @apiSuccess {numeric} val Transaction value
* @apiSuccess {numeric} version Transaction version
*/
$height = san($data['height']);
$block = san($data['block']);
$ret = $trx->get_transactions($height, $block);
if ($ret === false) {
api_err("Invalid block");
} else {
api_echo($ret);
}
} elseif ($q == "version") {
/**
* @api {get} /api.php?q=version 13. version
* @apiName version
* @apiGroup API
* @apiDescription Returns the node's version.
*
*
* @apiSuccess {string} data Version
*/
api_echo(VERSION);
} elseif ($q == "send") {
/**
* @api {get} /api.php?q=send 14. send
* @apiName send
* @apiGroup API
* @apiDescription Sends a transaction.
*
* @apiParam {numeric} val Transaction value (without fees)
* @apiParam {string} dst Destination address
* @apiParam {string} public_key Sender's public key
* @apiParam {string} [signature] Transaction signature. It's recommended that the transaction is signed before being sent to the node to avoid sending your private key to the node.
* @apiParam {string} [private_key] Sender's private key. Only to be used when the transaction is not signed locally.
* @apiParam {numeric} [date] Transaction's date in UNIX TIMESTAMP format. Requried when the transaction is pre-signed.
* @apiParam {string} [message] A message to be included with the transaction. Maximum 128 chars.
* @apiParam {numeric} [version] The version of the transaction. 1 to send coins.
*
* @apiSuccess {string} data Transaction id
*/
$current = $block->current();
if ($current['height'] > 10790 && $current['height'] < 10810) {
api_err("Hard fork in progress. Please retry the transaction later!"); //10800
}
$acc = new Account();
$block = new Block();
$trx = new Transaction();
$dst = san($data['dst']);
if (!$acc->valid($dst)) {
api_err("Invalid destination address");
}
$dst_b = base58_decode($dst);
if (strlen($dst_b) != 64) {
api_err("Invalid destination address");
}
*/ $public_key = san($data['public_key']);
if (!$acc->valid_key($public_key)) {
api_err("Invalid public key");
}
$private_key = san($data['private_key']);
if (!$acc->valid_key($private_key)) {
api_err("Invalid private key");
}
$signature = san($data['signature']);
if (!$acc->valid_key($signature)) {
api_err("Invalid signature");
}
$date = $data['date'] + 0;
$current=$block->current(); if ($date == 0) {
api_echo($current); $date = time();
}
} elseif($q=="getBlock"){ if ($date < time() - (3600 * 24 * 48)) {
/** api_err("The date is too old");
* @api {get} /api.php?q=getBlock 11. getBlock }
* @apiName getBlock if ($date > time() + 86400) {
* @apiGroup API api_err("Invalid Date");
* @apiDescription Returns the block. }
* $version = intval($data['version']);
* @apiParam {numeric} height Block Height $message = $data['message'];
* if (strlen($message) > 128) {
* @apiSuccess {string} id Block id api_err("The message must be less than 128 chars");
* @apiSuccess {string} generator Block Generator }
* @apiSuccess {numeric} height Height $val = $data['val'] + 0;
* @apiSuccess {numeric} date Block's date in UNIX TIMESTAMP format $fee = $val * 0.0025;
* @apiSuccess {string} nonce Mining nonce if ($fee < 0.00000001) {
* @apiSuccess {string} signature Signature signed by the generator $fee = 0.00000001;
* @apiSuccess {numeric} difficulty The base target / difficulty }
* @apiSuccess {string} argon Mining argon hash
*/
$height=san($data['height']);
$ret=$block->get($height);
if($ret==false) api_err("Invalid block");
else api_echo($ret);
} elseif($q=="getBlockTransactions"){
/**
* @api {get} /api.php?q=getBlockTransactions 12. getBlockTransactions
* @apiName getBlockTransactions
* @apiGroup API
* @apiDescription Returns the transactions of a specific block.
*
* @apiParam {numeric} [height] Block Height
* @apiParam {string} [block] Block id
*
* @apiSuccess {string} block Block ID
* @apiSuccess {numeric} confirmations Number of confirmations
* @apiSuccess {numeric} date Transaction's date in UNIX TIMESTAMP format
* @apiSuccess {string} dst Transaction destination
* @apiSuccess {numeric} fee The transaction's fee
* @apiSuccess {numeric} height Block height
* @apiSuccess {string} id Transaction ID/HASH
* @apiSuccess {string} message Transaction's message
* @apiSuccess {string} signature Transaction's signature
* @apiSuccess {string} public_key Account's public_key
* @apiSuccess {string} src Sender's address
* @apiSuccess {string} type "debit", "credit" or "mempool"
* @apiSuccess {numeric} val Transaction value
* @apiSuccess {numeric} version Transaction version
*/
$height=san($data['height']);
$block=san($data['block']);
$ret=$trx->get_transactions($height, $block);
if($ret===false) api_err("Invalid block");
else api_echo($ret);
} elseif($q=="version"){
/**
* @api {get} /api.php?q=version 13. version
* @apiName version
* @apiGroup API
* @apiDescription Returns the node's version.
*
*
* @apiSuccess {string} data Version
*/
api_echo(VERSION);
} elseif($q=="send"){
/**
* @api {get} /api.php?q=send 14. send
* @apiName send
* @apiGroup API
* @apiDescription Sends a transaction.
*
* @apiParam {numeric} val Transaction value (without fees)
* @apiParam {string} dst Destination address
* @apiParam {string} public_key Sender's public key
* @apiParam {string} [signature] Transaction signature. It's recommended that the transaction is signed before being sent to the node to avoid sending your private key to the node.
* @apiParam {string} [private_key] Sender's private key. Only to be used when the transaction is not signed locally.
* @apiParam {numeric} [date] Transaction's date in UNIX TIMESTAMP format. Requried when the transaction is pre-signed.
* @apiParam {string} [message] A message to be included with the transaction. Maximum 128 chars.
* @apiParam {numeric} [version] The version of the transaction. 1 to send coins.
*
* @apiSuccess {string} data Transaction id
*/
$current=$block->current();
if($current['height']>10790&&$current['height']<10810) api_err("Hard fork in progress. Please retry the transaction later!"); //10800
$acc = new Account;
$block = new Block;
$trx = new Transaction;
$dst=san($data['dst']);
if(!$acc->valid($dst)) api_err("Invalid destination address");
$dst_b=base58_decode($dst);
if(strlen($dst_b)!=64) api_err("Invalid destination address");
$public_key=san($data['public_key']); if ($fee > 10 && $current['height'] > 10800) {
if(!$acc->valid_key($public_key)) api_err("Invalid public key"); $fee = 10; //10800
$private_key=san($data['private_key']); }
if(!$acc->valid_key($private_key)) api_err("Invalid private key"); if ($val < 0.00000001) {
$signature=san($data['signature']); api_err("Invalid value");
if(!$acc->valid_key($signature)) api_err("Invalid signature"); }
$date=$data['date']+0;
if($date==0) $date=time();
if($date<time()-(3600*24*48)) api_err("The date is too old");
if($date>time()+86400) api_err("Invalid Date");
$version=intval($data['version']);
$message=$data['message'];
if(strlen($message)>128) api_err("The message must be less than 128 chars");
$val=$data['val']+0;
$fee=$val*0.0025;
if($fee<0.00000001) $fee=0.00000001;
if($fee>10&&$current['height']>10800) $fee=10; //10800 if ($version < 1) {
if($val<0.00000001) api_err("Invalid value"); $version = 1;
}
if($version<1) $version=1;
$val=number_format($val,8,'.',''); $val = number_format($val, 8, '.', '');
$fee=number_format($fee,8,'.',''); $fee = number_format($fee, 8, '.', '');
if(empty($public_key)&&empty($private_key)) api_err("Either the private key or the public key must be sent"); if (empty($public_key) && empty($private_key)) {
api_err("Either the private key or the public key must be sent");
}
if(empty($private_key)&&empty($signature)) api_err("Either the private_key or the signature must be sent");
if(empty($public_key)) if (empty($private_key) && empty($signature)) {
{ api_err("Either the private_key or the signature must be sent");
}
$pk=coin2pem($private_key,true); if (empty($public_key)) {
$pkey=openssl_pkey_get_private($pk); $pk = coin2pem($private_key, true);
$pkey = openssl_pkey_get_private($pk);
$pub = openssl_pkey_get_details($pkey); $pub = openssl_pkey_get_details($pkey);
$public_key= pem2coin($pub['key']); $public_key = pem2coin($pub['key']);
} }
$transaction=array("val"=>$val, "fee"=>$fee, "dst"=>$dst, "public_key"=>$public_key,"date"=>$date, "version"=>$version,"message"=>$message, "signature"=>$signature); $transaction = [
"val" => $val,
if(!empty($private_key)){ "fee" => $fee,
"dst" => $dst,
$signature=$trx->sign($transaction, $private_key); "public_key" => $public_key,
$transaction['signature']=$signature; "date" => $date,
"version" => $version,
"message" => $message,
"signature" => $signature,
];
if (!empty($private_key)) {
$signature = $trx->sign($transaction, $private_key);
$transaction['signature'] = $signature;
} }
$hash=$trx->hash($transaction); $hash = $trx->hash($transaction);
$transaction['id']=$hash; $transaction['id'] = $hash;
if (!$trx->check($transaction)) {
if(!$trx->check($transaction)) api_err("Transaction signature failed"); api_err("Transaction signature failed");
}
$res = $db->single("SELECT COUNT(1) FROM mempool WHERE id=:id", [":id" => $hash]);
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash)); if ($res != 0) {
if($res!=0) api_err("The transaction is already in mempool"); api_err("The transaction is already in mempool");
}
$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"); $res = $db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $hash]);
if ($res != 0) {
api_err("The transaction is already in a block");
}
$src=$acc->get_address($public_key);
$transaction['src']=$src;
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src)); $src = $acc->get_address($public_key);
if($balance<$val+$fee) api_err("Not enough funds"); $transaction['src'] = $src;
$balance = $db->single("SELECT balance FROM accounts WHERE id=:id", [":id" => $src]);
if ($balance < $val + $fee) {
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src)); api_err("Not enough funds");
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)"); }
$memspent = $db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src", [":src" => $src]);
if ($balance - $memspent < $val + $fee) {
api_err("Not enough funds (mempool)");
}
$trx->add_mempool($transaction, "local"); $trx->add_mempool($transaction, "local");
system("php propagate.php transaction $hash > /dev/null 2>&1 &"); system("php propagate.php transaction $hash > /dev/null 2>&1 &");
api_echo($hash); api_echo($hash);
} elseif($q=="mempoolSize"){ } elseif ($q == "mempoolSize") {
/** /**
* @api {get} /api.php?q=mempoolSize 15. mempoolSize * @api {get} /api.php?q=mempoolSize 15. mempoolSize
* @apiName mempoolSize * @apiName mempoolSize
* @apiGroup API * @apiGroup API
* @apiDescription Returns the number of transactions in mempool. * @apiDescription Returns the number of transactions in mempool.
* *
* @apiSuccess {numeric} data Number of mempool transactions * @apiSuccess {numeric} data Number of mempool transactions
*/ */
$res=$db->single("SELECT COUNT(1) FROM mempool"); $res = $db->single("SELECT COUNT(1) FROM mempool");
api_echo($res); api_echo($res);
} elseif ($q == 'randomNumber') {
/**
* @api {get} /api.php?q=randomNumber 16. randomNumber
* @apiName randomNumber
* @apiGroup API
* @apiDescription Returns a random number based on an ARO block id.
*
* @apiParam {numeric} height The height of the block on which the random number will be based on (should be a future block when starting)
* @apiParam {numeric} min Minimum number (default 1)
* @apiParam {numeric} max Maximum number
* @apiParam {string} seed A seed to generate different numbers for each use cases.
* @apiSuccess {numeric} data The random number
*/
} elseif($q=='randomNumber'){ $height = san($_GET['height']);
/** $max = intval($_GET['max']);
* @api {get} /api.php?q=randomNumber 16. randomNumber if (empty($_GET['min'])) {
* @apiName randomNumber $min = 1;
* @apiGroup API } else {
* @apiDescription Returns a random number based on an ARO block id. $min = intval($_GET['min']);
* }
* @apiParam {numeric} height The height of the block on which the random number will be based on (should be a future block when starting)
* @apiParam {numeric} min Minimum number (default 1)
* @apiParam {numeric} max Maximum number
* @apiParam {string} seed A seed to generate different numbers for each use cases.
* @apiSuccess {numeric} data The random number
*/
$height=san($_GET['height']); $blk = $db->single("SELECT id FROM blocks WHERE height=:h", [":h" => $height]);
$max=intval($_GET['max']); if ($blk === false) {
if(empty($_GET['min'])) $min=1; api_err("Unknown block. Future?");
else $min=intval($_GET['min']); }
$base = hash("sha256", $blk.$_GET['seed']);
$blk=$db->single("SELECT id FROM blocks WHERE height=:h",array(":h"=>$height));
if($blk===false) api_err("Unknown block. Future?");
$base=hash("sha256",$blk.$_GET['seed']);
$seed1=hexdec(substr($base,0,12)); $seed1 = hexdec(substr($base, 0, 12));
// generate random numbers based on the seed // generate random numbers based on the seed
mt_srand($seed1,MT_RAND_MT19937 ); mt_srand($seed1, MT_RAND_MT19937);
$res=mt_rand($min, $max); $res = mt_rand($min, $max);
api_echo($res); api_echo($res);
} else { } else {
api_err("Invalid request"); api_err("Invalid request");
} }
?>

19
composer.json Normal file
View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ $_config['db_connect']="mysql:host=localhost;dbname=ENTER-DB-NAME";
$_config['db_user']="ENTER-DB-USER"; $_config['db_user']="ENTER-DB-USER";
$_config['db_pass']="ENTER-DB-PASS"; $_config['db_pass']="ENTER-DB-PASS";
// Maximum number of connected peers // Maximum number of connected peers
$_config['max_peers']=30; $_config['max_peers']=30;
// Testnet, used for development // Testnet, used for development
$_config['testnet']=false; $_config['testnet']=false;
@@ -36,5 +36,3 @@ $_config['sanity_rebroadcast_locals']=true;
$_config['enable_logging']=false; $_config['enable_logging']=false;
// log file, should not be publicly viewable // log file, should not be publicly viewable
$_config['log_file']="/var/log/aro.log"; $_config['log_file']="/var/log/aro.log";
?>

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,275 +1,435 @@
<?php <?php
class Transaction { class Transaction
{
// reverse and remove all transactions from a block // reverse and remove all transactions from a block
public function reverse($block){ public function reverse($block)
{
global $db; global $db;
$acc=new Account; $acc = new Account();
$r=$db->run("SELECT * FROM transactions WHERE block=:block",array(":block"=>$block)); $r = $db->run("SELECT * FROM transactions WHERE block=:block", [":block" => $block]);
foreach($r as $x){ foreach ($r as $x) {
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']); if (empty($x['src'])) {
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val'])); $x['src'] = $acc->get_address($x['public_key']);
}
$db->run(
"UPDATE accounts SET balance=balance-:val WHERE id=:id",
[":id" => $x['dst'], ":val" => $x['val']]
);
// on version 0 / reward transaction, don't credit anyone // 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) {
$db->run(
// add the transactions to mempool "UPDATE accounts SET balance=balance+:val WHERE id=:id",
if($x['version']>0) $this->add_mempool($x); [":id" => $x['src'], ":val" => $x['val'] + $x['fee']]
$res= $db->run("DELETE FROM transactions WHERE id=:id",array(":id"=>$x['id'])); );
if($res!=1) return false; }
}
}
// clears the mempool
public function clean_mempool(){
global $db;
$block= new Block;
$current=$block->current();
$height=$current['height'];
$limit=$height-1000;
$db->run("DELETE FROM mempool WHERE height<:limit",array(":limit"=>$limit));
}
// returns X transactions from mempool
public function mempool($max){
global $db;
$block=new Block;
$current=$block->current();
$height=$current['height']+1;
// only get the transactions that are not locked with a future height
$r=$db->run("SELECT * FROM mempool WHERE height<=:height ORDER by val/fee DESC LIMIT :max",array(":height"=>$height, ":max"=>$max+50));
$transactions=array();
if(count($r)>0){
$i=0;
$balance=array();
foreach($r as $x){
$trans=array("id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
if($i>=$max) break;
if(empty($x['public_key'])){ // add the transactions to mempool
_log("$x[id] - Transaction has empty public_key"); if ($x['version'] > 0) {
continue; $this->add_mempool($x);
} }
if(empty($x['src'])){ $res = $db->run("DELETE FROM transactions WHERE id=:id", [":id" => $x['id']]);
_log("$x[id] - Transaction has empty src"); if ($res != 1) {
continue; return false;
} }
if(!$this->check($trans, $current['height'])){
_log("$x[id] - Transaction Check Failed");
continue;
}
$balance[$x['src']]+=$x['val']+$x['fee'];
if($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$x['id']))>0) {
_log("$x[id] - Duplicate transaction");
continue; //duplicate transaction
}
$res=$db->single("SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",array(":id"=>$x['src'], ":balance"=>$balance[$x['src']]));
if($res==0) {
_log("$x[id] - Not enough funds in balance");
continue; // not enough balance for the transactions
}
$i++;
ksort($trans);
$transactions[$x['id']]=$trans;
}
} }
// always sort the array }
// clears the mempool
public function clean_mempool()
{
global $db;
$block = new Block();
$current = $block->current();
$height = $current['height'];
$limit = $height - 1000;
$db->run("DELETE FROM mempool WHERE height<:limit", [":limit" => $limit]);
}
// returns X transactions from mempool
public function mempool($max)
{
global $db;
$block = new Block();
$current = $block->current();
$height = $current['height'] + 1;
// only get the transactions that are not locked with a future height
$r = $db->run(
"SELECT * FROM mempool WHERE height<=:height ORDER by val/fee DESC LIMIT :max",
[":height" => $height, ":max" => $max + 50]
);
$transactions = [];
if (count($r) > 0) {
$i = 0;
$balance = [];
foreach ($r as $x) {
$trans = [
"id" => $x['id'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
if ($i >= $max) {
break;
}
if (empty($x['public_key'])) {
_log("$x[id] - Transaction has empty public_key");
continue;
}
if (empty($x['src'])) {
_log("$x[id] - Transaction has empty src");
continue;
}
if (!$this->check($trans, $current['height'])) {
_log("$x[id] - Transaction Check Failed");
continue;
}
$balance[$x['src']] += $x['val'] + $x['fee'];
if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) {
_log("$x[id] - Duplicate transaction");
continue; //duplicate transaction
}
$res = $db->single(
"SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",
[":id" => $x['src'], ":balance" => $balance[$x['src']]]
);
if ($res == 0) {
_log("$x[id] - Not enough funds in balance");
continue; // not enough balance for the transactions
}
$i++;
ksort($trans);
$transactions[$x['id']] = $trans;
}
}
// always sort the array
ksort($transactions); ksort($transactions);
return $transactions; return $transactions;
} }
// add a new transaction to mempool and lock it with the current height // add a new transaction to mempool and lock it with the current height
public function add_mempool($x, $peer=""){ public function add_mempool($x, $peer = "")
{
global $db; global $db;
$block= new Block; $block = new Block();
$current=$block->current(); $current = $block->current();
$height=$current['height']; $height = $current['height'];
$x['id']=san($x['id']); $x['id'] = san($x['id']);
$bind=array(":peer"=>$peer, ":id"=>$x['id'],"public_key"=>$x['public_key'], ":height"=>$height, ":src"=>$x['src'],":dst"=>$x['dst'],":val"=>$x['val'], ":fee"=>$x['fee'],":signature"=>$x['signature'], ":version"=>$x['version'],":date"=>$x['date'], ":message"=>$x['message']); $bind = [
$db->run("INSERT into mempool SET peer=:peer, id=:id, public_key=:public_key, height=:height, src=:src, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",$bind); ":peer" => $peer,
":id" => $x['id'],
"public_key" => $x['public_key'],
":height" => $height,
":src" => $x['src'],
":dst" => $x['dst'],
":val" => $x['val'],
":fee" => $x['fee'],
":signature" => $x['signature'],
":version" => $x['version'],
":date" => $x['date'],
":message" => $x['message'],
];
$db->run(
"INSERT into mempool SET peer=:peer, id=:id, public_key=:public_key, height=:height, src=:src, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
return true; return true;
} }
// add a new transaction to the blockchain // add a new transaction to the blockchain
public function add($block,$height, $x){ public function add($block, $height, $x)
{
global $db; global $db;
$acc= new Account; $acc = new Account();
$acc->add($x['public_key'], $block); $acc->add($x['public_key'], $block);
$acc->add_id($x['dst'],$block); $acc->add_id($x['dst'], $block);
$x['id']=san($x['id']); $x['id'] = san($x['id']);
$bind=array(":id"=>$x['id'], ":public_key"=>$x['public_key'],":height"=>$height, ":block"=>$block, ":dst"=>$x['dst'],":val"=>$x['val'], ":fee"=>$x['fee'],":signature"=>$x['signature'], ":version"=>$x['version'],":date"=>$x['date'], ":message"=>$x['message']); $bind = [
$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); ":id" => $x['id'],
if($res!=1) return false; ":public_key" => $x['public_key'],
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val'])); ":height" => $height,
// no debit when the transaction is reward ":block" => $block,
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'])); ":dst" => $x['dst'],
$db->run("DELETE FROM mempool WHERE id=:id",array(":id"=>$x['id'])); ":val" => $x['val'],
":fee" => $x['fee'],
":signature" => $x['signature'],
":version" => $x['version'],
":date" => $x['date'],
":message" => $x['message'],
];
$res = $db->run(
"INSERT into transactions SET id=:id, public_key=:public_key, block=:block, height=:height, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",
$bind
);
if ($res != 1) {
return false;
}
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']]);
// no debit when the transaction is reward
if ($x['version'] > 0) {
$db->run(
"UPDATE accounts SET balance=(balance-:val)-:fee WHERE id=:id",
[":id" => $x['src'], ":val" => $x['val'], ":fee" => $x['fee']]
);
}
$db->run("DELETE FROM mempool WHERE id=:id", [":id" => $x['id']]);
return true; return true;
} }
// hash the transaction's most important fields and create the transaction ID // hash the transaction's most important fields and create the transaction ID
public function hash($x){ public function hash($x)
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature']; {
$hash= hash("sha512",$info); $info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature'];
return hex2coin($hash); $hash = hash("sha512", $info);
return hex2coin($hash);
} }
// check the transaction for validity // check the transaction for validity
public function check($x, $height=0){ public function check($x, $height = 0)
// if no specific block, use current {
if($height===0){ // if no specific block, use current
$block=new Block; if ($height === 0) {
$current=$block->current(); $block = new Block();
$height=$current['height']; $current = $block->current();
} $height = $current['height'];
$acc= new Account; }
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']; $acc = new Account();
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
// the value must be >=0 // the value must be >=0
if($x['val']<0){ _log("$x[id] - Value below 0"); return false; } if ($x['val'] < 0) {
_log("$x[id] - Value below 0");
// the fee must be >=0 return false;
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 // the fee must be >=0
if(!$acc->valid($x['dst'])) { _log("$x[id] - Invalid destination address"); return false; } if ($x['fee'] < 0) {
_log("$x[id] - Fee below 0");
// reward transactions are not added via this function return false;
if($x['version']<1) { _log("$x[id] - Invalid version <1"); return false; } }
//if($x['version']>1) { _log("$x[id] - Invalid version >1"); return false; }
// 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>16900&&$x['date']<1519327780) return false;
$id=$this->hash($x);
// the hash does not match our regenerated hash
if($x['id']!=$id) {
// fix for broken base58 library which was used until block 16900, accepts hashes without the first 1 or 2 bytes
$xs=base58_decode($x['id']);
if(((strlen($xs)!=63||substr($id,1)!=$x['id'])&&(strlen($xs)!=62||substr($id,2)!=$x['id']))||$height>16900){
_log("$x[id] - $id - Invalid hash");
return false;
}
} // the fee is 0.25%, hardcoded
$fee = $x['val'] * 0.0025;
//verify the ecdsa signature $fee = number_format($fee, 8, ".", "");
if(!$acc->check_signature($info, $x['signature'], $x['public_key'])) { _log("$x[id] - Invalid signature"); return false; } if ($fee < 0.00000001) {
$fee = 0.00000001;
return true; }
// 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; }
// 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 > 16900 && $x['date'] < 1519327780) {
return false;
}
$id = $this->hash($x);
// the hash does not match our regenerated hash
if ($x['id'] != $id) {
// fix for broken base58 library which was used until block 16900, accepts hashes without the first 1 or 2 bytes
$xs = base58_decode($x['id']);
if (((strlen($xs) != 63 || substr($id, 1) != $x['id']) && (strlen($xs) != 62 || substr(
$id,
2
) != $x['id'])) || $height > 16900) {
_log("$x[id] - $id - Invalid hash");
return false;
}
}
//verify the ecdsa signature
if (!$acc->check_signature($info, $x['signature'], $x['public_key'])) {
_log("$x[id] - Invalid signature");
return false;
}
return true;
} }
// sign a transaction
public function sign($x, $private_key){
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
$signature=ec_sign($info,$private_key);
return $signature;
// sign a transaction
public function sign($x, $private_key)
{
$info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
$signature = ec_sign($info, $private_key);
return $signature;
} }
//export a mempool transaction //export a mempool transaction
public function export($id){ public function export($id)
{
global $db; global $db;
$r=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id)); $r = $db->row("SELECT * FROM mempool WHERE id=:id", [":id" => $id]);
return $r; return $r;
} }
// get the transaction data as array // get the transaction data as array
public function get_transaction($id){ public function get_transaction($id)
{
global $db; global $db;
$acc=new Account; $acc = new Account();
$block=new Block; $block = new Block();
$current=$block->current(); $current = $block->current();
$x=$db->row("SELECT * FROM transactions WHERE id=:id",array(":id"=>$id)); $x = $db->row("SELECT * FROM transactions WHERE id=:id", [":id" => $id]);
if(!$x) return false;
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
$trans['src']=$acc->get_address($x['public_key']);
$trans['confirmations']=$current['height']-$x['height'];
if($x['version']==0) $trans['type']="mining"; if (!$x) {
elseif($x['version']==1){ return false;
if($x['dst']==$id) $trans['type']="credit"; }
else $trans['type']="debit"; $trans = [
} else { "block" => $x['block'],
$trans['type']="other"; "height" => $x['height'],
} "id" => $x['id'],
ksort($trans); "dst" => $x['dst'],
return $trans; "val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $acc->get_address($x['public_key']);
$trans['confirmations'] = $current['height'] - $x['height'];
if ($x['version'] == 0) {
$trans['type'] = "mining";
} elseif ($x['version'] == 1) {
if ($x['dst'] == $id) {
$trans['type'] = "credit";
} else {
$trans['type'] = "debit";
}
} else {
$trans['type'] = "other";
}
ksort($trans);
return $trans;
} }
// return the transactions for a specific block id or height // return the transactions for a specific block id or height
public function get_transactions($height="", $id=""){ public function get_transactions($height = "", $id = "")
{
global $db; global $db;
$block=new Block; $block = new Block();
$current=$block->current(); $current = $block->current();
$acc=new Account; $acc = new Account();
$height=san($height); $height = san($height);
$id=san($id); $id = san($id);
if(empty($id)&&empty($height)) return false; if (empty($id) && empty($height)) {
if(!empty($id)) $r=$db->run("SELECT * FROM transactions WHERE block=:id AND version>0",array(":id"=>$id)); return false;
else $r=$db->run("SELECT * FROM transactions WHERE height=:height AND version>0",array(":height"=>$height)); }
$res=array(); if (!empty($id)) {
foreach($r as $x){ $r = $db->run("SELECT * FROM transactions WHERE block=:id AND version>0", [":id" => $id]);
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']); } else {
$trans['src']=$acc->get_address($x['public_key']); $r = $db->run("SELECT * FROM transactions WHERE height=:height AND version>0", [":height" => $height]);
$trans['confirmations']=$current['height']-$x['height']; }
$res = [];
if($x['version']==0) $trans['type']="mining"; foreach ($r as $x) {
elseif($x['version']==1){ $trans = [
if($x['dst']==$id) $trans['type']="credit"; "block" => $x['block'],
else $trans['type']="debit"; "height" => $x['height'],
} else { "id" => $x['id'],
$trans['type']="other"; "dst" => $x['dst'],
} "val" => $x['val'],
ksort($trans); "fee" => $x['fee'],
$res[]=$trans; "signature" => $x['signature'],
} "message" => $x['message'],
return $res; "version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $acc->get_address($x['public_key']);
$trans['confirmations'] = $current['height'] - $x['height'];
if ($x['version'] == 0) {
$trans['type'] = "mining";
} elseif ($x['version'] == 1) {
if ($x['dst'] == $id) {
$trans['type'] = "credit";
} else {
$trans['type'] = "debit";
}
} else {
$trans['type'] = "other";
}
ksort($trans);
$res[] = $trans;
}
return $res;
} }
// get a specific mempool transaction as array // get a specific mempool transaction as array
public function get_mempool_transaction($id){ public function get_mempool_transaction($id)
{
global $db; global $db;
$x=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id)); $x = $db->row("SELECT * FROM mempool WHERE id=:id", [":id" => $id]);
if(!$x) return false; if (!$x) {
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']); return false;
$trans['src']=$x['src']; }
$trans = [
"block" => $x['block'],
"height" => $x['height'],
"id" => $x['id'],
"dst" => $x['dst'],
"val" => $x['val'],
"fee" => $x['fee'],
"signature" => $x['signature'],
"message" => $x['message'],
"version" => $x['version'],
"date" => $x['date'],
"public_key" => $x['public_key'],
];
$trans['src'] = $x['src'];
$trans['type']="mempool"; $trans['type'] = "mempool";
$trans['confirmations']=-1; $trans['confirmations'] = -1;
ksort($trans); ksort($trans);
return $trans; return $trans;
} }
} }
?>

View File

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

View File

@@ -1,7 +1,7 @@
<?php <?php
/* /*
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2018 AroDev Copyright (c) 2018 AroDev
www.arionum.com www.arionum.com
@@ -24,53 +24,60 @@ OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE. OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
require_once("include/init.inc.php"); require_once("include/init.inc.php");
$block=new Block(); $block = new Block();
$acc=new Account(); $acc = new Account();
set_time_limit(360); set_time_limit(360);
$q=$_GET['q']; $q = $_GET['q'];
$ip=san_ip($_SERVER['REMOTE_ADDR']); $ip = san_ip($_SERVER['REMOTE_ADDR']);
$ip=filter_var($ip, FILTER_VALIDATE_IP); $ip = filter_var($ip, FILTER_VALIDATE_IP);
// in case of testnet, all IPs are accepted for mining // in case of testnet, all IPs are accepted for mining
if($_config['testnet']==false&&!in_array($ip,$_config['allowed_hosts'])&&!empty($ip)&&!in_array('*',$_config['allowed_hosts'])) api_err("unauthorized"); if ($_config['testnet'] == false && !in_array($ip, $_config['allowed_hosts']) && !empty($ip) && !in_array(
'*',
if($q=="info"){ $_config['allowed_hosts']
// provides the mining info to the miner )) {
$diff=$block->difficulty(); api_err("unauthorized");
$current=$block->current();
$res=array("difficulty"=>$diff, "block"=>$current['id'], "height"=>$current['height'], "testnet"=>$_config['testnet']);
api_echo($res);
exit;
} elseif($q=="submitNonce"){
// in case the blocks are syncing, reject all
if($_config['sanity_sync']==1) api_err("sanity-sync");
$nonce = san($_POST['nonce']);
$argon=$_POST['argon'];
$public_key=san($_POST['public_key']);
$private_key=san($_POST['private_key']);
// check if the miner won the block
$result=$block->mine($public_key, $nonce, $argon);
if($result) {
// generate the new block
$res=$block->forge($nonce,$argon, $public_key, $private_key);
if($res){
//if the new block is generated, propagate it to all peers in background
$current=$block->current();
system("php propagate.php block $current[id] > /dev/null 2>&1 &");
api_echo("accepted");
}
}
api_err("rejected");
} else {
api_err("invalid command");
} }
?> if ($q == "info") {
// provides the mining info to the miner
$diff = $block->difficulty();
$current = $block->current();
$res = [
"difficulty" => $diff,
"block" => $current['id'],
"height" => $current['height'],
"testnet" => $_config['testnet'],
];
api_echo($res);
exit;
} elseif ($q == "submitNonce") {
// in case the blocks are syncing, reject all
if ($_config['sanity_sync'] == 1) {
api_err("sanity-sync");
}
$nonce = san($_POST['nonce']);
$argon = $_POST['argon'];
$public_key = san($_POST['public_key']);
$private_key = san($_POST['private_key']);
// check if the miner won the block
$result = $block->mine($public_key, $nonce, $argon);
if ($result) {
// generate the new block
$res = $block->forge($nonce, $argon, $public_key, $private_key);
if ($res) {
//if the new block is generated, propagate it to all peers in background
$current = $block->current();
system("php propagate.php block $current[id] > /dev/null 2>&1 &");
api_echo("accepted");
}
}
api_err("rejected");
} else {
api_err("invalid command");
}

503
peer.php
View File

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

View File

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

File diff suppressed because it is too large Load Diff

330
util.php
View File

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