mirror of
https://gitlab.com/pulsechaincom/staking-deposit-cli.git
synced 2024-12-23 11:57:19 +00:00
Merge pull request #28 from ethereum/carl_CredentialList
CredentialList class
This commit is contained in:
commit
1f6f63bce3
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,8 +1,12 @@
|
||||
validator_keys
|
||||
|
||||
# Python testing & linting:
|
||||
build/
|
||||
dist/
|
||||
venv/
|
||||
.pytest_cache
|
||||
.hypothesis
|
||||
.mypy_cache
|
||||
*.pytest_cache
|
||||
*.hypothesis
|
||||
*.mypy_cache
|
||||
*.egg-info
|
||||
*.egg
|
||||
__pycache__
|
@ -9,17 +9,20 @@ from eth2deposit.key_handling.keystore import (
|
||||
Keystore,
|
||||
ScryptKeystore,
|
||||
)
|
||||
from eth2deposit.utils.constants import BLS_WITHDRAWAL_PREFIX
|
||||
from eth2deposit.utils.constants import (
|
||||
BLS_WITHDRAWAL_PREFIX,
|
||||
DOMAIN_DEPOSIT,
|
||||
)
|
||||
from eth2deposit.utils.crypto import SHA256
|
||||
from eth2deposit.utils.ssz import (
|
||||
compute_domain,
|
||||
compute_signing_root,
|
||||
DepositData,
|
||||
DepositMessage,
|
||||
Deposit,
|
||||
)
|
||||
|
||||
|
||||
class ValidatorCredentials:
|
||||
class Credential:
|
||||
def __init__(self, *, mnemonic: str, index: int, amount: int):
|
||||
# Set path as EIP-2334 format
|
||||
# https://eips.ethereum.org/EIPS/eip-2334
|
||||
@ -63,45 +66,43 @@ class ValidatorCredentials:
|
||||
secret_bytes = saved_keystore.decrypt(password)
|
||||
return self.signing_sk == int.from_bytes(secret_bytes, 'big')
|
||||
|
||||
def unsigned_deposit(self) -> DepositMessage:
|
||||
return DepositMessage(
|
||||
pubkey=self.signing_pk,
|
||||
withdrawal_credentials=self.withdrawal_credentials,
|
||||
amount=self.amount,
|
||||
)
|
||||
|
||||
def mnemonic_to_credentials(*, mnemonic: str, num_keys: int,
|
||||
amounts: List[int], start_index: int=0,) -> List[ValidatorCredentials]:
|
||||
def signed_deposit(self) -> DepositData:
|
||||
domain = compute_domain(domain_type=DOMAIN_DEPOSIT)
|
||||
signing_root = compute_signing_root(self.unsigned_deposit(), domain)
|
||||
signed_deposit = DepositData(
|
||||
**self.unsigned_deposit().as_dict(),
|
||||
signature=bls.Sign(self.signing_sk, signing_root)
|
||||
)
|
||||
return signed_deposit
|
||||
|
||||
|
||||
class CredentialList:
|
||||
def __init__(self, credentials: List[Credential]):
|
||||
self.credentials = credentials
|
||||
|
||||
@classmethod
|
||||
def from_mnemonic(cls, *, mnemonic: str, num_keys: int, amounts: List[int], start_index: int=0) -> 'CredentialList':
|
||||
assert len(amounts) == num_keys
|
||||
key_indices = range(start_index, start_index + num_keys)
|
||||
credentials = [ValidatorCredentials(mnemonic=mnemonic, index=index, amount=amounts[index])
|
||||
for index in key_indices]
|
||||
return credentials
|
||||
return cls([Credential(mnemonic=mnemonic, index=index, amount=amounts[index])
|
||||
for index in key_indices])
|
||||
|
||||
def export_keystores(self, password: str, folder: str) -> List[str]:
|
||||
return [credential.save_signing_keystore(password=password, folder=folder) for credential in self.credentials]
|
||||
|
||||
def export_keystores(*, credentials: List[ValidatorCredentials], password: str, folder: str) -> List[str]:
|
||||
return [credential.save_signing_keystore(password=password, folder=folder) for credential in credentials]
|
||||
|
||||
|
||||
def sign_deposit_data(deposit_data: DepositMessage, sk: int) -> Deposit:
|
||||
'''
|
||||
Given a DepositMessage, it signs its root and returns a Deposit
|
||||
'''
|
||||
assert bls.PrivToPub(sk) == deposit_data.pubkey
|
||||
domain = compute_domain()
|
||||
signing_root = compute_signing_root(deposit_data, domain)
|
||||
signed_deposit_data = Deposit(
|
||||
**deposit_data.as_dict(),
|
||||
signature=bls.Sign(sk, signing_root)
|
||||
)
|
||||
return signed_deposit_data
|
||||
|
||||
|
||||
def export_deposit_data_json(*, credentials: List[ValidatorCredentials], folder: str) -> str:
|
||||
def export_deposit_data_json(self, folder: str) -> str:
|
||||
deposit_data: List[Dict[bytes, bytes]] = []
|
||||
for credential in credentials:
|
||||
deposit_datum = DepositMessage(
|
||||
pubkey=credential.signing_pk,
|
||||
withdrawal_credentials=credential.withdrawal_credentials,
|
||||
amount=credential.amount,
|
||||
)
|
||||
signed_deposit_datum = sign_deposit_data(deposit_datum, credential.signing_sk)
|
||||
for credential in self.credentials:
|
||||
signed_deposit_datum = credential.signed_deposit()
|
||||
datum_dict = signed_deposit_datum.as_dict()
|
||||
datum_dict.update({'deposit_data_root': deposit_datum.hash_tree_root})
|
||||
datum_dict.update({'deposit_data_root': credential.unsigned_deposit().hash_tree_root})
|
||||
datum_dict.update({'signed_deposit_data_root': signed_deposit_datum.hash_tree_root})
|
||||
deposit_data.append(datum_dict)
|
||||
|
||||
@ -110,8 +111,6 @@ def export_deposit_data_json(*, credentials: List[ValidatorCredentials], folder:
|
||||
json.dump(deposit_data, f, default=lambda x: x.hex())
|
||||
return filefolder
|
||||
|
||||
|
||||
def verify_keystores(*, credentials: List[ValidatorCredentials],
|
||||
keystore_filefolders: List[str], password: str) -> bool:
|
||||
def verify_keystores(self, keystore_filefolders: List[str], password: str) -> bool:
|
||||
return all(credential.verify_keystore(keystore_filefolder=filefolder, password=password)
|
||||
for credential, filefolder in zip(credentials, keystore_filefolders))
|
||||
for credential, filefolder in zip(self.credentials, keystore_filefolders))
|
||||
|
@ -3,16 +3,13 @@ import sys
|
||||
import click
|
||||
|
||||
from eth2deposit.credentials import (
|
||||
mnemonic_to_credentials,
|
||||
export_keystores,
|
||||
export_deposit_data_json,
|
||||
verify_keystores,
|
||||
CredentialList,
|
||||
)
|
||||
from eth2deposit.key_handling.key_derivation.mnemonic import (
|
||||
get_languages,
|
||||
get_mnemonic,
|
||||
)
|
||||
from eth2deposit.utils.eth2_deposit_check import verify_deposit_data_json
|
||||
from eth2deposit.utils.validation import verify_deposit_data_json
|
||||
from eth2deposit.utils.constants import (
|
||||
WORD_LISTS_PATH,
|
||||
MAX_DEPOSIT_AMOUNT,
|
||||
@ -78,13 +75,13 @@ def main(num_validators: int, mnemonic_language: str, folder: str, password: str
|
||||
click.clear()
|
||||
click.echo(RHINO_0)
|
||||
click.echo('Creating your keys.')
|
||||
credentials = mnemonic_to_credentials(mnemonic=mnemonic, num_keys=num_validators, amounts=amounts)
|
||||
credentials = CredentialList.from_mnemonic(mnemonic=mnemonic, num_keys=num_validators, amounts=amounts)
|
||||
click.echo('Saving your keystore(s).')
|
||||
keystore_filefolders = export_keystores(credentials=credentials, password=password, folder=folder)
|
||||
keystore_filefolders = credentials.export_keystores(password=password, folder=folder)
|
||||
click.echo('Creating your deposit(s).')
|
||||
deposits_file = export_deposit_data_json(credentials=credentials, folder=folder)
|
||||
deposits_file = credentials.export_deposit_data_json(folder=folder)
|
||||
click.echo('Verifying your keystore(s).')
|
||||
assert verify_keystores(credentials=credentials, keystore_filefolders=keystore_filefolders, password=password)
|
||||
assert credentials.verify_keystores(keystore_filefolders=keystore_filefolders, password=password)
|
||||
click.echo('Verifying your deposit(s).')
|
||||
assert verify_deposit_data_json(deposits_file)
|
||||
click.echo('\nSuccess!\nYour keys can be found at: %s' % folder)
|
||||
|
@ -42,8 +42,6 @@ def compute_signing_root(ssz_object: Serializable, domain: bytes) -> bytes:
|
||||
return domain_wrapped_object.hash_tree_root
|
||||
|
||||
|
||||
# DepositMessage SSZ
|
||||
|
||||
class DepositMessage(Serializable):
|
||||
fields = [
|
||||
('pubkey', bytes48),
|
||||
@ -52,7 +50,7 @@ class DepositMessage(Serializable):
|
||||
]
|
||||
|
||||
|
||||
class Deposit(Serializable):
|
||||
class DepositData(Serializable):
|
||||
fields = [
|
||||
('pubkey', bytes48),
|
||||
('withdrawal_credentials', bytes32),
|
||||
|
@ -10,7 +10,7 @@ from py_ecc.bls import G2ProofOfPossession as bls
|
||||
from eth2deposit.utils.ssz import (
|
||||
compute_domain,
|
||||
compute_signing_root,
|
||||
Deposit,
|
||||
DepositData,
|
||||
DepositMessage,
|
||||
)
|
||||
from eth2deposit.utils.constants import (
|
||||
@ -23,11 +23,11 @@ from eth2deposit.utils.constants import (
|
||||
def verify_deposit_data_json(filefolder: str) -> bool:
|
||||
with open(filefolder, 'r') as f:
|
||||
deposit_json = json.load(f)
|
||||
return all([verify_deposit(deposit) for deposit in deposit_json])
|
||||
return all([validate_deposit(deposit) for deposit in deposit_json])
|
||||
return False
|
||||
|
||||
|
||||
def verify_deposit(deposit_data_dict: Dict[str, Any]) -> bool:
|
||||
def validate_deposit(deposit_data_dict: Dict[str, Any]) -> bool:
|
||||
'''
|
||||
Checks whether a deposit is valid based on the eth2 rules.
|
||||
https://github.com/ethereum/eth2.0-specs/blob/dev/specs/phase0/beacon-chain.md#deposits
|
||||
@ -43,12 +43,17 @@ def verify_deposit(deposit_data_dict: Dict[str, Any]) -> bool:
|
||||
return False
|
||||
|
||||
# Verify deposit signature && pubkey
|
||||
deposit_message = DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
|
||||
unsigned_deposit = DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
|
||||
domain = compute_domain(domain_type=DOMAIN_DEPOSIT)
|
||||
signing_root = compute_signing_root(deposit_message, domain)
|
||||
signing_root = compute_signing_root(unsigned_deposit, domain)
|
||||
if not bls.Verify(pubkey, signing_root, signature):
|
||||
return False
|
||||
|
||||
# Verify Deposit Root
|
||||
deposit = Deposit(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature)
|
||||
return deposit.hash_tree_root == deposit_data_root
|
||||
signed_deposit = DepositData(
|
||||
pubkey=pubkey,
|
||||
withdrawal_credentials=withdrawal_credentials,
|
||||
amount=amount,
|
||||
signature=signature,
|
||||
)
|
||||
return signed_deposit.hash_tree_root == deposit_data_root
|
Loading…
Reference in New Issue
Block a user