From 89dd3fa0791776d24ab99c52e21985a473badd9d Mon Sep 17 00:00:00 2001 From: alexqrid <> Date: Fri, 4 Jul 2025 19:57:53 +0300 Subject: [PATCH] add zano support --- .env.example | 12 ++ Modules/Common/ZanoLikeMainModule.php | 269 ++++++++++++++++++++++++++ Modules/ZanoMainModule.php | 29 +++ 3 files changed, 310 insertions(+) create mode 100644 Modules/Common/ZanoLikeMainModule.php create mode 100644 Modules/ZanoMainModule.php diff --git a/.env.example b/.env.example index 0efb815..cddc400 100644 --- a/.env.example +++ b/.env.example @@ -1048,6 +1048,18 @@ MODULE_tron-trc-1155_NODES[]=http://login:password@127.0.0.1:1234/ MODULE_tron-trc-1155_REQUESTER_TIMEOUT=60 MODULE_tron-trc-1155REQUESTER_THREADS=2 +################## +# Main Zano Module +################## + +MODULES[]=zano-main +MODULE_zano-main_CLASS=ZanoMainModule +MODULE_zano-main_NODES[]=http://login:password@127.0.0.1:1234/ +MODULE_zano-main_NODES[]=http://login:password@127.0.0.2:1234/ +MODULE_zano-main_REQUESTER_TIMEOUT=60 +MODULE_zano-main_REQUESTER_THREADS=2 + + #################### ## Zcash Main Module #################### diff --git a/Modules/Common/ZanoLikeMainModule.php b/Modules/Common/ZanoLikeMainModule.php new file mode 100644 index 0000000..3b88cea --- /dev/null +++ b/Modules/Common/ZanoLikeMainModule.php @@ -0,0 +1,269 @@ +version = 1; + } + + final public function post_post_initialize() + { + // + } + + // + + final public function inquire_latest_block() + { + return (int)requester_single($this->select_node(), '/getheight')['height']; + } + + final public function ensure_block($block_id, $break_on_first = false) + { + $params = ['jsonrpc'=> '2.0', 'method' => 'getblockheaderbyheight', 'params' => ['height' => $block_id], 'id' => 0]; + + foreach ($this->nodes as $node) + { + $multi_curl[] = requester_multi_prepare($node, params: $params, endpoint: '/json_rpc', timeout: $this->timeout); + if ($break_on_first) break; + } + + try + { + $curl_results = requester_multi($multi_curl, limit: count($this->nodes), timeout: $this->timeout); + } + catch (RequesterException $e) + { + throw new RequesterException("ensure_block(block_id: {$block_id}): no connection, previously: " . $e->getMessage()); + } + + $result0 = requester_multi_process($curl_results[0], result_in: 'result'); + + $this->block_hash = $result0['block_header']['hash']; + $this->block_time = date('Y-m-d H:i:s', $result0['block_header']['timestamp']); + + if (count($curl_results) > 1) + { + foreach ($curl_results as $result) + { + if (requester_multi_process($result, result_in: 'result')['block_header']['hash'] !== $this->block_hash) + { + throw new ConsensusException("ensure_block(block_id: {$block_id}): no consensus"); + } + } + } + } + + final public function pre_process_block($block_id) + { + $transactions = null; + if ($block_id !== MEMPOOL) + { + $output = requester_single($this->select_node(), + params: ['jsonrpc' => '2.0', + 'method' => 'get_main_block_details', + 'id' => 0, + 'params' => [ 'id' => "{$this->block_hash}" ], + ], + endpoint: '/json_rpc', + result_in: 'result', + timeout: $this->timeout); + $transactions['txs'] = $output['block_details']['transactions_details']; + } + else // Processing mempool + { + $output = requester_single($this->select_node(), + params: ['jsonrpc' => '2.0', + 'method' => 'get_all_pool_tx_list', + 'id' => 0, + 'params' => [ 'id' => "{$this->block_hash}" ], + ], + endpoint: '/json_rpc', + result_in: 'result', + timeout: $this->timeout); + $mp = array_map(fn($id) => ['id' => $id], $output['ids']); + $transactions['txs'] = $mp; + } + + $multi_curl = []; + foreach ($transactions['txs'] as $tx) + { + $multi_curl[] = requester_multi_prepare( + $this->select_node(), + params: [ + 'jsonrpc' => '2.0', + 'method' => 'get_tx_details', + 'id' => 0, + 'params' => ['tx_hash' => $tx['id']], + ], + endpoint: "/json_rpc", + timeout: $this->timeout + ); + } + $multi_results = requester_multi_process_all( + requester_multi( + $multi_curl, + limit: envm($this->module, 'REQUESTER_THREADS'), + timeout: $this->timeout + ), + result_in: 'result', + reorder: false + ); + $transaction_details = []; + + foreach ($multi_results as $transaction) + { + $transaction_details[($transaction['tx_info']['id'])] = $transaction; + } + + $events = []; // This is an array for the results + $sort_key = 0; + + foreach ($transactions['txs'] as $transaction) + { + $hash = $transaction['id']; + + // if ($transaction['coinbase']) + // // there is something to do with such transactions + // // we can't trace to whom they are sent + // // But each block has a 1 ZANO reward + // // So for PoW blocks we would have 2 outputs and no inputs + // // https://explorer.zano.org/transaction/71e54a7fc1f92e9005b3a8d5ac5c92fa6d60cdd38d552c9637824f33d3c9b8df + // // For PoS blocks we have 1 input and 1 output + // // https://explorer.zano.org/transaction/a4a3ab82fb5d2d5454b45a3c7914d7326f2f9ac3dea9861d3f20c81a68073af4 + foreach ($transaction_details[$hash]['tx_info']['ins'] as $input) + { + $amount = $input['amount']; + + $events[] = [ + 'transaction' => $hash, + 'address' => 'shielded-pool', + 'effect' => "{$amount}", + 'sort_key' => $sort_key++, + ]; + } + + foreach ($transaction_details[$hash]['tx_info']['outs'] ?? [] as $output) + { + $amount = $output['amount']; + + $events[] = [ + 'transaction' => $hash, + 'address' => 'shielded-pool', + 'effect' => "{$amount}", + 'sort_key' => $sort_key++, + ]; + } + + // On Zano, all network fees are being burned, meaning that with enough network usage, + // the daily fee burning could surpass the emission from block rewards, + // resulting in supply becoming deflationary over time. + // https://docs.zano.org/docs/learn/emission/#emission-motivation + $events[] = [ + 'transaction' => $hash, + 'address' => 'the-void', + 'effect' => "{$transaction_details[$hash]['tx_info']['fee']}", + 'sort_key' => $sort_key++, + ]; + } + + foreach ($events as &$event) + { + $event['block'] = $block_id; + $event['time'] = ($block_id !== MEMPOOL) ? $this->block_time : date('Y-m-d H:i:s', time()); + } + + usort($events, function($a, $b) { + return $a['sort_key'] <=> $b['sort_key']; + }); + + $this->set_return_events($events); + } + + final public function api_get_transaction_specials(string $transaction): array + { + $transaction = requester_single( + $this->select_node(), + params: [ + 'jsonrpc' => '2.0', + 'method' => 'get_tx_details', + 'id' => 0, + 'params' => ['tx_hash' => $transaction], + ], + endpoint: "/json_rpc", + timeout: $this->timeout + )['result']; + + $specials = new Specials(); + + $specials->add('blob_size', (int)$transaction['tx_info']['blob_size'], function ($raw_value) { return "Blob size: {{$raw_value}} bytes"; }); + + $outs = $transaction['tx_info']['outs']; + $ins = $transaction['tx_info']['ins']; + + for ($i = 0; $i < count($ins); $i++) + { + $global_indexes = implode(",", $ins[$i]['global_indexes']); + $specials->add( + 'ins_' . ($i+1), + $ins[$i]['global_indexes'], + function () use ($global_indexes) { + return "Global Indexes: {[{$global_indexes}]}"; + } + ); + } + + foreach ($outs as $out) + { + $specials->add( + 'spent_global_index_' . $out['global_index'], + $out['is_spent'], + function () use ($out) { + return "Global Index {{$out['global_index']}} is " . ($out['is_spent'] ? "{spent}" : "{not spent}"); + } + ); + } + + $specials->add( + 'pub_key', + $transaction['tx_info']['pub_key'], + function ($pubkey) { + return "Tx pubkey {{$pubkey}}"; + } + ); + + return $specials->return(); + } + +} diff --git a/Modules/ZanoMainModule.php b/Modules/ZanoMainModule.php new file mode 100644 index 0000000..59f1026 --- /dev/null +++ b/Modules/ZanoMainModule.php @@ -0,0 +1,29 @@ +blockchain = 'zano'; + $this->module = 'zano-main'; + $this->is_main = true; + $this->currency = 'zano'; + $this->currency_details = ['name' => 'Zano', 'symbol' => 'ZANO', 'decimals' => 12, 'description' => null]; + $this->first_block_id = 0; + $this->first_block_date = '2019-05-08'; // TODO there is a genesis block, it has 0 timestamp + + $this->tests = [ + ['block' => 2185521, 'result' => 'a:2:{s:6:"events";a:13:{i:0;a:6:{s:11:"transaction";s:64:"abfface34516d5ef686a4d1f83d15836660e492c6e4995395e6d9f31dbef3533";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:0;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:1;a:6:{s:11:"transaction";s:64:"abfface34516d5ef686a4d1f83d15836660e492c6e4995395e6d9f31dbef3533";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:11:"10000000000";s:8:"sort_key";i:1;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:2;a:6:{s:11:"transaction";s:64:"abfface34516d5ef686a4d1f83d15836660e492c6e4995395e6d9f31dbef3533";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:13:"1000000000000";s:8:"sort_key";i:2;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:3;a:6:{s:11:"transaction";s:64:"abfface34516d5ef686a4d1f83d15836660e492c6e4995395e6d9f31dbef3533";s:7:"address";s:8:"the-void";s:6:"effect";s:1:"0";s:8:"sort_key";i:3;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:4;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:13:"1000000000000";s:8:"sort_key";i:4;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:5;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:13:"1000000000000";s:8:"sort_key";i:5;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:6;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:12:"700000000000";s:8:"sort_key";i:6;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:7;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:11:"50000000000";s:8:"sort_key";i:7;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:8;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:12:"400000000000";s:8:"sort_key";i:8;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:9;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:13:"2000000000000";s:8:"sort_key";i:9;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:10;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:11:"40000000000";s:8:"sort_key";i:10;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:11;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:12:"200000000000";s:8:"sort_key";i:11;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}i:12;a:6:{s:11:"transaction";s:64:"aae78d620397afcd6df718fef5eb16a03850c38ca4895f40c122661d76c0c2d4";s:7:"address";s:8:"the-void";s:6:"effect";s:11:"10000000000";s:8:"sort_key";i:12;s:5:"block";i:2185521;s:4:"time";s:19:"2023-07-08 21:15:10";}}s:10:"currencies";N;}'], + ['block' => 3000635, 'result' => 'a:2:{s:6:"events";a:8:{i:0;a:6:{s:11:"transaction";s:64:"7f8188b247d22160cbae03f5fdddf8da086ad949371a0165d3f915e4ea6ddc04";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:0;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:1;a:6:{s:11:"transaction";s:64:"7f8188b247d22160cbae03f5fdddf8da086ad949371a0165d3f915e4ea6ddc04";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:1;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:2;a:6:{s:11:"transaction";s:64:"7f8188b247d22160cbae03f5fdddf8da086ad949371a0165d3f915e4ea6ddc04";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:2;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:3;a:6:{s:11:"transaction";s:64:"7f8188b247d22160cbae03f5fdddf8da086ad949371a0165d3f915e4ea6ddc04";s:7:"address";s:8:"the-void";s:6:"effect";s:1:"0";s:8:"sort_key";i:3;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:4;a:6:{s:11:"transaction";s:64:"d372ed3fb1a70246157bc05b988d1003075d810baa0c6930e005cfd0ad0e6b05";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:4;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:5;a:6:{s:11:"transaction";s:64:"d372ed3fb1a70246157bc05b988d1003075d810baa0c6930e005cfd0ad0e6b05";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:5;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:6;a:6:{s:11:"transaction";s:64:"d372ed3fb1a70246157bc05b988d1003075d810baa0c6930e005cfd0ad0e6b05";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:6;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}i:7;a:6:{s:11:"transaction";s:64:"d372ed3fb1a70246157bc05b988d1003075d810baa0c6930e005cfd0ad0e6b05";s:7:"address";s:8:"the-void";s:6:"effect";s:11:"10000000000";s:8:"sort_key";i:7;s:5:"block";i:3000635;s:4:"time";s:19:"2025-01-25 00:40:15";}}s:10:"currencies";N;}'], + ['block' => 391, 'result' => 'a:2:{s:6:"events";a:3:{i:0;a:6:{s:11:"transaction";s:64:"9de9f068b2fed3efe8303b331a88195ab3118df534c8e84e1d54eb5ee7fa21da";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:1:"0";s:8:"sort_key";i:0;s:5:"block";i:391;s:4:"time";s:19:"2019-05-08 21:24:41";}i:1;a:6:{s:11:"transaction";s:64:"9de9f068b2fed3efe8303b331a88195ab3118df534c8e84e1d54eb5ee7fa21da";s:7:"address";s:13:"shielded-pool";s:6:"effect";s:13:"1000000000000";s:8:"sort_key";i:1;s:5:"block";i:391;s:4:"time";s:19:"2019-05-08 21:24:41";}i:2;a:6:{s:11:"transaction";s:64:"9de9f068b2fed3efe8303b331a88195ab3118df534c8e84e1d54eb5ee7fa21da";s:7:"address";s:8:"the-void";s:6:"effect";s:1:"0";s:8:"sort_key";i:2;s:5:"block";i:391;s:4:"time";s:19:"2019-05-08 21:24:41";}}s:10:"currencies";N;}'] + ]; + + } +}