Initial commit
This commit is contained in:
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user