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