From 616dde11ea33a7b90a61b0693676648b10956b39 Mon Sep 17 00:00:00 2001 From: Arionum Date: Thu, 30 Aug 2018 01:24:39 +0300 Subject: [PATCH] sync fix --- api.php | 17 ++++ include/account.inc.php | 36 ++++--- include/block.inc.php | 198 +++++++++++++++++++++--------------- include/functions.inc.php | 5 +- include/transaction.inc.php | 85 +++++++++++----- sanity.php | 167 ++++++++++++++++++++++-------- util.php | 97 +++++++++++++----- 7 files changed, 416 insertions(+), 189 deletions(-) diff --git a/api.php b/api.php index aca9d2f..c523bd3 100755 --- a/api.php +++ b/api.php @@ -670,6 +670,23 @@ if ($q == "getAddress") { $account = san($account); api_echo($acc->account2alias($account)); +} elseif ($q=="sanity"){ + $sanity=false; + if(file_exists("tmp/sanity-lock")) { + $sanity=true; + } + $last_sanity=$db->single("SELECT val FROM config WHERE cfg='sanity_last'"); + $sanity_sync=$db->single("SELECT val FROM config WHERE cfg='sanity_sync'"); + api_echo(["sanity_running"=>$sanity,"last_sanity"=>$last_sanity, "sanity_sync"=>$sanity_sync]); +} elseif ($q=="node-info"){ + $dbversion=$db->single("SELECT val FROM config WHERE cfg='dbversion'"); + $hostname=$db->single("SELECT val FROM config WHERE cfg='hostname'"); + $acc=$db->single("SELECT COUNT(1) FROM accounts"); + $tr=$db->single("SELECT COUNT(1) FROM transactions"); + $mns=$db->single("SELECT COUNT(1) FROM masternode"); + $mempool=$db->single("SELECT COUNT(1) FROM mempool"); + api_echo(["hostname"=>$hostname, "version"=>VERSION,"dbversion"=>$dbversion, "accounts"=>$acc, "transactions"=>$tr, "mempool"=>$mempool, "masternodes"=>$mns]); + } else { api_err("Invalid request"); } diff --git a/include/account.inc.php b/include/account.inc.php index 7961736..107d541 100755 --- a/include/account.inc.php +++ b/include/account.inc.php @@ -103,7 +103,7 @@ class Account if (strlen($id)<4||strlen($id)>25) { return false; } - if($orig!=$id){ + if ($orig!=$id) { return false; } @@ -115,12 +115,16 @@ class Account } //check if an account already has an alias - public function has_alias($public_key){ + public function has_alias($public_key) + { global $db; $public_key=san($public_key); - $res=$db->single("SELECT COUNT(1) FROM accounts WHERE public_key=:public_key AND alias IS NOT NULL",[":public_key"=>$public_key]); - if($res!=0) return true; - else return false; + $res=$db->single("SELECT COUNT(1) FROM accounts WHERE public_key=:public_key AND alias IS NOT NULL", [":public_key"=>$public_key]); + if ($res!=0) { + return true; + } else { + return false; + } } //check alias validity @@ -137,25 +141,27 @@ class Account if (strlen($id)<4||strlen($id)>25) { return false; } - if($orig!=$id){ + if ($orig!=$id) { return false; } return $db->single("SELECT COUNT(1) FROM accounts WHERE alias=:alias", [":alias"=>$id]); } //returns the account of an alias - public function alias2account($alias){ + public function alias2account($alias) + { global $db; $alias=strtoupper($alias); - $res=$db->single("SELECT id FROM accounts WHERE alias=:alias LIMIT 1",[":alias"=>$alias]); + $res=$db->single("SELECT id FROM accounts WHERE alias=:alias LIMIT 1", [":alias"=>$alias]); return $res; } //returns the alias of an account - public function account2alias($id){ + public function account2alias($id) + { global $db; $id=san($id); - $res=$db->single("SELECT alias FROM accounts WHERE id=:id LIMIT 1",[":id"=>$id]); + $res=$db->single("SELECT alias FROM accounts WHERE id=:id LIMIT 1", [":id"=>$id]); return $res; } // check the validity of an address. At the moment, it checks only the characters to be base58 and the length to be >=70 and <=128. @@ -212,7 +218,7 @@ class Account $block = new Block(); $current = $block->current(); $public_key = $this->public_key($id); - $alias = $this->account2alias($id); + $alias = $this->account2alias($id); $limit = intval($limit); if ($limit > 100 || $limit < 1) { $limit = 100; @@ -300,11 +306,13 @@ class Account return $res; } - public function get_masternode($public_key){ + public function get_masternode($public_key) + { global $db; $res = $db->row("SELECT * FROM masternode WHERE public_key=:public_key", [":public_key" => $public_key]); - if(empty($res['public_key'])) return false; + if (empty($res['public_key'])) { + return false; + } return $res; } - } diff --git a/include/block.inc.php b/include/block.inc.php index f501877..fb3ab87 100755 --- a/include/block.inc.php +++ b/include/block.inc.php @@ -43,23 +43,22 @@ class Block $msg = ''; - if($height>=80458){ - //reward the masternode + if ($height>=80458) { + //reward the masternode - $mn_winner=$db->single( + $mn_winner=$db->single( "SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 1", [":current"=>$height, ":start"=>$height-360] ); - _log("MN Winner: $mn_winner",2); - if($mn_winner!==false){ - $mn_reward=round(0.33*$reward,8); - $reward=round($reward-$mn_reward,8); - $reward=number_format($reward,8,".",""); - $mn_reward=number_format($mn_reward,8,".",""); - _log("MN Reward: $mn_reward",2); - } - -} + _log("MN Winner: $mn_winner", 2); + if ($mn_winner!==false) { + $mn_reward=round(0.33*$reward, 8); + $reward=round($reward-$mn_reward, 8); + $reward=number_format($reward, 8, ".", ""); + $mn_reward=number_format($mn_reward, 8, ".", ""); + _log("MN Reward: $mn_reward", 2); + } + } @@ -115,11 +114,17 @@ class Block } // insert the reward transaction in the db - $trx->add($hash, $height, $transaction); - -if($mn_winner!==false&&$height>=80458&&$mn_reward>0){ - $db->run("UPDATE accounts SET balance=balance+:bal WHERE public_key=:pub",[":pub"=>$mn_winner, ":bal"=>$mn_reward]); - $bind = [ + $res=$trx->add($hash, $height, $transaction); + if ($res == false) { + // rollback and exit if it fails + _log("Reward DB insert failed"); + $db->rollback(); + $db->exec("UNLOCK TABLES"); + return false; + } + if ($mn_winner!==false&&$height>=80458&&$mn_reward>0) { + $db->run("UPDATE accounts SET balance=balance+:bal WHERE public_key=:pub", [":pub"=>$mn_winner, ":bal"=>$mn_reward]); + $bind = [ ":id" => hex2coin(hash("sha512", "mn".$hash.$height.$mn_winner)), ":public_key" => $public_key, ":height" => $height, @@ -132,13 +137,27 @@ if($mn_winner!==false&&$height>=80458&&$mn_reward>0){ ":date" => $date, ":message" => 'masternode', ]; - $res = $db->run( + $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 ); -$this->reset_fails_masternodes($mn_winner, $height, $hash); - -} + if ($res != 1) { + // rollback and exit if it fails + _log("Masternode reward DB insert failed"); + $db->rollback(); + $db->exec("UNLOCK TABLES"); + return false; + } + $res=$this->reset_fails_masternodes($mn_winner, $height, $hash); + if (!$res) { + + // rollback and exit if it fails + _log("Masternode log DB insert failed"); + $db->rollback(); + $db->exec("UNLOCK TABLES"); + return false; + } + } // parse the block's transactions and insert them to db $res = $this->parse_block($hash, $height, $data, false, $bootstrapping); @@ -164,10 +183,17 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); { global $db; $res=$this->masternode_log($public_key, $height, $hash); - - if ($res) { - $db->run("UPDATE masternode SET last_won=:last_won,fails=0 WHERE public_key=:public_key", [":public_key"=>$public_key, ":last_won"=>$height]); + if ($res===5) { + return false; } + + if ($res) { + $rez=$db->run("UPDATE masternode SET last_won=:last_won,fails=0 WHERE public_key=:public_key", [":public_key"=>$public_key, ":last_won"=>$height]); + if ($rez!=1) { + return false; + } + } + return true; } //logs the current masternode status @@ -184,12 +210,15 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); $id = hex2coin(hash("sha512", "resetfails-$hash-$height-$public_key")); $msg="$mn[blacklist],$mn[last_won],$mn[fails]"; - $db->run( + $res=$db->run( "INSERT into transactions SET id=:id, block=:block, height=:height, dst=:dst, val=0, fee=0, signature=:sig, version=111, message=:msg, date=:date, public_key=:public_key", [":id"=>$id, ":block"=>$hash, ":height"=>$height, ":dst"=>$hash, ":sig"=>$hash, ":msg"=>$msg, ":date"=>time(), ":public_key"=>$public_key] ); + if ($res!=1) { + return 5; + } return true; } @@ -265,12 +294,12 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); // keep current difficulty $dif = $current['difficulty']; } - }elseif($height>=80458){ - $type=$height%2; - $current=$db->row("SELECT difficulty from blocks WHERE height<=:h ORDER by height DESC LIMIT 1,1",[":h"=>$height]); + } elseif ($height>=80458) { + $type=$height%2; + $current=$db->row("SELECT difficulty from blocks WHERE height<=:h ORDER by height DESC LIMIT 1,1", [":h"=>$height]); $blks=0; $total_time=0; - $blk = $db->run("SELECT `date`, height FROM blocks WHERE height<=:h ORDER by height DESC LIMIT 20",[":h"=>$height]); + $blk = $db->run("SELECT `date`, height FROM blocks WHERE height<=:h ORDER by height DESC LIMIT 20", [":h"=>$height]); for ($i=0;$i<19;$i++) { $ctype=$blk[$i+1]['height']%2; $time=$blk[$i]['date']-$blk[$i+1]['date']; @@ -282,7 +311,7 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); } $result=ceil($total_time/$blks); _log("Block time: $result", 3); - if ($result > 260) { + if ($result > 260) { $dif = bcmul($current['difficulty'], 1.05); } elseif ($result < 220) { // if lower, decrease by 5% @@ -291,8 +320,6 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); // keep current difficulty $dif = $current['difficulty']; } - - } else { // hardfork 80000, fix difficulty targetting @@ -301,7 +328,7 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); $type=$height%3; // for mn, we use gpu diff if ($type == 2) { - return $current['difficulty']; + return $current['difficulty']; } $blks=0; @@ -398,8 +425,8 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); } $acc = new Account(); - if($data['date']>time()+30){ - _log("Future block - $data[date] $data[public_key]",2); + if ($data['date']>time()+30) { + _log("Future block - $data[date] $data[public_key]", 2); return false; } @@ -460,24 +487,23 @@ $this->reset_fails_masternodes($mn_winner, $height, $hash); // reward transaction and signature $reward = $this->reward($height, $data); -if($height>=80458){ - //reward the masternode - global $db; - $mn_winner=$db->single( + if ($height>=80458) { + //reward the masternode + global $db; + $mn_winner=$db->single( "SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 1", [":current"=>$height, ":start"=>$height-360] ); - _log("MN Winner: $mn_winner",2); - if($mn_winner!==false){ - $mn_reward=round(0.33*$reward,8); - $reward=round($reward-$mn_reward,8); - $reward=number_format($reward,8,".",""); - $mn_reward=number_format($mn_reward,8,".",""); - _log("MN Reward: $mn_reward",2); + _log("MN Winner: $mn_winner", 2); + if ($mn_winner!==false) { + $mn_reward=round(0.33*$reward, 8); + $reward=round($reward-$mn_reward, 8); + $reward=number_format($reward, 8, ".", ""); + $mn_reward=number_format($mn_reward, 8, ".", ""); + _log("MN Reward: $mn_reward", 2); + } } -} - $msg = ''; $transaction = [ "src" => $generator, @@ -526,16 +552,18 @@ if($height>=80458){ if ($total_time<=600&&$current['height']<80500) { return; } - if($current['height']>=80500&&$total_time<360){ - return false; - } - if($current['height']>=80500){ - $total_time-=360; - $tem=floor($total_time/120)+1; - if($tem>5) $tem=5; - } else { - $tem=floor($total_time/600); - } + if ($current['height']>=80500&&$total_time<360) { + return false; + } + if ($current['height']>=80500) { + $total_time-=360; + $tem=floor($total_time/120)+1; + if ($tem>5) { + $tem=5; + } + } else { + $tem=floor($total_time/600); + } _log("We have masternodes to blacklist - $tem", 2); $ban=$db->run( "SELECT public_key, blacklist, fails, last_won FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 0,$tem", @@ -546,8 +574,10 @@ if($height>=80458){ foreach ($ban as $b) { $this->masternode_log($b['public_key'], $current['height'], $current['id']); _log("Blacklisting masternode - $i $b[public_key]", 2); - $btime=10; - if($current['height']>83000) $btime=360; + $btime=10; + if ($current['height']>83000) { + $btime=360; + } $db->run("UPDATE masternode SET fails=fails+1, blacklist=:blacklist WHERE public_key=:public_key", [":public_key"=>$b['public_key'], ":blacklist"=> $current['height']+(($b['fails']+1)*$btime)]); $i++; } @@ -559,7 +589,7 @@ if($height>=80458){ global $_config; // invalid future blocks - if($time>time()+30){ + if ($time>time()+30) { return false; } @@ -594,19 +624,17 @@ if($height>=80458){ _log("Block below 10800, using 16MB argon", 2); $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; } - - } elseif($current_height>=80458){ - if ($current_height%2==0) { - // cpu mining - _log("CPU Mining - $current_height", 2); - $argon = '$argon2i$v=19$m=524288,t=1,p=1'.$argon; - } else { - // gpu mining - _log("GPU Mining - $current_height", 2); - $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; - } - - } else { + } elseif ($current_height>=80458) { + if ($current_height%2==0) { + // cpu mining + _log("CPU Mining - $current_height", 2); + $argon = '$argon2i$v=19$m=524288,t=1,p=1'.$argon; + } else { + // gpu mining + _log("GPU Mining - $current_height", 2); + $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; + } + } else { _log("Block > 80000 - $current_height", 2); if ($current_height%3==0) { // cpu mining @@ -653,13 +681,12 @@ if($height>=80458){ _log("Last block time: $last_time, difference: ".($time-$last_time), 3); if (($time-$last_time>600&&$current_height<80500)||($time-$last_time>360&&$current_height>=80500)) { _log("Current public_key $public_key", 3); - if($current_height>=80500){ - $total_time=$time-$last_time; - $total_time-=360; - $tem=floor($total_time/120)+1; - - } else { - $tem=floor(($time-$last_time)/600); + if ($current_height>=80500) { + $total_time=$time-$last_time; + $total_time-=360; + $tem=floor($total_time/120)+1; + } else { + $tem=floor(($time-$last_time)/600); } $winner=$db->single( "SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT $tem,1", @@ -734,6 +761,7 @@ if($height>=80458){ global $db; // data must be array if ($data === false) { + _log("Block data is false", 3); return false; } $acc = new Account(); @@ -746,6 +774,7 @@ if($height>=80458){ // check if the number of transactions is not bigger than current block size $max = $this->max_transactions(); if (count($data) > $max) { + _log("Too many transactions in block", 3); return false; } @@ -760,6 +789,7 @@ if($height>=80458){ if (!$bootstrapping) { //validate the transaction if (!$trx->check($x, $height)) { + _log("Transaction check failed - $x[id]", 3); return false; } if ($x['version']>=100&&$x['version']<110) { @@ -772,6 +802,7 @@ if($height>=80458){ // check if the transaction is already on the blockchain if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) { + _log("Transaction already on the blockchain - $x[id]", 3); return false; } } @@ -790,6 +821,7 @@ if($height>=80458){ [":id" => $id, ":balance" => $bal] ); if ($res == 0) { + _log("Not enough balance for transaction - $id", 3); return false; // not enough balance for the transactions } } @@ -867,12 +899,14 @@ if($height>=80458){ foreach ($r as $x) { $res = $trx->reverse($x['id']); if ($res === false) { + _log("A transaction could not be reversed. Delete block failed."); $db->rollback(); $db->exec("UNLOCK TABLES"); return false; } $res = $db->run("DELETE FROM blocks WHERE id=:id", [":id" => $x['id']]); if ($res != 1) { + _log("Delete block failed."); $db->rollback(); $db->exec("UNLOCK TABLES"); return false; diff --git a/include/functions.inc.php b/include/functions.inc.php index 3d18d19..46abc9b 100755 --- a/include/functions.inc.php +++ b/include/functions.inc.php @@ -39,6 +39,10 @@ function api_echo($data) // log function, shows only in cli atm function _log($data, $verbosity = 0) { + global $_config; + if ($_config['log_verbosity'] < $verbosity) { + return; + } $date = date("[Y-m-d H:i:s]"); $trace = debug_backtrace(); $loc = count($trace) - 1; @@ -56,7 +60,6 @@ function _log($data, $verbosity = 0) if (php_sapi_name() === 'cli') { echo $res; } - global $_config; if ($_config['enable_logging'] == true && $_config['log_verbosity'] >= $verbosity) { @file_put_contents($_config['log_file'], $res, FILE_APPEND); } diff --git a/include/transaction.inc.php b/include/transaction.inc.php index 090f413..bb38321 100755 --- a/include/transaction.inc.php +++ b/include/transaction.inc.php @@ -6,54 +6,78 @@ class Transaction public function reverse($block) { global $db; + $acc = new Account(); $r = $db->run("SELECT * FROM transactions WHERE block=:block ORDER by `version` ASC", [":block" => $block]); foreach ($r as $x) { + _log("Reversing transaction $x[id]",4); if (empty($x['src'])) { $x['src'] = $acc->get_address($x['public_key']); } if ($x['version'] == 2) { // payment sent to alias - $db->run( + $rez=$db->run( "UPDATE accounts SET balance=balance-:val WHERE alias=:alias", [":alias" => $x['dst'], ":val" => $x['val']] ); + if($rez!=1) { + _log("Update alias balance minus failed",3); + return false; + } } else { // other type of transactions - if($x['version']!=100) { $db->run( + if ($x['version']!=100&&$x['version']<111) { + $rez=$db->run( "UPDATE accounts SET balance=balance-:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']] ); - } - - } + if($rez!=1) { + _log("Update accounts balance minus failed",3); + return false; + } + } + } // on version 0 / reward transaction, don't credit anyone - if ($x['version'] > 0) { - $db->run( + if ($x['version'] > 0 && $x['version']<111) { + $rez=$db->run( "UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['src'], ":val" => $x['val'] + $x['fee']] ); + if($rez!=1) { + _log("Update account balance plus failed",3); + return false; + } } // removing the alias if the alias transaction is reversed if ($x['version']==3) { - $db->run( + $rez=$db->run( "UPDATE accounts SET alias=NULL WHERE id=:id", [":id" => $x['src']] ); + if($rez!=1) { + _log("Clear alias failed",3); + return false; + } } if ($x['version']>=100&&$x['version']<110&&$x['height']>=80000) { if ($x['version']==100) { - $db->run("DELETE FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + $rez=$db->run("DELETE FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + if($rez!=1) { + _log("Delete from masternode failed",3); + return false; + } } elseif ($x['version']==101) { - $db->run( + $rez=$db->run( "UPDATE masternode SET status=1 WHERE public_key=:public_key", [':public_key'=>$x['public_key']] ); + } elseif ($x['version']==102) { - $db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + $rez=$db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + } elseif ($x['version']==103) { $mnt=$db->row("SELECT height, `message` FROM transactions WHERE version=100 AND public_key=:public_key ORDER by height DESC LIMIT 1", [":public_key"=>$x['public_key']]); $vers=$db->single( @@ -67,21 +91,35 @@ class Transaction $status=0; } - $db->run( + $rez=$db->run( "INSERT into masternode SET `public_key`=:public_key, `height`=:height, `ip`=:ip, `status`=:status", [":public_key"=>$x['public_key'], ":height"=>$mnt['height'], ":ip"=>$mnt['message'], ":status"=>$status] ); - $db->run("UPDATE accounts SET balance=balance-100000 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + if($rez!=1) { + _log("Insert into masternode failed",3); + return false; + } + $rez=$db->run("UPDATE accounts SET balance=balance-100000 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + if($rez!=1) { + _log("Update masternode balance failed",3); + return false; + } } } // internal masternode history if ($x['version']==111) { + _log("Masternode reverse: $x[message]",4); $m=explode(",", $x['message']); - $db->run( + $rez=$db->run( "UPDATE masternode SET fails=:fails, blacklist=:blacklist, last_won=:last_won WHERE public_key=:public_key", [":public_key"=>$x['public_key'], ":blacklist"=> $m[0], ":fails"=>$m[2], ":last_won"=>$m[1]] ); + if($rez!=1) { + _log("Update masternode log failed",3); + return false; + } + } // add the transactions to mempool @@ -90,6 +128,7 @@ class Transaction } $res = $db->run("DELETE FROM transactions WHERE id=:id", [":id" => $x['id']]); if ($res != 1) { + _log("Delete transaction failed",3); return false; } } @@ -256,9 +295,9 @@ class Transaction //master node deposit } elseif ($x['version']==103&&$height>=80000) { $blk=new Block(); - $blk->masternode_log($x['public_key'],$height,$block); + $blk->masternode_log($x['public_key'], $height, $block); - //master node withdrawal + //master node withdrawal } else { $db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']]); } @@ -332,9 +371,9 @@ class Transaction // } // internal transactions - if($x['version']>110){ + if ($x['version']>110) { return false; - } + } // the value must be >=0 if ($x['val'] < 0) { @@ -378,11 +417,11 @@ class Transaction _log("The Masternode IP is invalid", 3); return false; } - global $db; - $existing=$db->single("SELECT COUNT(1) FROM masternode WHERE public_key=:id or ip=:ip",["id"=>$x['public_key'], ":ip"=>$message]); - if($existing!=0){ - return false; - } + global $db; + $existing=$db->single("SELECT COUNT(1) FROM masternode WHERE public_key=:id or ip=:ip", ["id"=>$x['public_key'], ":ip"=>$message]); + if ($existing!=0) { + return false; + } } diff --git a/sanity.php b/sanity.php index 39f9ac5..b8169d4 100755 --- a/sanity.php +++ b/sanity.php @@ -63,6 +63,11 @@ if ($arg != "microsanity") { require_once("include/init.inc.php"); +if ($argv[1]=="dev") { + error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE); + ini_set("display_errors", "on"); +} + // the sanity can't run without the schema being installed if ($_config['dbversion'] < 2) { die("DB schema not created"); @@ -76,6 +81,11 @@ $block = new Block(); $acc = new Account(); $current = $block->current(); + + + + + // bootstrapping the initial sync if ($current['height']==1) { echo "Bootstrapping!\n"; @@ -315,63 +325,63 @@ foreach ($r as $x) { _log("Contacting peer $x[hostname]"); $url = $x['hostname']."/peer.php?q="; // get their peers list -if($_config['get_more_peers']==true){ - $data = peer_post($url."getPeers", [], 5); - if ($data === false) { - _log("Peer $x[hostname] unresponsive"); - // if the peer is unresponsive, mark it as failed and blacklist it for a while - $db->run( + if ($_config['get_more_peers']==true) { + $data = peer_post($url."getPeers", [], 5); + if ($data === false) { + _log("Peer $x[hostname] unresponsive"); + // if the peer is unresponsive, mark it as failed and blacklist it for a while + $db->run( "UPDATE peers SET fails=fails+1, blacklisted=UNIX_TIMESTAMP()+((fails+1)*3600) WHERE id=:id", [":id" => $x['id']] ); - continue; - } - foreach ($data as $peer) { - // store the hostname as md5 hash, for easier checking - $peer['hostname'] = san_host($peer['hostname']); - $peer['ip'] = san_ip($peer['ip']); - $pid = md5($peer['hostname']); - // do not peer if we are already peered - if ($peered[$pid] == 1) { continue; } - $peered[$pid] = 1; - $bad_peers = ["127.", "localhost", "10.", "192.168.","172.16.","172.17.","172.18.","172.19.","172.20.","172.21.","172.22.","172.23.","172.24.","172.25.","172.26.","172.27.","172.28.","172.29.","172.30.","172.31."]; - $tpeer=str_replace(["https://","http://","//"], "", $peer['hostname']); - foreach ($bad_peers as $bp) { - if (strpos($tpeer, $bp)===0) { + foreach ($data as $peer) { + // store the hostname as md5 hash, for easier checking + $peer['hostname'] = san_host($peer['hostname']); + $peer['ip'] = san_ip($peer['ip']); + $pid = md5($peer['hostname']); + // do not peer if we are already peered + if ($peered[$pid] == 1) { continue; } - } - // if it's our hostname, ignore - if ($peer['hostname'] == $_config['hostname']) { - continue; - } - // if invalid hostname, ignore - if (!filter_var($peer['hostname'], FILTER_VALIDATE_URL)) { - continue; - } - // make sure there's no peer in db with this ip or hostname - if (!$db->single( + $peered[$pid] = 1; + $bad_peers = ["127.", "localhost", "10.", "192.168.","172.16.","172.17.","172.18.","172.19.","172.20.","172.21.","172.22.","172.23.","172.24.","172.25.","172.26.","172.27.","172.28.","172.29.","172.30.","172.31."]; + $tpeer=str_replace(["https://","http://","//"], "", $peer['hostname']); + foreach ($bad_peers as $bp) { + if (strpos($tpeer, $bp)===0) { + continue; + } + } + // if it's our hostname, ignore + if ($peer['hostname'] == $_config['hostname']) { + continue; + } + // if invalid hostname, ignore + if (!filter_var($peer['hostname'], FILTER_VALIDATE_URL)) { + continue; + } + // make sure there's no peer in db with this ip or hostname + if (!$db->single( "SELECT COUNT(1) FROM peers WHERE ip=:ip or hostname=:hostname", [":ip" => $peer['ip'], ":hostname" => $peer['hostname']] )) { - $i++; - // check a max_test_peers number of peers from each peer - if ($i > $_config['max_test_peers']) { - break; - } - $peer['hostname'] = filter_var($peer['hostname'], FILTER_SANITIZE_URL); - // peer with each one - _log("Trying to peer with recommended peer: $peer[hostname]"); - $test = peer_post($peer['hostname']."/peer.php?q=peer", ["hostname" => $_config['hostname']], 5); - if ($test !== false) { - $total_peers++; - echo "Peered with: $peer[hostname]\n"; + $i++; + // check a max_test_peers number of peers from each peer + if ($i > $_config['max_test_peers']) { + break; + } + $peer['hostname'] = filter_var($peer['hostname'], FILTER_SANITIZE_URL); + // peer with each one + _log("Trying to peer with recommended peer: $peer[hostname]"); + $test = peer_post($peer['hostname']."/peer.php?q=peer", ["hostname" => $_config['hostname']], 5); + if ($test !== false) { + $total_peers++; + echo "Peered with: $peer[hostname]\n"; + } } } } -} // get the current block and check it's blockchain $data = peer_post($url."currentBlock", [], 5); @@ -441,7 +451,7 @@ 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"; - +$block_parse_failed=false; // if we're not on the largest height if ($current['height'] < $largest_height && $largest_height > 1) { // start sanity sync / block all other transactions/blocks @@ -553,6 +563,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) { $b['height'] = san($b['height']); if (!$block->check($b)) { + $block_parse_failed=true; _log("Block check: could not add block - $b[id] - $b[height]"); $good_peer = false; break; @@ -569,6 +580,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) { $b['argon'] ); if (!$res) { + $block_parse_failed=true; _log("Block add: could not add block - $b[id] - $b[height]"); $good_peer = false; break; @@ -585,6 +597,71 @@ if ($current['height'] < $largest_height && $largest_height > 1) { break; } } + + $resyncing=false; + if ($block_parse_failed==true&&$current['date']current(); + $rwpb=$db->single("SELECT COUNT(1) FROM transactions WHERE version=0 AND message=''"); + if($rwpb!=$current['height']){ + $failed=$db->single("SELECT blocks.height FROM blocks LEFT JOIN transactions ON transactions.block=blocks.id and transactions.version=0 and transactions.message='' WHERE transactions.height is NULL ORDER by blocks.height ASC LIMIT 1"); + if($failed>1){ + _log("Found failed block - $faield"); + $block->delete($failed); + $block_parse_failed==false; + } + } + } + if ($block_parse_failed==true||$argv[1]=="resync") { + + $last_resync=$db->single("SELECT val FROM config WHERE cfg='last_resync'"); + if ($last_resync0){ + $to_remove=intval($argv[2]); + } + _log("Removing $to_remove blocks, the blockchain is stale."); + $block->pop(to_remove); + $resyncing=true; + } elseif ($current['date']pop(200); + + $resyncing=true; + } + if ($resyncing==true) { + _log("Resyncing accounts"); + $db->run("INSERT into config SET val=UNIX_TIMESTAMP(), cfg='last_resync' ON DUPLICATE KEY UPDATE val=UNIX_TIMESTAMP()"); + $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE"); + + $r=$db->run("SELECT * FROM accounts"); + foreach ($r as $x) { + $alias=$x['alias']; + if (empty($alias)) { + $alias="A"; + } + $rec=$db->single("SELECT SUM(val) FROM transactions WHERE (dst=:id or dst=:alias) AND (height<80000 OR (version!=100 AND version!=103)) and version<111", [":id"=>$x['id'], ":alias"=>$alias]); + $spent=$db->single("SELECT SUM(val+fee) FROM transactions WHERE public_key=:pub AND version>0", [":pub"=>$x['public_key']]); + if ($spent==false) { + $spent=0; + } + $balance=round(($rec-$spent), 8); + if ($x['balance']!=$balance) { + // echo "rec: $rec, spent: $spent, bal: $x[balance], should be: $balance - $x[id] $x[public_key]\n"; + if (trim($argv[2])!="check") { + $db->run("UPDATE accounts SET balance=:bal WHERE id=:id", [":id"=>$x['id'], ":bal"=>$balance]); + } + } + } + $current = $block->current(); + $db->run("DELETE FROM masternode WHERE height>:h",[":h"=>$current['height']]); + $db->exec("UNLOCK TABLES"); + } + } + } + + $db->run("UPDATE config SET val=0 WHERE cfg='sanity_sync'", [":time" => $t]); } diff --git a/util.php b/util.php index 2b18278..5fbfa91 100755 --- a/util.php +++ b/util.php @@ -43,6 +43,8 @@ $cmd = trim($argv[1]); */ if ($cmd == 'clean') { + if(file_exists("tmp/sanity-lock")) die("Sanity running. Wait for it to finish"); + touch("tmp/sanity-lock"); $db->run("SET foreign_key_checks=0;"); $tables = ["accounts", "transactions", "mempool", "masternode","blocks"]; foreach ($tables as $table) { @@ -51,6 +53,7 @@ if ($cmd == 'clean') { $db->run("SET foreign_key_checks=1;"); echo "\n The database has been cleared\n"; + unlink("tmp/sanity-lock"); } /** * @api {php util.php} pop Pop * @apiName pop @@ -64,9 +67,12 @@ if ($cmd == 'clean') { */ elseif ($cmd == 'pop') { + if(file_exists("tmp/sanity-lock")) die("Sanity running. Wait for it to finish"); + touch("tmp/sanity-lock"); $no = intval($argv[2]); $block = new Block(); $block->pop($no); + unlink("tmp/sanity-lock"); } /** * @api {php util.php} block-time Block-time * @apiName block-time @@ -411,7 +417,7 @@ elseif ($cmd == "check-address") { } echo "The address is valid\n"; -} +} /** * @api {php util.php} get-address Get-Address * @apiName get-address @@ -443,35 +449,78 @@ elseif ($cmd == 'get-address') { * php util.php clean-blacklist * */ - } elseif ($cmd == 'clean-blacklist') { - $db->run("UPDATE peers SET blacklisted=0, fails=0, stuckfail=0"); - echo "All the peers have been removed from the blacklist\n"; -}elseif($cmd == 'resync-accounts'){ -// resyncs the balance on all accounts + $db->run("UPDATE peers SET blacklisted=0, fails=0, stuckfail=0"); + echo "All the peers have been removed from the blacklist\n"; +} elseif ($cmd == 'resync-accounts') { + // resyncs the balance on all accounts - // lock table to avoid race conditions on blocks - $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE"); + // lock table to avoid race conditions on blocks + $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE"); -$r=$db->run("SELECT * FROM accounts"); -foreach($r as $x){ + $r=$db->run("SELECT * FROM accounts"); + foreach ($r as $x) { $alias=$x['alias']; - if(empty($alias)) $alias="A"; - $rec=$db->single("SELECT SUM(val) FROM transactions WHERE (dst=:id or dst=:alias) AND (height<80000 OR (version!=100 AND version!=103)) and version<111",[":id"=>$x['id'], ":alias"=>$alias]); - $spent=$db->single("SELECT SUM(val+fee) FROM transactions WHERE public_key=:pub AND version>0",[":pub"=>$x['public_key']]); - if($spent==false) $spent=0; - $balance=round(($rec-$spent),8); - if($x['balance']!=$balance){ - echo "rec: $rec, spent: $spent, bal: $x[balance], should be: $balance - $x[id] $x[public_key]\n"; - if(trim($argv[2])!="check") { - $db->run("UPDATE accounts SET balance=:bal WHERE id=:id",[":id"=>$x['id'], ":bal"=>$balance]); - } + if (empty($alias)) { + $alias="A"; } -} -$db->exec("UNLOCK TABLES"); -echo "All done"; - + $rec=$db->single("SELECT SUM(val) FROM transactions WHERE (dst=:id or dst=:alias) AND (height<80000 OR (version!=100 AND version!=103)) and version<111", [":id"=>$x['id'], ":alias"=>$alias]); + $spent=$db->single("SELECT SUM(val+fee) FROM transactions WHERE public_key=:pub AND version>0", [":pub"=>$x['public_key']]); + if ($spent==false) { + $spent=0; + } + $balance=round(($rec-$spent), 8); + if ($x['balance']!=$balance) { + echo "rec: $rec, spent: $spent, bal: $x[balance], should be: $balance - $x[id] $x[public_key]\n"; + if (trim($argv[2])!="check") { + $db->run("UPDATE accounts SET balance=:bal WHERE id=:id", [":id"=>$x['id'], ":bal"=>$balance]); + } + } + } + $db->exec("UNLOCK TABLES"); + echo "All done"; +} elseif ($cmd=="compare-blocks") { + $block=new Block(); + $current=$block->current(); + $peer=trim($argv[2]); + $limit=intval($argv[3]); + if($limit==0) $limit=5000; + for($i=$current['height']-$limit;$i<=$current['height'];$i++){ + $data=peer_post($peer."/peer.php?q=getBlock",["height" => $i]); + if($data==false) { + continue; + } + $our=$block->export(false,$i); + if($data!=$our) { + echo "Failed block -> $i\n"; + if($argv[4]=="dump"){ + echo "\n\n ---- Internal ----\n\n"; + var_dump($our); + echo "\n\n ---- External ----\n\n"; + var_dump($data); + } + } + } +} elseif ($cmd=='compare-accounts'){ + $peer=trim($argv[2]); + $r=$db->run("SELECT id,balance FROM accounts"); + foreach($r as $x){ + $data=peer_post($peer."/api.php?q=getBalance",["account" => $x['id']]); + if($data==false) { + continue; + } + if($data!=$x['balance']) { + echo "$x[id]\t\t$x[balance]\t$data\n"; + } + } +} elseif ($cmd=='masternode-hash'){ + $res=$db->run("SELECT * FROM masternode ORDER by public_key ASC"); + $block=new Block(); + $current=$block->current(); + echo "Height:\t\t$current[height]\n"; + echo "Hash:\t\t".md5(json_encode($res))."\n\n"; + } else { echo "Invalid command\n"; }