|
34 | 34 | from eth_utils import (
|
35 | 35 | encode_hex,
|
36 | 36 | )
|
| 37 | +from eth_utils.toolz import ( |
| 38 | + concatv, |
| 39 | + sliding_window, |
| 40 | +) |
37 | 41 |
|
38 | 42 | from eth.constants import (
|
39 | 43 | BLANK_ROOT_HASH,
|
@@ -315,13 +319,37 @@ def validate_gaslimit(self, header: BlockHeader) -> None:
|
315 | 319 | def validate_uncles(self, block: BaseBlock) -> None:
|
316 | 320 | raise NotImplementedError("Chain classes must implement this method")
|
317 | 321 |
|
318 |
| - @abstractmethod |
| 322 | + @classmethod |
319 | 323 | def validate_chain(
|
320 |
| - self, |
321 |
| - parent: BlockHeader, |
322 |
| - chain: Tuple[BlockHeader, ...], |
| 324 | + cls, |
| 325 | + root: BlockHeader, |
| 326 | + descendants: Tuple[BlockHeader, ...], |
323 | 327 | seal_check_random_sample_rate: int = 1) -> None:
|
324 |
| - raise NotImplementedError("Chain classes must implement this method") |
| 328 | + """ |
| 329 | + Validate that all of the descendents are valid, given that the root header is valid. |
| 330 | +
|
| 331 | + By default, check the seal validity (Proof-of-Work on Ethereum 1.x mainnet) of all headers. |
| 332 | + This can be expensive. Instead, check a random sample of seals using |
| 333 | + seal_check_random_sample_rate. |
| 334 | + """ |
| 335 | + |
| 336 | + all_indices = range(len(descendants)) |
| 337 | + if seal_check_random_sample_rate == 1: |
| 338 | + indices_to_check_seal = set(all_indices) |
| 339 | + else: |
| 340 | + sample_size = len(all_indices) // seal_check_random_sample_rate |
| 341 | + indices_to_check_seal = set(random.sample(all_indices, sample_size)) |
| 342 | + |
| 343 | + header_pairs = sliding_window(2, concatv([root], descendants)) |
| 344 | + |
| 345 | + for index, (parent, child) in enumerate(header_pairs): |
| 346 | + if child.parent_hash != parent.hash: |
| 347 | + raise ValidationError( |
| 348 | + "Invalid header chain; {} has parent {}, but expected {}".format( |
| 349 | + child, child.parent_hash, parent.hash)) |
| 350 | + should_check_seal = index in indices_to_check_seal |
| 351 | + vm_class = cls.get_vm_class_for_block_number(child.block_number) |
| 352 | + vm_class.validate_header(child, parent, check_seal=should_check_seal) |
325 | 353 |
|
326 | 354 |
|
327 | 355 | class Chain(BaseChain):
|
@@ -820,31 +848,6 @@ def validate_uncles(self, block: BaseBlock) -> None:
|
820 | 848 | uncle_vm_class = self.get_vm_class_for_block_number(uncle.block_number)
|
821 | 849 | uncle_vm_class.validate_uncle(block, uncle, uncle_parent)
|
822 | 850 |
|
823 |
| - def validate_chain( |
824 |
| - self, |
825 |
| - parent: BlockHeader, |
826 |
| - chain: Tuple[BlockHeader, ...], |
827 |
| - seal_check_random_sample_rate: int = 1) -> None: |
828 |
| - |
829 |
| - all_indices = list(range(len(chain))) |
830 |
| - if seal_check_random_sample_rate == 1: |
831 |
| - headers_to_check_seal = set(all_indices) |
832 |
| - else: |
833 |
| - sample_size = len(all_indices) // seal_check_random_sample_rate |
834 |
| - headers_to_check_seal = set(random.sample(all_indices, sample_size)) |
835 |
| - |
836 |
| - for i, header in enumerate(chain): |
837 |
| - if header.parent_hash != parent.hash: |
838 |
| - raise ValidationError( |
839 |
| - "Invalid header chain; {} has parent {}, but expected {}".format( |
840 |
| - header, header.parent_hash, parent.hash)) |
841 |
| - vm_class = self.get_vm_class_for_block_number(header.block_number) |
842 |
| - if i in headers_to_check_seal: |
843 |
| - vm_class.validate_header(header, parent, check_seal=True) |
844 |
| - else: |
845 |
| - vm_class.validate_header(header, parent, check_seal=False) |
846 |
| - parent = header |
847 |
| - |
848 | 851 |
|
849 | 852 | @to_set
|
850 | 853 | def _extract_uncle_hashes(blocks: Iterable[BaseBlock]) -> Iterable[Hash32]:
|
|
0 commit comments