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
|
validator_keys
|
||||||
|
|
||||||
# Python testing & linting:
|
# Python testing & linting:
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
venv/
|
venv/
|
||||||
.pytest_cache
|
*.pytest_cache
|
||||||
.hypothesis
|
*.hypothesis
|
||||||
.mypy_cache
|
*.mypy_cache
|
||||||
|
*.egg-info
|
||||||
|
*.egg
|
||||||
__pycache__
|
__pycache__
|
@ -9,17 +9,20 @@ from eth2deposit.key_handling.keystore import (
|
|||||||
Keystore,
|
Keystore,
|
||||||
ScryptKeystore,
|
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.crypto import SHA256
|
||||||
from eth2deposit.utils.ssz import (
|
from eth2deposit.utils.ssz import (
|
||||||
compute_domain,
|
compute_domain,
|
||||||
compute_signing_root,
|
compute_signing_root,
|
||||||
|
DepositData,
|
||||||
DepositMessage,
|
DepositMessage,
|
||||||
Deposit,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ValidatorCredentials:
|
class Credential:
|
||||||
def __init__(self, *, mnemonic: str, index: int, amount: int):
|
def __init__(self, *, mnemonic: str, index: int, amount: int):
|
||||||
# Set path as EIP-2334 format
|
# Set path as EIP-2334 format
|
||||||
# https://eips.ethereum.org/EIPS/eip-2334
|
# https://eips.ethereum.org/EIPS/eip-2334
|
||||||
@ -63,55 +66,51 @@ class ValidatorCredentials:
|
|||||||
secret_bytes = saved_keystore.decrypt(password)
|
secret_bytes = saved_keystore.decrypt(password)
|
||||||
return self.signing_sk == int.from_bytes(secret_bytes, 'big')
|
return self.signing_sk == int.from_bytes(secret_bytes, 'big')
|
||||||
|
|
||||||
|
def unsigned_deposit(self) -> DepositMessage:
|
||||||
def mnemonic_to_credentials(*, mnemonic: str, num_keys: int,
|
return DepositMessage(
|
||||||
amounts: List[int], start_index: int=0,) -> List[ValidatorCredentials]:
|
pubkey=self.signing_pk,
|
||||||
assert len(amounts) == num_keys
|
withdrawal_credentials=self.withdrawal_credentials,
|
||||||
key_indices = range(start_index, start_index + num_keys)
|
amount=self.amount,
|
||||||
credentials = [ValidatorCredentials(mnemonic=mnemonic, index=index, amount=amounts[index])
|
|
||||||
for index in key_indices]
|
|
||||||
return 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:
|
|
||||||
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)
|
|
||||||
datum_dict = signed_deposit_datum.as_dict()
|
|
||||||
datum_dict.update({'deposit_data_root': deposit_datum.hash_tree_root})
|
|
||||||
datum_dict.update({'signed_deposit_data_root': signed_deposit_datum.hash_tree_root})
|
|
||||||
deposit_data.append(datum_dict)
|
|
||||||
|
|
||||||
filefolder = os.path.join(folder, 'deposit_data-%i.json' % time.time())
|
def signed_deposit(self) -> DepositData:
|
||||||
with open(filefolder, 'w') as f:
|
domain = compute_domain(domain_type=DOMAIN_DEPOSIT)
|
||||||
json.dump(deposit_data, f, default=lambda x: x.hex())
|
signing_root = compute_signing_root(self.unsigned_deposit(), domain)
|
||||||
return filefolder
|
signed_deposit = DepositData(
|
||||||
|
**self.unsigned_deposit().as_dict(),
|
||||||
|
signature=bls.Sign(self.signing_sk, signing_root)
|
||||||
|
)
|
||||||
|
return signed_deposit
|
||||||
|
|
||||||
|
|
||||||
def verify_keystores(*, credentials: List[ValidatorCredentials],
|
class CredentialList:
|
||||||
keystore_filefolders: List[str], password: str) -> bool:
|
def __init__(self, credentials: List[Credential]):
|
||||||
return all(credential.verify_keystore(keystore_filefolder=filefolder, password=password)
|
self.credentials = credentials
|
||||||
for credential, filefolder in zip(credentials, keystore_filefolders))
|
|
||||||
|
@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)
|
||||||
|
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_deposit_data_json(self, folder: str) -> str:
|
||||||
|
deposit_data: List[Dict[bytes, bytes]] = []
|
||||||
|
for credential in self.credentials:
|
||||||
|
signed_deposit_datum = credential.signed_deposit()
|
||||||
|
datum_dict = signed_deposit_datum.as_dict()
|
||||||
|
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)
|
||||||
|
|
||||||
|
filefolder = os.path.join(folder, 'deposit_data-%i.json' % time.time())
|
||||||
|
with open(filefolder, 'w') as f:
|
||||||
|
json.dump(deposit_data, f, default=lambda x: x.hex())
|
||||||
|
return filefolder
|
||||||
|
|
||||||
|
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(self.credentials, keystore_filefolders))
|
||||||
|
@ -3,16 +3,13 @@ import sys
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from eth2deposit.credentials import (
|
from eth2deposit.credentials import (
|
||||||
mnemonic_to_credentials,
|
CredentialList,
|
||||||
export_keystores,
|
|
||||||
export_deposit_data_json,
|
|
||||||
verify_keystores,
|
|
||||||
)
|
)
|
||||||
from eth2deposit.key_handling.key_derivation.mnemonic import (
|
from eth2deposit.key_handling.key_derivation.mnemonic import (
|
||||||
get_languages,
|
get_languages,
|
||||||
get_mnemonic,
|
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 (
|
from eth2deposit.utils.constants import (
|
||||||
WORD_LISTS_PATH,
|
WORD_LISTS_PATH,
|
||||||
MAX_DEPOSIT_AMOUNT,
|
MAX_DEPOSIT_AMOUNT,
|
||||||
@ -78,13 +75,13 @@ def main(num_validators: int, mnemonic_language: str, folder: str, password: str
|
|||||||
click.clear()
|
click.clear()
|
||||||
click.echo(RHINO_0)
|
click.echo(RHINO_0)
|
||||||
click.echo('Creating your keys.')
|
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).')
|
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).')
|
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).')
|
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).')
|
click.echo('Verifying your deposit(s).')
|
||||||
assert verify_deposit_data_json(deposits_file)
|
assert verify_deposit_data_json(deposits_file)
|
||||||
click.echo('\nSuccess!\nYour keys can be found at: %s' % folder)
|
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
|
return domain_wrapped_object.hash_tree_root
|
||||||
|
|
||||||
|
|
||||||
# DepositMessage SSZ
|
|
||||||
|
|
||||||
class DepositMessage(Serializable):
|
class DepositMessage(Serializable):
|
||||||
fields = [
|
fields = [
|
||||||
('pubkey', bytes48),
|
('pubkey', bytes48),
|
||||||
@ -52,7 +50,7 @@ class DepositMessage(Serializable):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class Deposit(Serializable):
|
class DepositData(Serializable):
|
||||||
fields = [
|
fields = [
|
||||||
('pubkey', bytes48),
|
('pubkey', bytes48),
|
||||||
('withdrawal_credentials', bytes32),
|
('withdrawal_credentials', bytes32),
|
||||||
|
@ -10,7 +10,7 @@ from py_ecc.bls import G2ProofOfPossession as bls
|
|||||||
from eth2deposit.utils.ssz import (
|
from eth2deposit.utils.ssz import (
|
||||||
compute_domain,
|
compute_domain,
|
||||||
compute_signing_root,
|
compute_signing_root,
|
||||||
Deposit,
|
DepositData,
|
||||||
DepositMessage,
|
DepositMessage,
|
||||||
)
|
)
|
||||||
from eth2deposit.utils.constants import (
|
from eth2deposit.utils.constants import (
|
||||||
@ -23,11 +23,11 @@ from eth2deposit.utils.constants import (
|
|||||||
def verify_deposit_data_json(filefolder: str) -> bool:
|
def verify_deposit_data_json(filefolder: str) -> bool:
|
||||||
with open(filefolder, 'r') as f:
|
with open(filefolder, 'r') as f:
|
||||||
deposit_json = json.load(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
|
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.
|
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
|
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
|
return False
|
||||||
|
|
||||||
# Verify deposit signature && pubkey
|
# 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)
|
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):
|
if not bls.Verify(pubkey, signing_root, signature):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Verify Deposit Root
|
# Verify Deposit Root
|
||||||
deposit = Deposit(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount, signature=signature)
|
signed_deposit = DepositData(
|
||||||
return deposit.hash_tree_root == deposit_data_root
|
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