forked from ethereum/staking-deposit-cli
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding command to create deposit with validator keystore (#113)
- Loading branch information
1 parent
3b8d32b
commit ab3646d
Showing
19 changed files
with
616 additions
and
29 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 |
---|---|---|
@@ -1,5 +1,6 @@ | ||
bls_to_execution_changes | ||
exit_transactions | ||
partial_deposits | ||
validator_keys | ||
|
||
# Python testing & linting: | ||
|
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,28 @@ | ||
# partial-deposit | ||
|
||
{{#include ./snippet/warning_message.md}} | ||
|
||
## Description | ||
Creates a deposit file with an existing validator key. Can be used to initiate a validator or deposit to an existing validator. | ||
If you wish to create a validator with 0x00 credentials, you must use the **[new-mnemonic](new_mnemonic.md)** or the **[existing-mnemonic](existing_mnemonic.md)** command. | ||
|
||
## Optional Arguments | ||
|
||
- **`--chain`**: The chain to use for generating the deposit data. Options are: 'mainnet', 'holesky', etc. | ||
|
||
- **`--keystore`**: The keystore file associating with the validator you wish to deposit to. | ||
|
||
- **`--keystore_password`**: The password that is used to encrypt the provided keystore. Note: It's not your mnemonic password. <span class="warning"></span> | ||
|
||
- **`--amount`**: The amount you wish to deposit in ether. Must be at least 1 and can not have precision beyond 1 gwei. Defaults to 32 ether. | ||
|
||
- **`--withdrawal_address`**: The withdrawal address of the existing validator or the desired withdrawal address. | ||
|
||
- **`--output_folder`**: The folder path for the `deposit-*` JSON file. | ||
|
||
|
||
## Example Usage | ||
|
||
```sh | ||
./deposit partial-deposit --keystore /path/to/keystore.json | ||
``` |
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,180 @@ | ||
import json | ||
import click | ||
import os | ||
import time | ||
|
||
from eth_typing import HexAddress | ||
from eth_utils import to_canonical_address | ||
from py_ecc.bls import G2ProofOfPossession as bls | ||
from typing import Any | ||
|
||
from ethstaker_deposit.key_handling.keystore import Keystore | ||
from ethstaker_deposit.settings import ( | ||
DEPOSIT_CLI_VERSION, | ||
MAINNET, | ||
ALL_CHAIN_KEYS, | ||
get_chain_setting, | ||
) | ||
from ethstaker_deposit.utils.click import ( | ||
captive_prompt_callback, | ||
choice_prompt_func, | ||
jit_option, | ||
) | ||
from ethstaker_deposit.utils.constants import DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME, EXECUTION_ADDRESS_WITHDRAWAL_PREFIX | ||
from ethstaker_deposit.utils.deposit import export_deposit_data_json | ||
from ethstaker_deposit.utils.intl import ( | ||
closest_match, | ||
load_text, | ||
) | ||
from ethstaker_deposit.utils.ssz import ( | ||
DepositData, | ||
DepositMessage, | ||
compute_deposit_domain, | ||
compute_signing_root, | ||
) | ||
from ethstaker_deposit.utils.validation import ( | ||
validate_deposit, | ||
validate_keystore_file, | ||
validate_partial_deposit_amount, | ||
validate_withdrawal_address, | ||
) | ||
|
||
|
||
FUNC_NAME = 'partial_deposit' | ||
|
||
|
||
@click.command( | ||
help=load_text(['arg_partial_deposit', 'help'], func=FUNC_NAME), | ||
) | ||
@jit_option( | ||
callback=captive_prompt_callback( | ||
lambda x: closest_match(x, ALL_CHAIN_KEYS), | ||
choice_prompt_func( | ||
lambda: load_text(['arg_partial_deposit_chain', 'prompt'], func=FUNC_NAME), | ||
ALL_CHAIN_KEYS | ||
), | ||
), | ||
default=MAINNET, | ||
help=lambda: load_text(['arg_partial_deposit_chain', 'help'], func=FUNC_NAME), | ||
param_decls='--chain', | ||
prompt=choice_prompt_func( | ||
lambda: load_text(['arg_partial_deposit_chain', 'prompt'], func=FUNC_NAME), | ||
ALL_CHAIN_KEYS | ||
), | ||
) | ||
@jit_option( | ||
callback=captive_prompt_callback( | ||
lambda file: validate_keystore_file(file), | ||
lambda: load_text(['arg_partial_deposit_keystore', 'prompt'], func=FUNC_NAME), | ||
), | ||
help=lambda: load_text(['arg_partial_deposit_keystore', 'help'], func=FUNC_NAME), | ||
param_decls='--keystore', | ||
prompt=lambda: load_text(['arg_partial_deposit_keystore', 'prompt'], func=FUNC_NAME), | ||
type=click.Path(exists=True, file_okay=True, dir_okay=False), | ||
) | ||
@jit_option( | ||
callback=captive_prompt_callback( | ||
lambda x: x, | ||
lambda: load_text(['arg_partial_deposit_keystore_password', 'prompt'], func=FUNC_NAME), | ||
None, | ||
lambda: load_text(['arg_partial_deposit_keystore_password', 'invalid'], func=FUNC_NAME), | ||
True, | ||
), | ||
help=lambda: load_text(['arg_partial_deposit_keystore_password', 'help'], func=FUNC_NAME), | ||
hide_input=True, | ||
param_decls='--keystore_password', | ||
prompt=lambda: load_text(['arg_partial_deposit_keystore_password', 'prompt'], func=FUNC_NAME), | ||
) | ||
@jit_option( | ||
callback=captive_prompt_callback( | ||
lambda amount: validate_partial_deposit_amount(amount), | ||
lambda: load_text(['arg_partial_deposit_amount', 'prompt'], func=FUNC_NAME), | ||
default="32", | ||
prompt_if_none=True, | ||
), | ||
default="32", | ||
help=lambda: load_text(['arg_partial_deposit_amount', 'help'], func=FUNC_NAME), | ||
param_decls='--amount', | ||
prompt=False, # the callback handles the prompt, to avoid second callback with gwei | ||
) | ||
@jit_option( | ||
callback=captive_prompt_callback( | ||
lambda address: validate_withdrawal_address(None, None, address, True), | ||
lambda: load_text(['arg_withdrawal_address', 'prompt'], func=FUNC_NAME), | ||
lambda: load_text(['arg_withdrawal_address', 'confirm'], func=FUNC_NAME), | ||
lambda: load_text(['arg_withdrawal_address', 'mismatch'], func=FUNC_NAME), | ||
prompt_if_none=True, | ||
), | ||
help=lambda: load_text(['arg_withdrawal_address', 'help'], func=FUNC_NAME), | ||
param_decls=['--withdrawal_address', '--execution_address', '--eth1_withdrawal_credentials'], | ||
prompt=False, # the callback handles the prompt | ||
) | ||
@jit_option( | ||
default=os.getcwd(), | ||
help=lambda: load_text(['arg_partial_deposit_output_folder', 'help'], func=FUNC_NAME), | ||
param_decls='--output_folder', | ||
type=click.Path(exists=True, file_okay=False, dir_okay=True), | ||
) | ||
@click.pass_context | ||
def partial_deposit( | ||
ctx: click.Context, | ||
chain: str, | ||
keystore: Keystore, | ||
keystore_password: str, | ||
amount: int, | ||
withdrawal_address: HexAddress, | ||
output_folder: str, | ||
**kwargs: Any) -> None: | ||
try: | ||
secret_bytes = keystore.decrypt(keystore_password) | ||
except ValueError: | ||
click.echo(load_text(['arg_partial_deposit_keystore_password', 'mismatch'])) | ||
exit(1) | ||
|
||
signing_key = int.from_bytes(secret_bytes, 'big') | ||
chain_settings = get_chain_setting(chain) | ||
|
||
withdrawal_credentials = EXECUTION_ADDRESS_WITHDRAWAL_PREFIX | ||
withdrawal_credentials += b'\x00' * 11 | ||
withdrawal_credentials += to_canonical_address(withdrawal_address) | ||
|
||
deposit_message = DepositMessage( # type: ignore[no-untyped-call] | ||
pubkey=bls.SkToPk(signing_key), | ||
withdrawal_credentials=withdrawal_credentials, | ||
amount=amount | ||
) | ||
|
||
domain = compute_deposit_domain(fork_version=chain_settings.GENESIS_FORK_VERSION) | ||
|
||
signing_root = compute_signing_root(deposit_message, domain) | ||
signature = bls.Sign(signing_key, signing_root) | ||
|
||
signed_deposit = DepositData( # type: ignore[no-untyped-call] | ||
**deposit_message.as_dict(), # type: ignore[no-untyped-call] | ||
signature=signature | ||
) | ||
|
||
folder = os.path.join(output_folder, DEFAULT_PARTIAL_DEPOSIT_FOLDER_NAME) | ||
if not os.path.exists(folder): | ||
os.mkdir(folder) | ||
|
||
click.echo(load_text(['msg_partial_deposit_creation'])) | ||
deposit_data = signed_deposit.as_dict() # type: ignore[no-untyped-call] | ||
deposit_data.update({'deposit_message_root': deposit_message.hash_tree_root}) | ||
deposit_data.update({'deposit_data_root': signed_deposit.hash_tree_root}) | ||
deposit_data.update({'fork_version': chain_settings.GENESIS_FORK_VERSION}) | ||
deposit_data.update({'network_name': chain_settings.NETWORK_NAME}) | ||
deposit_data.update({'deposit_cli_version': DEPOSIT_CLI_VERSION}) | ||
saved_folder = export_deposit_data_json(folder, time.time(), [deposit_data]) | ||
|
||
click.echo(load_text(['msg_verify_partial_deposit'])) | ||
deposit_json = [] | ||
with open(saved_folder, 'r', encoding='utf-8') as f: | ||
deposit_json = json.load(f) | ||
|
||
if (not validate_deposit(deposit_json[0])): | ||
click.echo(load_text(['err_verify_partial_deposit'])) | ||
return | ||
|
||
click.echo(load_text(['msg_creation_success']) + saved_folder) | ||
click.pause(load_text(['msg_pause'])) |
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
Oops, something went wrong.