mirror of
https://gitlab.com/pulsechaincom/staking-deposit-cli.git
synced 2025-01-08 20:11:22 +00:00
Adds verification of deposits against eth2 rules.
This commit is contained in:
parent
f69198970e
commit
d57d08badd
@ -5,12 +5,16 @@ from key_handling.key_derivation.mnemonic import (
|
||||
get_languages,
|
||||
get_mnemonic,
|
||||
)
|
||||
from utils.eth2_deposit_check import verify_deposit_data_json
|
||||
from utils.credentials import (
|
||||
mnemonic_to_credentials,
|
||||
export_keystores,
|
||||
export_deposit_data_json,
|
||||
)
|
||||
from utils.constants import WORD_LISTS_PATH
|
||||
from utils.constants import (
|
||||
WORD_LISTS_PATH,
|
||||
MAX_DEPOSIT_AMOUNT,
|
||||
)
|
||||
|
||||
words_path = os.path.join(os.getcwd(), WORD_LISTS_PATH)
|
||||
languages = get_languages(words_path)
|
||||
@ -53,17 +57,19 @@ def generate_mnemonic(language: str, words_path: str) -> str:
|
||||
)
|
||||
def main(num_validators: int, mnemonic_language: str, password: str, folder: str):
|
||||
mnemonic = generate_mnemonic(mnemonic_language, words_path)
|
||||
amounts = [32 * 10 ** 9] * num_validators
|
||||
amounts = [MAX_DEPOSIT_AMOUNT] * num_validators
|
||||
folder = os.path.join(folder, 'validator_keys')
|
||||
if not os.path.exists(folder):
|
||||
os.mkdir(folder)
|
||||
click.clear()
|
||||
click.echo('Creating your keys.')
|
||||
credentials = mnemonic_to_credentials(mnemonic=mnemonic, num_keys=num_validators, amounts=amounts)
|
||||
click.echo('Saving your keystores.')
|
||||
click.echo('Saving your keystore(s).')
|
||||
export_keystores(credentials=credentials, password=password, folder=folder)
|
||||
click.echo('Creating your deposits.')
|
||||
export_deposit_data_json(credentials=credentials, folder=folder)
|
||||
click.echo('Creating your deposit(s).')
|
||||
deposits_file = export_deposit_data_json(credentials=credentials, folder=folder)
|
||||
click.echo('Verifying your deposit(s).')
|
||||
assert verify_deposit_data_json(deposits_file)
|
||||
click.echo('\nSuccess!\nYour keys can be found at: %s' % folder)
|
||||
click.pause('\n\nPress any key.')
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
DOMAIN_DEPOSIT = bytes.fromhex('03000000')
|
||||
GENESIS_FORK_VERSION = bytes.fromhex('00000000')
|
||||
|
||||
MIN_DEPOSIT_AMOUNT = 2 ** 0 * 10 ** 9
|
||||
MAX_DEPOSIT_AMOUNT = 2 ** 5 * 10 ** 9
|
||||
|
||||
WORD_LISTS_PATH = 'src/key_handling/key_derivation/word_lists/'
|
||||
|
@ -10,8 +10,8 @@ from utils.crypto import SHA256
|
||||
from utils.ssz import (
|
||||
compute_domain,
|
||||
compute_signing_root,
|
||||
DepositData,
|
||||
SignedDepositData,
|
||||
DepositMessage,
|
||||
Deposit,
|
||||
)
|
||||
|
||||
|
||||
@ -54,14 +54,14 @@ def export_keystores(*, credentials: List[ValidatorCredentials], password: str,
|
||||
credential.save_signing_keystore(password=password, folder=folder)
|
||||
|
||||
|
||||
def sign_deposit_data(deposit_data: DepositData, sk: int) -> SignedDepositData:
|
||||
def sign_deposit_data(deposit_data: DepositMessage, sk: int) -> Deposit:
|
||||
'''
|
||||
Given a DepositData, it signs its root and returns a SignedDepositData
|
||||
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 = SignedDepositData(
|
||||
signed_deposit_data = Deposit(
|
||||
**deposit_data.as_dict(),
|
||||
signature=bls.Sign(sk, signing_root)
|
||||
)
|
||||
@ -70,9 +70,8 @@ def sign_deposit_data(deposit_data: DepositData, sk: int) -> SignedDepositData:
|
||||
|
||||
def export_deposit_data_json(*, credentials: List[ValidatorCredentials], folder: str):
|
||||
deposit_data: List[dict] = []
|
||||
domain = compute_domain()
|
||||
for credential in credentials:
|
||||
deposit_datum = DepositData(
|
||||
deposit_datum = DepositMessage(
|
||||
pubkey=credential.signing_pk,
|
||||
withdrawal_credentials=SHA256(credential.withdrawal_pk),
|
||||
amount=credential.amount,
|
||||
@ -85,3 +84,4 @@ def export_deposit_data_json(*, credentials: List[ValidatorCredentials], folder:
|
||||
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
|
||||
|
48
src/utils/eth2_deposit_check.py
Normal file
48
src/utils/eth2_deposit_check.py
Normal file
@ -0,0 +1,48 @@
|
||||
import json
|
||||
from py_ecc.bls import G2ProofOfPossession as bls
|
||||
|
||||
from utils.ssz import (
|
||||
compute_domain,
|
||||
compute_signing_root,
|
||||
Deposit,
|
||||
DepositMessage,
|
||||
)
|
||||
from utils.constants import (
|
||||
DOMAIN_DEPOSIT,
|
||||
MAX_DEPOSIT_AMOUNT,
|
||||
MIN_DEPOSIT_AMOUNT,
|
||||
)
|
||||
|
||||
|
||||
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 False
|
||||
|
||||
|
||||
def verify_deposit(deposit_data_dict: dict) -> 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
|
||||
'''
|
||||
pubkey = bytes.fromhex(deposit_data_dict['pubkey'])
|
||||
withdrawal_credentials = bytes.fromhex(deposit_data_dict['withdrawal_credentials'])
|
||||
amount = deposit_data_dict['amount']
|
||||
signature = bytes.fromhex(deposit_data_dict['signature'])
|
||||
deposit_data_root = bytes.fromhex(deposit_data_dict['deposit_data_root'])
|
||||
|
||||
# Verify deposit amount
|
||||
if not MIN_DEPOSIT_AMOUNT < amount <= MAX_DEPOSIT_AMOUNT:
|
||||
return False
|
||||
|
||||
# Verify deposit signature && pubkey
|
||||
deposit_message = DepositMessage(pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, amount=amount)
|
||||
domain = compute_domain(domain_type=DOMAIN_DEPOSIT)
|
||||
signing_root = compute_signing_root(deposit_message, 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
|
@ -14,6 +14,7 @@ from utils.constants import (
|
||||
|
||||
bytes8 = ByteVector(8)
|
||||
|
||||
|
||||
# Crypto Domain SSZ
|
||||
|
||||
class SigningRoot(Serializable):
|
||||
@ -41,9 +42,9 @@ def compute_signing_root(ssz_object: Serializable, domain: bytes) -> bytes:
|
||||
return domain_wrapped_object.hash_tree_root
|
||||
|
||||
|
||||
# DepositData SSZ
|
||||
# DepositMessage SSZ
|
||||
|
||||
class DepositData(Serializable):
|
||||
class DepositMessage(Serializable):
|
||||
fields = [
|
||||
('pubkey', bytes48),
|
||||
('withdrawal_credentials', bytes32),
|
||||
@ -51,7 +52,7 @@ class DepositData(Serializable):
|
||||
]
|
||||
|
||||
|
||||
class SignedDepositData(Serializable):
|
||||
class Deposit(Serializable):
|
||||
fields = [
|
||||
('pubkey', bytes48),
|
||||
('withdrawal_credentials', bytes32),
|
||||
|
Loading…
Reference in New Issue
Block a user