|
17 | 17 | from .partner_chain_rpc import PartnerChainRpc, PartnerChainRpcResponse, PartnerChainRpcException, DParam
|
18 | 18 | import string
|
19 | 19 | import time
|
20 |
| -from scalecodec.base import RuntimeConfiguration |
| 20 | +from scalecodec.base import RuntimeConfiguration, ScaleBytes |
21 | 21 |
|
22 | 22 |
|
23 | 23 | def _keypair_type_to_name(type):
|
@@ -650,73 +650,38 @@ def get_block(self, block_no):
|
650 | 650 | block_hash = self.substrate.get_block_hash(block_no)
|
651 | 651 | return self.substrate.get_block(block_hash)
|
652 | 652 |
|
653 |
| - def _block_header_encoder_and_signature_extractor(self, header: dict): |
654 |
| - signature = False |
655 |
| - header_encoded = bytes.fromhex(header["parentHash"][2:]).hex() |
656 |
| - # Convert block number to compact |
657 |
| - header["number"] = self.compact_encoder.encode(header["number"]).to_hex() |
658 |
| - header_encoded += bytes.fromhex(header["number"][2:]).hex() |
659 |
| - header_encoded += bytes.fromhex(header["stateRoot"][2:]).hex() |
660 |
| - header_encoded += bytes.fromhex(header["extrinsicsRoot"][2:]).hex() |
661 |
| - logs_encoded = "" |
662 |
| - consensus_cnt = 0 |
663 |
| - consensus_encoded = "" |
664 |
| - for log in header["digest"]["logs"]: |
665 |
| - log = log.value_serialized |
666 |
| - if "Seal" in log.keys(): |
667 |
| - # Do not include the signature in the encoded header. |
668 |
| - # We want to hash the header and sign to get this signature |
669 |
| - signature = log["Seal"][1] |
670 |
| - elif "PreRuntime" in log.keys(): |
671 |
| - if is_hex(log["PreRuntime"][0]): |
672 |
| - prefix = str(log["PreRuntime"][0])[2:] |
673 |
| - else: |
674 |
| - logger.error(f"PreRuntime key is not hex: {log['PreRuntime'][0]}") |
675 |
| - return None, None |
676 |
| - if is_hex(log["PreRuntime"][1]): |
677 |
| - suffix = str(log["PreRuntime"][1])[2:] |
678 |
| - else: |
679 |
| - suffix = str(log["PreRuntime"][1]).encode("utf-8").hex() |
680 |
| - suffix_length = str(hex(2 * len(suffix)))[2:] |
681 |
| - logs_encoded += "06" + prefix + suffix_length + suffix |
682 |
| - elif "Consensus" in log.keys(): |
683 |
| - consensus_cnt += 1 |
684 |
| - prefix = str(log["Consensus"][0])[2:] |
685 |
| - suffix = str(log["Consensus"][1])[2:] |
686 |
| - if "0100000000000000" in suffix: # Grandpa committee keys |
687 |
| - suffix_prepend = self.config.block_encoding_suffix_grandpa |
688 |
| - else: # Aura committee keys |
689 |
| - suffix_prepend = self.config.block_encoding_suffix_aura |
690 |
| - consensus_encoded += "04" + prefix + suffix_prepend + suffix |
691 |
| - # Keep adding key to decode as the are added to the block header |
692 |
| - if consensus_cnt == 0: |
693 |
| - logs_prefix = "08" |
694 |
| - elif consensus_cnt == 1: |
695 |
| - logs_prefix = "0c" |
696 |
| - elif consensus_cnt == 2: |
697 |
| - logs_prefix = "10" |
698 |
| - else: |
699 |
| - logger.debug("New block type detected with more than 2 consensus logs. Please update encoder") |
700 |
| - return False, False |
701 |
| - header_encoded += logs_prefix + logs_encoded + consensus_encoded |
702 |
| - return header_encoded, signature |
703 |
| - |
704 |
| - def extract_block_author(self, block, candidates_pub_keys): |
705 |
| - block_header = block["header"] |
706 |
| - scale_header, signature = self._block_header_encoder_and_signature_extractor(block_header) |
707 |
| - if not scale_header or not signature: |
708 |
| - raise Exception(f'Could not encode header of block {block_header["number"]}') |
709 |
| - header_hash = hashlib.blake2b(bytes.fromhex(scale_header), digest_size=32).hexdigest() |
710 |
| - |
711 |
| - for pub_key in candidates_pub_keys: |
712 |
| - keypair_public = Keypair( |
713 |
| - ss58_address=self.substrate.ss58_encode(pub_key), |
714 |
| - crypto_type=KeypairType.SR25519, # For our substrate implementation SR25519 is block authorship type |
715 |
| - ) |
716 |
| - is_author = keypair_public.verify(bytes.fromhex(header_hash), bytes.fromhex(signature[2:])) |
717 |
| - if is_author: |
718 |
| - return pub_key |
719 |
| - return None |
| 653 | + def get_block_author(self, block_number): |
| 654 | + """Custom implementation of substrate.get_block(include_author=True) to get block author. |
| 655 | + py-substrate-interface does not work because it calls "Validators" function from "Session" pallet, |
| 656 | + which in our node is disabled and returns empty list. Here we use "ValidatorsAndKeys". |
| 657 | + The function then iterates over "PreRuntime" logs and once it finds aura engine, it gets the slot |
| 658 | + number and uses the result of modulo to get the author by index from the validator set. |
| 659 | + Note: py-substrate-interface was also breaking at this point because we have another "PreRuntime" log |
| 660 | + for mcsh engine (main chain hash) which is not supported by py-substrate-interface. |
| 661 | + """ |
| 662 | + block_data = self.get_block(block_number) |
| 663 | + validator_set = self.substrate.query( |
| 664 | + "Session", "ValidatorsAndKeys", block_hash=block_data["header"]["parentHash"] |
| 665 | + ) |
| 666 | + for log_data in block_data["header"]["digest"]["logs"]: |
| 667 | + engine = bytes(log_data[1][0]) |
| 668 | + if "PreRuntime" in log_data and engine == b'aura': |
| 669 | + aura_predigest = self.substrate.runtime_config.create_scale_object( |
| 670 | + type_string='RawAuraPreDigest', data=ScaleBytes(bytes(log_data[1][1])) |
| 671 | + ) |
| 672 | + |
| 673 | + aura_predigest.decode(check_remaining=self.config.get("strict_scale_decode")) |
| 674 | + |
| 675 | + rank_validator = aura_predigest.value["slot_number"] % len(validator_set) |
| 676 | + |
| 677 | + block_author = validator_set[rank_validator] |
| 678 | + block_data["author"] = block_author.value[1]["aura"] |
| 679 | + break |
| 680 | + |
| 681 | + if "author" not in block_data: |
| 682 | + logger.error(f"Could not find author for block {block_number}. No PreRuntime log found with aura engine.") |
| 683 | + return None |
| 684 | + return block_data["author"] |
720 | 685 |
|
721 | 686 | def get_mc_hash_from_pc_block_header(self, block):
|
722 | 687 | mc_hash_key = "0x6d637368"
|
|
0 commit comments