Initial commit
This commit is contained in:
202
api.php
Executable file
202
api.php
Executable file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
require_once("include/init.inc.php");
|
||||
error_reporting(0);
|
||||
$ip=$_SERVER['REMOTE_ADDR'];
|
||||
if($_config['public_api']==false&&!in_array($ip,$_config['allowed_hosts'])){
|
||||
api_err("private-api");
|
||||
}
|
||||
|
||||
$acc = new Account;
|
||||
$block = new Block;
|
||||
|
||||
$trx = new Transaction;
|
||||
$q=$_GET['q'];
|
||||
if(!empty($_POST['data'])){
|
||||
$data=json_decode($_POST['data'],true);
|
||||
} else {
|
||||
$data=$_GET;
|
||||
}
|
||||
|
||||
|
||||
if($q=="getAddress"){
|
||||
$public_key=$data['public_key'];
|
||||
if(strlen($public_key)<32) api_err("Invalid public key");
|
||||
api_echo($acc->get_address($public_key));
|
||||
}
|
||||
elseif($q=="base58"){
|
||||
api_echo(base58_encode($data['data']));
|
||||
}
|
||||
elseif($q=="getBalance"){
|
||||
$public_key=$data['public_key'];
|
||||
$account=$data['account'];
|
||||
if(!empty($public_key)&&strlen($public_key)<32) api_err("Invalid public key");
|
||||
if(!empty($public_key)) $account=$acc->get_address($public_key);
|
||||
if(empty($account)) api_err("Invalid account id");
|
||||
$account=san($account);
|
||||
api_echo($acc->balance($account));
|
||||
}
|
||||
elseif($q=="getPendingBalance"){
|
||||
|
||||
$account=$data['account'];
|
||||
if(empty($account)) api_err("Invalid account id");
|
||||
$account=san($account);
|
||||
api_echo($acc->pending_balance($account));
|
||||
}
|
||||
elseif($q=="getTransactions"){
|
||||
$account=san($data['account']);
|
||||
$transactions=$acc->get_mempool_transactions($account);
|
||||
$transactions=array_merge($transactions, $acc->get_transactions($account));
|
||||
api_echo($transactions);
|
||||
|
||||
}
|
||||
elseif($q=="getPublicKey"){
|
||||
$account=san($data['account']);
|
||||
if(empty($account)) api_err("Invalid account id");
|
||||
$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=="getTransaction"){
|
||||
|
||||
$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);
|
||||
|
||||
}
|
||||
elseif($q=="currentBlock"){
|
||||
$current=$block->current();
|
||||
api_echo($current);
|
||||
} elseif($q=="version"){
|
||||
api_echo(VERSION);
|
||||
|
||||
} elseif($q=="send"){
|
||||
|
||||
$acc = new Account;
|
||||
$block = new Block;
|
||||
|
||||
$trx = new Transaction;
|
||||
|
||||
$dst=san($data['dst']);
|
||||
|
||||
if(!$acc->valid($dst)) 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;
|
||||
|
||||
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($val<0.00000001) api_err("Invalid value");
|
||||
|
||||
|
||||
if($version<1) api_err("Invalid version");
|
||||
|
||||
$val=number_format($val,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($private_key)&&empty($signature)) api_err("Either the private_key or the signature must be sent");
|
||||
if(empty($public_key))
|
||||
{
|
||||
|
||||
$pk=coin2pem($private_key,true);
|
||||
$pkey=openssl_pkey_get_private($pk);
|
||||
$pub = openssl_pkey_get_details($pkey);
|
||||
$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);
|
||||
|
||||
if(!empty($private_key)){
|
||||
|
||||
$signature=$trx->sign($transaction, $private_key);
|
||||
$transaction['signature']=$signature;
|
||||
|
||||
}
|
||||
|
||||
|
||||
$hash=$trx->hash($transaction);
|
||||
$transaction['id']=$hash;
|
||||
|
||||
|
||||
|
||||
if(!$trx->check($transaction)) api_err("Transaction signature failed");
|
||||
|
||||
|
||||
|
||||
|
||||
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash));
|
||||
if($res!=0) api_err("The transaction is already in mempool");
|
||||
|
||||
$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");
|
||||
|
||||
|
||||
|
||||
$src=$acc->get_address($public_key);
|
||||
$transaction['src']=$src;
|
||||
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src));
|
||||
if($balance<$val+$fee) api_err("Not enough funds");
|
||||
|
||||
|
||||
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src));
|
||||
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)");
|
||||
|
||||
|
||||
|
||||
$trx->add_mempool($transaction, "local");
|
||||
system("php propagate.php transaction $hash &>/dev/null &");
|
||||
api_echo($hash);
|
||||
} elseif($q=="mempoolSize"){
|
||||
$res=$db->single("SELECT COUNT(1) FROM mempool");
|
||||
api_echo($res);
|
||||
|
||||
} else {
|
||||
api_err("Invalid request");
|
||||
}
|
||||
?>
|
||||
138
include/account.inc.php
Executable file
138
include/account.inc.php
Executable file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
class Account {
|
||||
|
||||
|
||||
|
||||
public function add($public_key, $block){
|
||||
global $db;
|
||||
$id=$this->get_address($public_key);
|
||||
$bind=array(":id"=>$id, ":public_key"=>$public_key, ":block"=>$block,":public_key2"=>$public_key );
|
||||
|
||||
$db->run("INSERT INTO accounts SET id=:id, public_key=:public_key, block=:block, balance=0 ON DUPLICATE KEY UPDATE public_key=if(public_key='',:public_key2,public_key)",$bind);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
public function get_address($hash){
|
||||
for($i=0;$i<9;$i++) $hash=hash('sha512',$hash, true);
|
||||
|
||||
|
||||
|
||||
|
||||
return base58_encode($hash);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function check_signature($data, $signature, $public_key){
|
||||
|
||||
return ec_verify($data ,$signature, $public_key);
|
||||
}
|
||||
|
||||
|
||||
public function generate_account(){
|
||||
|
||||
$args = array(
|
||||
"curve_name" => "secp256k1",
|
||||
"private_key_type" => OPENSSL_KEYTYPE_EC,
|
||||
);
|
||||
|
||||
|
||||
$key1 = openssl_pkey_new($args);
|
||||
|
||||
openssl_pkey_export($key1, $pvkey);
|
||||
|
||||
$private_key= pem2coin($pvkey);
|
||||
|
||||
$pub = openssl_pkey_get_details($key1);
|
||||
|
||||
$public_key= pem2coin($pub['key']);
|
||||
|
||||
$address=$this->get_address($public_key);
|
||||
return array("address"=>$address, "public_key"=>$public_key,"private_key"=>$private_key);
|
||||
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
public function balance($id){
|
||||
global $db;
|
||||
$res=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$id));
|
||||
if($res===false) $res="0.00000000";
|
||||
return $res;
|
||||
}
|
||||
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($res=="0.00000000") return $res;
|
||||
$mem=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:id",array(":id"=>$id));
|
||||
$rez=$res-$mem;
|
||||
return $rez;
|
||||
|
||||
}
|
||||
public function get_transactions($id){
|
||||
global $db;
|
||||
$block=new Block;
|
||||
$current=$block->current();
|
||||
$public_key=$this->public_key($id);
|
||||
$res=$db->run("SELECT * FROM transactions WHERE dst=:dst or public_key=:src ORDER by height DESC LIMIT 100",array(":src"=>$public_key, ":dst"=>$id));
|
||||
|
||||
$transactions=array();
|
||||
foreach($res as $x){
|
||||
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
|
||||
$trans['src']=$this->get_address($x['public_key']);
|
||||
$trans['confirmations']=$current['height']-$x['height'];
|
||||
|
||||
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;
|
||||
}
|
||||
public function get_mempool_transactions($id){
|
||||
global $db;
|
||||
$transactions=array();
|
||||
$res=$db->run("SELECT * FROM mempool WHERE src=:src ORDER by height DESC LIMIT 100",array(":src"=>$id, ":dst"=>$id));
|
||||
foreach($res as $x){
|
||||
$trans=array("block"=>$x['block'],"height"=>$x['height'], "id"=>$x['id'],"src"=>$x['src'],"dst"=>$x['dst'],"val"=>$x['val'],"fee"=>$x['fee'],"signature"=>$x['signature'], "message"=>$x['message'],"version"=>$x['version'],"date"=>$x['date'], "public_key"=>$x['public_key']);
|
||||
$trans['type']="mempool";
|
||||
$trans['confirmations']=-1;
|
||||
ksort($trans);
|
||||
$transactions[]=$trans;
|
||||
}
|
||||
return $transactions;
|
||||
}
|
||||
public function public_key($id){
|
||||
global $db;
|
||||
$res=$db->single("SELECT public_key FROM accounts WHERE id=:id",array(":id"=>$id));
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
405
include/block.inc.php
Executable file
405
include/block.inc.php
Executable file
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
class Block {
|
||||
|
||||
|
||||
|
||||
public function add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon){
|
||||
global $db;
|
||||
$acc=new Account;
|
||||
$trx=new Transaction;
|
||||
//try {
|
||||
|
||||
// } catch (Exception $e){
|
||||
|
||||
// }
|
||||
|
||||
$generator=$acc->get_address($public_key);
|
||||
|
||||
ksort($data);
|
||||
|
||||
$hash=$this->hash($generator, $height, $date, $nonce, $data, $signature, $difficulty, $argon);
|
||||
|
||||
|
||||
$json=json_encode($data);
|
||||
|
||||
$info="{$generator}-{$height}-{$date}-{$nonce}-{$json}-{$difficulty}-{$argon}";
|
||||
|
||||
if(!$acc->check_signature($info,$signature,$public_key)) return false;
|
||||
|
||||
|
||||
if(!$this->parse_block($hash,$height,$data, true)) return false;
|
||||
|
||||
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
|
||||
|
||||
$reward=$this->reward($height,$data);
|
||||
|
||||
$msg='';
|
||||
|
||||
|
||||
$transaction=array("src"=>$generator, "dst"=>$generator, "val"=>$reward, "version"=>0, "date"=>$date, "message"=>$msg, "fee"=>"0.00000000","public_key"=>$public_key);
|
||||
$transaction['signature']=$reward_signature;
|
||||
$transaction['id']=$trx->hash($transaction);
|
||||
$info=$transaction['val']."-".$transaction['fee']."-".$transaction['dst']."-".$transaction['message']."-".$transaction['version']."-".$transaction['public_key']."-".$transaction['date'];
|
||||
|
||||
if(!$acc->check_signature($info,$reward_signature,$public_key)) return false;
|
||||
|
||||
$db->beginTransaction();
|
||||
$total=count($data);
|
||||
$bind=array(":id"=>$hash,":generator"=>$generator, ":signature"=>$signature, ":height"=>$height, ":date"=>$date, ":nonce"=>$nonce, ":difficulty"=>$difficulty,":argon"=>$argon, ":transactions"=>$total);
|
||||
$res=$db->run("INSERT into blocks SET id=:id, generator=:generator, height=:height,`date`=:date,nonce=:nonce, signature=:signature, difficulty=:difficulty, argon=:argon, transactions=:transactions",$bind);
|
||||
if($res!=1) {
|
||||
$db->rollback();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$trx->add($hash, $height,$transaction);
|
||||
|
||||
|
||||
$res=$this->parse_block($hash,$height,$data, false);
|
||||
if($res==false) $db->rollback();
|
||||
else $db->commit();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function current(){
|
||||
global $db;
|
||||
$current=$db->row("SELECT * FROM blocks ORDER by height DESC LIMIT 1");
|
||||
if(!$current){
|
||||
$this->genesis();
|
||||
return $this->current(true);
|
||||
}
|
||||
return $current;
|
||||
|
||||
}
|
||||
|
||||
public function prev(){
|
||||
global $db;
|
||||
$current=$db->row("SELECT * FROM blocks ORDER by height DESC LIMIT 1,1");
|
||||
|
||||
return $current;
|
||||
|
||||
}
|
||||
|
||||
public function difficulty($height=0){
|
||||
global $db;
|
||||
if($height==0){
|
||||
$current=$this->current();
|
||||
} else{
|
||||
$current=$this->get($height);
|
||||
}
|
||||
|
||||
|
||||
$height=$current['height'];
|
||||
|
||||
$limit=20;
|
||||
if($height<20)
|
||||
$limit=$height-1;
|
||||
|
||||
if($height<10) return $current['difficulty'];
|
||||
|
||||
|
||||
$first=$db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT $limit,1");
|
||||
$time=$current['date']-$first['date'];
|
||||
$result=ceil($time/$limit);
|
||||
if($result>220){
|
||||
$dif= bcmul($current['difficulty'], 1.05);
|
||||
} elseif($result<260){
|
||||
$dif= bcmul($current['difficulty'], 0.95);
|
||||
} else {
|
||||
$dif=$current['difficulty'];
|
||||
}
|
||||
if($dif<1000) $dif=1000;
|
||||
if($dif>9223372036854775800) $dif=9223372036854775800;
|
||||
|
||||
return $dif;
|
||||
}
|
||||
|
||||
public function max_transactions(){
|
||||
global $db;
|
||||
$current=$this->current();
|
||||
$limit=$current['height']-100;
|
||||
$avg=$db->single("SELECT AVG(transactions) FROM blocks WHERE height>:limit",array(":limit"=>$limit));
|
||||
if($avg<100) return 100;
|
||||
return ceil($avg*1.1);
|
||||
}
|
||||
|
||||
public function reward($id,$data=array()){
|
||||
|
||||
|
||||
$reward=1000;
|
||||
|
||||
$factor=floor($id/10800)/100;
|
||||
$reward-=$reward*$factor;
|
||||
if($reward<0) $reward=0;
|
||||
|
||||
$fees=0;
|
||||
if(count($data)>0){
|
||||
|
||||
foreach($data as $x){
|
||||
$fees+=$x['fee'];
|
||||
}
|
||||
}
|
||||
return number_format($reward+$fees,8,'.','');
|
||||
}
|
||||
|
||||
|
||||
public function check($data){
|
||||
if(strlen($data['argon'])<20) return false;
|
||||
$acc=new Account;
|
||||
if(!$acc->valid_key($data['public_key'])) return false;
|
||||
if($data['difficulty']!=$this->difficulty()) return false;
|
||||
if(!$this->mine($data['public_key'],$data['nonce'], $data['argon'])) return false;
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function forge($nonce, $argon, $public_key, $private_key){
|
||||
|
||||
if(!$this->mine($public_key,$nonce, $argon)) return false;
|
||||
|
||||
$current=$this->current();
|
||||
$height=$current['height']+=1;
|
||||
$date=time();
|
||||
if($date<=$current['date']) return 0;
|
||||
|
||||
$txn=new Transaction;
|
||||
$data=$txn->mempool($this->max_transactions());
|
||||
|
||||
|
||||
$difficulty=$this->difficulty();
|
||||
$acc=new Account;
|
||||
$generator=$acc->get_address($public_key);
|
||||
ksort($data);
|
||||
$signature=$this->sign($generator, $height, $date, $nonce, $data, $private_key, $difficulty, $argon);
|
||||
|
||||
// reward signature
|
||||
$reward=$this->reward($height,$data);
|
||||
$msg='';
|
||||
$transaction=array("src"=>$generator, "dst"=>$generator, "val"=>$reward, "version"=>0, "date"=>$date, "message"=>$msg, "fee"=>"0.00000000","public_key"=>$public_key);
|
||||
ksort($transaction);
|
||||
$reward_signature=$txn->sign($transaction, $private_key);
|
||||
|
||||
|
||||
$res=$this->add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon);
|
||||
if(!$res) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public function mine($public_key, $nonce, $argon, $difficulty=0, $current_id=0){
|
||||
|
||||
if($current_id===0){
|
||||
$current=$this->current();
|
||||
$current_id=$current['id'];
|
||||
}
|
||||
if($difficulty===0) $difficulty=$this->difficulty();
|
||||
|
||||
|
||||
$argon='$argon2i$v=19$m=16384,t=4,p=4'.$argon;
|
||||
$base="$public_key-$nonce-".$current_id."-$difficulty";
|
||||
|
||||
|
||||
|
||||
|
||||
if(!password_verify($base,$argon)) { return false; }
|
||||
|
||||
$hash=$base.$argon;
|
||||
|
||||
for($i=0;$i<5;$i++) $hash=hash("sha512",$hash,true);
|
||||
$hash=hash("sha512",$hash);
|
||||
|
||||
$m=str_split($hash,2);
|
||||
|
||||
$duration=hexdec($m[10]).hexdec($m[15]).hexdec($m[20]).hexdec($m[23]).hexdec($m[31]).hexdec($m[40]).hexdec($m[45]).hexdec($m[55]);
|
||||
$duration=ltrim($duration, '0');
|
||||
$result=gmp_div($duration, $difficulty);
|
||||
|
||||
if($result>0&&$result<=240) return true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function parse_block($block, $height, $data, $test=true){
|
||||
global $db;
|
||||
|
||||
if($data===false) return false;
|
||||
$acc=new Account;
|
||||
$trx=new Transaction;
|
||||
if(count($data)==0) return true;
|
||||
|
||||
$max=$this->max_transactions();
|
||||
|
||||
if(count($data)>$max) return false;
|
||||
$balance=array();
|
||||
foreach($data as &$x){
|
||||
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']);
|
||||
|
||||
if(!$trx->check($x)) return false;
|
||||
|
||||
$balance[$x['src']]+=$x['val']+$x['fee'];
|
||||
|
||||
if($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$x['id']))>0) return false; //duplicate transaction
|
||||
|
||||
}
|
||||
|
||||
foreach($balance as $id=>$bal){
|
||||
$res=$db->single("SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance",array(":id"=>$id, ":balance"=>$bal));
|
||||
if($res==0) return false; // not enough balance for the transactions
|
||||
}
|
||||
|
||||
if($test==false){
|
||||
|
||||
foreach($data as $d){
|
||||
$res=$trx->add($block, $height, $d);
|
||||
if($res==false) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function genesis(){
|
||||
global $db;
|
||||
$signature='AN1rKvtLTWvZorbiiNk5TBYXLgxiLakra2byFef9qoz1bmRzhQheRtiWivfGSwP6r8qHJGrf8uBeKjNZP1GZvsdKUVVN2XQoL';
|
||||
$generator='2P67zUANj7NRKTruQ8nJRHNdKMroY6gLw4NjptTVmYk6Hh1QPYzzfEa9z4gv8qJhuhCNM8p9GDAEDqGUU1awaLW6';
|
||||
$public_key='PZ8Tyr4Nx8MHsRAGMpZmZ6TWY63dXWSCyjGMdVDanywM3CbqvswVqysqU8XS87FcjpqNijtpRSSQ36WexRDv3rJL5X8qpGvzvznuErSRMfb2G6aNoiaT3aEJ';
|
||||
$reward_signature='381yXZ3yq2AXHHdXfEm8TDHS4xJ6nkV4suXtUUvLjtvuyi17jCujtwcwXuYALM1F3Wiae2A4yJ6pXL1kTHJxZbrJNgtsKEsb';
|
||||
$argon='$M1ZpVzYzSUxYVFp6cXEwWA$CA6p39MVX7bvdXdIIRMnJuelqequanFfvcxzQjlmiik';
|
||||
|
||||
$difficulty="5555555555";
|
||||
$height=1;
|
||||
$data=array();
|
||||
$date='1515324995';
|
||||
$nonce='4QRKTSJ+i9Gf9ubPo487eSi+eWOnIBt9w4Y+5J+qbh8=';
|
||||
|
||||
|
||||
$res=$this->add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature,$argon);
|
||||
if(!$res) api_err("Could not add the genesis block.");
|
||||
}
|
||||
// delete last X blocks
|
||||
public function pop($no=1){
|
||||
$current=$this->current();
|
||||
$this->delete($current['height']-$no+1);
|
||||
}
|
||||
|
||||
public function delete($height){
|
||||
if($height<2) $height=2;
|
||||
global $db;
|
||||
$trx=new Transaction;
|
||||
|
||||
$r=$db->run("SELECT * FROM blocks WHERE height>=:height ORDER by height DESC",array(":height"=>$height));
|
||||
|
||||
if(count($r)==0) return;
|
||||
$db->beginTransaction();
|
||||
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
|
||||
foreach($r as $x){
|
||||
$res=$trx->reverse($x['id']);
|
||||
if($res===false) {
|
||||
$db->rollback();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return false;
|
||||
}
|
||||
$res=$db->run("DELETE FROM blocks WHERE id=:id",array(":id"=>$x['id']));
|
||||
if($res!=1){
|
||||
$db->rollback();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function delete_id($id){
|
||||
|
||||
global $db;
|
||||
$trx=new Transaction;
|
||||
|
||||
$x=$db->row("SELECT * FROM blocks WHERE id=:id",array(":id"=>$id));
|
||||
|
||||
if($x===false) return false;
|
||||
$db->beginTransaction();
|
||||
$db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE");
|
||||
|
||||
$res=$trx->reverse($x['id']);
|
||||
if($res===false) {
|
||||
$db->rollback();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return false;
|
||||
}
|
||||
$res=$db->run("DELETE FROM blocks WHERE id=:id",array(":id"=>$x['id']));
|
||||
if($res!=1){
|
||||
$db->rollback();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return false;
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
$db->exec("UNLOCK TABLES");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sign($generator, $height, $date, $nonce, $data, $key, $difficulty, $argon){
|
||||
|
||||
$json=json_encode($data);
|
||||
$info="{$generator}-{$height}-{$date}-{$nonce}-{$json}-{$difficulty}-{$argon}";
|
||||
|
||||
$signature=ec_sign($info,$key);
|
||||
return $signature;
|
||||
|
||||
}
|
||||
|
||||
public function hash($public_key, $height, $date, $nonce, $data, $signature, $difficulty, $argon){
|
||||
$json=json_encode($data);
|
||||
$hash= hash("sha512", "{$public_key}-{$height}-{$date}-{$nonce}-{$json}-{$signature}-{$difficulty}-{$argon}");
|
||||
return hex2coin($hash);
|
||||
}
|
||||
|
||||
|
||||
public function export($id="",$height=""){
|
||||
if(empty($id)&&empty($height)) return false;
|
||||
|
||||
global $db;
|
||||
$trx=new Transaction;
|
||||
if(!empty($height)) $block=$db->row("SELECT * FROM blocks WHERE height=:height",array(":height"=>$height));
|
||||
else $block=$db->row("SELECT * FROM blocks WHERE id=:id",array(":id"=>$id));
|
||||
|
||||
if(!$block) return false;
|
||||
$r=$db->run("SELECT * FROM transactions WHERE version>0 AND block=:block",array(":block"=>$block['id']));
|
||||
$transactions=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']);
|
||||
ksort($trans);
|
||||
$transactions[$x['id']]=$trans;
|
||||
}
|
||||
ksort($transactions);
|
||||
$block['data']=$transactions;
|
||||
|
||||
$gen=$db->row("SELECT public_key, signature FROM transactions WHERE version=0 AND block=:block",array(":block"=>$block['id']));
|
||||
$block['public_key']=$gen['public_key'];
|
||||
$block['reward_signature']=$gen['signature'];
|
||||
return $block;
|
||||
|
||||
}
|
||||
|
||||
public function get($height){
|
||||
global $db;
|
||||
if(empty($height)) return false;
|
||||
$block=$db->row("SELECT * FROM blocks WHERE height=:height",array(":height"=>$height));
|
||||
return $block;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
17
include/config.inc.php
Executable file
17
include/config.inc.php
Executable file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
$_config['db_connect']="mysql:host=localhost;dbname=ENTER-DB-NAME";
|
||||
$_config['db_user']="ENTER-DB-USER";
|
||||
$_config['db_pass']="ENTER-DB-PASS";
|
||||
$_config['max_peers']=30;
|
||||
$_config['testnet']=false;
|
||||
$_config['coin']="arionum";
|
||||
$_config['peer_max_mempool']=100;
|
||||
$_config['max_mempool_rebroadcast']=5000;
|
||||
$_config['sanity_rebroadcast_height']=30;
|
||||
$_config['transaction_propagation_peers']=5;
|
||||
$_config['max_test_peers']=5;
|
||||
$_config['sanity_recheck_blocks']=10;
|
||||
$_config['public_api']=true;
|
||||
$_config['allowed_hosts']=array("127.0.0.1");
|
||||
$_config['sanity_interval']=900;
|
||||
?>
|
||||
121
include/db.inc.php
Executable file
121
include/db.inc.php
Executable file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
class db extends PDO {
|
||||
|
||||
|
||||
private $error;
|
||||
private $sql;
|
||||
private $bind;
|
||||
private $debugger=0;
|
||||
public $working="yes";
|
||||
|
||||
public function __construct($dsn, $user="", $passwd="",$debug_level=0) {
|
||||
$options = array(
|
||||
PDO::ATTR_PERSISTENT => true,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
|
||||
);
|
||||
$this->debugger=$debug_level;
|
||||
try {
|
||||
parent::__construct($dsn, $user, $passwd, $options);
|
||||
} catch (PDOException $e) {
|
||||
$this->error = $e->getMessage();
|
||||
die("Could not connect to the DB");
|
||||
}
|
||||
}
|
||||
|
||||
private function debug() {
|
||||
if(!$this->debugger) return;
|
||||
$error = array("Error" => $this->error);
|
||||
if(!empty($this->sql))
|
||||
$error["SQL Statement"] = $this->sql;
|
||||
if(!empty($this->bind))
|
||||
$error["Bind Parameters"] = trim(print_r($this->bind, true));
|
||||
|
||||
$backtrace = debug_backtrace();
|
||||
if(!empty($backtrace)) {
|
||||
foreach($backtrace as $info) {
|
||||
if($info["file"] != __FILE__)
|
||||
$error["Backtrace"] = $info["file"] . " at line " . $info["line"];
|
||||
}
|
||||
}
|
||||
$msg = "";
|
||||
$msg .= "SQL Error\n" . str_repeat("-", 50);
|
||||
foreach($error as $key => $val)
|
||||
$msg .= "\n\n$key:\n$val";
|
||||
|
||||
if($this->debugger){
|
||||
|
||||
echo nl2br($msg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanup($bind,$sql="") {
|
||||
if(!is_array($bind)) {
|
||||
if(!empty($bind))
|
||||
$bind = array($bind);
|
||||
else
|
||||
$bind = array();
|
||||
}
|
||||
|
||||
foreach($bind as $key=>$val){
|
||||
if(str_replace($key,"",$sql)==$sql) unset($bind[$key]);
|
||||
}
|
||||
return $bind;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function single($sql,$bind="") {
|
||||
$this->sql = trim($sql);
|
||||
$this->bind = $this->cleanup($bind,$sql);
|
||||
$this->error = "";
|
||||
try {
|
||||
$pdostmt = $this->prepare($this->sql);
|
||||
if($pdostmt->execute($this->bind) !== false) {
|
||||
return $pdostmt->fetchColumn();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$this->error = $e->getMessage();
|
||||
$this->debug();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function run($sql, $bind="") {
|
||||
$this->sql = trim($sql);
|
||||
$this->bind = $this->cleanup($bind,$sql);
|
||||
$this->error = "";
|
||||
|
||||
try {
|
||||
$pdostmt = $this->prepare($this->sql);
|
||||
if($pdostmt->execute($this->bind) !== false) {
|
||||
if(preg_match("/^(" . implode("|", array("select", "describe", "pragma")) . ") /i", $this->sql))
|
||||
return $pdostmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
elseif(preg_match("/^(" . implode("|", array("delete", "insert", "update")) . ") /i", $this->sql))
|
||||
return $pdostmt->rowCount();
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
$this->error = $e->getMessage();
|
||||
$this->debug();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function row($sql,$bind=""){
|
||||
$query=$this->run($sql,$bind);
|
||||
if(count($query)==0) return false;
|
||||
if(count($query)>1) return $query;
|
||||
if(count($query)==1){
|
||||
foreach($query as $row) $result=$row;
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
212
include/functions.inc.php
Executable file
212
include/functions.inc.php
Executable file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
function san($a,$b=""){
|
||||
$a = preg_replace("/[^a-zA-Z0-9".$b."]/", "", $a);
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
function api_err($data){
|
||||
global $_config;
|
||||
echo json_encode(array("status"=>"error","data"=>$data, "coin"=>$_config['coin']));
|
||||
exit;
|
||||
}
|
||||
function api_echo($data){
|
||||
global $_config;
|
||||
echo json_encode(array("status"=>"ok","data"=>$data, "coin"=>$_config['coin']));
|
||||
exit;
|
||||
}
|
||||
|
||||
function _log($data){
|
||||
$date=date("[Y-m-d H:s:]");
|
||||
$trace=debug_backtrace();
|
||||
$location=$trace[1]['class'].'->'.$trace[1]['function'].'()';
|
||||
//echo "$date [$location] $data\n";
|
||||
}
|
||||
|
||||
function pem2hex ($data) {
|
||||
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
|
||||
$data=str_replace("-----END PUBLIC KEY-----","",$data);
|
||||
$data=str_replace("-----BEGIN EC PRIVATE KEY-----","",$data);
|
||||
$data=str_replace("-----END EC PRIVATE KEY-----","",$data);
|
||||
$data=str_replace("\n","",$data);
|
||||
$data=base64_decode($data);
|
||||
$data=bin2hex($data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
function hex2pem ($data, $is_private_key=false) {
|
||||
$data=hex2bin($data);
|
||||
$data=base64_encode($data);
|
||||
if($is_private_key) return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----";
|
||||
return "-----BEGIN PUBLIC KEY-----\n".$data."\n-----END PUBLIC KEY-----";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//all credits for this base58 functions should go to tuupola / https://github.com/tuupola/base58/
|
||||
function baseConvert(array $source, $source_base, $target_base)
|
||||
{
|
||||
$result = [];
|
||||
while ($count = count($source)) {
|
||||
$quotient = [];
|
||||
$remainder = 0;
|
||||
for ($i = 0; $i !== $count; $i++) {
|
||||
$accumulator = $source[$i] + $remainder * $source_base;
|
||||
$digit = (integer) ($accumulator / $target_base);
|
||||
$remainder = $accumulator % $target_base;
|
||||
if (count($quotient) || $digit) {
|
||||
array_push($quotient, $digit);
|
||||
};
|
||||
}
|
||||
array_unshift($result, $remainder);
|
||||
$source = $quotient;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
function base58_encode($data)
|
||||
{
|
||||
if (is_integer($data)) {
|
||||
$data = [$data];
|
||||
} else {
|
||||
$data = str_split($data);
|
||||
$data = array_map(function ($character) {
|
||||
return ord($character);
|
||||
}, $data);
|
||||
}
|
||||
|
||||
|
||||
$converted = baseConvert($data, 256, 58);
|
||||
|
||||
return implode("", array_map(function ($index) {
|
||||
$chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
return $chars[$index];
|
||||
}, $converted));
|
||||
}
|
||||
function base58_decode($data, $integer = false)
|
||||
{
|
||||
$data = str_split($data);
|
||||
$data = array_map(function ($character) {
|
||||
$chars="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
return strpos($chars, $character);
|
||||
}, $data);
|
||||
/* Return as integer when requested. */
|
||||
if ($integer) {
|
||||
$converted = baseConvert($data, 58, 10);
|
||||
return (integer) implode("", $converted);
|
||||
}
|
||||
$converted = baseConvert($data, 58, 256);
|
||||
return implode("", array_map(function ($ascii) {
|
||||
return chr($ascii);
|
||||
}, $converted));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function pem2coin ($data) {
|
||||
$data=str_replace("-----BEGIN PUBLIC KEY-----","",$data);
|
||||
$data=str_replace("-----END PUBLIC KEY-----","",$data);
|
||||
$data=str_replace("-----BEGIN EC PRIVATE KEY-----","",$data);
|
||||
$data=str_replace("-----END EC PRIVATE KEY-----","",$data);
|
||||
$data=str_replace("\n","",$data);
|
||||
$data=base64_decode($data);
|
||||
|
||||
|
||||
return base58_encode($data);
|
||||
|
||||
}
|
||||
|
||||
function coin2pem ($data, $is_private_key=false) {
|
||||
|
||||
|
||||
|
||||
$data=base58_decode($data);
|
||||
$data=base64_encode($data);
|
||||
|
||||
$dat=str_split($data,64);
|
||||
$data=implode("\n",$dat);
|
||||
|
||||
if($is_private_key) return "-----BEGIN EC PRIVATE KEY-----\n".$data."\n-----END EC PRIVATE KEY-----\n";
|
||||
return "-----BEGIN PUBLIC KEY-----\n".$data."\n-----END PUBLIC KEY-----\n";
|
||||
}
|
||||
|
||||
|
||||
function ec_sign($data, $key){
|
||||
|
||||
$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);
|
||||
|
||||
|
||||
|
||||
return base58_encode($signature);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function ec_verify($data, $signature, $key){
|
||||
|
||||
|
||||
|
||||
$public_key=coin2pem($key);
|
||||
|
||||
$signature=base58_decode($signature);
|
||||
|
||||
$pkey=openssl_pkey_get_public($public_key);
|
||||
|
||||
$res=openssl_verify($data,$signature,$pkey,OPENSSL_ALGO_SHA256);
|
||||
|
||||
|
||||
if($res===1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function peer_post($url, $data=array()){
|
||||
global $_config;
|
||||
$postdata = http_build_query(
|
||||
array(
|
||||
'data' => json_encode($data),
|
||||
"coin"=>$_config['coin']
|
||||
)
|
||||
);
|
||||
|
||||
$opts = array('http' =>
|
||||
array(
|
||||
'timeout' => "60",
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
)
|
||||
);
|
||||
|
||||
$context = stream_context_create($opts);
|
||||
|
||||
$result = file_get_contents($url, false, $context);
|
||||
|
||||
$res=json_decode($result,true);
|
||||
if($res['status']!="ok"||$res['coin']!=$_config['coin']) return false;
|
||||
return $res['data'];
|
||||
}
|
||||
|
||||
|
||||
function hex2coin($hex){
|
||||
|
||||
$data=hex2bin($hex);
|
||||
return base58_encode($data);
|
||||
}
|
||||
function coin2hex($data){
|
||||
|
||||
$bin= base58_decode($data);
|
||||
return bin2hex($bin);
|
||||
}
|
||||
?>
|
||||
71
include/init.inc.php
Executable file
71
include/init.inc.php
Executable file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
define("VERSION", "0.1a");
|
||||
|
||||
date_default_timezone_set("Europe/Amsterdam");
|
||||
|
||||
|
||||
//error_reporting(E_ALL & ~E_NOTICE);
|
||||
error_reporting(0);
|
||||
ini_set('display_errors',"off");
|
||||
require_once("include/config.inc.php");
|
||||
require_once("include/db.inc.php");
|
||||
require_once("include/functions.inc.php");
|
||||
require_once("include/block.inc.php");
|
||||
require_once("include/account.inc.php");
|
||||
require_once("include/transaction.inc.php");
|
||||
|
||||
if($_config['db_pass']=="ENTER-DB-PASS") die("Please update your config file and set your db password");
|
||||
// initial DB connection
|
||||
$db=new DB($_config['db_connect'],$_config['db_user'],$_config['db_pass'],0);
|
||||
if(!$db) die("Could not connect to the DB backend.");
|
||||
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(floatval(phpversion())<7.1) api_err("The minimum php version required is 7.1");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Getting extra configs from the database
|
||||
$query=$db->run("SELECT cfg, val FROM config");
|
||||
foreach($query as $res){
|
||||
$_config[$res['cfg']]=trim($res['val']);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if($_config['maintenance']==1) api_err("under-maintenance");
|
||||
|
||||
|
||||
if(file_exists("tmp/db-update")){
|
||||
|
||||
$res=unlink("tmp/db-update");
|
||||
if($res){
|
||||
require_once("include/schema.inc.php");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if($_config['dbversion']<2) exit;
|
||||
|
||||
if($_config['testnet']==true) $_config['coin'].="-testnet";
|
||||
|
||||
$hostname=(!empty($_SERVER['HTTPS'])?'https':'http')."://".$_SERVER['HTTP_HOST'];
|
||||
if($_SERVER['SERVER_PORT']!=80&&$_SERVER['SERVER_PORT']!=443) $hostname.=":".$_SERVER['SERVER_PORT'];
|
||||
|
||||
if($hostname!=$_config['hostname']&&$_SERVER['HTTP_HOST']!="localhost"&&$_SERVER['HTTP_HOST']!="127.0.0.1"&&$_SERVER['hostname']!='::1'&&php_sapi_name() !== 'cli'){
|
||||
$db->run("UPDATE config SET val=:hostname WHERE cfg='hostname' LIMIT 1",array(":hostname"=>$hostname));
|
||||
$_config['hostname']=$hostname;
|
||||
}
|
||||
if(empty($_config['hostname'])||$_config['hostname']=="http://"||$_config['hostname']=="https://") api_err("Invalid hostname");
|
||||
|
||||
|
||||
$t=time();
|
||||
if($t-$_config['sanity_last']>$_config['sanity_interval']&& php_sapi_name() !== 'cli') system("php sanity.php &>>/dev/null &");
|
||||
|
||||
|
||||
?>
|
||||
142
include/schema.inc.php
Executable file
142
include/schema.inc.php
Executable file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
|
||||
$dbversion=intval($_config['dbversion']);
|
||||
$db->beginTransaction();
|
||||
if($dbversion==0){
|
||||
$db->run("
|
||||
CREATE TABLE `accounts` (
|
||||
`id` varbinary(128) NOT NULL,
|
||||
`public_key` varbinary(1024) NOT NULL,
|
||||
`block` varbinary(128) NOT NULL,
|
||||
`balance` decimal(20,8) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPACT;");
|
||||
|
||||
$db->run("CREATE TABLE `blocks` (
|
||||
`id` varbinary(128) NOT NULL,
|
||||
`generator` varbinary(128) NOT NULL,
|
||||
`height` int(11) NOT NULL,
|
||||
`date` int(11) NOT NULL,
|
||||
`nonce` varbinary(128) NOT NULL,
|
||||
`signature` varbinary(256) NOT NULL,
|
||||
`difficulty` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`argon` varbinary(128) NOT NULL,
|
||||
`transactions` INT NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
$db->run("CREATE TABLE `config` (
|
||||
`cfg` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`val` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
|
||||
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
|
||||
('hostname', '');");
|
||||
|
||||
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES
|
||||
('dbversion', '1');");
|
||||
|
||||
$db->run("CREATE TABLE `mempool` (
|
||||
`id` varbinary(128) NOT NULL,
|
||||
`height` int(11) NOT NULL,
|
||||
`src` varbinary(128) NOT NULL,
|
||||
`dst` varbinary(128) NOT NULL,
|
||||
`val` decimal(20,8) NOT NULL,
|
||||
`fee` decimal(20,8) NOT NULL,
|
||||
`signature` varbinary(256) NOT NULL,
|
||||
`version` tinyint(4) NOT NULL,
|
||||
`message` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '',
|
||||
`public_key` varbinary(1024) NOT NULL,
|
||||
`date` bigint(20) NOT NULL,
|
||||
`peer` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
$db->run("CREATE TABLE `peers` (
|
||||
`id` int(11) NOT NULL,
|
||||
`hostname` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||
`blacklisted` int(11) NOT NULL DEFAULT 0,
|
||||
`ping` int(11) NOT NULL,
|
||||
`reserve` tinyint(4) NOT NULL DEFAULT 1,
|
||||
`ip` varchar(45) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
|
||||
$db->run("CREATE TABLE `transactions` (
|
||||
`id` varbinary(128) NOT NULL,
|
||||
`block` varbinary(128) NOT NULL,
|
||||
`height` int(11) NOT NULL,
|
||||
`dst` varbinary(128) NOT NULL,
|
||||
`val` decimal(20,8) NOT NULL,
|
||||
`fee` decimal(20,8) NOT NULL,
|
||||
`signature` varbinary(256) NOT NULL,
|
||||
`version` tinyint(4) NOT NULL,
|
||||
`message` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT '',
|
||||
`date` int(11) NOT NULL,
|
||||
`public_key` varbinary(1024) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");
|
||||
|
||||
$db->run("ALTER TABLE `peers`
|
||||
ADD PRIMARY KEY (`id`);");
|
||||
$db->run("ALTER TABLE `peers`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;");
|
||||
|
||||
$db->run("ALTER TABLE `accounts`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `accounts` (`block`);");
|
||||
|
||||
$db->run("ALTER TABLE `blocks`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `height` (`height`);");
|
||||
|
||||
$db->run("ALTER TABLE `config` ADD PRIMARY KEY (`cfg`);");
|
||||
|
||||
$db->run("ALTER TABLE `mempool`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `height` (`height`);");
|
||||
|
||||
$db->run("ALTER TABLE `peers`
|
||||
ADD UNIQUE KEY `hostname` (`hostname`),
|
||||
ADD UNIQUE KEY `ip` (`ip`),
|
||||
ADD KEY `blacklisted` (`blacklisted`),
|
||||
ADD KEY `ping` (`ping`),
|
||||
ADD KEY `reserve` (`reserve`);");
|
||||
|
||||
$db->run("ALTER TABLE `transactions`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `block_id` (`block`);");
|
||||
|
||||
$db->run("ALTER TABLE `accounts`
|
||||
ADD CONSTRAINT `accounts` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
|
||||
|
||||
$db->run("ALTER TABLE `transactions`
|
||||
ADD CONSTRAINT `block_id` FOREIGN KEY (`block`) REFERENCES `blocks` (`id`) ON DELETE CASCADE;");
|
||||
|
||||
$dbversion++;
|
||||
}
|
||||
if($dbversion==1){
|
||||
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_last', '0');");
|
||||
$dbversion++;
|
||||
}
|
||||
if($dbversion==2){
|
||||
$db->run("INSERT INTO `config` (`cfg`, `val`) VALUES ('sanity_sync', '0');");
|
||||
$dbversion++;
|
||||
}
|
||||
if($dbversion==3){
|
||||
$dbversion++;
|
||||
}
|
||||
|
||||
if($dbversion==4){
|
||||
$db->run("ALTER TABLE `mempool` ADD INDEX(`src`);");
|
||||
$db->run("ALTER TABLE `mempool` ADD INDEX(`peer`); ");
|
||||
$db->run("ALTER TABLE `mempool` ADD INDEX(`val`); ");
|
||||
$dbversion++;
|
||||
}
|
||||
if($dbversion==5){
|
||||
$db->run("ALTER TABLE `peers` ADD `fails` TINYINT NOT NULL DEFAULT '0' AFTER `ip`; ");
|
||||
$dbversion++;
|
||||
}
|
||||
if($dbversion!=$_config['dbversion']) $db->run("UPDATE config SET val=:val WHERE cfg='dbversion'",array(":val"=>$dbversion));
|
||||
$db->commit();
|
||||
|
||||
|
||||
?>
|
||||
205
include/transaction.inc.php
Executable file
205
include/transaction.inc.php
Executable file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
class Transaction {
|
||||
|
||||
|
||||
public function reverse($block){
|
||||
global $db;
|
||||
$acc=new Account;
|
||||
$r=$db->run("SELECT * FROM transactions WHERE block=:block",array(":block"=>$block));
|
||||
foreach($r as $x){
|
||||
if(empty($x['src'])) $x['src']=$acc->get_address($x['public_key']);
|
||||
$db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val']));
|
||||
if($x['version']>0) $db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['src'], ":val"=>$x['val']+$x['fee']));
|
||||
|
||||
if($x['version']>0) $this->add_mempool($x);
|
||||
$res= $db->run("DELETE FROM transactions WHERE id=:id",array(":id"=>$x['id']));
|
||||
if($res!=1) return false;
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public function mempool($max){
|
||||
global $db;
|
||||
$block=new Block;
|
||||
$current=$block->current();
|
||||
$height=$current['height']+1;
|
||||
$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'])){
|
||||
_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)){
|
||||
var_dump($trans);
|
||||
_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;
|
||||
}
|
||||
}
|
||||
ksort($transactions);
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
public function add_mempool($x, $peer=""){
|
||||
global $db;
|
||||
$block= new Block;
|
||||
$current=$block->current();
|
||||
$height=$current['height'];
|
||||
$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']);
|
||||
$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;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function add($block,$height, $x){
|
||||
global $db;
|
||||
$acc= new Account;
|
||||
$acc->add($x['public_key'], $block);
|
||||
$acc->add_id($x['dst'],$block);
|
||||
$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']);
|
||||
$res=$db->run("INSERT into transactions SET id=:id, public_key=:public_key, block=:block, height=:height, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date",$bind);
|
||||
if($res!=1) return false;
|
||||
$db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id",array(":id"=>$x['dst'], ":val"=>$x['val']));
|
||||
if($x['version']>0) $db->run("UPDATE accounts SET balance=balance-:val WHERE id=:id",array(":id"=>$x['src'], ":val"=>$x['val']+$x['fee']));
|
||||
$db->run("DELETE FROM mempool WHERE id=:id",array(":id"=>$x['id']));
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function hash($x){
|
||||
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']."-".$x['signature'];
|
||||
$hash= hash("sha512",$info);
|
||||
return hex2coin($hash);
|
||||
}
|
||||
|
||||
|
||||
public function check($x){
|
||||
|
||||
$acc= new Account;
|
||||
$info=$x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date'];
|
||||
|
||||
if($x['val']<0){ _log("$x[id] - Value below 0"); return false; }
|
||||
if($x['fee']<0) { _log("$x[id] - Fee below 0"); return false; }
|
||||
|
||||
$fee=$x['val']*0.0025;
|
||||
if($fee<0.00000001) $fee=0.00000001;
|
||||
if($fee!=$x['fee']) { _log("$x[id] - Fee not 0.25%"); return false; }
|
||||
|
||||
if(!$acc->valid($x['dst'])) { _log("$x[id] - Invalid destination address"); 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; }
|
||||
|
||||
if(strlen($x['public_key'])<15) { _log("$x[id] - Invalid public key size"); return false; }
|
||||
if($x['date']<1511725068) { _log("$x[id] - Date before genesis"); return false; }
|
||||
if($x['date']>time()+86400) { _log("$x[id] - Date in the future"); return false; }
|
||||
|
||||
$id=$this->hash($x);
|
||||
if($x['id']!=$id) { _log("$x[id] - Invalid hash"); return false; }
|
||||
|
||||
|
||||
if(!$acc->check_signature($info, $x['signature'], $x['public_key'])) { _log("$x[id] - Invalid signature"); return false; }
|
||||
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function export($id){
|
||||
global $db;
|
||||
$r=$db->row("SELECT * FROM mempool WHERE id=:id",array(":id"=>$id));
|
||||
//unset($r['peer']);
|
||||
return $r;
|
||||
|
||||
}
|
||||
public function get_transaction($id){
|
||||
global $db;
|
||||
$block=new Block;
|
||||
$current=$block->current();
|
||||
$acc=new Account;
|
||||
$x=$db->row("SELECT * FROM transactions WHERE id=:id",array(":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";
|
||||
elseif($x['version']==1){
|
||||
if($x['dst']==$id) $trans['type']="credit";
|
||||
else $trans['type']="debit";
|
||||
} else {
|
||||
$trans['type']="other";
|
||||
}
|
||||
ksort($trans);
|
||||
return $trans;
|
||||
|
||||
}
|
||||
|
||||
public function get_mempool_transaction($id){
|
||||
global $db;
|
||||
$x=$db->row("SELECT * FROM mempool WHERE id=:id",array(":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']=$x['src'];
|
||||
|
||||
$trans['type']="mempool";
|
||||
$trans['confirmations']=-1;
|
||||
ksort($trans);
|
||||
return $trans;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
37
index.php
Executable file
37
index.php
Executable file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
require_once("include/init.inc.php");
|
||||
$block=new Block;
|
||||
$current=$block->current();
|
||||
|
||||
echo "<h3>Arionum Node</h3>";
|
||||
echo "System check complete.<br><br> Current block: $current[height]";
|
||||
|
||||
|
||||
|
||||
?>
|
||||
68
mine.php
Executable file
68
mine.php
Executable file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
require_once("include/init.inc.php");
|
||||
$block=new Block();
|
||||
$acc=new Account();
|
||||
set_time_limit(360);
|
||||
$q=$_GET['q'];
|
||||
|
||||
$ip=$_SERVER['REMOTE_ADDR'];
|
||||
if(!in_array($ip,$_config['allowed_hosts'])) api_err("unauthorized");
|
||||
|
||||
if($q=="info"){
|
||||
$diff=$block->difficulty();
|
||||
$current=$block->current();
|
||||
api_echo(array("difficulty"=>$diff, "block"=>$current['id']));
|
||||
exit;
|
||||
} elseif($q=="submitNonce"){
|
||||
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']);
|
||||
|
||||
$result=$block->mine($public_key, $nonce, $argon);
|
||||
|
||||
if($result) {
|
||||
|
||||
$res=$block->forge($nonce,$argon, $public_key, $private_key);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if($res){
|
||||
$current=$block->current();
|
||||
system("php propagate.php block $current[id] &>/dev/null &");
|
||||
api_echo("accepted");
|
||||
}
|
||||
}
|
||||
api_err("rejected");
|
||||
} else {
|
||||
api_err("invalid command");
|
||||
}
|
||||
|
||||
?>
|
||||
164
peer.php
Executable file
164
peer.php
Executable file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
require_once("include/init.inc.php");
|
||||
$trx = new Transaction;
|
||||
$block=new Block;
|
||||
$q=$_GET['q'];
|
||||
if(!empty($_POST['data'])){
|
||||
$data=json_decode(trim($_POST['data']),true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if($_POST['coin']!=$_config['coin']) api_err("Invalid coin");
|
||||
$ip=$_SERVER['REMOTE_ADDR'];
|
||||
if($q=="peer"){
|
||||
|
||||
$hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL);
|
||||
|
||||
if (!filter_var($hostname, FILTER_VALIDATE_URL)) api_err("invalid-hostname");
|
||||
|
||||
$res=$db->single("SELECT COUNT(1) FROM peers WHERE hostname=:hostname AND ip=:ip",array(":hostname"=>$hostname,":ip"=>$ip));
|
||||
|
||||
if($res==1) api_echo("peer-ok-already");
|
||||
|
||||
$res=$db->single("SELECT COUNT(1) FROM peers WHERE blacklisted<UNIX_TIMESTAMP() AND ping >UNIX_TIMESTAMP()-86400 AND reserve=0");
|
||||
$reserve=1;
|
||||
if($res<$_config['max_peers']) $reserve=0;
|
||||
$db->run("INSERT ignore INTO peers SET hostname=:hostname, reserve=:reserve, ping=UNIX_TIMESTAMP(), ip=:ip ON DUPLICATE KEY UPDATE hostname=:hostname2",array(":ip"=>$ip, ":hostname2"=>$hostname,":hostname"=>$hostname, ":reserve"=>$reserve));
|
||||
|
||||
$res=peer_post($hostname."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
|
||||
if($res!==false) api_echo("re-peer-ok");
|
||||
else api_err("re-peer failed - $result");
|
||||
}
|
||||
elseif($q=="ping"){
|
||||
api_echo("pong");
|
||||
}
|
||||
|
||||
|
||||
elseif($q=="submitTransaction"){
|
||||
if($_config['sanity_sync']==1) api_err("sanity-sync");
|
||||
|
||||
$data['id']=san($data['id']);
|
||||
|
||||
if(!$trx->check($data)) api_err("Invalid transaction");
|
||||
$hash=$data['id'];
|
||||
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE id=:id",array(":id"=>$hash));
|
||||
if($res!=0) api_err("The transaction is already in mempool");
|
||||
|
||||
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE src=:src",array(":src"=>$data['src']));
|
||||
if($res>25) api_err("Too many transactions from this address in mempool. Please rebroadcast later.");
|
||||
$res=$db->single("SELECT COUNT(1) FROM mempool WHERE peer=:peer",array(":peer"=>$_SERVER['REMOTE_ADDR']));
|
||||
if($res>$_config['peer_max_mempool']) api_error("Too many transactions broadcasted from this peer");
|
||||
|
||||
|
||||
|
||||
$res=$db->single("SELECT COUNT(1) FROM transactions WHERE id=:id",array(":id"=>$hash));
|
||||
if($res!=0) api_err("The transaction is already in a block");
|
||||
$acc=new Account;
|
||||
$src=$acc->get_address($data['public_key']);
|
||||
|
||||
$balance=$db->single("SELECT balance FROM accounts WHERE id=:id",array(":id"=>$src));
|
||||
if($balance<$val+$fee) api_err("Not enough funds");
|
||||
|
||||
|
||||
$memspent=$db->single("SELECT SUM(val+fee) FROM mempool WHERE src=:src",array(":src"=>$src));
|
||||
if($balance-$memspent<$val+$fee) api_err("Not enough funds (mempool)");
|
||||
$trx->add_mempool($data, $_SERVER['REMOTE_ADDR']);
|
||||
|
||||
$res=$db->row("SELECT COUNT(1) as c, sum(val) as v FROM mempool ",array(":src"=>$data['src']));
|
||||
if($res['c']<$_config['max_mempool_rebroadcast']&&$res['v']/$res['c']<$data['val']) system("php propagate.php transaction $data[id] &>/dev/null &");
|
||||
api_echo("transaction-ok");
|
||||
}
|
||||
elseif($q=="submitBlock"){
|
||||
if($_config['sanity_sync']==1) api_err("sanity-sync");
|
||||
$data['id']=san($data['id']);
|
||||
$current=$block->current();
|
||||
if($current['id']==$data['id']) api_echo("block-ok");
|
||||
if($current['height']==$data['height']&&$current['id']!=$data['id']){
|
||||
$accept_new=false;
|
||||
if($current['transactions']<$data['transactions']){
|
||||
$accept_new=true;
|
||||
} elseif($current['transactions']==$data['transactions']) {
|
||||
$no1=hexdec(substr(coin2hex($current['id']),0,12));
|
||||
$no2=hexdec(substr(coin2hex($data['id']),0,12));
|
||||
if(gmp_cmp($no1,$no2)==1){
|
||||
$accept_new=true;
|
||||
}
|
||||
}
|
||||
if($accept_new){
|
||||
system("php sanity.php microsanity $ip &>/dev/null &");
|
||||
api_echo("microsanity");
|
||||
}
|
||||
}
|
||||
|
||||
if($current['height']!=$data['height']-1) {
|
||||
if($data['height']<$current['height']) api_err("block-too-old");
|
||||
if($data['height']-$current['height']>30) api_err("block-out-of-sync");
|
||||
api_echo(array("request"=>"microsync","height"=>$current['height'], "block"=>$current['id']));
|
||||
|
||||
}
|
||||
if(!$block->check($data)) api_err("invalid-block");
|
||||
$b=$data;
|
||||
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
|
||||
|
||||
if(!$res) api_err("invalid-block-data");
|
||||
api_echo("block-ok");
|
||||
|
||||
system("php propagate.php block $data[id] &>/dev/null &");
|
||||
|
||||
}
|
||||
|
||||
elseif($q=="currentBlock"){
|
||||
$current=$block->current();
|
||||
api_echo($current);
|
||||
}
|
||||
elseif($q=="getBlock"){
|
||||
$height=intval($data['height']);
|
||||
|
||||
$export=$block->export("",$height);
|
||||
if(!$export) api_err("invalid-block");
|
||||
api_echo($export);
|
||||
}
|
||||
elseif($q=="getBlocks"){
|
||||
$height=intval($data['height']);
|
||||
|
||||
$r=$db->run("SELECT id,height FROM blocks WHERE height>=:height ORDER by height ASC LIMIT 100",array(":height"=>$height));
|
||||
foreach($r as $x){
|
||||
$blocks[$x['height']]=$block->export($x['id']);
|
||||
}
|
||||
api_echo($blocks);
|
||||
|
||||
}
|
||||
|
||||
elseif($q=="getPeers"){
|
||||
$peers=$db->run("SELECT ip,hostname FROM peers ORDER by RAND()");
|
||||
api_echo($peers);
|
||||
} else {
|
||||
api_err("Invalid request");
|
||||
}
|
||||
|
||||
?>
|
||||
106
propagate.php
Executable file
106
propagate.php
Executable file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
set_time_limit(360);
|
||||
require_once("include/init.inc.php");
|
||||
$block= new Block();
|
||||
$type=san($argv[1]);
|
||||
|
||||
$id=san($argv[2]);
|
||||
|
||||
$peer=san(trim($argv[3]));
|
||||
if(empty($peer)&&$type=="block"){
|
||||
$whr="";
|
||||
|
||||
$data=$block->export($id);
|
||||
|
||||
if($data===false||empty($data)) die("Could not export block");
|
||||
$data=json_encode($data);
|
||||
// cache it to reduce the load
|
||||
$res=file_put_contents("tmp/$id",$data);
|
||||
if($res===false) die("Could not write the cachce file");
|
||||
$r=$db->run("SELECT * FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0");
|
||||
foreach($r as $x) {
|
||||
$host=base58_encode($x['hostname']);
|
||||
system("php propagate.php $type $id $host &>/dev/null &");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if($type=="block"){
|
||||
|
||||
|
||||
$data=file_get_contents("tmp/$id");
|
||||
if(empty($data)) { echo "Invalid Block data"; exit; }
|
||||
$data=json_decode($data,true);
|
||||
$hostname=base58_decode($peer);
|
||||
|
||||
echo "Peer response - $hostname:\n";
|
||||
$response= peer_post($hostname."/peer.php?q=submitBlock",$data);
|
||||
if($response=="block-ok") { echo "Block $i accepted. Exiting.\n"; exit;}
|
||||
elseif($response['request']=="microsync"){
|
||||
echo "Microsync request\n";
|
||||
$height=intval($response['height']);
|
||||
$bl=san($response['block']);
|
||||
$current=$block->current();
|
||||
if($current['height']-$height>10) { echo "Height Differece too high\n"; exit; }
|
||||
$last_block=$block->get($height);
|
||||
|
||||
if ($last_block['id'] != $bl ) { echo "Last block does not match\n"; exit; }
|
||||
echo "Sending the requested blocks\n";
|
||||
|
||||
for($i=$height+1;$i<=$current['height'];$i++){
|
||||
$data=$block->export("",$i);
|
||||
$response = peer_post($hostname."/peer.php?q=submitBlock",$data);
|
||||
|
||||
if($response!="block-ok") { echo "Block $i not accepted. Exiting.\n"; exit;}
|
||||
echo "Block\t$i\t accepted\n";
|
||||
}
|
||||
|
||||
}
|
||||
else echo "Block not accepted!\n";
|
||||
|
||||
}
|
||||
if($type=="transaction"){
|
||||
|
||||
$trx=new Transaction;
|
||||
|
||||
$data=$trx->export($id);
|
||||
|
||||
if(!$data){ echo "Invalid transaction id\n"; exit; }
|
||||
|
||||
if($data['peer']=="local") $r=$db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP()");
|
||||
else $r=$db->run("SELECT hostname FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 ORDER by RAND() LIMIT ".$_config['transaction_propagation_peers']);
|
||||
foreach($r as $x){
|
||||
$res= peer_post($x['hostname']."/peer.php?q=submitTransaction",$data);
|
||||
if(!$res) echo "Transaction not accepted\n";
|
||||
else echo "Transaction accepted\n";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
354
sanity.php
Executable file
354
sanity.php
Executable file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
set_time_limit(0);
|
||||
if(php_sapi_name() !== 'cli') die("This should only be run as cli");
|
||||
|
||||
if(file_exists("tmp/sanity-lock")){
|
||||
|
||||
$pid_time=filemtime("tmp/sanity-lock");
|
||||
if(time()-$pid_time>3600){
|
||||
@unlink("tmp/sanity-lock");
|
||||
}
|
||||
die("Sanity lock in place");
|
||||
}
|
||||
$lock = fopen("tmp/sanity-lock", "w");
|
||||
fclose($lock);
|
||||
$arg=trim($argv[1]);
|
||||
$arg2=trim($argv[2]);
|
||||
|
||||
if($arg!="microsanity") sleep(10);
|
||||
|
||||
|
||||
require_once("include/init.inc.php");
|
||||
|
||||
|
||||
if($_config['dbversion']<2){
|
||||
die("DB schema not created");
|
||||
@unlink("tmp/sanity-lock");
|
||||
exit;
|
||||
}
|
||||
|
||||
$block=new Block();
|
||||
$acc=new Account();
|
||||
$current=$block->current();
|
||||
|
||||
|
||||
$microsanity=false;
|
||||
if($arg=="microsanity"&&!empty($arg2)){
|
||||
|
||||
do {
|
||||
$x=$db->row("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP() AND ip=:ip",array(":ip"=>$arg2));
|
||||
|
||||
if(!$x){ echo "Invalid node - $arg2\n"; break; }
|
||||
$url=$x['hostname']."/peer.php?q=";
|
||||
$data=peer_post($url."getBlock",array("height"=>$current['height']));
|
||||
|
||||
if(!$data) {echo "Invalid getBlock result\n"; break; }
|
||||
if($data['id']==$current['id']) {echo "Same block\n"; break;}
|
||||
|
||||
if($current['transactions']>$data['transactions']){
|
||||
echo "Block has less transactions\n";
|
||||
break;
|
||||
} elseif($current['transactions']==$data['transactions']) {
|
||||
$no1=hexdec(substr(coin2hex($current['id']),0,12));
|
||||
$no2=hexdec(substr(coin2hex($data['id']),0,12));
|
||||
|
||||
if(gmp_cmp($no1,$no2)!=-1){
|
||||
echo "Block hex larger than current\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$prev = $block->get($current['height']-1);
|
||||
|
||||
$public=$acc->public_key($data['generator']);
|
||||
if(!$block->mine($public, $data['nonce'],$data['argon'],$block->difficulty($current['height']-1),$prev['id'])) { echo "Invalid prev-block\n"; break;}
|
||||
$block->pop(1);
|
||||
if(!$block->check($data)) break;
|
||||
|
||||
|
||||
echo "Starting to sync last block from $x[hostname]\n";
|
||||
$b=$data;
|
||||
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
|
||||
if(!$res) {
|
||||
|
||||
_log("Block add: could not add block - $b[id] - $b[height]");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_log("Synced block from $host - $b[height] $b[difficulty]");
|
||||
|
||||
|
||||
|
||||
|
||||
} while(0);
|
||||
|
||||
@unlink("tmp/sanity-lock");
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$t=time();
|
||||
//if($t-$_config['sanity_last']<300) {@unlink("tmp/sanity-lock"); die("The sanity cron was already run recently"); }
|
||||
$db->run("UPDATE config SET val=:time WHERE cfg='sanity_last'",array(":time"=>$t));
|
||||
$block_peers=array();
|
||||
$longest_size=0;
|
||||
$longest=0;
|
||||
$blocks=array();
|
||||
$blocks_count=array();
|
||||
$most_common="";
|
||||
$most_common_size=0;
|
||||
|
||||
|
||||
// checking peers
|
||||
|
||||
$db->run("DELETE from peers WHERE fails>50");
|
||||
|
||||
$r=$db->run("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP()");
|
||||
|
||||
$total_peers=count($r);
|
||||
|
||||
if($total_peers==0){
|
||||
$i=0;
|
||||
echo "No peers found. Attempting to get peers from arionum.com\n";
|
||||
$f=file("https://www.arionum.com/peers.txt");
|
||||
shuffle($f);
|
||||
if(count($f)<2) die("Could nto connect to arionum.com! Will try later!\n");
|
||||
foreach($f as $peer){
|
||||
$peer=trim($peer);
|
||||
|
||||
$res=peer_post($peer."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
|
||||
if($res!==false) {$i++; echo "Peering OK - $peer\n"; }
|
||||
else echo "Peering FAIL - $peer\n";
|
||||
if($i>$_config['max_peers']) break;
|
||||
}
|
||||
$r=$db->run("SELECT id,hostname FROM peers WHERE reserve=0 AND blacklisted<UNIX_TIMESTAMP()");
|
||||
$total_peers=count($r);
|
||||
if($total_peers==0) die("Could not peer to any peers! Please check internet connectivity!\n");
|
||||
}
|
||||
|
||||
|
||||
foreach($r as $x){
|
||||
$url=$x['hostname']."/peer.php?q=";
|
||||
$data=peer_post($url."getPeers");
|
||||
if($data===false) {
|
||||
|
||||
$db->run("UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id",array(":id"=>$x['id']));
|
||||
continue;
|
||||
}
|
||||
|
||||
$i=0;
|
||||
foreach($data as $peer){
|
||||
if($peer['hostname']==$_config['hostname']) continue;
|
||||
if (!filter_var($peer['hostname'], FILTER_VALIDATE_URL)) continue;
|
||||
|
||||
if(!$db->single("SELECT COUNT(1) FROM peers WHERE ip=:ip or hostname=:hostname",array(":ip"=>$peer['ip'],":hostname"=>$peer['hostname']))){
|
||||
$i++;
|
||||
if($i>$_config['max_test_peers']) break;
|
||||
$test=peer_post($peer['hostname']."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
|
||||
if($test!==false){
|
||||
$total_peers++;
|
||||
echo "Peered with: $peer[hostname]\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$data=peer_post($url."currentBlock");
|
||||
if($data===false) continue;
|
||||
$db->run("UPDATE peers SET fails=0 WHERE id=:id",array(":id"=>$x['id']));
|
||||
|
||||
|
||||
$block_peers[$data['id']][]=$x['hostname'];
|
||||
$blocks_count[$data['id']]++;
|
||||
$blocks[$data['id']]=$data;
|
||||
if($blocks_count[$data['id']]>$most_common_size){
|
||||
$most_common=$data['id'];
|
||||
$most_common_size=$blocks_count[$data['id']];
|
||||
}
|
||||
if($data['height']>$largest_height){
|
||||
$largest_height=$data['height'];
|
||||
$largest_height_block=$data['id'];
|
||||
} elseif($data['height']==$largestblock&&$data['id']!=$largest_height_block){
|
||||
if($data['difficulty']==$blocks[$largest_height_block]['difficulty']){
|
||||
if($most_common==$data['id']){
|
||||
$largest_height=$data['height'];
|
||||
$largest_height_block=$data['id'];
|
||||
} else {
|
||||
if($blocks[$largest_height_block]['transactions']<$data['transactions']){
|
||||
$largest_height=$data['height'];
|
||||
$largest_height_block=$data['id'];
|
||||
} elseif($blocks[$largest_height_block]['transactions']==$data['transactions']) {
|
||||
$no1=hexdec(substr(coin2hex($largest_height_block),0,12));
|
||||
$no2=hexdec(substr(coin2hex($data['id']),0,12));
|
||||
if(gmp_cmp($no1,$no2)==1){
|
||||
$largest_height=$data['height'];
|
||||
$largest_height_block=$data['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif($data['difficulty']<$blocks[$largest_height_block]['difficulty']){
|
||||
$largest_height=$data['height'];
|
||||
$largest_height_block=$data['id'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
echo "Most common: $most_common\n";
|
||||
echo "Most common block: $most_common_size\n";
|
||||
echo "Max height: $largest_height\n";
|
||||
echo "Current block: $current[height]\n";
|
||||
if($current['height']<$largest_height&&$largest_height>1){
|
||||
$db->run("UPDATE config SET val=1 WHERE cfg='sanity_sync'");
|
||||
sleep(10);
|
||||
_log("Longest chain rule triggered - $largest_height - $largest_height_block");
|
||||
$peers=$block_peers[$largest_height_block];
|
||||
shuffle($peers);
|
||||
foreach($peers as $host){
|
||||
_log("Starting to sync from $host");
|
||||
$url=$host."/peer.php?q=";
|
||||
$data=peer_post($url."getBlock",array("height"=>$current['height']));
|
||||
|
||||
if($data===false){ _log("Could not get block from $host - $current[height]"); continue; }
|
||||
|
||||
while($data['id']!=$current['id']){
|
||||
$block->delete($current['height']-10);
|
||||
$current=$block->current();
|
||||
$data=peer_post($url."getBlock",array("height"=>$current['height']));
|
||||
|
||||
if($data===false){_log("Could not get block from $host - $current[height]"); break; }
|
||||
}
|
||||
if($data['id']!=$current['id']) continue;
|
||||
while($current['height']<$largest_height){
|
||||
$data=peer_post($url."getBlocks",array("height"=>$current['height']+1));
|
||||
|
||||
if($data===false){_log("Could not get blocks from $host - height: $current[height]"); break; }
|
||||
$good_peer=true;
|
||||
foreach($data as $b){
|
||||
if(!$block->check($b)){
|
||||
_log("Block check: could not add block - $b[id] - $b[height]");
|
||||
$good_peer=false;
|
||||
break;
|
||||
}
|
||||
$res=$block->add($b['height'], $b['public_key'], $b['nonce'], $b['data'], $b['date'], $b['signature'], $b['difficulty'], $b['reward_signature'], $b['argon']);
|
||||
if(!$res) {
|
||||
|
||||
_log("Block add: could not add block - $b[id] - $b[height]");
|
||||
$good_peer=false;
|
||||
break;
|
||||
}
|
||||
|
||||
_log("Synced block from $host - $b[height] $b[difficulty]");
|
||||
|
||||
}
|
||||
if(!$good_peer) break;
|
||||
$current=$block->current();
|
||||
|
||||
}
|
||||
if($good_peer) break;
|
||||
|
||||
}
|
||||
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'",array(":time"=>$t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//rebroadcasting transactions
|
||||
$forgotten=$current['height']-$_config['sanity_rebroadcast_height'];
|
||||
$r=$db->run("SELECT id FROM mempool WHERE height<:forgotten ORDER by val DESC LIMIT 10",array(":forgotten"=>$forgotten));
|
||||
foreach($r as $x){
|
||||
system("php propagate.php transaction $x[id] &>/dev/null &");
|
||||
$db->run("UPDATE mempool SET height=:current WHERE id=:id",array(":id"=>$x['id'], ":current"=>$current['height']));
|
||||
}
|
||||
|
||||
|
||||
//add new peers if there aren't enough active
|
||||
if($total_peers<$_config['max_peers']*0.7){
|
||||
$res=$_config['max_peers']-$total_peers;
|
||||
$db->run("UPDATE peers SET reserve=0 WHERE reserve=1 AND blacklisted<UNIX_TIMESTAMP() LIMIT $res");
|
||||
}
|
||||
|
||||
//random peer check
|
||||
$r=$db->run("SELECT * FROM peers WHERE blacklisted<UNIX_TIMESTAMP() and reserve=1 LIMIT ".$_config['max_test_peers']);
|
||||
foreach($r as $x){
|
||||
$url=$x['hostname']."/peer.php?q=";
|
||||
$data=peer_post($url."ping");
|
||||
if($data===false) $db->run("UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id",array(":id"=>$x['id']));
|
||||
else $db->run("UPDATE peers SET fails=0 WHERE id=:id",array(":id"=>$x['id']));
|
||||
}
|
||||
|
||||
//clean tmp files
|
||||
$f=scandir("tmp/");
|
||||
$time=time();
|
||||
foreach($f as $x){
|
||||
if(strlen($x)<5&&substr($x,0,1)==".") continue;
|
||||
$pid_time=filemtime("tmp/$x");
|
||||
if($time-$pid_time>7200) @unlink("tmp/$x");
|
||||
}
|
||||
|
||||
|
||||
//recheck blocks
|
||||
if($_config['sanity_recheck_blocks']>0){
|
||||
$blocks=array();
|
||||
$all_blocks_ok=true;
|
||||
$start=$current['height']-$_config['sanity_recheck_blocks'];
|
||||
if($start<2) $start=2;
|
||||
$r=$db->run("SELECT * FROM blocks WHERE height>=:height ORDER by height ASC",array(":height"=>$start));
|
||||
foreach($r as $x){
|
||||
$blocks[$x['height']]=$x;
|
||||
$max_height=$x['height'];
|
||||
}
|
||||
|
||||
for($i=$start+1;$i<=$max_height;$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'])) {
|
||||
$db->run("UPDATE config SET val=1 WHERE cfg='sanity_sync'");
|
||||
_log("Invalid block detected. Deleting everything after $data[height] - $data[id]");
|
||||
sleep(10);
|
||||
$all_blocks_ok=false;
|
||||
$block->delete($i);
|
||||
|
||||
$db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($all_blocks_ok) echo "All checked blocks are ok\n";
|
||||
}
|
||||
|
||||
|
||||
@unlink("tmp/sanity-lock");
|
||||
?>
|
||||
0
tmp/db-update
Normal file
0
tmp/db-update
Normal file
113
util.php
Executable file
113
util.php
Executable file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
Copyright (c) 2018 AroDev
|
||||
|
||||
www.arionum.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
||||
OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
require_once("include/init.inc.php");
|
||||
$cmd=trim($argv[1]);
|
||||
|
||||
|
||||
|
||||
if($cmd=='clean'){
|
||||
$tables=array("blocks","accounts","transactions","mempool");
|
||||
foreach($tables as $table) $db->run("DELETE FROM {$table}");
|
||||
|
||||
echo "\n The database has been cleared\n";
|
||||
|
||||
}
|
||||
|
||||
|
||||
elseif($cmd=='pop'){
|
||||
$no=intval($argv[2]);
|
||||
$block=new Block;
|
||||
$block->pop($no);
|
||||
}
|
||||
|
||||
|
||||
elseif($cmd=='block-time'){
|
||||
$t=time();
|
||||
$r=$db->run("SELECT * FROM blocks ORDER by height DESC LIMIT 100");
|
||||
$start=0;
|
||||
foreach($r as $x){
|
||||
if($start==0) $start=$x['date'];
|
||||
$time=$t-$x['date'];
|
||||
$t=$x['date'];
|
||||
echo "$x[height] -> $time\n";
|
||||
$end=$x['date'];
|
||||
}
|
||||
echo "Average block time: ".ceil(($start-$end)/100)." seconds\n";
|
||||
|
||||
|
||||
}
|
||||
|
||||
elseif($cmd=="peer"){
|
||||
$res=peer_post($argv[2]."/peer.php?q=peer",array("hostname"=>$_config['hostname']));
|
||||
if($res!==false) echo "Peering OK\n";
|
||||
else echo "Peering FAIL\n";
|
||||
}
|
||||
elseif ($cmd=="current") {
|
||||
$block=new Block;
|
||||
var_dump($block->current());
|
||||
} elseif($cmd=="blocks"){
|
||||
$height=intval($argv[2]);
|
||||
$limit=intval($argv[3]);
|
||||
if($limit<1) $limit=100;
|
||||
$r=$db->run("SELECT * FROM blocks WHERE height>:height ORDER by height ASC LIMIT $limit",array(":height"=>$height));
|
||||
foreach($r as $x){
|
||||
echo "$x[height]\t$x[id]\n";
|
||||
}
|
||||
}
|
||||
|
||||
elseif($cmd=="recheck-blocks"){
|
||||
$blocks=array();
|
||||
$block=new Block();
|
||||
$r=$db->run("SELECT * FROM blocks ORDER by height ASC");
|
||||
foreach($r as $x){
|
||||
$blocks[$x['height']]=$x;
|
||||
$max_height=$x['height'];
|
||||
}
|
||||
for($i=2;$i<=$max_height;$i++){
|
||||
$data=$blocks[$i];
|
||||
|
||||
$key=$db->single("SELECT public_key FROM accounts WHERE id=:id",array(":id"=>$data['generator']));
|
||||
|
||||
if(!$block->mine($key,$data['nonce'], $data['argon'], $data['difficulty'], $blocks[$i-1]['id'])) {
|
||||
_log("Invalid block detected. We should delete everything after $data[height] - $data[id]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif($cmd=="peers"){
|
||||
$r=$db->run("SELECT * FROM peers ORDER by reserve ASC LIMIT 100");
|
||||
foreach($r as $x){
|
||||
echo "$x[hostname]\t$x[reserve]\n";
|
||||
}
|
||||
|
||||
} elseif($cmd=="mempool"){
|
||||
$res=$db->single("SELECT COUNT(1) from mempool");
|
||||
echo "Mempool size: $res\n";
|
||||
} else {
|
||||
echo "Invalid command\n";
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user