Skip to content
This repository was archived by the owner on Jul 1, 2021. It is now read-only.

Commit 8eca0b1

Browse files
authored
Merge pull request #1400 from NIC619/set_up_eth1_monitor_component
Set up eth1 monitor component
2 parents 4637079 + 6ffe1ad commit 8eca0b1

File tree

11 files changed

+570
-113
lines changed

11 files changed

+570
-113
lines changed

eth2/beacon/scripts/quickstart_state/generate_beacon_genesis.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from pathlib import Path
22
import time
33

4-
from eth_utils import decode_hex
54
import ssz
65

7-
from eth2._utils.hash import hash_eth2
86
from eth2.beacon.genesis import initialize_beacon_state_from_eth1
9-
from eth2.beacon.tools.builder.initializer import create_mock_deposits_and_root
7+
from eth2.beacon.tools.builder.initializer import (
8+
create_keypair_and_mock_withdraw_credentials,
9+
create_mock_deposits_and_root,
10+
)
1011
from eth2.beacon.tools.fixtures.config_types import Minimal
1112
from eth2.beacon.tools.fixtures.loading import load_config_at_path, load_yaml_at
1213
from eth2.beacon.tools.misc.ssz_vector import override_lengths
@@ -30,22 +31,10 @@ def _main():
3031

3132
key_set = load_yaml_at(KEY_DIR / KEY_SET_FILE)
3233

33-
pubkeys = ()
34-
privkeys = ()
35-
withdrawal_credentials = ()
36-
keymap = {}
37-
for key_pair in key_set:
38-
pubkey = decode_hex(key_pair["pubkey"])
39-
privkey = int.from_bytes(decode_hex(key_pair["privkey"]), "big")
40-
withdrawal_credential = (
41-
config.BLS_WITHDRAWAL_PREFIX.to_bytes(1, byteorder="big")
42-
+ hash_eth2(pubkey)[1:]
43-
)
44-
45-
pubkeys += (pubkey,)
46-
privkeys += (privkey,)
47-
withdrawal_credentials += (withdrawal_credential,)
48-
keymap[pubkey] = privkey
34+
pubkeys, privkeys, withdrawal_credentials = create_keypair_and_mock_withdraw_credentials(
35+
config, key_set
36+
)
37+
keymap = {pubkey: privkey for pubkey, privkey in zip(pubkeys, privkeys)}
4938

5039
deposits, _ = create_mock_deposits_and_root(
5140
pubkeys, keymap, config, withdrawal_credentials

eth2/beacon/tools/builder/initializer.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from typing import Dict, Sequence, Tuple, Type
1+
from typing import Any, Dict, Sequence, Tuple, Type
22

33
from eth.constants import ZERO_HASH32
44
from eth_typing import BLSPubkey, Hash32
5+
from eth_utils import decode_hex
56
from py_ecc.optimized_bls12_381.optimized_curve import (
67
curve_order as BLS12_381_CURVE_ORDER,
78
)
@@ -32,6 +33,27 @@ def generate_privkey_from_index(index: int) -> int:
3233
)
3334

3435

