From 5cd96a14a70b49fba2f68a2d9a3fd0b1585c1c59 Mon Sep 17 00:00:00 2001 From: Nikita Zhavoronkov Date: Wed, 26 Apr 2023 03:33:25 -0400 Subject: [PATCH 1/4] Add basic Polkadot support (WIP) --- .env.example | 17 ++ Modules/Common/PolkadotLikeMinimalModule.php | 234 +++++++++++++++++++ Modules/PolkadotMinimalModule.php | 22 ++ 3 files changed, 273 insertions(+) create mode 100644 Modules/Common/PolkadotLikeMinimalModule.php create mode 100644 Modules/PolkadotMinimalModule.php diff --git a/.env.example b/.env.example index 98e6c5aa..0e988d9b 100644 --- a/.env.example +++ b/.env.example @@ -186,6 +186,17 @@ MODULE_litecoin-mweb_NODES[]=http://login:password@127.0.0.2:1234/ MODULE_litecoin-mweb_REQUESTER_TIMEOUT=60 MODULE_litecoin-mweb_REQUESTER_THREADS=12 +########################## +## Minimal Polkadot Module +########################## + +MODULES[]=polkadot-minimal +MODULE_polkadot-minimal_CLASS=PolkadotMinimalModule +MODULE_polkadot-minimal_NODES[]=http://login:password@127.0.0.1:1234/ +MODULE_polkadot-minimal_NODES[]=http://login:password@127.0.0.2:1234/ +MODULE_polkadot-minimal_REQUESTER_TIMEOUT=60 +MODULE_polkadot-minimal_REQUESTER_THREADS=12 + ##################### ## Ton Minimal Module ##################### @@ -221,6 +232,7 @@ FRONT_ton_ECOSYSTEM_TITLE=TON Ecosystem FRONT_bitcoin-cash_ECOSYSTEM_TITLE=Bitcoin Cash Ecosystem FRONT_zcash_ECOSYSTEM_TITLE=Zcash Ecosystem FRONT_cardano_ECOSYSTEM_TITLE=Cardano Ecosystem +FRONT_polkadot_ECOSYSTEM_TITLE=Polkadot Ecosystem FRONT_bitcoin_ECOSYSTEM_DESCRIPTION=Includes Bitcoin only FRONT_litecoin_ECOSYSTEM_DESCRIPTION=Includes Litecoin only @@ -231,6 +243,7 @@ FRONT_ton_ECOSYSTEM_DESCRIPTION=Includes TON only FRONT_bitcoin-cash_ECOSYSTEM_DESCRIPTION=Includes Bitcoin Cash only FRONT_zcash_ECOSYSTEM_DESCRIPTION=Includes Zcash only FRONT_cardano_ECOSYSTEM_DESCRIPTION=Includes Cardano only +FRONT_polkadot_ECOSYSTEM_DESCRIPTION=Includes Polkadot only FRONT_bitcoin_BLOCKCHAIN_TITLE=Bitcoin FRONT_litecoin_BLOCKCHAIN_TITLE=Litecoin @@ -241,6 +254,7 @@ FRONT_ton_BLOCKCHAIN_TITLE=TON FRONT_bitcoin-cash_BLOCKCHAIN_TITLE=Bitcoin Cash FRONT_zcash_BLOCKCHAIN_TITLE=Zcash FRONT_cardano_BLOCKCHAIN_TITLE=Cardano +FRONT_polkadot_BLOCKCHAIN_TITLE=Polkadot FRONT_bitcoin_BLOCKCHAIN_DESCRIPTION=The one and only blockchain FRONT_litecoin_BLOCKCHAIN_DESCRIPTION=The digital silver blockchain @@ -251,6 +265,7 @@ FRONT_ton_BLOCKCHAIN_DESCRIPTION=The Open Network's BaseChain (previously Telegr FRONT_bitcoin-cash_BLOCKCHAIN_DESCRIPTION=The first Bitcoin fork FRONT_zcash_BLOCKCHAIN_DESCRIPTION=Supports transparent, Sprout, Sapling, and Orchard shielded pools FRONT_cardano_BLOCKCHAIN_DESCRIPTION=Cardano +FRONT_polkadot_BLOCKCHAIN_DESCRIPTION=Polkadot FRONT_bitcoin-main_MODULE_TITLE=Main FRONT_bitcoin-omni_MODULE_TITLE=Omni Layer @@ -271,6 +286,7 @@ FRONT_ton-main_MODULE_TITLE=Main FRONT_bitcoin-cash-main_MODULE_TITLE=Main FRONT_zcash-main_MODULE_TITLE=Main FRONT_cardano-main_MODULE_TITLE=Main +FRONT_polkadot-minimal_MODULE_TITLE=Minimal FRONT_bitcoin-main_MODULE_DESCRIPTION=Main Bitcoin transfers FRONT_bitcoin-omni_MODULE_DESCRIPTION=Omni Layer transfers @@ -291,3 +307,4 @@ FRONT_ton-main_MODULE_DESCRIPTION=Main TON transfers FRONT_bitcoin-cash-main_MODULE_DESCRIPTION=Main Bitcoin Cash transfers FRONT_zcash-main_MODULE_DESCRIPTION=Supports transparent, Sprout, Sapling, and Orchard shielded pools FRONT_cardano-main_MODULE_DESCRIPTION=Main Cardano transfers (UTXO) +FRONT_polkadot-minimal_MODULE_DESCRIPTION=Polkadot transfers between accounts diff --git a/Modules/Common/PolkadotLikeMinimalModule.php b/Modules/Common/PolkadotLikeMinimalModule.php new file mode 100644 index 00000000..048d474f --- /dev/null +++ b/Modules/Common/PolkadotLikeMinimalModule.php @@ -0,0 +1,234 @@ +version = 1; + } + + final public function post_post_initialize() + { + // + } + + final public function inquire_latest_block() + { + return (int)requester_single($this->select_node(), + endpoint: 'api/scan/metadata', + params: true, + result_in: 'data', + timeout: $this->timeout)['blockNum']; + } + + final public function ensure_block($block_id, $break_on_first = false) + { + $this->block_data = requester_single($this->select_node(), + endpoint: 'api/scan/block', + params: ['block_num' => $block_id], + result_in: 'data', + timeout: $this->timeout); + + $this->block_hash = $this->block_data['hash']; + $this->block_time = date('Y-m-d H:i:s', (int)$this->block_data['block_timestamp']); + } + + final public function pre_process_block($block_id) + { + $extrinsics = $extrinsics_with_hashes = []; + + foreach ($this->block_data['extrinsics'] as $extrinsic) + if ($extrinsic['extrinsic_hash']) + $extrinsics_with_hashes[] = $extrinsic['extrinsic_hash']; + + if (!$extrinsics_with_hashes) + { + $this->set_return_events([]); + return; + } + + $events = []; + $multi_curl = []; + + foreach ($extrinsics_with_hashes as $extrinsic_hash) + $multi_curl[] = requester_multi_prepare($this->select_node(), + endpoint: 'api/scan/extrinsic', + params: ['hash' => $extrinsic_hash], + timeout: $this->timeout); + + $curl_results = requester_multi($multi_curl, limit: envm($this->module, 'REQUESTER_THREADS'), timeout: $this->timeout); + + foreach ($curl_results as $v) + { + $extrinsics[] = requester_multi_process($v, result_in: 'data'); + } + + $transfers = []; + + foreach ($extrinsics as $i) + { + $this_i = $fee = null; + + if (count($i) !== 1) + { + $found = false; + foreach ($i as $j => $k) + { + if ((int)$k['block_num'] === $block_id) + { + if ($found) + throw new ModuleError('Two extrinsics with the same hash in one block'); + + $found = true; + $this_i = $j; + } + } + } + else + { + $this_i = 0; + } + + if (!isset($this_i)) + continue; // Block 12831012 contains 0xa36adc0ddedb4b9f9f8de5ef59fa6c99bbb8ca8bbc44d189dd0bedf6bf61b92e + // which is present in 12857725, 12856380, 12855736, and many more, but not in 12831012 ¯\_(ツ)_/¯ + + $initiating_address = $i[$this_i]['account_id']; + $fee_found = false; + $extrinsic_failed = $i[$this_i]['failed']; + + foreach ($i[$this_i]['event'] as $event) + { + if ($event['event_id'] === 'TransactionFeePaid') + { + if ($fee_found) + throw new ModuleError('Two fees'); + + $fee_data = json_decode($event['params'], true); + + if (count($fee_data) !== 3) + throw new ModuleError('Wrong fee data'); + if ($fee_data[1]['type_name'] !== 'BalanceOf') + throw new ModuleError('Wrong fee data'); + + $fee = $fee_data[1]['value']; + $fee_found = true; + $fee_idx = $event['event_idx']; + } + } + + if (!$fee_found) // Uh-oh + { + $fee = $i[$this_i]['fee']; + $fee_idx = $i[$this_i]['event'][0]['event_idx']; + } + + if (!$i[$this_i]['transfers']) + continue; + + foreach ($i[$this_i]['transfers'] as $this_transfer) + { + if ((int)$this_transfer['block_num'] !== $block_id) + throw new ModuleError('Wrong block'); + + if ($extrinsic_failed && !$this_transfer['failed']) + throw new ModuleError('Not failed'); + + $transfers[] = + ['index' => $this_transfer['event_idx'], + 'hash' => $this_transfer['hash'], + 'from' => $this_transfer['from'], + 'to' => $this_transfer['to'], + 'amount' => $this_transfer['amount'], + 'failed' => $this_transfer['failed'], + 'type' => 'transfer' + ]; + } + + $transfers[] = + ['index' => $fee_idx, + 'hash' => $this_transfer['hash'], + 'from' => $initiating_address, + 'to' => 'the-void', + 'amount' => $fee, + 'failed' => false, + 'type' => 'fee' + ]; + } + + usort($transfers, function($a, $b) { + return [$a['index']] <=> [$b['index']]; + }); + + $sort_key = 0; + + foreach ($transfers as $transfer) + { + $events[] = [ + 'transaction' => $transfer['hash'], + 'address' => $transfer['from'], + 'sort_key' => $sort_key++, + 'effect' => '-' . $transfer['amount'], + 'failed' => ($transfer['failed']) ? 't' : 'f', + 'extra' => ($transfer['type'] === 'fee') ? 'f' : null, + ]; + + $events[] = [ + 'transaction' => $transfer['hash'], + 'address' => $transfer['to'], + 'sort_key' => $sort_key++, + 'effect' => $transfer['amount'], + 'failed' => ($transfer['failed']) ? 't' : 'f', + 'extra' => ($transfer['type'] === 'fee') ? 'f' : null, + ]; + } + + if ($events) + foreach ($events as &$event) + { + $event['block'] = $block_id; + $event['time'] = $this->block_time; + } + + $this->set_return_events($events); + } +} diff --git a/Modules/PolkadotMinimalModule.php b/Modules/PolkadotMinimalModule.php new file mode 100644 index 00000000..30204223 --- /dev/null +++ b/Modules/PolkadotMinimalModule.php @@ -0,0 +1,22 @@ +blockchain = 'polkadot'; + $this->module = 'polkadot-minimal'; + $this->is_main = true; + $this->currency = 'polkadot'; + $this->currency_details = ['name' => 'DOT', 'symbol' => 'DOT', 'decimals' => 10, 'description' => null]; + $this->first_block_date = '2020-05-26'; + $this->first_block_id = 0; + } +} From fb0b4fae71ecb5959cf5e64df26b74cdc4a3b04f Mon Sep 17 00:00:00 2001 From: Nikita Zhavoronkov Date: Thu, 4 May 2023 05:18:58 -0400 Subject: [PATCH 2/4] Process block #0 correctly --- Modules/Common/PolkadotLikeMinimalModule.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Modules/Common/PolkadotLikeMinimalModule.php b/Modules/Common/PolkadotLikeMinimalModule.php index 048d474f..6b4ce5f4 100644 --- a/Modules/Common/PolkadotLikeMinimalModule.php +++ b/Modules/Common/PolkadotLikeMinimalModule.php @@ -74,6 +74,12 @@ final public function ensure_block($block_id, $break_on_first = false) final public function pre_process_block($block_id) { + if ($block_id === 0) + { + $this->set_return_events([]); + return; + } + $extrinsics = $extrinsics_with_hashes = []; foreach ($this->block_data['extrinsics'] as $extrinsic) From 4efccf1ba1e6ff3f18f62cdccb7cfec807d294f2 Mon Sep 17 00:00:00 2001 From: Nikita Zhavoronkov Date: Wed, 21 Jun 2023 01:50:44 -0400 Subject: [PATCH 3/4] Fix empty address issue in Polkadot --- Modules/Common/PolkadotLikeMinimalModule.php | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Modules/Common/PolkadotLikeMinimalModule.php b/Modules/Common/PolkadotLikeMinimalModule.php index 6b4ce5f4..635e595b 100644 --- a/Modules/Common/PolkadotLikeMinimalModule.php +++ b/Modules/Common/PolkadotLikeMinimalModule.php @@ -37,7 +37,7 @@ abstract class PolkadotLikeMinimalModule extends CoreModule // - public ?array $block_data = null; + private ?array $block_data = null; // @@ -139,6 +139,10 @@ final public function pre_process_block($block_id) // which is present in 12857725, 12856380, 12855736, and many more, but not in 12831012 ¯\_(ツ)_/¯ $initiating_address = $i[$this_i]['account_id']; + + if ($initiating_address === '') // This is a bit strange + $initiating_address = $i[$this_i]['transfers'][0]['from']; + $fee_found = false; $extrinsic_failed = $i[$this_i]['failed']; @@ -214,7 +218,7 @@ final public function pre_process_block($block_id) 'address' => $transfer['from'], 'sort_key' => $sort_key++, 'effect' => '-' . $transfer['amount'], - 'failed' => ($transfer['failed']) ? 't' : 'f', + 'failed' => $transfer['failed'], 'extra' => ($transfer['type'] === 'fee') ? 'f' : null, ]; @@ -223,17 +227,16 @@ final public function pre_process_block($block_id) 'address' => $transfer['to'], 'sort_key' => $sort_key++, 'effect' => $transfer['amount'], - 'failed' => ($transfer['failed']) ? 't' : 'f', + 'failed' => $transfer['failed'], 'extra' => ($transfer['type'] === 'fee') ? 'f' : null, ]; } - if ($events) - foreach ($events as &$event) - { - $event['block'] = $block_id; - $event['time'] = $this->block_time; - } + foreach ($events as &$event) + { + $event['block'] = $block_id; + $event['time'] = $this->block_time; + } $this->set_return_events($events); } From 31e10df9d14b7b9baa536c231f076474eb3782a6 Mon Sep 17 00:00:00 2001 From: Nikita Zhavoronkov Date: Thu, 22 Jun 2023 01:46:48 -0400 Subject: [PATCH 4/4] Fix processing some Polkadot blocks --- Modules/Common/PolkadotLikeMinimalModule.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Common/PolkadotLikeMinimalModule.php b/Modules/Common/PolkadotLikeMinimalModule.php index 635e595b..2a7ab8fe 100644 --- a/Modules/Common/PolkadotLikeMinimalModule.php +++ b/Modules/Common/PolkadotLikeMinimalModule.php @@ -138,6 +138,9 @@ final public function pre_process_block($block_id) continue; // Block 12831012 contains 0xa36adc0ddedb4b9f9f8de5ef59fa6c99bbb8ca8bbc44d189dd0bedf6bf61b92e // which is present in 12857725, 12856380, 12855736, and many more, but not in 12831012 ¯\_(ツ)_/¯ + if (!$i[$this_i]['transfers']) + continue; + $initiating_address = $i[$this_i]['account_id']; if ($initiating_address === '') // This is a bit strange @@ -172,9 +175,6 @@ final public function pre_process_block($block_id) $fee_idx = $i[$this_i]['event'][0]['event_idx']; } - if (!$i[$this_i]['transfers']) - continue; - foreach ($i[$this_i]['transfers'] as $this_transfer) { if ((int)$this_transfer['block_num'] !== $block_id)