Skip to content

Commit

Permalink
Add command to sign a withdrawal credentials update message using a v…
Browse files Browse the repository at this point in the history
…alidator keystore (#88)

* Adding command to create keystore signature for validator PoO

* Fixing withdrawal address prompt for bls keystore command

* Updating bls keystore domain as 0x0B is used by consolidation

* Updating readme to include reference to UCWM github
  • Loading branch information
valefar-on-discord authored Sep 9, 2024
1 parent 7708681 commit e15b4e5
Show file tree
Hide file tree
Showing 16 changed files with 702 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
bls_to_execution_changes
bls_to_execution_changes_keystore
exit_transactions
partial_deposits
validator_keys
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- [`existing-mnemonic` Arguments](#existing-mnemonic-arguments)
- [Successful message](#successful-message)
- [`generate-bls-to-execution-change` Arguments](#generate-bls-to-execution-change-arguments)
- [`generate-bls-to-execution-change-keystore` Arguments](#generate-bls-to-execution-change-keystore-arguments)
- [`exit-transaction-keystore` Arguments](#exit-transaction-keystore-arguments)
- [`exit-transaction-mnemonic` Arguments](#exit-transaction-mnemonic-arguments)
- [`partial-deposit` Arguments](#partial-deposit-arguments)
Expand Down Expand Up @@ -153,6 +154,7 @@ The CLI offers different commands depending on what you want to do with the tool
| `new-mnemonic` | (Recommended) This command is used to generate keystores with a new mnemonic. |
| `existing-mnemonic` | This command is used to re-generate or derive new keys from your existing mnemonic. Use this command, if (i) you have already generated keys with this CLI before, (ii) you want to reuse your mnemonic that you know is secure that you generated elsewhere (reusing your eth wallet mnemonic .etc), or (iii) you lost your keystores and need to recover your keys. |
| `generate-bls-to-execution-change` | This command is used to generate BLS to execution address change message. This is used to add a withdrawal address to a validator that does not currently have one. |
| `generate-bls-to-execution-change-keystore` | This command is used to sign a BLS to execution address change message with the provided validator key. This is used for a proposed solution to update withdrawal credentials for users who have lost their mnemonic. |
| `exit-transaction-keystore` | This command is used to create an exit transaction using a keystore file. |
| `exit-transaction-mnemonic` | This command is used to create an exit transaction using a mnemonic phrase. |
| `partial-deposit` | This command is used to create a deposit file using a keystore file. |
Expand Down Expand Up @@ -216,6 +218,20 @@ You can use `generate-bls-to-execution-change --help` to see all arguments. Note
| `--withdrawal_address` | String. Ethereum execution address in hexadecimal encoded form | If this field is set and valid, the given execution address will be used to create the withdrawal credentials. Otherwise, it will generate withdrawal credentials with the mnemonic-derived withdrawal public key in [ERC-2334 format](https://eips.ethereum.org/EIPS/eip-2334#eth2-specific-parameters). |
| `--devnet_chain_setting` | String. JSON string `'{"network_name": "<NETWORK_NAME>", "genesis_fork_version": "<GENESIS_FORK_VERSION>", "exit_fork_version": "<EXIT_FORK_VERSION>", "genesis_validator_root": "<GENESIS_VALIDATOR_ROOT>"}'` | The custom chain setting of a devnet or testnet. Note that it will override your `--chain` choice. |

###### `generate-bls-to-execution-change-keystore` Arguments

You can use `generate-bls-to-execution-change-keystore --help` to see all arguments. Note that if there are missing arguments that the CLI needs, it will ask you for them.

| Argument | Type | Description |
| -------- | -------- | -------- |
| `--chain` | String. `mainnet` by default | The chain setting for the signing domain. |
| `--keystore` | File | The keystore file associating with the validator you wish to sign with. This keystore file should match the provided validator index. |
| `--keystore_password` | String | The password that is used to encrypt the provided keystore. Note: It's not your mnemonic password. |
| `--validator_index` | Integer | The validator index corresponding to the provided keystore. |
| `--withdrawal_address` | String. Ethereum execution address in hexadecimal encoded form that you wish to set as your withdrawal credentials. |
| `--output_folder` | String. Pointing to `./bls_to_execution_changes_keystore` by default | The folder path for the `bls_to_execution_change_keystore_signature-*` JSON file |
| `--devnet_chain_setting` | String. JSON string `'{"network_name": "<NETWORK_NAME>", "genesis_fork_version": "<GENESIS_FORK_VERSION>", "exit_fork_version": "<EXIT_FORK_VERSION>", "genesis_validator_root": "<GENESIS_VALIDATOR_ROOT>"}'` | The custom chain setting of a devnet or testnet. Note that it will override your `--chain` choice. |

###### `exit-transaction-keystore` Arguments

You can use `exit-transaction-keystore --help` to see all arguments. Note that if there are missing arguments that the CLI needs, it will ask you for them.
Expand Down Expand Up @@ -316,6 +332,7 @@ See [here](#commands)
See [here](#new-mnemonic-arguments) for `new-mnemonic` arguments\
See [here](#existing-mnemonic-arguments) for `existing-mnemonic` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-keystore-arguments) for `generate-bls-to-execution-change-keystore` arguments\
See [here](#exit-transaction-keystore-arguments) for `exit-transaction-keystore` arguments\
See [here](#exit-transaction-mnemonic-arguments) for `exit-transaction-mnemonic` arguments\
See [here](#partial-deposit-arguments) for `partial-deposit` arguments
Expand Down Expand Up @@ -391,6 +408,7 @@ See [here](#commands)
See [here](#new-mnemonic-arguments) for `new-mnemonic` arguments\
See [here](#existing-mnemonic-arguments) for `existing-mnemonic` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-keystore-arguments) for `generate-bls-to-execution-change-keystore` arguments\
See [here](#exit-transaction-keystore-arguments) for `exit-transaction-keystore` arguments\
See [here](#exit-transaction-mnemonic-arguments) for `exit-transaction-mnemonic` arguments\
See [here](#partial-deposit-arguments) for `partial-deposit` arguments
Expand Down Expand Up @@ -508,6 +526,7 @@ See [here](#commands)
See [here](#new-mnemonic-arguments) for `new-mnemonic` arguments\
See [here](#existing-mnemonic-arguments) for `existing-mnemonic` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-keystore-arguments) for `generate-bls-to-execution-change-keystore` arguments\
See [here](#exit-transaction-keystore-arguments) for `exit-transaction-keystore` arguments\
See [here](#exit-transaction-mnemonic-arguments) for `exit-transaction-mnemonic` arguments\
See [here](#partial-deposit-arguments) for `partial-deposit` arguments
Expand Down Expand Up @@ -573,7 +592,7 @@ See [here](#commands)
See [here](#new-mnemonic-arguments) for `new-mnemonic` arguments\
See [here](#existing-mnemonic-arguments) for `existing-mnemonic` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-keystore-arguments) for `generate-bls-to-execution-change-keystore` arguments\
See [here](#exit-transaction-keystore-arguments) for `exit-transaction-keystore` arguments\
See [here](#exit-transaction-mnemonic-arguments) for `exit-transaction-mnemonic` arguments\
See [here](#partial-deposit-arguments) for `partial-deposit` arguments
Expand Down Expand Up @@ -640,6 +659,7 @@ See [here](#commands)
See [here](#new-mnemonic-arguments) for `new-mnemonic` arguments\
See [here](#existing-mnemonic-arguments) for `existing-mnemonic` arguments\
See [here](#generate-bls-to-execution-change-arguments) for `generate-bls-to-execution-change` arguments\
See [here](#generate-bls-to-execution-change-keystore-arguments) for `generate-bls-to-execution-change-keystore` arguments\
See [here](#exit-transaction-keystore-arguments) for `exit-transaction-keystore` arguments\
See [here](#exit-transaction-mnemonic-arguments) for `exit-transaction-mnemonic` arguments\
See [here](#partial-deposit-arguments) for `partial-deposit` arguments
Expand Down
32 changes: 32 additions & 0 deletions docs/src/generate_bls_to_execution_change_keystore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# generate-bls-to-execution-change-keystore

<div class="warning">
This command is associated with the a proposed solution to update withdrawal credentials for those who are missing their mnemonic. At this point this has not been approved or implemented and there is no guarantee credentials will be modified in the future.

The project is located [here](https://github.com/eth-educators/update-credentials-without-mnemonic) if you would like to learn more.
</div>

## Description
Signs a withdrawal credential update message using the provided keystore. This signature is one of the required proofs of ownership for validators who have lost or are missing their mnemonic and are unable to perform the BLS change needed to update their withdrawal credentials.

## 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 sign with. This keystore file should match the provided validator index.

- **`--keystore_password`**: The password that is used to encrypt the provided keystore. Note: It's not your mnemonic password. <span class="warning"></span>

- **`--validator_index`**: The validator index corresponding to the provided keystore.

- **`--withdrawal_address`**: Ethereum execution address in hexadecimal encoded form that you wish to set as your withdrawal credentials.

- **`--output_folder`**: The folder path for the `bls_to_execution_change_keystore_signature-*` JSON file.

- **`--devnet_chain_setting`**: The custom chain setting of a devnet or testnet. Note that it will override your `--chain` choice.

## Example Usage

```sh
./deposit generate-bls-to-execution-change
```
2 changes: 2 additions & 0 deletions docs/src/landing.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ If there is a specific command you would like to understand more, please choose

- **[generate-bls-to-execution-change](generate_bls_to_execution_change.md)**: Update your withdrawal credentials of existing validators. It is **required** to have the corresponding mnemonic.

- **[generate-bls-to-execution-change-keystore](generate_bls_to_execution_change_keystore.md)**: Sign an update withdrawal credentials message using your validator keystore.

- **[exit-transaction-keystore](exit_transaction_keystore.md)**: Generate an exit message using the keystore of your validators.

- **[exit-transaction-mnemonic](exit_transaction_mnemonic.md)**: Generate an exit message using the mnemonic of your validators.
Expand Down
71 changes: 71 additions & 0 deletions ethstaker_deposit/bls_to_execution_change_keystore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import json
import os
from typing import Any, Dict
from eth_typing import HexAddress
from eth_utils import to_canonical_address
from py_ecc.bls import G2ProofOfPossession as bls

from ethstaker_deposit.exceptions import ValidationError
from ethstaker_deposit.settings import BaseChainSetting
from ethstaker_deposit.utils.ssz import (
BLSToExecutionChangeKeystore,
SignedBLSToExecutionChangeKeystore,
compute_signing_root,
compute_bls_to_execution_change_keystore_domain,
)


def bls_to_execution_change_keystore_generation(
chain_settings: BaseChainSetting,
signing_key: int,
execution_address: HexAddress,
validator_index: int) -> SignedBLSToExecutionChangeKeystore:
if execution_address is None:
raise ValueError("The execution address should NOT be empty.")
if chain_settings.GENESIS_VALIDATORS_ROOT is None:
raise ValidationError("The genesis validators root should NOT be empty "
"for this chain to obtain the BLS to execution change.")

message = BLSToExecutionChangeKeystore( # type: ignore[no-untyped-call]
validator_index=validator_index,
to_execution_address=to_canonical_address(execution_address),
)
domain = compute_bls_to_execution_change_keystore_domain(
fork_version=chain_settings.GENESIS_FORK_VERSION,
genesis_validators_root=chain_settings.GENESIS_VALIDATORS_ROOT,
)
signing_root = compute_signing_root(message, domain)
signature = bls.Sign(signing_key, signing_root)

return SignedBLSToExecutionChangeKeystore( # type: ignore[no-untyped-call]
message=message,
signature=signature,
)


def export_bls_to_execution_change_keystore_json(folder: str,
signed_execution_change: SignedBLSToExecutionChangeKeystore,
timestamp: float) -> str:
signed_bls_to_execution_change_keystore_json: Dict[str, Any] = {}

address = '0x' + signed_execution_change.message.to_execution_address.hex() # type: ignore[attr-defined]
index = signed_execution_change.message.validator_index # type: ignore[attr-defined]
signature = '0x' + signed_execution_change.signature.hex() # type: ignore[attr-defined]

message = {
'to_execution_address': address,
'validator_index': index,
}
signed_bls_to_execution_change_keystore_json.update({'message': message})
signed_bls_to_execution_change_keystore_json.update({'signature': signature})

filefolder = os.path.join(
folder,
'bls_to_execution_change_keystore_signature-%s-%i.json' % (index, timestamp)
)

with open(filefolder, 'w') as f:
json.dump(signed_bls_to_execution_change_keystore_json, f)
if os.name == 'posix':
os.chmod(filefolder, int('440', 8)) # Read for owner & group
return filefolder
5 changes: 3 additions & 2 deletions ethstaker_deposit/cli/exit_transaction_keystore.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import click
import os
import time

from typing import Any

from ethstaker_deposit.exceptions import ValidationError
from ethstaker_deposit.utils.exit_transaction import exit_transaction_generation, export_exit_transaction_json
from ethstaker_deposit.key_handling.keystore import Keystore
from ethstaker_deposit.settings import (
Expand Down Expand Up @@ -123,7 +124,7 @@ def exit_transaction_keystore(

click.echo(load_text(['msg_verify_exit_transaction']))
if (not verify_signed_exit_json(saved_folder, keystore.pubkey, chain_settings)):
click.echo(['err_verify_exit_transaction'])
raise ValidationError(load_text(['err_verify_exit_transaction']))

click.echo(load_text(['msg_creation_success']) + saved_folder)
click.pause(load_text(['msg_pause']))
4 changes: 0 additions & 4 deletions ethstaker_deposit/cli/generate_bls_to_execution_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@
)


def get_password(text: str) -> str:
return click.prompt(text, hide_input=True, show_default=False, type=str)


def _validate_credentials_match(kwargs: Dict[str, Any]) -> Optional[ValidationError]:
credential: Credential = kwargs.pop('credential')
bls_withdrawal_credentials: bytes = kwargs.pop('bls_withdrawal_credentials')
Expand Down
Loading

0 comments on commit e15b4e5

Please sign in to comment.