36+
def create_keypair_and_mock_withdraw_credentials(
37+
config: Eth2Config, key_set: Sequence[Dict[str, Any]]
38+
) -> Tuple[Tuple[BLSPubkey, ...], Tuple[int, ...], Tuple[Hash32, ...]]:
39+
pubkeys: Tuple[BLSPubkey, ...] = ()
40+
privkeys: Tuple[int, ...] = ()
41+
withdrawal_credentials: Tuple[Hash32, ...] = ()
42+
for key_pair in key_set:
43+
pubkey = BLSPubkey(decode_hex(key_pair["pubkey"]))
44+
privkey = int.from_bytes(decode_hex(key_pair["privkey"]), "big")
45+
withdrawal_credential = Hash32(
46+
config.BLS_WITHDRAWAL_PREFIX.to_bytes(1, byteorder="big")
47+
+ hash_eth2(pubkey)[1:]
48+
)
49+
50+
pubkeys += (pubkey,)
51+
privkeys += (privkey,)
52+
withdrawal_credentials += (withdrawal_credential,)
53+
54+
return (pubkeys, privkeys, withdrawal_credentials)
55+
56+
3557
def create_mock_deposits_and_root(
3658
pubkeys: Sequence[BLSPubkey],
3759
keymap: Dict[BLSPubkey, int],

tests-trio/eth1-monitor/conftest.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from trinity.components.eth2.eth1_monitor.configs import deposit_contract_json
2222
from trinity.components.eth2.eth1_monitor.eth1_monitor import Eth1Monitor
23+
from trinity.components.eth2.eth1_monitor.eth1_data_provider import Web3Eth1DataProvider
2324
from trinity.components.eth2.eth1_monitor.factories import DepositDataFactory
2425
from trinity.tools.factories.db import AtomicDBFactory
2526

@@ -82,19 +83,25 @@ def func_do_deposit(w3, deposit_contract):
8283
return functools.partial(deposit, w3=w3, deposit_contract=deposit_contract)
8384

8485

86+
@pytest.fixture
87+
async def eth1_data_provider(w3, deposit_contract):
88+
return Web3Eth1DataProvider(
89+
w3=w3,
90+
deposit_contract_address=deposit_contract.address,
91+
deposit_contract_abi=deposit_contract.abi,
92+
)
93+
94+
8595
@pytest.fixture
8696
async def eth1_monitor(
87-
w3,
88-
deposit_contract,
97+
eth1_data_provider,
8998
num_blocks_confirmed,
9099
polling_period,
91100
endpoint_server,
92101
start_block_number,
93102
):
94103
m = Eth1Monitor(
95-
w3=w3,
96-
deposit_contract_address=deposit_contract.address,
97-
deposit_contract_abi=deposit_contract.abi,
104+
eth1_data_provider=eth1_data_provider,
98105
num_blocks_confirmed=num_blocks_confirmed,
99106
polling_period=polling_period,
100107
start_block_number=start_block_number,

tests-trio/eth1-monitor/test_eth1_monitor.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from eth2._utils.merkle.common import verify_merkle_branch
1111
from trinity.components.eth2.eth1_monitor.eth1_monitor import (
1212
make_deposit_tree_and_root,
13+
GetDistanceRequest,
1314
GetEth1DataRequest,
1415
GetDepositRequest,
1516
Eth1Monitor,
@@ -28,8 +29,7 @@
2829

2930
@pytest.mark.trio
3031
async def test_logs_handling(
31-
w3,
32-
deposit_contract,
32+
eth1_data_provider,
3333
tester,
3434
num_blocks_confirmed,
3535
polling_period,
@@ -40,9 +40,7 @@ async def test_logs_handling(
4040
amount_0 = func_do_deposit()
4141
amount_1 = func_do_deposit()
4242
m = Eth1Monitor(
43-
w3=w3,
44-
deposit_contract_address=deposit_contract.address,
45-
deposit_contract_abi=deposit_contract.abi,
43+
eth1_data_provider=eth1_data_provider,
4644
num_blocks_confirmed=num_blocks_confirmed,
4745
polling_period=polling_period,
4846
start_block_number=start_block_number,
@@ -280,6 +278,8 @@ async def test_ipc(
280278
async def request(request_type, **kwargs):
281279
await endpoint_client.wait_until_any_endpoint_subscribed_to(request_type)
282280
resp = await endpoint_client.request(request_type(**kwargs), broadcast_config)
281+
if request_type is GetDistanceRequest:
282+
return resp
283283
return resp.to_data()
284284

285285
# Result from IPC should be the same as the direct call with the same args.
@@ -311,3 +311,27 @@ async def request(request_type, **kwargs):
311311
}
312312
with pytest.raises(Eth1MonitorValidationError):
313313
await request(GetEth1DataRequest, **get_eth1_data_kwargs_fails)
314+
315+
# Test: `get_distance`
316+
# Fast forward for a few blocks
317+
tester.mine_blocks(5)
318+
latest_block = w3.eth.getBlock("latest")
319+
latest_confirmed_block_number = latest_block.number - num_blocks_confirmed
320+
latest_confirmed_block = w3.eth.getBlock(latest_confirmed_block_number)
321+
eth1_voting_period_start_timestamp = latest_confirmed_block["timestamp"]
322+
for distance in range(latest_confirmed_block_number):
323+
block = w3.eth.getBlock(latest_confirmed_block_number - distance)
324+
get_distance_kwargs = {
325+
"block_hash": block["hash"],
326+
"eth1_voting_period_start_timestamp": eth1_voting_period_start_timestamp,
327+
}
328+
resp = await request(GetDistanceRequest, **get_distance_kwargs)
329+
assert distance == resp.distance
330+
assert resp.error is None
331+
# Fails
332+
get_distance_fail_kwargs = {
333+
"block_hash": b'\x12' * 32,
334+
"eth1_voting_period_start_timestamp": eth1_voting_period_start_timestamp,
335+
}
336+
resp = await request(GetDistanceRequest, **get_distance_fail_kwargs)
337+
assert resp.error is not None

trinity/components/eth2/beacon/validator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@
8080
GetReadyAttestationsFn = Callable[[Slot], Sequence[Attestation]]
8181

8282

83+
# FIXME: Read this from validator config
84+
ETH1_FOLLOW_DISTANCE = 16
85+
86+
8387
class Validator(BaseService):
8488
chain: BaseBeaconChain
8589
p2p_node: Node
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
from argparse import (
2+
ArgumentParser,
3+
_SubParsersAction,
4+
)
5+
import json
6+
from pathlib import Path
7+
import time
8+
9+
from async_service import Service, TrioManager
10+
11+
from eth_typing import BlockNumber
12+
13+
from lahja import EndpointAPI
14+
15+
# from web3 import Web3
16+
17+
from eth2.beacon.tools.builder.initializer import (
18+
create_keypair_and_mock_withdraw_credentials,
19+
)
20+
from eth2.beacon.tools.builder.validator import create_mock_deposit_data
21+
from eth2.beacon.tools.fixtures.loading import load_yaml_at
22+
from eth2.beacon.typing import Timestamp
23+
from trinity.boot_info import BootInfo
24+
from trinity.components.eth2.eth1_monitor.configs import deposit_contract_json
25+
from trinity.components.eth2.eth1_monitor.eth1_data_provider import FakeEth1DataProvider
26+
from trinity.config import BeaconAppConfig
27+
from trinity.db.manager import DBClient
28+
from trinity.events import ShutdownRequest
29+
from trinity.extensibility import (
30+
TrioIsolatedComponent,
31+
)
32+
33+
from .eth1_monitor import Eth1Monitor
34+
35+
36+
# Fake eth1 monitor config
37+
# TODO: These configs should be read from a config file, e.g., `eth1_monitor_config.yaml`.
38+
DEPOSIT_CONTRACT_ABI = json.loads(deposit_contract_json)["abi"]
39+
DEPOSIT_CONTRACT_ADDRESS = b"\x12" * 20
40+
NUM_BLOCKS_CONFIRMED = 100
41+
POLLING_PERIOD = 10
42+
START_BLOCK_NUMBER = BlockNumber(1000)
43+
44+
# Configs for fake Eth1DataProvider
45+
NUM_DEPOSITS_PER_BLOCK = 0
46+
START_BLOCK_TIMESTAMP = Timestamp(int(time.time()) - 2100) # Around 45 mins ago
47+
48+
49+
class Eth1MonitorComponent(TrioIsolatedComponent):
50+
51+
name = "Eth1 Monitor"
52+
endpoint_name = "eth1-monitor"
53+
54+
@property
55+
def is_enabled(self) -> bool:
56+
return self._boot_info.trinity_config.has_app_config(BeaconAppConfig)
57+
58+
@classmethod
59+
def configure_parser(cls,
60+
arg_parser: ArgumentParser,
61+
subparser: _SubParsersAction) -> None:
62+
# TODO: For now we use fake eth1 monitor.
63+
pass
64+
# arg_parser.add_argument(
65+
# "--eth1client-rpc",
66+
# help="RPC HTTP endpoint of Eth1 client ",
67+
# )
68+
69+
@classmethod
70+
async def do_run(cls, boot_info: BootInfo, event_bus: EndpointAPI) -> None:
71+
trinity_config = boot_info.trinity_config
72+
beacon_app_config = trinity_config.get_app_config(BeaconAppConfig)
73+
chain_config = beacon_app_config.get_chain_config()
74+
base_db = DBClient.connect(trinity_config.database_ipc_path)
75+
76+
# TODO: For now we use fake eth1 monitor.
77+
# if boot_info.args.eth1client_rpc:
78+
# w3: Web3 = Web3.HTTPProvider(boot_info.args.eth1client_rpc)
79+
# else:
80+
# w3: Web3 = None
81+
82+
# TODO: For now we use fake eth1 monitor. So we load validators data from
83+
# interop setting and hardcode the deposit data into fake eth1 data provider.
84+
chain = chain_config.beacon_chain_class(
85+
base_db,
86+
chain_config.genesis_config,
87+
)
88+
config = chain.get_state_machine().config
89+
key_set = load_yaml_at(
90+
Path('eth2/beacon/scripts/quickstart_state/keygen_16_validators.yaml')
91+
)
92+
pubkeys, privkeys, withdrawal_credentials = create_keypair_and_mock_withdraw_credentials(
93+
config,
94+
key_set, # type: ignore
95+
)
96+
initial_deposits = (
97+
create_mock_deposit_data(
98+
config=config,
99+
pubkey=pubkey,
100+
privkey=privkey,
101+
withdrawal_credentials=withdrawal_credential,
102+
)
103+
for pubkey, privkey, withdrawal_credential in zip(
104+
pubkeys, privkeys, withdrawal_credentials)
105+
)
106+
107+
with base_db:
108+
fake_eth1_data_provider = FakeEth1DataProvider(
109+
start_block_number=START_BLOCK_NUMBER,
110+
start_block_timestamp=START_BLOCK_TIMESTAMP,
111+
num_deposits_per_block=NUM_DEPOSITS_PER_BLOCK,
112+
initial_deposits=tuple(initial_deposits),
113+
)
114+
115+
eth1_monitor_service: Service = Eth1Monitor(
116+
eth1_data_provider=fake_eth1_data_provider,
117+
num_blocks_confirmed=NUM_BLOCKS_CONFIRMED,
118+
polling_period=POLLING_PERIOD,
119+
start_block_number=START_BLOCK_NUMBER,
120+
event_bus=event_bus,
121+
base_db=base_db,
122+
)
123+
124+
try:
125+
await TrioManager.run_service(eth1_monitor_service)
126+
except Exception:
127+
await event_bus.broadcast(ShutdownRequest("Eth1 Monitor ended unexpectedly"))
128+
raise

trinity/components/eth2/eth1_monitor/db.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,17 @@ def __init__(
114114
) -> None:
115115
self.db = db
116116
self._deposit_count = self._get_deposit_count()
117-
# If the parameter `highest_processed_block_number` is given, set it in the database.
117+
latest_processed_block_number = self._get_highest_processed_block_number()
118118
if highest_processed_block_number is not None:
119119
if highest_processed_block_number < 0:
120120
raise DepositDataDBValidationError(
121121
"`highest_processed_block_number` should be non-negative: "
122122
f"highest_processed_block_number={highest_processed_block_number}"
123123
)
124-
self._set_highest_processed_block_number(
125-
self.db, highest_processed_block_number
126-
)
124+
elif highest_processed_block_number > latest_processed_block_number:
125+
self._set_highest_processed_block_number(
126+
self.db, highest_processed_block_number
127+
)
127128
self._highest_processed_block_number = (
128129
self._get_highest_processed_block_number()
129130
)

0 commit comments

Comments
 (0)