forked from ethereum/trinity
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow beam sync to start from a trusted checkpoint
This teaches beam sync a new CLI parameter: --beam-from-checkpoint="block://blockhash/blocknumber/score" When given, beam sync will use this as a checkpoint to avoid having to download the entire chain of headers first. Relates to ethereum#892
- Loading branch information
Showing
15 changed files
with
442 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
Allow beam sync to start from a trusted checkpoint. | ||
Specify a checkpoint via CLI parameter such as: | ||
|
||
``--beam-from-checkpoint="eth://block/byhash/<hash>?score=<score>"`` | ||
|
||
When given, beam sync will use this as a checkpoint | ||
to avoid having to download the entire chain of headers | ||
first. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import pytest | ||
|
||
from eth_utils import ( | ||
encode_hex, | ||
ValidationError, | ||
) | ||
|
||
from trinity.plugins.builtin.syncer.cli import ( | ||
parse_checkpoint_uri | ||
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'uri, expected', | ||
( | ||
( | ||
'eth://block/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=11', # noqa: E501 | ||
('0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080', 78, 11), | ||
), | ||
( | ||
'eth://block/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=1,1', # noqa: E501 | ||
('0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080', 78, 11), | ||
), | ||
( | ||
'eth://block/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=1 1', # noqa: E501 | ||
('0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080', 78, 11), | ||
), | ||
) | ||
) | ||
def test_parse_checkpoint(uri, expected): | ||
block_hash, block_number, block_score = expected | ||
checkpoint = parse_checkpoint_uri(uri) | ||
assert encode_hex(checkpoint.block_hash) == block_hash | ||
assert checkpoint.score == block_score | ||
|
||
|
||
@pytest.mark.parametrize( | ||
'uri', | ||
( | ||
'meh://block/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=11', # noqa: E501 | ||
'eth://meh/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=11', # noqa: E501 | ||
'eth://block/meh/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=11', # noqa: E501 | ||
'eth://block/byhash/meh?score=78', | ||
'eth://block/meh/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=11', # noqa: E501 | ||
'eth://block/meh/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080?score=meh', # noqa: E501 | ||
'eth://block/byhash/0x113f05289c685eb5b87d433c3e09ec2bfa51d6472cc37108d03b0113b11e3080', | ||
) | ||
) | ||
def test_throws_validation_error(uri): | ||
with pytest.raises(ValidationError): | ||
parse_checkpoint_uri(uri) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import argparse | ||
from pathlib import PurePosixPath | ||
import re | ||
import urllib | ||
from typing import ( | ||
Any, | ||
) | ||
|
||
from eth_typing import ( | ||
Hash32, | ||
) | ||
|
||
from eth_utils import ( | ||
is_hex, | ||
remove_0x_prefix, | ||
decode_hex, | ||
ValidationError, | ||
) | ||
|
||
from trinity.sync.common.checkpoint import Checkpoint | ||
|
||
|
||
def is_block_hash(value: str) -> bool: | ||
return is_hex(value) and len(remove_0x_prefix(value)) == 64 | ||
|
||
|
||
def remove_non_digits(value: str) -> str: | ||
return re.sub("\D", "", value) | ||
|
||
|
||
def parse_checkpoint_uri(uri: str) -> Checkpoint: | ||
try: | ||
parsed = urllib.parse.urlparse(uri) | ||
except ValueError as e: | ||
raise ValidationError(str(e)) | ||
|
||
scheme, netloc, path, query = parsed.scheme, parsed.netloc, parsed.path.lower(), parsed.query | ||
|
||
try: | ||
parsed_query = urllib.parse.parse_qsl(query) | ||
except ValueError as e: | ||
raise ValidationError(str(e)) | ||
|
||
query_dict = dict(parsed_query) | ||
|
||
# we allow any kind of separator for a nicer UX. e.g. instead of "11487662456884849810705" | ||
# one can use "114 876 624 568 848 498 107 05" or "1,487,662,456,884,849,810,705". This also | ||
# allows copying out a value from e.g etherscan. | ||
score = remove_non_digits(query_dict.get('score', '')) | ||
|
||
is_by_hash = path.startswith('/byhash') | ||
parts = PurePosixPath(parsed.path).parts | ||
|
||
if len(parts) != 3 or scheme != 'eth' or netloc != 'block' or not is_by_hash or not score: | ||
raise ValidationError( | ||
'checkpoint string must be of the form' | ||
'"eth://block/byhash/<hash>?score=<score>"' | ||
) | ||
|
||
block_hash = parts[2] | ||
|
||
if not is_block_hash(block_hash): | ||
raise ValidationError(f'Block hash must be valid hex string, got: {block_hash}') | ||
|
||
if not score.isdigit(): | ||
raise ValidationError(f'Score (total difficulty) must be an integer, got: {score}') | ||
|
||
return Checkpoint(Hash32(decode_hex(block_hash)), int(score)) | ||
|
||
|
||
class NormalizeCheckpointURI(argparse.Action): | ||
""" | ||
Normalize the URI describing a sync checkpoint. | ||
""" | ||
def __call__(self, | ||
parser: argparse.ArgumentParser, | ||
namespace: argparse.Namespace, | ||
value: Any, | ||
option_string: str=None) -> None: | ||
parsed = parse_checkpoint_uri(value) | ||
setattr(namespace, self.dest, parsed) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.