From 8b006fc26a094a2f3c62f8cccf53a80575267fca Mon Sep 17 00:00:00 2001 From: Arionum Date: Mon, 6 Aug 2018 00:13:36 +0300 Subject: [PATCH] hf alias and mining --- README.md | 6 + api.php | 121 +++++++++++-- include/account.inc.php | 72 ++++++++ include/block.inc.php | 349 ++++++++++++++++++++++++++++++------ include/config.inc.php | 6 + include/db.inc.php | 7 +- include/functions.inc.php | 4 +- include/init.inc.php | 17 +- include/schema.inc.php | 19 ++ include/transaction.inc.php | 246 ++++++++++++++++++++++--- mine.php | 32 ++++ peer.php | 15 +- propagate.php | 2 + sanity.php | 139 ++++++++++---- util.php | 26 ++- 15 files changed, 918 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index 928219d..0abc0da 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ The Arionum (ARO) cryptocurrency node. ## Install +**Hardware Requirements:** + +2GB RAM +1 CPU Core +50GB DISK + **Requirements:** - PHP 7.2 diff --git a/api.php b/api.php index 1a65f69..261a71b 100755 --- a/api.php +++ b/api.php @@ -124,18 +124,23 @@ if ($q == "getAddress") { * * @apiParam {string} [public_key] Public key * @apiParam {string} [account] Account id / address + * @apiParam {string} [alias] alias * * @apiSuccess {string} data The ARO balance */ $public_key = $data['public_key']; $account = $data['account']; + $alias = $data['alias']; 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($alias)) { + $account = $acc->alias2account($alias); + } if (empty($account)) { api_err("Invalid account id"); } @@ -395,16 +400,28 @@ if ($q == "getAddress") { $block = new Block(); $trx = new Transaction(); - + $version = intval($data['version']); $dst = san($data['dst']); + if ($version < 1) { + $version = 1; + } - if (!$acc->valid($dst)) { - api_err("Invalid destination address"); - } - $dst_b = base58_decode($dst); - if (strlen($dst_b) != 64) { - api_err("Invalid destination address"); + if ($version==1) { + if (!$acc->valid($dst)) { + api_err("Invalid destination address"); + } + $dst_b = base58_decode($dst); + if (strlen($dst_b) != 64) { + api_err("Invalid destination address"); + } + } elseif ($version==2) { + $dst=strtoupper($dst); + $dst = san($dst); + if (!$acc->valid_alias($dst)) { + api_err("Invalid destination alias"); + } } + $public_key = san($data['public_key']); @@ -430,8 +447,8 @@ if ($q == "getAddress") { if ($date > time() + 86400) { api_err("Invalid Date"); } - $version = intval($data['version']); - $message = $data['message']; + + $message=$data['message']; if (strlen($message) > 128) { api_err("The message must be less than 128 chars"); } @@ -448,9 +465,28 @@ if ($q == "getAddress") { if ($val < 0.00000001) { api_err("Invalid value"); } + + // set alias + if ($version==3) { + $fee=10; + $message = san($message); + $message=strtoupper($message); + if (!$acc->free_alias($message)) { + api_err("Invalid alias"); + } + if ($acc->has_alias($public_key)) { + api_err("This account already has an alias"); + } + } - if ($version < 1) { - $version = 1; + if ($version>=100&&$version<110) { + if ($version==100) { + $message=preg_replace("/[^0-9\.]/", "", $message); + if (!filter_var($message, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + api_err("Invalid Node IP - $message !"); + } + $val=100000; + } } $val = number_format($val, 8, '.', ''); @@ -570,6 +606,69 @@ if ($q == "getAddress") { mt_srand($seed1, MT_RAND_MT19937); $res = mt_rand($min, $max); api_echo($res); +} elseif ($q == "checkSignature") { + /** + * @api {get} /api.php?q=checkSignature 17. checkSignature + * @apiName checkSignature + * @apiGroup API + * @apiDescription Checks a signature against a public key + * + * @apiParam {string} [public_key] Public key + * @apiParam {string} [signature] signature + * @apiParam {string} [data] signed data + * + * + * @apiSuccess {boolean} data true or false + */ + + $public_key=san($data['public_key']); + $signature=san($data['signature']); + $data=$data['data']; + + api_echo(ec_verify($data, $signature, $public_key)); +} elseif ($q == "masternodes") { + /** + * @api {get} /api.php?q=masternodes 18. masternodes + * @apiName masternodes + * @apiGroup API + * @apiDescription Returns all the masternode data + * + * + * + * @apiSuccess {boolean} data masternode date + */ + + $res=$db->run("SELECT * FROM masternode"); + api_echo($res); +} elseif ($q == "getAlias") { + /** + * @api {get} /api.php?q=getAlias 189. getAlias + * @apiName getAlias + * @apiGroup API + * @apiDescription Returns the alias of an account + * + * @apiParam {string} [public_key] Public key + * @apiParam {string} [account] Account id / address + * + * + * @apiSuccess {string} data alias + */ + + $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->account2alias($account)); } else { api_err("Invalid request"); } diff --git a/include/account.inc.php b/include/account.inc.php index ee90df8..8f8c092 100755 --- a/include/account.inc.php +++ b/include/account.inc.php @@ -93,7 +93,71 @@ class Account return true; } + //check alias validity + public function free_alias($id) + { + global $db; + $orig=$id; + $id=strtoupper($id); + $id = san($id); + if (strlen($id)<4||strlen($id)>25) { + return false; + } + if($orig!=$id){ + return false; + } + + if ($db->single("SELECT COUNT(1) FROM accounts WHERE alias=:alias", [":alias"=>$id])==0) { + return true; + } else { + return false; + } + } + //check if an account already has an alias + public function has_alias($public_key){ + global $db; + $public_key=san($public_key); + $res=$db->single("SELECT COUNT(1) FROM accounts WHERE public_key=:public_key AND alias IS NOT NULL",[":public_key"=>$public_key]); + if($res!=0) return true; + else return false; + } + + //check alias validity + public function valid_alias($id) + { + global $db; + $orig=$id; + $banned=["MERCURY","DEVS","DEVELOPMENT", "MARKETING", "MERCURY80","DEVARO", "DEVELOPER","DEVELOPERS","ARODEV", "DONATION","MERCATOX", "OCTAEX", "MERCURY", "ARIONUM", "ESCROW","OKEX","BINANCE","CRYPTOPIA","HUOBI","ITFINEX","HITBTC","UPBIT","COINBASE","KRAKEN","BITSTAMP","BITTREX","POLONIEX"]; + $id=strtoupper($id); + $id = san($id); + if (in_array($id, $banned)) { + return false; + } + if (strlen($id)<4||strlen($id)>25) { + return false; + } + if($orig!=$id){ + return false; + } + return $db->single("SELECT COUNT(1) FROM accounts WHERE alias=:alias", [":alias"=>$id]); + } + + //returns the account of an alias + public function alias2account($alias){ + global $db; + $alias=strtoupper($alias); + $res=$db->single("SELECT id FROM accounts WHERE alias=:alias LIMIT 1",[":alias"=>$alias]); + return $res; + } + + //returns the alias of an account + public function account2alias($id){ + global $db; + $id=san($id); + $res=$db->single("SELECT alias FROM accounts WHERE id=:id LIMIT 1",[":id"=>$id]); + return $res; + } // check the validity of an address. At the moment, it checks only the characters to be base58 and the length to be >=70 and <=128. public function valid($id) { @@ -234,4 +298,12 @@ class Account $res = $db->single("SELECT public_key FROM accounts WHERE id=:id", [":id" => $id]); return $res; } + + public function get_masternode($public_key){ + global $db; + $res = $db->row("SELECT * FROM masternode WHERE public_key=:public_key", [":public_key" => $public_key]); + if(empty($res['public_key'])) return false; + return $res; + } + } diff --git a/include/block.inc.php b/include/block.inc.php index 57b87e2..ab3d0c4 100755 --- a/include/block.inc.php +++ b/include/block.inc.php @@ -2,7 +2,7 @@ class Block { - public function add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon) + public function add($height, $public_key, $nonce, $data, $date, $signature, $difficulty, $reward_signature, $argon, $bootstrapping=false) { global $db; $acc = new Account(); @@ -24,16 +24,17 @@ class Block // create the block data and check it against the signature $info = "{$generator}-{$height}-{$date}-{$nonce}-{$json}-{$difficulty}-{$argon}"; - if (!$acc->check_signature($info, $signature, $public_key)) { - _log("Block signature check failed"); - return false; - } + if (!$bootstrapping) { + if (!$acc->check_signature($info, $signature, $public_key)) { + _log("Block signature check failed"); + return false; + } - if (!$this->parse_block($hash, $height, $data, true)) { - _log("Parse block failed"); - return false; + if (!$this->parse_block($hash, $height, $data, true)) { + _log("Parse block failed"); + return false; + } } - // lock table to avoid race conditions on blocks $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE"); @@ -55,13 +56,14 @@ class Block $transaction['signature'] = $reward_signature; // hash the transaction $transaction['id'] = $trx->hash($transaction); - // check the signature - $info = $transaction['val']."-".$transaction['fee']."-".$transaction['dst']."-".$transaction['message']."-".$transaction['version']."-".$transaction['public_key']."-".$transaction['date']; - if (!$acc->check_signature($info, $reward_signature, $public_key)) { - _log("Reward signature failed"); - return false; + if (!$bootstrapping) { + // check the signature + $info = $transaction['val']."-".$transaction['fee']."-".$transaction['dst']."-".$transaction['message']."-".$transaction['version']."-".$transaction['public_key']."-".$transaction['date']; + if (!$acc->check_signature($info, $reward_signature, $public_key)) { + _log("Reward signature failed"); + return false; + } } - // insert the block into the db $db->beginTransaction(); $total = count($data); @@ -92,7 +94,12 @@ class Block $trx->add($hash, $height, $transaction); // parse the block's transactions and insert them to db - $res = $this->parse_block($hash, $height, $data, false); + $res = $this->parse_block($hash, $height, $data, false, $bootstrapping); + + if (($height-1)%3==2 && $height>=80000) { + $this->blacklist_masternodes(); + $this->reset_fails_masternodes($public_key, $height, $hash); + } // if any fails, rollback if ($res == false) { $db->rollback(); @@ -104,6 +111,41 @@ class Block return true; } + // resets the number of fails when winning a block and marks it with a transaction + + public function reset_fails_masternodes($public_key, $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]); + } + } + + //logs the current masternode status + public function masternode_log($public_key, $height, $hash) + { + global $db; + + $mn=$db->row("SELECT blacklist,last_won,fails FROM masternode WHERE public_key=:public_key", [":public_key"=>$public_key]); + + if (!$mn) { + return false; + } + + $id = hex2coin(hash("sha512", "resetfails-$hash-$height-$public_key")); + $msg="$mn[blacklist],$mn[last_won],$mn[fails]"; + + $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] + + ); + return true; + } + // returns the current block, without the transactions public function current() { @@ -155,23 +197,70 @@ class Block return $current['difficulty']; } - // elapsed time between the last 20 blocks - $first = $db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT $limit,1"); - $time = $current['date'] - $first['date']; + // before mnn hf + if ($height<80000) { + // elapsed time between the last 20 blocks + $first = $db->row("SELECT `date` FROM blocks ORDER by height DESC LIMIT $limit,1"); + $time = $current['date'] - $first['date']; - // avg block time - $result = ceil($time / $limit); + // avg block time + $result = ceil($time / $limit); + _log("Block time: $result", 3); - // if larger than 200 sec, increase by 5% - if ($result > 220) { - $dif = bcmul($current['difficulty'], 1.05); - } elseif ($result < 260) { - // if lower, decrease by 5% - $dif = bcmul($current['difficulty'], 0.95); + + // if larger than 200 sec, increase by 5% + if ($result > 220) { + $dif = bcmul($current['difficulty'], 1.05); + } elseif ($result < 260) { + // if lower, decrease by 5% + $dif = bcmul($current['difficulty'], 0.95); + } else { + // keep current difficulty + $dif = $current['difficulty']; + } } else { - // keep current difficulty - $dif = $current['difficulty']; + // hardfork 80000, fix difficulty targetting + + + + $type=$height%3; + // for mn, we use gpu diff + if ($type == 2) { + return $current['difficulty']; + } + + $blks=0; + $total_time=0; + $blk = $db->run("SELECT `date`, height FROM blocks ORDER by height DESC LIMIT 60"); + for ($i=0;$i<59;$i++) { + $ctype=$blk[$i+1]['height']%3; + $time=$blk[$i]['date']-$blk[$i+1]['date']; + if ($type!=$ctype) { + continue; + } + $blks++; + $total_time+=$time; + } + $result=ceil($total_time/$blks); + _log("Block time: $result", 3); + + // if larger than 260 sec, increase by 5% + if ($result > 260) { + $dif = bcmul($current['difficulty'], 1.05); + } elseif ($result < 220) { + // if lower, decrease by 5% + $dif = bcmul($current['difficulty'], 0.95); + } else { + // keep current difficulty + $dif = $current['difficulty']; + } } + + + + + + if (strpos($dif, '.') !== false) { $dif = substr($dif, 0, strpos($dif, '.')); } @@ -183,7 +272,7 @@ class Block if ($dif > 9223372036854775800) { $dif = 9223372036854775800; } - + _log("Difficulty: $dif", 3); return $dif; } @@ -233,8 +322,13 @@ class Block return false; } $acc = new Account(); - // generator's public key must be valid + if($data['date']>time()+30){ + _log("Future block - $data[date] $data[public_key]",2); + return false; + } + + // generator's public key must be valid if (!$acc->valid_key($data['public_key'])) { _log("Invalid public key - $data[public_key]"); return false; @@ -247,7 +341,7 @@ class Block } //check the argon hash and the nonce to produce a valid block - if (!$this->mine($data['public_key'], $data['nonce'], $data['argon'])) { + if (!$this->mine($data['public_key'], $data['nonce'], $data['argon'], $data['difficulty'], 0, 0, $data['date'])) { _log("Mine check failed"); return false; } @@ -322,27 +416,148 @@ class Block } return true; } + + public function blacklist_masternodes() + { + global $db; + _log("Checking if there are masternodes to be blacklisted", 2); + $current = $this->current(); + if (($current['height']-1)%3!=2) { + _log("bad height"); + return; + } + $last=$this->get($current['height']-1); + $total_time=$current['date']-$last['date']; + _log("blacklist total time $total_time"); + if ($total_time<=600) { + return; + } + $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", + [":current"=>$last['height'], ":start"=>$last['height']-360] + ); + _log(json_encode($ban)); + $i=0; + foreach ($ban as $b) { + $this->masternode_log($b['public_key'], $current['height'], $current['id']); + _log("Blacklisting masternode - $i $b[public_key]", 2); + $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)*10)]); + $i++; + } + } // check if the arguments are good for mining a specific block - public function mine($public_key, $nonce, $argon, $difficulty = 0, $current_id = 0, $current_height = 0) + public function mine($public_key, $nonce, $argon, $difficulty = 0, $current_id = 0, $current_height = 0, $time=0) { global $_config; + + // invalid future blocks + if($time>time()+30){ + return false; + } + + // if no id is specified, we use the current - if ($current_id === 0) { + if ($current_id === 0 || $current_height === 0) { $current = $this->current(); $current_id = $current['id']; $current_height = $current['height']; } + _log("Block Timestamp $time", 3); + if ($time == 0) { + $time=time(); + } // get the current difficulty if empty if ($difficulty === 0) { $difficulty = $this->difficulty(); } - - // the argon parameters are hardcoded to avoid any exploits - if ($current_height > 10800) { - $argon = '$argon2i$v=19$m=524288,t=1,p=1'.$argon; //10800 block hard fork - resistance against gpu + + if (empty($public_key)) { + _log("Empty public key", 1); + return false; + } + + if ($current_height<80000) { + + // the argon parameters are hardcoded to avoid any exploits + if ($current_height > 10800) { + _log("Block below 80000 but after 10800, using 512MB argon", 2); + $argon = '$argon2i$v=19$m=524288,t=1,p=1'.$argon; //10800 block hard fork - resistance against gpu + } else { + _log("Block below 10800, using 16MB argon", 2); + $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; + } } else { - $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; + _log("Block > 80000 - $current_height", 2); + if ($current_height%3==0) { + // cpu mining + _log("CPU Mining - $current_height", 2); + $argon = '$argon2i$v=19$m=524288,t=1,p=1'.$argon; + } elseif ($current_height%3==1) { + // gpu mining + _log("GPU Mining - $current_height", 2); + $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; + } else { + _log("Masternode Mining - $current_height", 2); + // masternode + global $db; + + // fake time + if ($time>time()) { + _log("Masternode block in the future - $time", 1); + return false; + } + + // selecting the masternode winner in order + $winner=$db->single( + "SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 1", + [":current"=>$current_height, ":start"=>$current_height-360] + ); + + // if there are no active masternodes, give the block to gpu + if ($winner===false) { + _log("No active masternodes, reverting to gpu", 1); + $argon = '$argon2i$v=19$m=16384,t=4,p=4'.$argon; + } else { + _log("The first masternode winner should be $winner", 1); + // 4 mins need to pass since last block + $last_time=$db->single("SELECT `date` FROM blocks WHERE height=:height", [":height"=>$current_height]); + if ($time-$last_time<240&&$_config['testnet']==false) { + _log("4 minutes have not passed since the last block - $time", 1); + return false; + } + + if ($public_key==$winner) { + return true; + } + // if 10 mins have passed, try to give the block to the next masternode and do this every 10mins + _log("Last block time: $last_time, difference: ".($time-$last_time), 3); + if ($time-$last_time>600) { + _log("Current public_key $public_key", 3); + $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", + [":current"=>$current_height, ":start"=>$current_height-360] + ); + _log("Moving to the next masternode - $tem - $winner", 1); + // if all masternodes are dead, give the block to gpu + if ($winner===false) { + _log("All masternodes failed, giving the block to gpu", 1); + $argon = '$argon2i$v=19$m=16384,t=1,p=1'.$argon; + } elseif ($winner==$public_key) { + return true; + } else { + return false; + } + } else { + _log("A different masternode should win this block $public_key - $winner", 2); + return false; + } + } + } } // the hash base for agon @@ -351,6 +566,7 @@ class Block // check argon's hash validity if (!password_verify($base, $argon)) { + _log("Argon verify failed - $base - $argon", 2); return false; } @@ -390,7 +606,7 @@ class Block // parse the block transactions - public function parse_block($block, $height, $data, $test = true) + public function parse_block($block, $height, $data, $test = true, $bootstrapping=false) { global $db; // data must be array @@ -411,37 +627,50 @@ class Block } $balance = []; + $mns = []; + foreach ($data as &$x) { // get the sender's account if empty if (empty($x['src'])) { $x['src'] = $acc->get_address($x['public_key']); } + if (!$bootstrapping) { + //validate the transaction + if (!$trx->check($x, $height)) { + return false; + } + if ($x['version']>=100&&$x['version']<110) { + $mns[] = $x['public_key']; + } + - //validate the transaction - if (!$trx->check($x, $height)) { - return false; - } + // prepare total balance + $balance[$x['src']] += $x['val'] + $x['fee']; - // prepare total balance - $balance[$x['src']] += $x['val'] + $x['fee']; - - // check if the transaction is already on the blockchain - if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) { - return false; + // check if the transaction is already on the blockchain + if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) { + return false; + } } } + //only a single masternode transaction per block for any masternode + if (count($mns) != count(array_unique($mns))) { + _log("Too many masternode transactions", 3); + return false; + } - // check if the account has enough balance to perform the transaction - foreach ($balance as $id => $bal) { - $res = $db->single( + if (!$bootstrapping) { + // check if the account has enough balance to perform the transaction + foreach ($balance as $id => $bal) { + $res = $db->single( "SELECT COUNT(1) FROM accounts WHERE id=:id AND balance>=:balance", [":id" => $id, ":balance" => $bal] ); - if ($res == 0) { - return false; // not enough balance for the transactions + if ($res == 0) { + return false; // not enough balance for the transactions + } } } - // if the test argument is false, add the transactions to the blockchain if ($test == false) { foreach ($data as $d) { @@ -511,7 +740,7 @@ class Block return; } $db->beginTransaction(); - $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE"); + $db->exec("LOCK TABLES blocks WRITE, accounts WRITE, transactions WRITE, mempool WRITE, masternode WRITE"); foreach ($r as $x) { $res = $trx->reverse($x['id']); if ($res === false) { @@ -536,7 +765,6 @@ class Block // delete specific block public function delete_id($id) { - global $db; $trx = new Transaction(); @@ -574,7 +802,6 @@ class Block // sign a new block, used when mining public function sign($generator, $height, $date, $nonce, $data, $key, $difficulty, $argon) { - $json = json_encode($data); $info = "{$generator}-{$height}-{$date}-{$nonce}-{$json}-{$difficulty}-{$argon}"; @@ -612,6 +839,10 @@ class Block $r = $db->run("SELECT * FROM transactions WHERE version>0 AND block=:block", [":block" => $block['id']]); $transactions = []; foreach ($r as $x) { + if ($x['version']>110) { + //internal transactions + continue; + } $trans = [ "id" => $x['id'], "dst" => $x['dst'], diff --git a/include/config.inc.php b/include/config.inc.php index ce26506..4017986 100755 --- a/include/config.inc.php +++ b/include/config.inc.php @@ -36,3 +36,9 @@ $_config['sanity_rebroadcast_locals']=true; $_config['enable_logging']=false; // log file, should not be publicly viewable $_config['log_file']="/var/log/aro.log"; +//log verbosity, default 0, maximum 3 +$_config['log_verbosity']=0; +//will this run as a masternode? +$_config['masternode']=false; +//masternode public key +$_config['masternode_public_key']=""; diff --git a/include/db.inc.php b/include/db.inc.php index 48651bb..15465c9 100755 --- a/include/db.inc.php +++ b/include/db.inc.php @@ -31,6 +31,7 @@ class DB extends PDO private function debug() { + global $_config; if (!$this->debugger) { return; } @@ -56,9 +57,9 @@ class DB extends PDO $msg .= "\n\n$key:\n$val"; } - if ($this->debugger) { - echo nl2br($msg); - } + + _log($msg); + } private function cleanup($bind, $sql = "") diff --git a/include/functions.inc.php b/include/functions.inc.php index ac42d9a..3d18d19 100755 --- a/include/functions.inc.php +++ b/include/functions.inc.php @@ -37,7 +37,7 @@ function api_echo($data) } // log function, shows only in cli atm -function _log($data) +function _log($data, $verbosity = 0) { $date = date("[Y-m-d H:i:s]"); $trace = debug_backtrace(); @@ -57,7 +57,7 @@ function _log($data) echo $res; } global $_config; - if ($_config['enable_logging'] == true) { + if ($_config['enable_logging'] == true && $_config['log_verbosity'] >= $verbosity) { @file_put_contents($_config['log_file'], $res, FILE_APPEND); } } diff --git a/include/init.inc.php b/include/init.inc.php index d1a54b1..72af49a 100755 --- a/include/init.inc.php +++ b/include/init.inc.php @@ -1,8 +1,8 @@ run("ALTER TABLE `transactions` ADD KEY `dst` (`dst`), ADD KEY `height` (`height`), ADD KEY `public_key` (`public_key`);"); $dbversion++; } +if ($dbversion == 8) { + $db->run("CREATE TABLE `masternode` ( + `public_key` varchar(128) COLLATE utf8mb4_bin NOT NULL, + `height` int(11) NOT NULL, + `ip` varchar(16) COLLATE utf8mb4_bin NOT NULL, + `last_won` int(11) NOT NULL DEFAULT '0', + `blacklist` int(11) NOT NULL DEFAULT '0', + `fails` int(11) NOT NULL DEFAULT '0', + `status` tinyint(4) NOT NULL DEFAULT '1' + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;"); + + $db->run("ALTER TABLE `masternode` + ADD PRIMARY KEY (`public_key`), + ADD KEY `last_won` (`last_won`), + ADD KEY `status` (`status`), + ADD KEY `blacklist` (`blacklist`), + ADD KEY `height` (`height`);"); + $dbversion++; +} diff --git a/include/transaction.inc.php b/include/transaction.inc.php index 1663307..1e4e25b 100755 --- a/include/transaction.inc.php +++ b/include/transaction.inc.php @@ -7,16 +7,24 @@ class Transaction { global $db; $acc = new Account(); - $r = $db->run("SELECT * FROM transactions WHERE block=:block", [":block" => $block]); + $r = $db->run("SELECT * FROM transactions WHERE block=:block ORDER by `version` ASC", [":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", - [":id" => $x['dst'], ":val" => $x['val']] - ); - + if ($x['version'] == 2) { + // payment sent to alias + $db->run( + "UPDATE accounts SET balance=balance-:val WHERE alias=:alias", + [":alias" => $x['dst'], ":val" => $x['val']] + ); + } else { + // other type of transactions + $db->run( + "UPDATE accounts SET balance=balance-:val WHERE id=:id", + [":id" => $x['dst'], ":val" => $x['val']] + ); + } // on version 0 / reward transaction, don't credit anyone if ($x['version'] > 0) { $db->run( @@ -24,9 +32,57 @@ class Transaction [":id" => $x['src'], ":val" => $x['val'] + $x['fee']] ); } + // removing the alias if the alias transaction is reversed + if ($x['version']==3) { + $db->run( + "UPDATE accounts SET alias=NULL WHERE id=:id", + [":id" => $x['src']] + ); + } + + 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']]); + } elseif ($x['version']==101) { + $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']]); + } 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( + "SELECT `version` FROM transactions WHERE (version=101 or version=102) AND public_key=:public_key AND height>:height ORDER by height DESC LIMIT 1", + [":public_key"=>$x['public_key'],":height"=>$mnt['height']] + ); + + $status=1; + + if ($vers==101) { + $status=0; + } + + $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']]); + } + } + // internal masternode history + if ($x['version']==111) { + $m=explode(",", $x['message']); + + $db->run( + "UPDATE masternode SET fails=:fails, blacklist=:blacklist, last_won=:last_won WHERE public_key=:public_key", + [":public_key"=>$x['public_key'], ":blacklist"=> $m[0], ":fails"=>$m[2], ":last_won"=>$m[1]] + ); + } + // add the transactions to mempool - if ($x['version'] > 0) { + if ($x['version'] > 0 && $x['version']<=1000) { $this->add_mempool($x); } $res = $db->run("DELETE FROM transactions WHERE id=:id", [":id" => $x['id']]); @@ -92,7 +148,7 @@ class Transaction _log("$x[id] - Transaction Check Failed"); continue; } - + $balance[$x['src']] += $x['val'] + $x['fee']; if ($db->single("SELECT COUNT(1) FROM transactions WHERE id=:id", [":id" => $x['id']]) > 0) { _log("$x[id] - Duplicate transaction"); @@ -124,6 +180,9 @@ class Transaction { global $db; $block = new Block(); + if ($x['version']>110) { + return true; + } $current = $block->current(); $height = $current['height']; $x['id'] = san($x['id']); @@ -141,6 +200,16 @@ class Transaction ":date" => $x['date'], ":message" => $x['message'], ]; + + //only a single masternode command of same type, per block + if ($x['version']>=100&&$x['version']<110) { + $check=$db->single("SELECT COUNT(1) FROM mempool WHERE public_key=:public_key", [":public_key"=>$x['public_key']]); + if ($check!=0) { + _log("Masternode transaction already in mempool", 3); + return false; + } + } + $db->run( "INSERT into mempool SET peer=:peer, id=:id, public_key=:public_key, height=:height, src=:src, dst=:dst, val=:val, fee=:fee, signature=:signature, version=:version, message=:message, `date`=:date", $bind @@ -154,7 +223,9 @@ class Transaction global $db; $acc = new Account(); $acc->add($x['public_key'], $block); - $acc->add_id($x['dst'], $block); + if ($x['version']==1) { + $acc->add_id($x['dst'], $block); + } $x['id'] = san($x['id']); $bind = [ ":id" => $x['id'], @@ -176,7 +247,21 @@ class Transaction if ($res != 1) { return false; } - $db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']]); + if ($x['version'] == 2&&$height>=80000) { + $db->run("UPDATE accounts SET balance=balance+:val WHERE alias=:alias", [":alias" => $x['dst'], ":val" => $x['val']]); + } elseif ($x['version']==100&&$height>=80000) { + //master node deposit + } elseif ($x['version']==103&&$height>=80000) { + $blk=new Block(); + $blk->masternode_log($x['public_key'],$height,$block); + + //master node withdrawal + } else { + $db->run("UPDATE accounts SET balance=balance+:val WHERE id=:id", [":id" => $x['dst'], ":val" => $x['val']]); + } + + + // no debit when the transaction is reward if ($x['version'] > 0) { $db->run( @@ -184,6 +269,36 @@ class Transaction [":id" => $x['src'], ":val" => $x['val'], ":fee" => $x['fee']] ); } + + + // set the alias + if ($x['version']==3&&$height>=80000) { + $db->run( + "UPDATE accounts SET alias=:alias WHERE id=:id", + [":id" => $x['src'], ":alias"=>$x['message']] + ); + } + + + if ($x['version']>=100&&$x['version']<110&&$height>=80000) { + $message=$x['message']; + $message=preg_replace("/[^0-9\.]/", "", $message); + if ($x['version']==100) { + $db->run("INSERT into masternode SET `public_key`=:public_key, `height`=:height, `ip`=:ip, `status`=1", [":public_key"=>$x['public_key'], ":height"=>$height, ":ip"=>$message]); + } else { + if ($x['version']==101) { + $db->run("UPDATE masternode SET status=0 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + } elseif ($x['version']==102) { + $db->run("UPDATE masternode SET status=1 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + } elseif ($x['version']==103) { + $db->run("DELETE FROM masternode WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + $db->run("UPDATE accounts SET balance=balance+100000 WHERE public_key=:public_key", [':public_key'=>$x['public_key']]); + } + } + } + + + $db->run("DELETE FROM mempool WHERE id=:id", [":id" => $x['id']]); return true; } @@ -208,15 +323,25 @@ class Transaction $acc = new Account(); $info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']; + // hard fork at 80000 to implement alias, new mining system, assets + // if($x['version']>1 && $height<80000){ + // return false; + // } + + // internal transactions + if($x['version']>110){ + return false; + } + // the value must be >=0 if ($x['val'] < 0) { - _log("$x[id] - Value below 0"); + _log("$x[id] - Value below 0", 3); return false; } // the fee must be >=0 if ($x['fee'] < 0) { - _log("$x[id] - Fee below 0"); + _log("$x[id] - Fee below 0", 3); return false; } @@ -226,46 +351,115 @@ class Transaction if ($fee < 0.00000001) { $fee = 0.00000001; } + //alias fee + if ($x['version']==3&&$height>=80000) { + $fee=10; + if (!$acc->free_alias($x['message'])) { + _log("Alias not free", 3); + return false; + } + // alias can only be set once per account + if ($acc->has_alias($x['public_key'])) { + _log("The account already has an alias", 3); + return false; + } + } + + //masternode transactions + + if ($x['version']>=100&&$x['version']<110&&$height>=80000) { + if ($x['version']==100) { + $message=$x['message']; + $message=preg_replace("/[^0-9\.]/", "", $message); + if (!filter_var($message, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { + _log("The Masternode IP is invalid", 3); + return false; + } + } + + + if ($x['version']==100&&$x['val']!=100000) { + _log("The masternode transaction is not 100k", 3); + return false; + } elseif ($x['version']!=100) { + $mn=$acc->get_masternode($x['public_key']); + + if (!$mn) { + _log("The masternode does not exist", 3); + return false; + } + if ($x['version']==101&&$mn['status']!=1) { + _log("The masternode does is not running", 3); + return false; + } elseif ($x['version']==102 && $mn['status']!=0) { + _log("The masternode is not paused", 3); + return false; + } elseif ($x['version']==103) { + if ($mn['status']!=0) { + _log("The masternode is not paused", 3); + return false; + } elseif ($height-$mn['last_won']<10800) { //10800 + _log("The masternode last won block is less than 10800 blocks", 3); + return false; + } elseif ($height-$mn['height']<32400) { //32400 + _log("The masternode start height is less than 32400 blocks! $height - $mn[height]", 3); + return false; + } + } + } + } + + // max fee after block 10800 is 10 if ($height > 10800 && $fee > 10) { $fee = 10; //10800 } // added fee does not match if ($fee != $x['fee']) { - _log("$x[id] - Fee not 0.25%"); + _log("$x[id] - Fee not 0.25%", 3); + _log(json_encode($x), 3); return false; } - // invalid destination address - if (!$acc->valid($x['dst'])) { - _log("$x[id] - Invalid destination address"); - return false; + if ($x['version']==1) { + // invalid destination address + if (!$acc->valid($x['dst'])) { + _log("$x[id] - Invalid destination address", 3); + return false; + } + } elseif ($x['version']==2&&$height>=80000) { + if (!$acc->valid_alias($x['dst'])) { + _log("$x[id] - Invalid destination alias", 3); + return false; + } } + // reward transactions are not added via this function if ($x['version'] < 1) { - _log("$x[id] - Invalid version <1"); + _log("$x[id] - Invalid version <1", 3); return false; } //if($x['version']>1) { _log("$x[id] - Invalid version >1"); return false; } // public key must be at least 15 chars / probably should be replaced with the validator function if (strlen($x['public_key']) < 15) { - _log("$x[id] - Invalid public key size"); + _log("$x[id] - Invalid public key size", 3); return false; } // no transactions before the genesis if ($x['date'] < 1511725068) { - _log("$x[id] - Date before genesis"); + _log("$x[id] - Date before genesis", 3); return false; } // no future transactions if ($x['date'] > time() + 86400) { - _log("$x[id] - Date in the future"); + _log("$x[id] - Date in the future", 3); return false; } // prevent the resending of broken base58 transactions if ($height > 16900 && $x['date'] < 1519327780) { + _log("$x[id] - Broken base58 transaction", 3); return false; } $id = $this->hash($x); @@ -284,7 +478,7 @@ class Transaction //verify the ecdsa signature if (!$acc->check_signature($info, $x['signature'], $x['public_key'])) { - _log("$x[id] - Invalid signature"); + _log("$x[id] - Invalid signature - $info"); return false; } @@ -295,6 +489,7 @@ class Transaction public function sign($x, $private_key) { $info = $x['val']."-".$x['fee']."-".$x['dst']."-".$x['message']."-".$x['version']."-".$x['public_key']."-".$x['date']; + $signature = ec_sign($info, $private_key); return $signature; @@ -339,7 +534,7 @@ class Transaction if ($x['version'] == 0) { $trans['type'] = "mining"; - } elseif ($x['version'] == 1) { + } elseif ($x['version'] == 1 || $x['version'] == 2) { if ($x['dst'] == $id) { $trans['type'] = "credit"; } else { @@ -371,6 +566,9 @@ class Transaction } $res = []; foreach ($r as $x) { + if ($x['version']>110) { + continue; //internal transactions + } $trans = [ "block" => $x['block'], "height" => $x['height'], @@ -389,7 +587,7 @@ class Transaction if ($x['version'] == 0) { $trans['type'] = "mining"; - } elseif ($x['version'] == 1) { + } elseif ($x['version'] == 1||$x['version'] == 2) { if ($x['dst'] == $id) { $trans['type'] = "credit"; } else { diff --git a/mine.php b/mine.php index 3f3da8b..6c28e64 100755 --- a/mine.php +++ b/mine.php @@ -45,11 +45,43 @@ if ($q == "info") { $diff = $block->difficulty(); $current = $block->current(); + $current_height=$current['height']; + $recommendation="mine"; + $argon_mem=16384; + $argon_threads=4; + $argon_time=4; + if ($current_height<80000) { + if ($current_height > 10800) { + $argon_mem=524288; + $argon_threads=1; + $argon_time=1; + } + } else { + if ($current_height%3==0) { + $argon_mem=524288; + $argon_threads=1; + $argon_time=1; + } elseif ($current_height%3==2) { + global $db; + $winner=$db->single( + "SELECT public_key FROM masternode WHERE status=1 AND blacklist<:current AND height<:start ORDER by last_won ASC, public_key ASC LIMIT 1", + [":current"=>$current_height, ":start"=>$current_height-360] + ); + $recommendation="pause"; + if ($winner===false) { + $recommendation="mine"; + } + } + } $res = [ "difficulty" => $diff, "block" => $current['id'], "height" => $current['height'], "testnet" => $_config['testnet'], + "recommendation"=> $recommendation, + "argon_mem" => $argon_mem, + "argon_threads" => $argon_threads, + "argon_time" => $argon_time, ]; api_echo($res); exit; diff --git a/peer.php b/peer.php index 5d70627..22a8b34 100755 --- a/peer.php +++ b/peer.php @@ -44,6 +44,14 @@ if ($q == "peer") { // sanitize the hostname $hostname = filter_var($data['hostname'], FILTER_SANITIZE_URL); + $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://","//"], "", $hostname); + foreach ($bad_peers as $bp) { + if (strpos($tpeer, $bp)===0) { + api_err("invalid-hostname"); + } + } + if (!filter_var($hostname, FILTER_VALIDATE_URL)) { api_err("invalid-hostname"); } @@ -166,17 +174,14 @@ if ($q == "peer") { if ($current['height'] == $data['height'] && $current['id'] != $data['id']) { // different forks, same height $accept_new = false; - if ($current['transactions'] < $data['transactions']) { - // accept the one with most transactions - $accept_new = true; - } elseif ($current['transactions'] == $data['transactions']) { + // convert the first 12 characters from hex to decimal and the block with the largest number wins $no1 = hexdec(substr(coin2hex($current['id']), 0, 12)); $no2 = hexdec(substr(coin2hex($data['id']), 0, 12)); if (gmp_cmp($no1, $no2) == 1) { $accept_new = true; } - } + if ($accept_new) { // if the new block is accepted, run a microsanity to sync it _log('['.$ip."] Starting microsanity - $data[height]"); diff --git a/propagate.php b/propagate.php index d54a3c9..f9c7114 100755 --- a/propagate.php +++ b/propagate.php @@ -67,6 +67,7 @@ if ((empty($peer) || $peer == 'all') && $type == "block") { } $r = $db->run("SELECT * FROM peers WHERE blacklisted < UNIX_TIMESTAMP() AND reserve=0 $ewhr"); foreach ($r as $x) { + if($x['hostname']==$_config['hostname']) continue; // encode the hostname in base58 and sanitize the IP to avoid any second order shell injections $host = base58_encode($x['hostname']); $ip = filter_var($x['ip'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE); @@ -105,6 +106,7 @@ if ($type == "block") { // send the block as POST to the peer echo "Block sent to $hostname:\n"; $response = peer_post($hostname."/peer.php?q=submitBlock", $data, 60, $debug); + _log("Propagating block to $hostname - [result: $response] $data[height] - $data[id]",2); if ($response == "block-ok") { echo "Block $i accepted. Exiting.\n"; exit; diff --git a/sanity.php b/sanity.php index 9ad151f..5debf50 100755 --- a/sanity.php +++ b/sanity.php @@ -70,10 +70,72 @@ if ($_config['dbversion'] < 2) { exit; } +ini_set('memory_limit', '2G'); + $block = new Block(); $acc = new Account(); $current = $block->current(); +// bootstrapping the initial sync +if ($current['height']==1) { + echo "Bootstrapping!\n"; + $last=file_get_contents("http://dumps.arionum.com/last"); + $last=intval($last); + $failed_sync=false; + for ($i=1000;$i<=$last;$i=$i+1000) { + echo "Download file $i\n"; + $res=trim(file_get_contents("http://dumps.arionum.com/aro.db.$i")); + if ($res===false) { + echo "Could not download the bootstrap file $i. Syncing the old fashioned way.\n"; + break; + } + $data=json_decode($res, true); + + if ($data===false||is_null($data)) { + echo "Could not parse the bootstrap file $i. Syncing the old fashioned way.\n"; + echo json_last_error_msg(); + break; + } + foreach ($data as $x) { + if (count($x['data'])>0) { + $transactions=[]; + + foreach ($x['data'] as $d) { + $trans = [ + "id" => $d[0], + "dst" => $d[1], + "val" => $d[2], + "fee" => $d[3], + "signature" => $d[4], + "message" => $d[5], + "version" => $d[6], + "date" => $d[7], + "public_key" => $d[8], + ]; + ksort($trans); + $transactions[$d[0]] = $trans; + } + ksort($transactions); + $x['data']=$transactions; + } + + + echo "-> Adding block $x[height]\n"; + + $res=$block->add($x['height'], $x['public_key'], $x['nonce'], $x['data'], $x['date'], $x['signature'], $x['difficulty'], $x['reward_signature'], $x['argon'], true); + if (!$res) { + echo "Error: Adding the block failed. Syncing the old way.\n"; + $failed_sync=true; + break; + } + } + if ($failed_sync) { + break; + } + } + + $current = $block->current(); +} // the microsanity process is an anti-fork measure that will determine the best blockchain to choose for the last block $microsanity = false; if ($arg == "microsanity" && !empty($arg2)) { @@ -104,19 +166,16 @@ if ($arg == "microsanity" && !empty($arg2)) { } // the blockchain with the most transactions wins the fork (to encourage the miners to include as many transactions as possible) / might backfire on garbage - if ($current['transactions'] > $data['transactions']) { - echo "Block has less transactions\n"; - break; - } elseif ($current['transactions'] == $data['transactions']) { - // transform the first 12 chars into an integer and choose the blockchain with the biggest value - $no1 = hexdec(substr(coin2hex($current['id']), 0, 12)); - $no2 = hexdec(substr(coin2hex($data['id']), 0, 12)); - if (gmp_cmp($no1, $no2) != -1) { - echo "Block hex larger than current\n"; - break; - } + // transform the first 12 chars into an integer and choose the blockchain with the biggest value + $no1 = hexdec(substr(coin2hex($current['id']), 0, 12)); + $no2 = hexdec(substr(coin2hex($data['id']), 0, 12)); + + if (gmp_cmp($no1, $no2) != -1) { + echo "Block hex larger than current\n"; + break; } + // make sure the block is valid $prev = $block->get($current['height'] - 1); $public = $acc->public_key($data['generator']); @@ -126,7 +185,8 @@ if ($arg == "microsanity" && !empty($arg2)) { $data['argon'], $block->difficulty($current['height'] - 1), $prev['id'], - $prev['height'] + $prev['height'], + $data['date'] )) { echo "Invalid prev-block\n"; break; @@ -142,6 +202,8 @@ if ($arg == "microsanity" && !empty($arg2)) { // add the new block echo "Starting to sync last block from $x[hostname]\n"; $b = $data; + + $res = $block->add( $b['height'], $b['public_key'], @@ -206,10 +268,15 @@ if ($total_peers == 0 && $_config['testnet'] == false) { foreach ($f as $peer) { //peer with all until max_peers, this will ask them to send a peering request to our peer.php where we add their peer to the db. $peer = trim(san_host($peer)); - $bad_peers = ["127.0.0.1", "localhost", "10.0.0", "192.168.0"]; - if (str_replace($bad_peers, "", $peer) != $peer) { - continue; + $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); + foreach ($bad_peers as $bp) { + if (strpos($tpeer, $bp)===0) { + continue; + } } + $peer = filter_var($peer, FILTER_SANITIZE_URL); if (!filter_var($peer, FILTER_VALIDATE_URL)) { continue; @@ -269,9 +336,12 @@ foreach ($r as $x) { continue; } $peered[$pid] = 1; - $bad_peers = ["127.0.0.1", "localhost", "10.0.0.", "192.168.0."]; - if (str_replace($bad_peers, "", $peer['hostname']) != $peer['hostname']) { - continue; + $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']) { @@ -350,18 +420,14 @@ foreach ($r as $x) { $largest_height = $data['height']; $largest_height_block = $data['id']; } else { - // if this block has more transactions, declare it as winner - if ($blocks[$largest_height_block]['transactions'] < $data['transactions']) { + + + // if the blocks have the same number of transactions, choose the one with the highest derived integer from the first 12 hex characters + $no1 = hexdec(substr(coin2hex($largest_height_block), 0, 12)); + $no2 = hexdec(substr(coin2hex($data['id']), 0, 12)); + if (gmp_cmp($no1, $no2) == 1) { $largest_height = $data['height']; $largest_height_block = $data['id']; - } elseif ($blocks[$largest_height_block]['transactions'] == $data['transactions']) { - // if the blocks have the same number of transactions, choose the one with the highest derived integer from the first 12 hex characters - $no1 = hexdec(substr(coin2hex($largest_height_block), 0, 12)); - $no2 = hexdec(substr(coin2hex($data['id']), 0, 12)); - if (gmp_cmp($no1, $no2) == 1) { - $largest_height = $data['height']; - $largest_height_block = $data['id']; - } } } } elseif ($data['difficulty'] < $blocks[$largest_height_block]['difficulty']) { @@ -428,6 +494,9 @@ if ($current['height'] < $largest_height && $largest_height > 1) { $last_good = $i; } } + if ($last_good==$current['height']-1&&$last_good%3==2) { + $block->pop(1); + } // if last 10 blocks are good, verify all the blocks if ($invalid == false) { $cblock = []; @@ -441,13 +510,17 @@ if ($current['height'] < $largest_height && $largest_height > 1) { } // check if the block mining data is correct for ($i = $last_good + 1; $i <= $largest_height; $i++) { + if (($i-1)%3==2) { + continue; + } if (!$block->mine( $cblock[$i]['public_key'], $cblock[$i]['nonce'], $cblock[$i]['argon'], $cblock[$i]['difficulty'], $cblock[$i - 1]['id'], - $cblock[$i - 1]['height'] + $cblock[$i - 1]['height'], + $cblock[$i]['date'] )) { $invalid = true; break; @@ -456,6 +529,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) { } // if the blockchain proves ok, delete until the last block if ($invalid == false) { + _log("Changing fork, deleting $last_good", 1); $block->delete($last_good); $current = $block->current(); $data = $current; @@ -477,7 +551,7 @@ if ($current['height'] < $largest_height && $largest_height > 1) { foreach ($data as $b) { $b['id'] = san($b['id']); $b['height'] = san($b['height']); - + if (!$block->check($b)) { _log("Block check: could not add block - $b[id] - $b[height]"); $good_peer = false; @@ -591,7 +665,7 @@ foreach ($f as $x) { //recheck the last blocks -if ($_config['sanity_recheck_blocks'] > 0) { +if ($_config['sanity_recheck_blocks'] > 0 && $_config['testnet'] == false) { _log("Rechecking blocks"); $blocks = []; $all_blocks_ok = true; @@ -616,7 +690,8 @@ if ($_config['sanity_recheck_blocks'] > 0) { $data['argon'], $data['difficulty'], $blocks[$i - 1]['id'], - $blocks[$i - 1]['height'] + $blocks[$i - 1]['height'], + $data['date'] )) { $db->run("UPDATE config SET val=1 WHERE cfg='sanity_sync'"); _log("Invalid block detected. Deleting everything after $data[height] - $data[id]"); diff --git a/util.php b/util.php index 7fe4b84..ef6f065 100755 --- a/util.php +++ b/util.php @@ -43,10 +43,12 @@ $cmd = trim($argv[1]); */ if ($cmd == 'clean') { - $tables = ["blocks", "accounts", "transactions", "mempool"]; + $db->run("SET foreign_key_checks=0;"); + $tables = ["accounts", "transactions", "mempool", "masternode","blocks"]; foreach ($tables as $table) { - $db->run("DELETE FROM {$table}"); + $db->run("TRUNCATE TABLE {$table}"); } + $db->run("SET foreign_key_checks=1;"); echo "\n The database has been cleared\n"; } /** @@ -214,7 +216,8 @@ elseif ($cmd == "recheck-blocks") { $data['argon'], $data['difficulty'], $blocks[$i - 1]['id'], - $blocks[$i - 1]['height'] + $blocks[$i - 1]['height'], + $data['date'] )) { _log("Invalid block detected. We should delete everything after $data[height] - $data[id]"); break; @@ -408,7 +411,8 @@ elseif ($cmd == "check-address") { } echo "The address is valid\n"; -} /** +} +/** * @api {php util.php} get-address Get-Address * @apiName get-address * @apiGroup UTIL @@ -429,6 +433,20 @@ elseif ($cmd == 'get-address') { die("Invalid public key"); } print($acc->get_address($public_key)); +/** + * @api {php util.php} clean-blacklist Clean-Blacklist + * @apiName clean-blacklist + * @apiGroup UTIL + * @apiDescription Removes all the peers from blacklist + * + * @apiExample {cli} Example usage: + * php util.php clean-blacklist + * + */ + +} elseif ($cmd == 'clean-blacklist') { + $db->run("UPDATE peers SET blacklisted=0, fails=0, stuckfail=0"); + echo "All the peers have been removed from the blacklist\n"; } else { echo "Invalid command\n"; }