Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev -> master: v1.2.0 #196

Merged
merged 15 commits into from
Apr 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,10 @@ You can use `new-mnemonic --help` to see all arguments. Note that if there are m
| Argument | Type | Description |
| -------- | -------- | -------- |
| `--num_validators` | Non-negative integer | The number of signing keys you want to generate. Note that the child key(s) are generated via the same master key. |
| `--mnemonic_language` | String. Options: `czech`, `chinese_traditional`, `chinese_simplified`, `english`, `spanish`, `italian`, `korean`. Default to `english` | The mnemonic language |
| `--mnemonic_language` | String. Options: `chinese_simplified`, `chinese_traditional`, `czech`, `english`, `italian`, `korean`, `portuguese`, `spanish`. Default to `english` | The mnemonic language |
| `--folder` | String. Pointing to `./validator_keys` by default | The folder path for the keystore(s) and deposit(s) |
| `--chain` | String. `mainnet` by default | The chain setting for the signing domain. |
| `--eth1_withdrawal_address` | String. Eth1 address in hexadecimal encoded form | If this field is set and valid, the given Eth1 address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in [EIP-2334 format](https://eips.ethereum.org/EIPS/eip-2334#eth2-specific-parameters). |

###### `existing-mnemonic` Arguments

Expand All @@ -132,6 +133,7 @@ You can use `existing-mnemonic --help` to see all arguments. Note that if there
| `--num_validators` | Non-negative integer | The number of signing keys you want to generate. Note that the child key(s) are generated via the same master key. |
| `--folder` | String. Pointing to `./validator_keys` by default | The folder path for the keystore(s) and deposit(s) |
| `--chain` | String. `mainnet` by default | The chain setting for the signing domain. |
| `--eth1_withdrawal_address` | String. Eth1 address in hexadecimal encoded form | If this field is set and valid, the given Eth1 address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in [EIP-2334 format](https://eips.ethereum.org/EIPS/eip-2334#eth2-specific-parameters). |

###### Successful message

Expand Down
29 changes: 27 additions & 2 deletions eth2deposit/cli/generate_keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
Callable,
)

from eth_typing import HexAddress
from eth_utils import is_hex_address, to_normalized_address

from eth2deposit.credentials import (
CredentialList,
)
Expand Down Expand Up @@ -57,6 +60,18 @@ def validate_password(cts: click.Context, param: Any, password: str) -> str:
return password


def validate_eth1_withdrawal_address(cts: click.Context, param: Any, address: str) -> HexAddress:
if address is None:
return None
if not is_hex_address(address):
raise ValueError("The given Eth1 address is not in hexadecimal encoded form.")

normalized_address = to_normalized_address(address)
click.echo(f'\n**[Warning] you are setting Eth1 address {normalized_address} as your withdrawal address. '
'Please ensure that you have control over this address.**\n')
return normalized_address


def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[..., Any]:
'''
This is a decorator that, when applied to a parent-command, implements the
Expand Down Expand Up @@ -91,6 +106,14 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
'to ask you for your mnemonic as otherwise it will appear in your shell history.)'),
prompt='Type the password that secures your validator keystore(s)',
),
click.option(
'--eth1_withdrawal_address',
default=None,
callback=validate_eth1_withdrawal_address,
help=('If this field is set and valid, the given Eth1 address will be used to create the '
'withdrawal credentials. Otherwise, it will generate withdrawal credentials with the '
'mnemonic-derived withdrawal public key.'),
),
]
for decorator in reversed(decorators):
function = decorator(function)
Expand All @@ -100,7 +123,8 @@ def generate_keys_arguments_decorator(function: Callable[..., Any]) -> Callable[
@click.command()
@click.pass_context
def generate_keys(ctx: click.Context, validator_start_index: int,
num_validators: int, folder: str, chain: str, keystore_password: str, **kwargs: Any) -> None:
num_validators: int, folder: str, chain: str, keystore_password: str,
eth1_withdrawal_address: HexAddress, **kwargs: Any) -> None:
mnemonic = ctx.obj['mnemonic']
mnemonic_password = ctx.obj['mnemonic_password']
amounts = [MAX_DEPOSIT_AMOUNT] * num_validators
Expand All @@ -118,12 +142,13 @@ def generate_keys(ctx: click.Context, validator_start_index: int,
amounts=amounts,
chain_setting=chain_setting,
start_index=validator_start_index,
hex_eth1_withdrawal_address=eth1_withdrawal_address,
)
keystore_filefolders = credentials.export_keystores(password=keystore_password, folder=folder)
deposits_file = credentials.export_deposit_data_json(folder=folder)
if not credentials.verify_keystores(keystore_filefolders=keystore_filefolders, password=keystore_password):
raise ValidationError("Failed to verify the keystores.")
if not verify_deposit_data_json(deposits_file):
if not verify_deposit_data_json(deposits_file, credentials.credentials):
raise ValidationError("Failed to verify the deposit data JSON files.")
click.echo('\nSuccess!\nYour keys can be found at: %s' % folder)
click.pause('\n\nPress any key.')
58 changes: 52 additions & 6 deletions eth2deposit/credentials.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import os
import click
from enum import Enum
import time
import json
from typing import Dict, List
from typing import Dict, List, Optional

from eth_typing import Address, HexAddress
from eth_utils import to_canonical_address
from py_ecc.bls import G2ProofOfPossession as bls

from eth2deposit.exceptions import ValidationError
Expand All @@ -14,6 +18,7 @@
from eth2deposit.settings import DEPOSIT_CLI_VERSION, BaseChainSetting
from eth2deposit.utils.constants import (
BLS_WITHDRAWAL_PREFIX,
ETH1_ADDRESS_WITHDRAWAL_PREFIX,
ETH2GWEI,
MAX_DEPOSIT_AMOUNT,
MIN_DEPOSIT_AMOUNT,
Expand All @@ -27,13 +32,19 @@
)


class WithdrawalType(Enum):
BLS_WITHDRAWAL = 0
ETH1_ADDRESS_WITHDRAWAL = 1


class Credential:
"""
A Credential object contains all of the information for a single validator and the corresponding functionality.
Once created, it is the only object that should be required to perform any processing for a validator.
"""
def __init__(self, *, mnemonic: str, mnemonic_password: str,
index: int, amount: int, chain_setting: BaseChainSetting):
index: int, amount: int, chain_setting: BaseChainSetting,
hex_eth1_withdrawal_address: Optional[HexAddress]):
# Set path as EIP-2334 format
# https://eips.ethereum.org/EIPS/eip-2334
purpose = '12381'
Expand All @@ -48,6 +59,7 @@ def __init__(self, *, mnemonic: str, mnemonic_password: str,
mnemonic=mnemonic, path=self.signing_key_path, password=mnemonic_password)
self.amount = amount
self.chain_setting = chain_setting
self.hex_eth1_withdrawal_address = hex_eth1_withdrawal_address

@property
def signing_pk(self) -> bytes:
Expand All @@ -57,10 +69,42 @@ def signing_pk(self) -> bytes:
def withdrawal_pk(self) -> bytes:
return bls.SkToPk(self.withdrawal_sk)

@property
def eth1_withdrawal_address(self) -> Optional[Address]:
if self.hex_eth1_withdrawal_address is None:
return None
return to_canonical_address(self.hex_eth1_withdrawal_address)

@property
def withdrawal_prefix(self) -> bytes:
if self.eth1_withdrawal_address is not None:
return ETH1_ADDRESS_WITHDRAWAL_PREFIX
else:
return BLS_WITHDRAWAL_PREFIX

@property
def withdrawal_type(self) -> WithdrawalType:
if self.withdrawal_prefix == BLS_WITHDRAWAL_PREFIX:
return WithdrawalType.BLS_WITHDRAWAL
elif self.withdrawal_prefix == ETH1_ADDRESS_WITHDRAWAL_PREFIX:
return WithdrawalType.ETH1_ADDRESS_WITHDRAWAL
else:
raise ValueError(f"Invalid withdrawal_prefix {self.withdrawal_prefix.hex()}")

@property
def withdrawal_credentials(self) -> bytes:
withdrawal_credentials = BLS_WITHDRAWAL_PREFIX
withdrawal_credentials += SHA256(self.withdrawal_pk)[1:]
if self.withdrawal_type == WithdrawalType.BLS_WITHDRAWAL:
withdrawal_credentials = BLS_WITHDRAWAL_PREFIX
withdrawal_credentials += SHA256(self.withdrawal_pk)[1:]
elif (
self.withdrawal_type == WithdrawalType.ETH1_ADDRESS_WITHDRAWAL
and self.eth1_withdrawal_address is not None
):
withdrawal_credentials = ETH1_ADDRESS_WITHDRAWAL_PREFIX
withdrawal_credentials += b'\x00' * 11
withdrawal_credentials += self.eth1_withdrawal_address
else:
raise ValueError(f"Invalid withdrawal_type {self.withdrawal_type}")
return withdrawal_credentials

@property
Expand Down Expand Up @@ -129,7 +173,8 @@ def from_mnemonic(cls,
num_keys: int,
amounts: List[int],
chain_setting: BaseChainSetting,
start_index: int) -> 'CredentialList':
start_index: int,
hex_eth1_withdrawal_address: Optional[HexAddress]) -> 'CredentialList':
if len(amounts) != num_keys:
raise ValueError(
f"The number of keys ({num_keys}) doesn't equal to the corresponding deposit amounts ({len(amounts)})."
Expand All @@ -138,7 +183,8 @@ def from_mnemonic(cls,
with click.progressbar(key_indices, label='Creating your keys:\t\t',
show_percent=False, show_pos=True) as indices:
return cls([Credential(mnemonic=mnemonic, mnemonic_password=mnemonic_password,
index=index, amount=amounts[index - start_index], chain_setting=chain_setting)
index=index, amount=amounts[index - start_index], chain_setting=chain_setting,
hex_eth1_withdrawal_address=hex_eth1_withdrawal_address)
for index in indices])

def export_keystores(self, password: str, folder: str) -> List[str]:
Expand Down
Loading